Tensorflow③ Keras的LSTM和TF的LSTM實(shí)現(xiàn)的源碼剖析

學(xué)號(hào):20021210668

姓名:梅詩(shī)國(guó)

原文鏈接:https://samuel92.blog.csdn.net/article/details/85089453

【嵌牛導(dǎo)讀】Tensorflow③ Keras的LSTM和TF的LSTM實(shí)現(xiàn)的源碼剖析

【嵌牛鼻子】LSTM源碼剖析

【嵌牛正文】

0. 常見(jiàn)的LSTM層選擇

經(jīng)過(guò)初步調(diào)查煮嫌,常用的LSTM層有Keras.layers.LSTM?和?Tensorflow.contrib.nn.LSTMCell?及?Tensorflow.nn.rnn_cell.LSTMCell?挑辆,其中后面兩個(gè)的實(shí)現(xiàn)邏輯是一樣的。這里缀旁,Keras.layers.LSTM的計(jì)算源碼文件為keras/layers/recurrent.py中的LSTMCell類(lèi)。Tensorflow.contrib.nn.LSTMCell和Tensorflow.nn.rnn_cell.LSTMCell的計(jì)算源碼文件為tensorflow/python/ops/rnn_cell_impl.py中的LSTMCell類(lèi)嵌戈。

1. Keras的LSTM計(jì)算邏輯梳理

從代碼的清晰程度和模型實(shí)現(xiàn)的方便情況來(lái)說(shuō)耐亏,Keras確實(shí)很方便,為了搞清楚實(shí)現(xiàn)邏輯芦拿,我搭了一個(gè)根據(jù)ABC—>D, BCD—>E, …, WXY—>Z的根據(jù)前三個(gè)字母預(yù)測(cè)下一個(gè)字母的模型砾肺。我將每個(gè)字母用一個(gè)數(shù)字表示,A = 0防嗡, B = 1,…蚁趁,Z = 25裙盾,時(shí)間步為3,每個(gè)時(shí)間步對(duì)應(yīng)的輸入維度為1(因?yàn)閷⒚總€(gè)字母都編成長(zhǎng)度為1的數(shù)字/數(shù)組):

# coding: UTF-8

"""

? ? @author: samuel ko

? ? @date: 2018/12/12

? ? @link: https://blog.csdn.net/zwqjoy/article/details/80493341

"""

import numpy

from keras.models import Sequential

from keras.utils import np_utils

numpy.random.seed(5)

# 定義數(shù)據(jù)集

alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

print(len(alphabet))

# create mapping of characters to integers (0-25) and the reverse

char_to_int = dict((c, i) for i, c in enumerate(alphabet))

int_to_char = dict((i, c) for i, c in enumerate(alphabet))

# 預(yù)備數(shù)據(jù)集

seq_length = 3

dataX = []

dataY = []

for i in range(0, len(alphabet) - seq_length, 1):

? ? seq_in = alphabet[i:i + seq_length]

? ? seq_out = alphabet[i + seq_length]

? ? dataX.append([char_to_int[char] for char in seq_in])

? ? dataY.append(char_to_int[seq_out])

? ? print(seq_in, '->', seq_out)

# 喂入網(wǎng)絡(luò)的特征為 [batch_size, time_step, input_dim] 3D的Tensor

# 用易懂的語(yǔ)言就是: time_step為時(shí)間步的個(gè)數(shù), input_dim為每個(gè)時(shí)間步喂入的數(shù)據(jù)

X = numpy.reshape(dataX, (len(dataX), seq_length, 1))

X = X / float(len(alphabet))

# 對(duì)標(biāo)簽進(jìn)行one-hot處理

y = np_utils.to_categorical(dataY)

由上面代碼可以看出他嫡,X是輸入數(shù)據(jù)番官,y是標(biāo)簽,那么搭建模型進(jìn)行訓(xùn)練(簡(jiǎn)單起見(jiàn)钢属,一層LSTM加一個(gè)全連接層徘熔,Tensorflow里面也是采用這樣的結(jié)構(gòu)):

model = Sequential()

# input_shape = (time_step, 每個(gè)時(shí)間步的input_dim)

# LSTM的第一個(gè)參數(shù)5表示LSTM的單元數(shù)為5,我們可以把LSTM理解為一個(gè)特殊的且?guī)в袝r(shí)序信息的全連接層淆党。

# Dense的第一個(gè)參數(shù)為y.shape[1] = 26酷师,也就是label個(gè)數(shù),顯而易見(jiàn)染乌,有26個(gè)字母可能被預(yù)測(cè)出來(lái)山孔,即26分類(lèi)任務(wù)。

model.add(LSTM(5, input_shape=(X.shape[1], X.shape[2])))

model.add(Dense(y.shape[1], activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

model.fit(X, y, nb_epoch=100, batch_size=1, verbose=2)

model.save("simplelstm.h5")

整體代碼為:

# coding: UTF-8

"""

? ? @author: samuel ko

? ? @date: 2018/12/12

? ? @link: https://blog.csdn.net/zwqjoy/article/details/80493341

"""

import numpy

from keras.models import Sequential

from keras.layers import Dense

from keras.layers import LSTM, SimpleRNN

from keras.utils import np_utils

# fix random seed for reproducibility

numpy.random.seed(5)

# define the raw dataset

alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

print(len(alphabet))

# create mapping of characters to integers (0-25) and the reverse

char_to_int = dict((c, i) for i, c in enumerate(alphabet))

int_to_char = dict((i, c) for i, c in enumerate(alphabet))

# prepare the dataset of input to output pairs encoded as integers

seq_length = 3

dataX = []

dataY = []

for i in range(0, len(alphabet) - seq_length, 1):

? ? seq_in = alphabet[i:i + seq_length]

? ? seq_out = alphabet[i + seq_length]

? ? dataX.append([char_to_int[char] for char in seq_in])

? ? dataY.append(char_to_int[seq_out])

? ? print(seq_in, '->', seq_out)

# 我們運(yùn)行上面的代碼荷憋,來(lái)觀察現(xiàn)在我們的input和output數(shù)據(jù)集是這樣一種情況

# A -> B

# B -> C

# ...

# Y -> Z

# 喂入網(wǎng)絡(luò)的特征為 [batch_size, time_step, input_dim] 3D的Tensor

# 用易懂的語(yǔ)言就是: time_step為時(shí)間步的個(gè)數(shù), input_dim為每個(gè)時(shí)間步喂入的數(shù)據(jù)

X = numpy.reshape(dataX, (len(dataX), seq_length, 1))

# print(X)

# [[[ 0]]

#? [[ 1]]

#? [[ 2]]

#? [[ 3]]

#? ...

#? [[24]]]

# normalize 最后接一個(gè)分類(lèi)的任務(wù)

X = X / float(len(alphabet))

print(X.shape)

# (25, 3, 1)

# one hot編碼輸出label

y = np_utils.to_categorical(dataY)

print(y.shape)

# 創(chuàng)建&訓(xùn)練&保存模型

model = Sequential()

# input_shape = (time_step, 每個(gè)時(shí)間步的input_dim)

model.add(LSTM(5, input_shape=(X.shape[1], X.shape[2])))

model.add(Dense(y.shape[1], activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

model.fit(X, y, nb_epoch=100, batch_size=1, verbose=2)

model.save("simplelstm.h5")

代碼跑完之后台颠,得到simplelstm.h5模型,下面我從Netron[1]里面勒庄,可以拆分得到權(quán)重串前。這里面涉及到LSTM的一點(diǎn)知識(shí),我們知道实蔽,LSTM有4個(gè)branch荡碾,對(duì)應(yīng)有4個(gè)權(quán)重,按Keras的說(shuō)法盐须,分別為i: input輸入門(mén), c: new_input: 新輸出玩荠,f: forget遺忘門(mén)漆腌,o: output輸出門(mén)贼邓,具體情況請(qǐng)參考[2]:

① forget門(mén)對(duì)應(yīng)位置

② new_input門(mén)(C ~ t \tilde{C}_tC~t?)和input輸入門(mén)

③ 更新cell狀態(tài)得到下一時(shí)間步的輸出C t C_tCt?

④ 計(jì)算輸出門(mén)output, 根據(jù)o t o_tot?和c t c_tct?得到這一時(shí)間步的輸出h t h_tht?

可能大家會(huì)問(wèn)了闷尿,4個(gè)權(quán)重比較容易理解塑径,但是為什么看simplelstm.h5的可視化結(jié)構(gòu)時(shí)候,會(huì)有kernel和recurrent_kernel兩個(gè)東西呢填具?

以我們的3個(gè)時(shí)間步的結(jié)構(gòu)為例统舀,如下匆骗,每個(gè)時(shí)間步的輸入都有兩個(gè),一個(gè)是x t x_txt?對(duì)應(yīng)數(shù)據(jù)X每個(gè)時(shí)間步輸入的維度誉简,對(duì)我們的例子是1x1的數(shù)據(jù)碉就;而h t h_tht?則對(duì)應(yīng)了同層間不同時(shí)間步傳遞的memory state/hidden state

這個(gè)跟我們之前設(shè)置的LSTM(5, input_shape=(X.shape[1], X.shape[2]))的5直接相關(guān)闷串。對(duì)于4個(gè)不同的權(quán)重瓮钥,它的維度都是5(LSTM層的units設(shè)置) x 5(LSTM層的units設(shè)置)的。

而對(duì)于x t x_txt?對(duì)應(yīng)的權(quán)重烹吵,它們的維度都是1(輸入維度) x 5(LSTM層的units設(shè)置)

碉熄。

下面繼續(xù)返回看Netron里面的kernel,recurrent_kernel以及bias的內(nèi)容肋拔,我們發(fā)現(xiàn)其形狀分別為1 x 20,?5 x 20,?1 x 20:

那么聰明的你應(yīng)該可以想到锈津,Keras是將i, j, c, o對(duì)應(yīng)的4個(gè)1 x 5的kernel和bias以及4個(gè)5 x 5的recurrent kernel合在一起了,那么看源碼進(jìn)行對(duì)應(yīng)的拆解就行了凉蜂。

class LSTMCell(Layer):

...

? ? def build(self, input_shape):

? ? ? ? input_dim = input_shape[-1]

? ? ? ? # self.kernel處理傳入本層的輸入

? ? ? ? self.kernel = self.add_weight(shape=(input_dim, self.units * 4),

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? name='kernel',

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? initializer=self.kernel_initializer,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? regularizer=self.kernel_regularizer,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? constraint=self.kernel_constraint)

? ? ? ? # self.recurrent_kernel處理本層不同時(shí)間步的輸入

? ? ? ? self.recurrent_kernel = self.add_weight(

? ? ? ? ? ? shape=(self.units, self.units * 4),

? ? ? ? ? ? name='recurrent_kernel',

? ? ? ? ? ? initializer=self.recurrent_initializer,

? ? ? ? ? ? regularizer=self.recurrent_regularizer,

? ? ? ? ? ? constraint=self.recurrent_constraint)

? ? ? ? if self.use_bias:

? ? ? ? ? ? if self.unit_forget_bias:

? ? ? ? ? ? ? ? def bias_initializer(_, *args, **kwargs):

? ? ? ? ? ? ? ? ? ? return K.concatenate([

? ? ? ? ? ? ? ? ? ? ? ? self.bias_initializer((self.units,), *args, **kwargs),

? ? ? ? ? ? ? ? ? ? ? ? initializers.Ones()((self.units,), *args, **kwargs),

? ? ? ? ? ? ? ? ? ? ? ? self.bias_initializer((self.units * 2,), *args, **kwargs),

? ? ? ? ? ? ? ? ? ? ])

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? bias_initializer = self.bias_initializer

? ? ? ? ? ? self.bias = self.add_weight(shape=(self.units * 4,),

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? name='bias',

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? initializer=bias_initializer,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? regularizer=self.bias_regularizer,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? constraint=self.bias_constraint)

? ? ? ? else:

? ? ? ? ? ? self.bias = None

# 解析順序

? ? ? ? self.kernel_i = self.kernel[:, :self.units]

? ? ? ? self.kernel_f = self.kernel[:, self.units: self.units * 2]

? ? ? ? self.kernel_c = self.kernel[:, self.units * 2: self.units * 3]

? ? ? ? self.kernel_o = self.kernel[:, self.units * 3:]

? ? ? ? self.recurrent_kernel_i = self.recurrent_kernel[:, :self.units]

? ? ? ? self.recurrent_kernel_f = (

? ? ? ? ? ? self.recurrent_kernel[:, self.units: self.units * 2])

? ? ? ? self.recurrent_kernel_c = (

? ? ? ? ? ? self.recurrent_kernel[:, self.units * 2: self.units * 3])

? ? ? ? self.recurrent_kernel_o = self.recurrent_kernel[:, self.units * 3:]

? ? ? ? if self.use_bias:

? ? ? ? ? ? self.bias_i = self.bias[:self.units]

? ? ? ? ? ? self.bias_f = self.bias[self.units: self.units * 2]

? ? ? ? ? ? self.bias_c = self.bias[self.units * 2: self.units * 3]

? ? ? ? ? ? self.bias_o = self.bias[self.units * 3:]

? ? ? ? ...

可以看出琼梆,1 x 20?的kernel和bias以及?5 x 20?的recurrent kernel對(duì)應(yīng)的解析順序?yàn)?b>i, f, c, o,以kernel為例窿吩,我們對(duì)kernel的權(quán)重解析順序如下:

下面叮叹,我將把權(quán)重和bias都解析出來(lái),并按照源碼中定好的計(jì)算邏輯爆存,基于numpy科學(xué)計(jì)算庫(kù)蛉顽,實(shí)現(xiàn)一版。并驗(yàn)證其結(jié)果和Keras原生的效果:

① 首先先较,我們先做一個(gè)shape為(1, 3, 1)的輸入携冤,輸入網(wǎng)絡(luò),將LSTM層的輸出打印出來(lái):

"""

? ? @author: samuel ko

? ? @date:? 2018/12/17

? ? @target: 研究模型的中間輸出結(jié)果

? ? @ref: 作者:揮揮灑灑

? ? ? ? ? 來(lái)源:CSDN

? ? ? ? ? 原文:https://blog.csdn.net/u010420283/article/details/80303231

"""

from keras.models import load_model

from keras import backend as K

import numpy as np

model = load_model("simplelstm.h5")

layer_1 = K.function([model.layers[0].input], [model.layers[0].output])#第一個(gè) model.layers[0],不修改,表示輸入數(shù)據(jù)闲勺;第二個(gè)model.layers[you wanted],修改為你需要輸出的層數(shù)的編號(hào)

layer_11 = K.function([model.layers[0].input], [model.layers[1].input])#第一個(gè) model.layers[0],不修改,表示輸入數(shù)據(jù)曾棕;第二個(gè)model.layers[you wanted],修改為你需要輸出的層數(shù)的編號(hào)

# 定義shape為(1, 3, 1)的輸入,輸入網(wǎng)絡(luò)

inputs = np.array([[0], [0.03846154], [0.07692308]])

inputs = np.expand_dims(inputs, 0)

print(layer_1([inputs])[0]); print(layer_1([inputs])[0].shape)

print(layer_11([inputs])[0]); print(layer_11([inputs])[0].shape)

輸出為(可以看到菜循,LSTM層輸出的結(jié)果跟Dense層的輸入是一樣的~):

[[-0.6918077-0.5736012-0.6106971-0.23724467-0.28232932]](1,5)[[-0.6918077-0.5736012-0.6106971-0.23724467-0.28232932]](1,5)


② 接著翘地,我們根據(jù)Netron的網(wǎng)絡(luò)圖結(jié)果,拆解權(quán)重癌幕,并把Keras.layers.LSTM的計(jì)算邏輯用numpy重新實(shí)現(xiàn):

"""

? ? @author: samuel ko

? ? @date:? 2018/12/17

? ? @target: 研究模型的中間輸出結(jié)果

? ? @ref: 作者:揮揮灑灑

? ? ? ? ? 來(lái)源:CSDN

? ? ? ? ? 原文:https://blog.csdn.net/u010420283/article/details/80303231

"""

from keras.models import load_model

from keras import backend as K

import numpy as np

h_tm_i, h_tm_o, h_tm_c, h_tm_f, c_tm = None, None, None, None, None

def hard_sigmoid(x):

? ? x = 0.2 * x + 0.5

? ? x[x < -2.5] = 0

? ? x[x > 2.5] = 1

? ? return x

def lstm_keras_verify(inputs):

? ? global h_tm_c, h_tm_f, h_tm_i, h_tm_o, c_tm

? ? # kernel初始化

? ? kernel_i = np.array([0.4309869408607483, 1.184934139251709, 1.1755656003952026, 0.29152509570121765, 0.9355264902114868])

? ? kernel_f = np.array([0.4721968472003937, 0.8939654231071472, 0.3940809667110443, 0.32647714018821716, 0.3925175964832306])

? ? kernel_c = np.array([0.43232300877571106, 0.9761391282081604, 0.4974423944950104, -0.5713692307472229, 0.6272905468940735])

? ? kernel_o = np.array([0.4851478338241577, 0.4159347116947174, 0.8334378600120544, 0.6494604349136353, 1.4963207244873047])

? ? recurrent_kernel_i = np.array([[-0.15266947448253632, -0.4967867434024811, -0.2602699398994446, -0.3376578092575073, 0.18315182626247406],

? ? ? ? ? ? ? ? ? ? ? ? ? [0.40668627619743347, 0.11702277511358261, 0.2870166599750519, -0.09417486935853958, 1.2248116731643677],

? ? ? ? ? ? ? ? ? ? ? ? ? [0.13948452472686768, -0.2935984432697296, -0.18430666625499725, 0.04545489326119423, 0.8304147720336914],

? ? ? ? ? ? ? ? ? ? ? ? ? [-0.9957871437072754, -1.2020113468170166, -1.1591960191726685, -0.2052622139453888, -1.3381662368774414],

? ? ? ? ? ? ? ? ? ? ? ? ? [1.1894947290420532, 0.675262451171875, 0.6069576144218445, 0.5705539584159851, 0.9218697547912598]])

? ? recurrent_kernel_f = np.array([[-0.548134982585907, -0.12552201747894287, -0.41158366203308105, 0.09746172279119492, 0.19226618111133575],

? ? ? ? ? ? ? ? ? ? ? ? ? [0.10524879395961761, 0.032132066786289215, 0.0605274997651577, 0.07235733419656754, 0.7413577437400818],

? ? ? ? ? ? ? ? ? ? ? ? ? [-0.17540045082569122, -0.40539026260375977, -0.18782351911067963, 0.20610281825065613, 0.8710744380950928],

? ? ? ? ? ? ? ? ? ? ? ? ? [-0.7760279178619385, -0.9006417393684387, -0.7003670334815979, -0.22393617033958435, -0.5202550888061523],

? ? ? ? ? ? ? ? ? ? ? ? ? [0.7772086262702942, 0.7663999199867249, 0.5117960572242737, 0.13461880385875702, 0.7836397290229797]])

? ? recurrent_kernel_c = np.array([[1.580788493156433, 1.0911318063735962, 0.6749269366264343, 0.30827417969703674, 0.7559695839881897],

? ? ? ? ? ? ? ? ? ? ? ? ? [0.7300652265548706, 0.9139286875724792, 1.1172183752059937, 0.043491244316101074, 0.8009109497070312],

? ? ? ? ? ? ? ? ? ? ? ? ? [1.49398934841156, 0.5944592356681824, 0.8874677419662476, -0.1583320051431656, 1.3592860698699951],

? ? ? ? ? ? ? ? ? ? ? ? ? [0.032015360891819, -0.5035645365715027, -0.3792402148246765, 0.42566269636154175, -0.6349631547927856],

? ? ? ? ? ? ? ? ? ? ? ? ? [0.12018230557441711, 0.33967509865760803, 0.5114297270774841, -0.062018051743507385, 0.5401539206504822]])

? ? recurrent_kernel_o = np.array([[-0.41055813431739807, -0.017661772668361664, 0.06882145255804062, 0.09856614470481873, 0.44098445773124695],

? ? ? ? ? ? ? ? ? ? ? ? ? [0.5692929625511169, 0.5409368872642517, 0.3319447338581085, 0.4997922480106354, 0.9462743401527405],

? ? ? ? ? ? ? ? ? ? ? ? ? [0.1794481724500656, 0.10621143877506256, -0.0016202644910663366, -0.010369917377829552, 0.4268817901611328],

? ? ? ? ? ? ? ? ? ? ? ? ? [-1.026210904121399, -0.6898611783981323, -0.9652346968650818, -0.07141508907079697, -0.6710768938064575],

? ? ? ? ? ? ? ? ? ? ? ? ? [0.5829002261161804, 0.6890853047370911, 0.5738061666488647, -0.16630153357982635, 1.2376824617385864]])

? ? bias_i = np.array([1.1197513341903687, 1.0861579179763794, 1.0329890251159668, 0.3536357581615448, 0.9598652124404907])

? ? bias_f = np.array([2.020589828491211, 1.940927267074585, 1.9546188116073608, 1.1743367910385132, 1.7189750671386719])

? ? bias_c = np.array([-0.41391095519065857, -0.21292796730995178, -0.30117690563201904, -0.24005982279777527, 0.053657304495573044])

? ? bias_o = np.array([1.222458004951477, 1.1024200916290283, 1.0836670398712158, 0.3483290672302246, 0.9281882643699646])

? ? # step 1 計(jì)算W * x

? ? x_i = inputs * kernel_i

? ? x_f = inputs * kernel_f

? ? x_c = inputs * kernel_c

? ? x_o = inputs * kernel_o

? ? # step 2 加上bias

? ? x_i += bias_i

? ? x_f += bias_f

? ? x_c += bias_c

? ? x_o += bias_o

? ? # step 3 計(jì)算

? ? if not isinstance(h_tm_i, np.ndarray):

? ? ? ? h_tm_i = np.zeros((1, 5))

? ? ? ? h_tm_o = np.zeros((1, 5))

? ? ? ? h_tm_f = np.zeros((1, 5))

? ? ? ? h_tm_c = np.zeros((1, 5))

? ? ? ? c_tm = np.zeros((1, 5))

? ? i = hard_sigmoid(x_i + np.dot(h_tm_i, recurrent_kernel_i))

? ? f = hard_sigmoid(x_f + np.dot(h_tm_f, recurrent_kernel_f))

? ? c = f * c_tm + i * np.tanh(x_c + np.dot(h_tm_c, recurrent_kernel_c))

? ? o = hard_sigmoid(x_o + np.dot(h_tm_o, recurrent_kernel_o))

? ? h = o * np.tanh(c)

? ? h_tm_c = h_tm_f = h_tm_o = h_tm_i = h

? ? c_tm = c

? ? print("當(dāng)前的hidden state", h)

? ? print("當(dāng)前的cell state", c)

? ? return h, c

得到結(jié)果:

[[-0.6918077 -0.5736012 -0.6106971 -0.23724467 -0.28232932]]

(1, 5)

[[-0.6918077? -0.5736012? -0.6106971? -0.23724467 -0.28232932]]

(1, 5)

輸入內(nèi)容: [[0.]]

當(dāng)前的hidden state [[-0.20567793 -0.10758754 -0.14600677 -0.07612558? 0.02542126]]

當(dāng)前的cell state [[-0.2836353? -0.15045176 -0.20660162 -0.13443607? 0.03709382]]

輸入內(nèi)容: [[0.03846154]]

當(dāng)前的hidden state [[-0.52542272 -0.34593632 -0.39644344 -0.1596688? -0.1078329 ]]

當(dāng)前的cell state [[-0.83987432 -0.52042347 -0.6076283? -0.29302937 -0.16417923]]

輸入內(nèi)容: [[0.07692308]]

當(dāng)前的hidden state [[-0.69180776 -0.57360109 -0.61069705 -0.23724468 -0.28232936]]

當(dāng)前的cell state [[-1.51751077 -1.19211365 -1.25843129 -0.46999835 -0.55761341]]

可以看到衙耕,Keras的LSTM層輸出的結(jié)果跟LSTM層最后一個(gè)時(shí)間步輸出的memory state/hidden state一致。(有一點(diǎn)精度損失勺远,可能是Cuda導(dǎo)致的…

# Keras結(jié)果[[-0.6918077-0.5736012-0.6106971-0.23724467-0.28232932]]

# Numpy自己實(shí)現(xiàn)結(jié)果[[-0.69180776-0.57360109-0.61069705-0.23724468-0.28232936]]

2. Tensorflow的LSTM計(jì)算邏輯梳理

正如在文章開(kāi)頭提到的橙喘,Tensorflow.contrib.nn.LSTMCell和Tensorflow.nn.rnn_cell.LSTMCell的計(jì)算源碼文件為tensorflow/python/ops/rnn_cell_impl.py中的LSTMCell類(lèi),是一樣的胶逢。所以我這里使用的是tf.contrib.rnn.LSTMCell厅瞎,輸入數(shù)據(jù)X和標(biāo)簽y跟Keras采用的一樣(直接拿過(guò)來(lái)用就行饰潜,這里就不貼了),模型定義也很相似和簸,遵循TF的特定范式:

"""

? ? @author: samuel ko

? ? @date: 2018/12/18

? ? @target: 訓(xùn)練一個(gè)只帶一層LSTM的TF模型

? ? @ref: 作者:謝小小XH

? ? ? ? ? 來(lái)源:CSDN

? ? ? ? ? 原文:https://blog.csdn.net/xierhacker/article/details/78772560

"""

inputs = tf.placeholder(shape=(None, 3, 1), dtype=tf.float32, name='Inputs')

labels = tf.placeholder(shape=(None, 26), dtype=tf.float32, name="Labels")

lstm_cell = tf.contrib.rnn.LSTMCell(num_units=5)

# initialize to zero

init_state = lstm_cell.zero_state(batch_size=1, dtype=tf.float32)

output, state = tf.nn.dynamic_rnn(

? ? cell=lstm_cell,

? ? inputs=inputs,

? ? dtype=tf.float32,

? ? initial_state=init_state,

)

print("output.shape:", output.shape)

print("len of state tuple", len(state))

print("state.h.shape:", state.h.shape)

print("state.c.shape:", state.c.shape)

# output = tf.layers.dense(output, 26)

output = tf.layers.dense(state.h, 26, name="Outputs")

loss = tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=output)

optimizer = tf.train.AdamOptimizer(0.001).minimize(loss=loss)

init = tf.global_variables_initializer()

saver = tf.train.Saver(max_to_keep=5)

#-------------------------------------------Define Session---------------------------------------#

with tf.Session() as sess:

? ? sess.run(init)

? ? for epoch in range(1, 100+1):

? ? ? ? train_losses = []

? ? ? ? print("epoch:", epoch)

? ? ? ? for j in range(23):

? ? ? ? ? ? _, train_loss = sess.run(

? ? ? ? ? ? ? ? ? ? fetches=(optimizer, loss),

? ? ? ? ? ? ? ? ? ? feed_dict={

? ? ? ? ? ? ? ? ? ? ? ? ? ? inputs: X[j: j+1],

? ? ? ? ? ? ? ? ? ? ? ? ? ? labels: y[j: j+1]

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? )

? ? ? ? ? ? train_losses.append(train_loss)

? ? ? ? print("average training loss:", sum(train_losses) / len(train_losses))

? ? saver.save(sess, "model/simple_lstm")

訓(xùn)練完成后彭雾,得到

形式。

跟Keras的LSTM拆解類(lèi)似锁保,我們首先根據(jù)源碼分析不同的kernel冠跷,bias,recurrent_kernel的存放位置身诺,然后再去拆解并用Numpy重新實(shí)現(xiàn)計(jì)算邏輯蜜托,代碼如下:

# coding: UTF-8

"""

? ? @author: samuel ko

? ? @date:? 2018/12/18

? ? @target: 研究TF模型的中間輸出結(jié)果

"""

import sys

import os

import numpy as np

import tensorflow as tf

h_tm_i, h_tm_o, h_tm_c, h_tm_f, c_tm = None, None, None, None, None

def sigmoid(x):

? ? return 1.0 / (1.0 + np.exp(-x))

def lstm_tf_verify(inputs):

? ? """

? ? ? ? 2018/12/18

? ? ? ? TF原生的解析順序?yàn)閕, j, f, o (j就是keras中的c)

? ? :param inputs:

? ? :return:

? ? """

? ? global h_tm_c, h_tm_f, h_tm_i, h_tm_o, c_tm

? ? bias_i = ...

? ? bias_j = ...

? ? bias_f = ...

? ? bias_o = ...

? ? kernel_i = ...

? ? kernel_j = ...

? ? kernel_f = ...

? ? kernel_o = ...

? ? recurrent_i = ...

? ? recurrent_j = ...

? ? recurrent_f = ...

? ? recurrent_o = ...

? ? # step 1 計(jì)算W * x

? ? x_i = inputs * kernel_i

? ? x_f = inputs * kernel_f

? ? x_j = inputs * kernel_j

? ? x_o = inputs * kernel_o

? ? # step 2 加上bias

? ? x_i += bias_i

? ? x_f += bias_f

? ? x_j += bias_j

? ? x_o += bias_o

? ? # step 3 計(jì)算

? ? if not isinstance(h_tm_i, np.ndarray):

? ? ? ? h_tm_i = np.zeros((1, 5))

? ? ? ? h_tm_o = np.zeros((1, 5))

? ? ? ? h_tm_f = np.zeros((1, 5))

? ? ? ? h_tm_c = np.zeros((1, 5))

? ? ? ? c_tm = np.zeros((1, 5))

? ? i = sigmoid(x_i + np.dot(h_tm_i, recurrent_i))

? ? # Tensorflow默認(rèn)有一個(gè)forget_bias, 默認(rèn)設(shè)置為1.0

? ? f = sigmoid(x_f + np.dot(h_tm_f, recurrent_f) + 1.0)

? ? c = f * c_tm + i * np.tanh(x_j + np.dot(h_tm_c, recurrent_j))

? ? o = sigmoid(x_o + np.dot(h_tm_o, recurrent_o))

? ? h = o * np.tanh(c)

? ? h_tm_c = h_tm_f = h_tm_o = h_tm_i = h

? ? c_tm = c

? ? print("當(dāng)前的hidden state", h)

? ? print("當(dāng)前的cell state", c)

? ? return h, c

跟Tensorflow的模型的LSTM層輸出結(jié)果進(jìn)行比較,根據(jù)定義

output, state = tf.nn.dynamic_rnn(

? ? cell=lstm_cell,

? ? inputs=inputs,

? ? dtype=tf.float32,

? ? initial_state=init_state,

)

輸出有output和state兩個(gè)霉赡,其中output是每個(gè)時(shí)間步輸出的h t h_tht?的匯總橄务,state有兩個(gè)內(nèi)容:state.hstate.c,前者是本層最后一個(gè)時(shí)間步輸出的hidden state/memory state,后者是本層最后一個(gè)時(shí)間步輸出的cell state(細(xì)胞狀態(tài))穴亏。

整體代碼如下:

# coding: UTF-8

"""

? ? @author: samuel ko

? ? @date:? 2018/12/18

? ? @target: 研究TF模型的中間輸出結(jié)果

"""

import sys

import os

import numpy as np

import tensorflow as tf

path_file = __file__

dir_name = os.path.dirname(path_file)

# 1. 準(zhǔn)備輸入

inputs = np.array([[0], [0.03846154], [0.07692308]])

inputs = np.expand_dims(inputs, 0)

labels = np.array([[0, 1, 0, 0, 0, 0, 0, 0, 0, 0,

? ? ? ? ? ? ? ? ? ? 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

? ? ? ? ? ? ? ? ? ? 0, 0, 0, 0, 0, 0]])

# 2. 加載模型, 輸出中間結(jié)果和最后結(jié)果

with tf.Session() as sess:

? ? graph = tf.get_default_graph()

? ? new_saver = tf.train.import_meta_graph(os.path.join(dir_name, 'model/simple_lstm.meta'))

? ? # 注: tf.train_get_checkpoint_state不允許接收中文, tf.train.latest_checkpoint就沒(méi)問(wèn)題...

? ? # new_saver.restore(sess, tf.train.get_checkpoint_state(os.path.join(dir_name, "model/")))

? ? new_saver.restore(sess, tf.train.latest_checkpoint(os.path.join(dir_name, "model/")))

? ? input_x = graph.get_tensor_by_name("Inputs:0")

? ? label_x = graph.get_tensor_by_name("Labels:0")

? ? # out 是輸入到下一層的匯總 3 x 1 x 5

? ? out = graph.get_tensor_by_name('rnn/TensorArrayStack/TensorArrayGatherV3:0')

? ? # state_h 是LSTM層最后一個(gè)時(shí)間步的結(jié)果 1 x 5

? ? state_h = graph.get_tensor_by_name('rnn/while/Exit_4:0') # 最后一個(gè)時(shí)間步的memory state 和state_h = graph.get_tensor_by_name('rnn/while/Switch_4:0') 一樣蜂挪!

? ? # state_h = graph.get_tensor_by_name('rnn/while/Exit_3:0') # 最后一個(gè)時(shí)間步的cell state

? ? print(sess.run(out, feed_dict={input_x: inputs,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? label_x: labels,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }))

? ? print(sess.run(state_h, feed_dict={input_x: inputs,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? label_x: labels,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }))

h_tm_i, h_tm_o, h_tm_c, h_tm_f, c_tm = None, None, None, None, None

def sigmoid(x):

? ? return 1.0 / (1.0 + np.exp(-x))

def lstm_tf_verify(inputs):

? ? """

? ? ? ? 2018/12/18

? ? ? ? TF原生的解析順序?yàn)閕, j, f, o (j就是keras中的c)

? ? :param inputs:

? ? :return:

? ? """

? ? global h_tm_c, h_tm_f, h_tm_i, h_tm_o, c_tm

? ? bias_i = np.array([0.9502341, 1.1212865, 0.5962041, 0.56686985, 0.65736747])

? ? bias_j = np.array([-0.28798968, 0.31724977, -0.08590735, -0.13165179, -0.05694159])

? ? bias_f = np.array([0.89209175, 1.0639387, 0.3089665, 0.42762548, 0.4232108])

? ? bias_o = np.array([1.0723785, 1.2605966, 0.5964751, 0.6030057, 0.6930808])

? ? kernel_i = np.array([0.96915483, 0.5620192, 0.5136176, 0.1521692, 0.96555483])

? ? kernel_j = np.array([0.6295774, -0.72134864, 0.64238673, 0.48595947, 0.570404])

? ? kernel_f = np.array([0.7884312, 0.56634164, 0.14510694, 0.19882877, 0.6444183])

? ? kernel_o = np.array([0.55998164, 0.5682311, 0.9390488, 0.8536483, 0.9704966])

? ? recurrent_i = np.array([[-0.30848396, -0.13132317, 0.6034289, 0.59028447, 0.09684605],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.28015903, -0.24312414, -0.42499176, -0.3367074, -0.06846467],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.7987564, 0.93413734, -0.15053841, 0.66372687, 0.06576955],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.24111897, 0.1684269, 0.5229809, 0.09525479, 0.28952646],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.70739645, 0.8474347, 0.19091478, 0.02707534, 0.52820826]])

? ? recurrent_j = np.array([[1.272224, -1.475185, 0.38326767, 0.64769256, 0.83099645],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [-0.5344824, 1.2404263, -0.88588023, -0.7727197, -1.167835],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.86383224, -0.8951096, 0.08373257, 0.89576524, 0.53091526],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.7915831, -0.93986595, -0.02958089, 0.82741463, 0.55338454],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.39262557, -0.86354613, 0.62125677, 0.82101977, 0.13056423]])

? ? recurrent_f = np.array([[0.17595771, 0.27790356, 0.6525466, 0.05647744, 0.06983535],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.26703873, 0.04883758, 0.0888641, -0.05813761, 0.0277635],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.6442748, 0.4176797, 0.5382307, 0.48299634, 0.7003999],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.19449034, 0.01752495, 0.13846086, 0.00932326, 0.4014144],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.6212245, 0.59203285, 0.05094814, 0.85539377, 0.6473349]])

? ? recurrent_o = np.array([[0.29326066, 0.50268304, 0.544091, 0.76660025, 0.29213676],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [-0.44291726, -0.338039, -0.17275955, -0.7254445, -0.7070001],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.13272414, 0.8238844, -0.09202695, 0.9273238, 0.15251717],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.06204496, 0.6531808, 0.00607, 0.33238858, 0.04696886],

? ? ? ? ? ? ? ? ? ? ? ? ? ? [0.9217779, 0.6748385, 0.61127436, 0.5573597, 0.21182081]])

? ? # step 1 計(jì)算W * x

? ? x_i = inputs * kernel_i

? ? x_f = inputs * kernel_f

? ? x_j = inputs * kernel_j

? ? x_o = inputs * kernel_o

? ? # step 2 加上bias

? ? x_i += bias_i

? ? x_f += bias_f

? ? x_j += bias_j

? ? x_o += bias_o

? ? # step 3 計(jì)算

? ? if not isinstance(h_tm_i, np.ndarray):

? ? ? ? h_tm_i = np.zeros((1, 5))

? ? ? ? h_tm_o = np.zeros((1, 5))

? ? ? ? h_tm_f = np.zeros((1, 5))

? ? ? ? h_tm_c = np.zeros((1, 5))

? ? ? ? c_tm = np.zeros((1, 5))

? ? i = sigmoid(x_i + np.dot(h_tm_i, recurrent_i))

? ? # Tensorflow默認(rèn)有一個(gè)forget_bias, 默認(rèn)設(shè)置為1.0

? ? f = sigmoid(x_f + np.dot(h_tm_f, recurrent_f) + 1.0)

? ? c = f * c_tm + i * np.tanh(x_j + np.dot(h_tm_c, recurrent_j))

? ? o = sigmoid(x_o + np.dot(h_tm_o, recurrent_o))

? ? h = o * np.tanh(c)

? ? h_tm_c = h_tm_f = h_tm_o = h_tm_i = h

? ? c_tm = c

? ? print("當(dāng)前的hidden state", h)

? ? print("當(dāng)前的cell state", c)

? ? return h, c

if __name__ == "__main__":

? ? for i in range(3):

? ? ? ? print("輸入內(nèi)容:", inputs[:, i])

? ? ? ? # lstm_keras_verify(inputs[:, i])

? ? ? ? lstm_tf_verify(inputs[:, i])

輸出結(jié)果為:

# output 3 x 1 x 5 當(dāng)前層的每個(gè)時(shí)間步的hidden state匯總

[[[-0.14857864? 0.17725913 -0.03559565 -0.05385567 -0.02496454]]

[[-0.3793954? 0.45447606 -0.13174371 -0.17756298 -0.17771873]]

[[-0.5253717? 0.55423415 -0.25274208 -0.25586015 -0.34587777]]]

# state.h 最后一個(gè)時(shí)間步的hidden state

[[-0.5253717? 0.55423415 -0.25274208 -0.25586015 -0.34587777]]

輸入內(nèi)容: [[0.]]

當(dāng)前的hidden state [[-0.14857867? 0.17725915 -0.03559565 -0.05385567 -0.02496454]]

當(dāng)前的cell state [[-0.20212986? 0.23156138 -0.05525611 -0.08351723 -0.03746516]]

輸入內(nèi)容: [[0.03846154]]

當(dāng)前的hidden state [[-0.37939543? 0.45447602 -0.13174374 -0.17756298 -0.17771877]]

當(dāng)前的cell state [[-0.58665553? 0.71037671 -0.21416421 -0.31547094 -0.28813169]]

輸入內(nèi)容: [[0.07692308]]

當(dāng)前的hidden state [[-0.5253716? 0.55423418 -0.25274209 -0.25586014 -0.34587777]]

當(dāng)前的cell state [[-1.12897442? 1.26972863 -0.47543917 -0.66030582 -0.70899148]]

可以看出,我們的實(shí)現(xiàn)跟TF基本一樣(跟Keras一樣嗓化,都有一點(diǎn)點(diǎn)精度損失)棠涮。

# TF結(jié)果

[[-0.5253717? 0.55423415 -0.25274208 -0.25586015 -0.34587777]]

# Numpy自己實(shí)現(xiàn)結(jié)果

[[-0.5253716? 0.55423418 -0.25274209 -0.25586014 -0.34587777]]

3. Keras和TF的LSTM層異同分析

這部分,我們將對(duì)Keras和Tensorflow的LSTM層的計(jì)算邏輯進(jìn)行細(xì)致的分析刺覆,源碼位置在文章一開(kāi)頭严肪,建議大家進(jìn)去看后再來(lái)看這部分,會(huì)更加明白谦屑。

實(shí)現(xiàn)的代碼主要對(duì)比lstm_keras_verify函數(shù)和lstm_tf_verify函數(shù):顧名思義驳糯,前面是Keras的LSTM實(shí)現(xiàn)邏輯,后面的是Tensorflow的LSTM實(shí)現(xiàn)邏輯氢橙,下面講到的異同點(diǎn)如果源碼里面不好理解酝枢,直接看這里的實(shí)現(xiàn)區(qū)別也行

① TF的self._kernel包含了input_depth(本例為1)和h_depth(本例為num_units,為5)悍手,即把Keras里面的kernel和recurrent_kernel統(tǒng)一放到了self._kernel里面了帘睦。

所以,當(dāng)我打印simple_lstm的Tensorflow模型時(shí)發(fā)現(xiàn)坦康,rnn/lstm_cell/kernel的size為6 x 20竣付, 6是啥意思呢?6也很簡(jiǎn)單涝焙,其包含了一個(gè)1 x 20的(input_w_kernel)和 5 x 20的(recurrent_w_kernel)——解析順序也是這樣的卑笨。(即不像Keras分為kernel和recurrent_kernel兩個(gè)分別保存權(quán)重。)

Tensorflow中LSTM用于存儲(chǔ)權(quán)重的self._kernel代碼:

@tf_export("nn.rnn_cell.LSTMCell")

class LSTMCell(LayerRNNCell):

...

? @tf_utils.shape_type_conversion

? def build(self, inputs_shape):

? ? if inputs_shape[-1] is None:

? ? ? raise ValueError("Expected inputs.shape[-1] to be known, saw shape: %s"

? ? ? ? ? ? ? ? ? ? ? % str(inputs_shape))

? ? input_depth = inputs_shape[-1]

? ? h_depth = self._num_units if self._num_proj is None else self._num_proj

...

# self._kernel即包含Keras里面的kernel仑撞,也包含recurrent_kernel,是對(duì)Keras的LSTM層權(quán)重的2合1.

? ? self._kernel = self.add_variable(

? ? ? ? _WEIGHTS_VARIABLE_NAME,

? ? ? ? shape=[input_depth + h_depth, 4 * self._num_units],

? ? ? ? initializer=self._initializer,

? ? ? ? partitioner=maybe_partitioner)

? ? ...

? ? self._bias = self.add_variable(

? ? ? ? _BIAS_VARIABLE_NAME,

? ? ? ? shape=[4 * self._num_units],

? ? ? ? initializer=initializer)


② TF里面的i, j, f, o分別對(duì)應(yīng)Keras的LSTM中的i, c, f, o赤兴。也就是說(shuō):Keras對(duì)應(yīng)的權(quán)重和Tensorflow的權(quán)重順序不一樣了!K硐桶良!

3.2.1 Tensorflow的LSTM權(quán)重拆解順序

@tf_export("nn.rnn_cell.LSTMCell")

class LSTMCell(LayerRNNCell):

...

def call(self, inputs, state):

# i, j, f, o其中,j為下面Keras對(duì)應(yīng)的c

i, j, f, o = array_ops.split(

? ? value=lstm_matrix, num_or_size_splits=4, axis=1)

# Diagonal connections

if self._use_peepholes:

? ? # 我們先不看peephole這個(gè)LSTM變種.

? ? ...

else:

? c = (sigmoid(f + self._forget_bias) * c_prev + sigmoid(i) *

? ? ? self._activation(j))

...

m = sigmoid(o) * self._activation(c)


3.2.2 Keras的LSTM權(quán)重拆解順序

class LSTMCell(Layer):

def build(self, input_shape):

...

# Keras的4個(gè)權(quán)重存儲(chǔ)順序i, f, c, o與Tensorflow的權(quán)重存儲(chǔ)順序i, j, f, o中間順序調(diào)了一下沮翔,

# 也就是Keras的權(quán)重順序是a, b, c, d那么Tensorflow對(duì)應(yīng)的權(quán)重存儲(chǔ)為a, c, b, d.

? ? ? ? self.kernel_i = self.kernel[:, :self.units]

? ? ? ? self.kernel_f = self.kernel[:, self.units: self.units * 2]

? ? ? ? self.kernel_c = self.kernel[:, self.units * 2: self.units * 3]

? ? ? ? self.kernel_o = self.kernel[:, self.units * 3:]

# recurrent_kernel與kernel的順序是一樣的.

? ? ? ? self.recurrent_kernel_i = self.recurrent_kernel[:, :self.units]

? ? ? ? self.recurrent_kernel_f = (

? ? ? ? ? ? self.recurrent_kernel[:, self.units: self.units * 2])

? ? ? ? self.recurrent_kernel_c = (

? ? ? ? ? ? self.recurrent_kernel[:, self.units * 2: self.units * 3])

? ? ? ? self.recurrent_kernel_o = self.recurrent_kernel[:, self.units * 3:]

? ? ? ? if self.use_bias:

? ? ? ? ? ? self.bias_i = self.bias[:self.units]

? ? ? ? ? ? self.bias_f = self.bias[self.units: self.units * 2]

? ? ? ? ? ? self.bias_c = self.bias[self.units * 2: self.units * 3]

? ? ? ? ? ? self.bias_o = self.bias[self.units * 3:]

? ? ? ? ...

③ Keras的LSTM中的recurrent_activation: (對(duì)應(yīng)Part1的Keras的LSTM計(jì)算邏輯梳理介紹里面的σ σσ)用的是一種叫做hard_sigmoid的實(shí)現(xiàn)陨帆,TF的兩個(gè)的實(shí)現(xiàn)都是一樣的,用的是正常的sigmoid采蚀。而無(wú)論是Keras還是Tensorflow疲牵,它們的activation都是tanh,這個(gè)是一樣的榆鼠。

# Tensorflow LSTM用的recurrent_activation.

def sigmoid(x):

? ? return 1.0 / (1.0 + np.exp(-x))

# Keras LSTM用的recurrent_activation.

def hard_sigmoid(x):

? ? x = 0.2 * x + 0.5

? ? x[x < -2.5] = 0

? ? x[x > 2.5] = 1

? ? return x

④ Tensorflow還有一個(gè)叫做forget_bias的東西纲爸,默認(rèn)為1.0,關(guān)于這個(gè)參數(shù)的介紹如下:

Biases of the forget gate are initialized by default to 1 in order to reduce the scale of forgetting at the beginning of the training. Must set it manually to?0.0?when restoring from CudnnLSTM trained checkpoints.

它用在遺忘門(mén)(forget gate)(上面的lstm_tf_verify函數(shù)),如下:

# Tensorflow默認(rèn)有一個(gè)forget_bias, 默認(rèn)設(shè)置為1.0

f = sigmoid(x_f + np.dot(h_tm_f, recurrent_f) + 1.0)

# 而Keras默認(rèn)不帶這個(gè)東西:

f = hard_sigmoid(x_f + np.dot(h_tm_f, recurrent_kernel_f))

⑤ Keras的LSTM實(shí)現(xiàn)起來(lái)很清爽妆够,沒(méi)有什么亂78糟的參數(shù)识啦;而Tensorflow可以直接在LSTM上面做變種——比如peephole connection[3], 就是說(shuō),我們讓門(mén)層也會(huì)接受細(xì)胞狀態(tài)(cell state)的輸入神妹。

4. 一點(diǎn)思考

還有就是TF和Keras的LSTM實(shí)現(xiàn)上有一些不一致的地方颓哮,需要大家小心對(duì)待,找出異同點(diǎn)鸵荠,根據(jù)自己的情況對(duì)層進(jìn)行拆解冕茅,方便的完成解耦工作。

關(guān)于Keras和Tensorflow的LSTM層分析基本也就到此結(jié)束了蛹找,如果想更加深入的理解它們的實(shí)現(xiàn)嵌赠,比如分析這種帶時(shí)間信息的層的反向傳播邏輯,建議深挖源碼熄赡,這塊我也不甚了解姜挺。希望能跟大家多多交流,謝謝~

5. 參考資料

[1]?Netron: a viewer for neural network, deep learning and machine learning models.

[2]?理解 LSTM(Long Short-Term Memory, LSTM) 網(wǎng)絡(luò)

[3]?Gers & Schmidhuber (2000) : Recurrent Nets that Time and Count

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末彼硫,一起剝皮案震驚了整個(gè)濱河市炊豪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拧篮,老刑警劉巖词渤,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異串绩,居然都是意外死亡缺虐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)礁凡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)高氮,“玉大人慧妄,你說(shuō)我怎么就攤上這事〖羯郑” “怎么了塞淹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)罪裹。 經(jīng)常有香客問(wèn)我饱普,道長(zhǎng),這世上最難降的妖魔是什么状共? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任套耕,我火速辦了婚禮,結(jié)果婚禮上峡继,老公的妹妹穿的比我還像新娘冯袍。我一直安慰自己,他們只是感情好鬓椭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布颠猴。 她就那樣靜靜地躺著,像睡著了一般小染。 火紅的嫁衣襯著肌膚如雪翘瓮。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天裤翩,我揣著相機(jī)與錄音资盅,去河邊找鬼。 笑死踊赠,一個(gè)胖子當(dāng)著我的面吹牛呵扛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筐带,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼今穿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了伦籍?” 一聲冷哼從身側(cè)響起蓝晒,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帖鸦,沒(méi)想到半個(gè)月后芝薇,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡作儿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年洛二,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡晾嘶,死狀恐怖妓雾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情变擒,我是刑警寧澤君珠,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布寝志,位于F島的核電站娇斑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏材部。R本人自食惡果不足惜毫缆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望乐导。 院中可真熱鬧苦丁,春花似錦、人聲如沸物臂。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)棵磷。三九已至蛾狗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仪媒,已是汗流浹背沉桌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留算吩,地道東北人留凭。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像偎巢,于是被迫代替她去往敵國(guó)和親蔼夜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355