深度学习笔记2:关于LSTM神经网络输入输出的理解
我們在理解RNN或者LSTM的時候,對它們的輸入和輸出的維度都很迷惑,下面是我結合代碼和網上的一些資料進行理解
首先,正常的全連接層網絡拓撲是這樣的:
有輸入層、隱層和輸出層,隱層中有神經元,無論是CNN還是RNN都可以這樣概括,圖中每個參數都跟全部的神經元有鏈接。關于RNN的理解可以看我上一篇的文章
接下來看下LSTM的拓撲圖:
LSTM的精華就來了,就是如下公式:
在拓撲圖中,我們看到的是不同時刻的X輸入到LSTM,還可以看到中間的 cell 里面有四個黃色小框,你如果理解了那個代表的含義一切就明白了,其中每一個小黃框代表一個前饋網絡層,對,就是經典的神經網絡的結構,num_units就是這個層的隱藏神經元個數,就這么簡單。其中1、2、4的激活函數是 sigmoid,第三個的激活函數是 tanh。
我們只需要記住:
1)cell的狀態是一個向量,是一個多值,而我們訓練的網絡的目的就是為了得到這個值
2)[ht?1,xt][h_{t-1},x_{t}][ht?1?,xt?]是通過concat結合起來的,也就是拼接,比如,如果x為28向量,h為128位,結合后就是156位向量
3)cell是共享權重,雖然上圖中我們看到的是三個重復的結構圖,但實際上是每個結構圖是不同時序的輸入情況,不同時序的x不斷輸入該結構中不斷更新cell的值。
4)LSTM的參數數量就是把Wf、Wi、Wc、WoW_{f}、W_{i}、W_{c}、W_{o}Wf?、Wi?、Wc?、Wo?和bf、bi、bc、bob_{f}、b_{i}、b_{c}、b_{o}bf?、bi?、bc?、bo?的元素加起來,假設num_units是128,輸入x是28位,h的向量維度也是128,所以LSTM的神經元個數為:
( 128 + 25 ) x ( 128 x 4 ) + 4 x 128
源碼如下(參考這篇博客)
LSTM 關鍵代碼:@tf_export("nn.rnn_cell.BasicLSTMCell") class BasicLSTMCell(LayerRNNCell):"""Basic LSTM recurrent network cell.The implementation is based on: http://arxiv.org/abs/1409.2329.We add forget_bias (default: 1) to the biases of the forget gate in order toreduce the scale of forgetting in the beginning of the training.It does not allow cell clipping, a projection layer, and does notuse peep-hole connections: it is the basic baseline.For advanced models, please use the full @{tf.nn.rnn_cell.LSTMCell}that follows."""def __init__(self,num_units,forget_bias=1.0,state_is_tuple=True,activation=None,reuse=None,name=None,dtype=None):"""Initialize the basic LSTM cell.Args:num_units: int, The number of units in the LSTM cell.forget_bias: float, The bias added to forget gates (see above).Must set to `0.0` manually when restoring from CudnnLSTM-trainedcheckpoints.state_is_tuple: If True, accepted and returned states are 2-tuples ofthe `c_state` and `m_state`. If False, they are concatenatedalong the column axis. The latter behavior will soon be deprecated.activation: Activation function of the inner states. Default: `tanh`.reuse: (optional) Python boolean describing whether to reuse variablesin an existing scope. If not `True`, and the existing scope already hasthe given variables, an error is raised.name: String, the name of the layer. Layers with the same name willshare weights, but to avoid mistakes we require reuse=True in suchcases.dtype: Default dtype of the layer (default of `None` means use the typeof the first input). Required when `build` is called before `call`.When restoring from CudnnLSTM-trained checkpoints, must use`CudnnCompatibleLSTMCell` instead."""super(BasicLSTMCell, self).__init__(_reuse=reuse, name=name, dtype=dtype)if not state_is_tuple:logging.warn("%s: Using a concatenated state is slower and will soon be ""deprecated. Use state_is_tuple=True.", self)# Inputs must be 2-dimensional.self.input_spec = base_layer.InputSpec(ndim=2)self._num_units = num_unitsself._forget_bias = forget_biasself._state_is_tuple = state_is_tupleself._activation = activation or math_ops.tanh@propertydef state_size(self):# 隱藏層的 size:return (LSTMStateTuple(self._num_units, self._num_units)if self._state_is_tuple else 2 * self._num_units)@propertydef output_size(self):# 輸出層的size:Hidden_sizereturn self._num_unitsdef build(self, inputs_shape):if inputs_shape[1].value is None:raise ValueError("Expected inputs.shape[-1] to be known, saw shape: %s"% inputs_shape)#inputs的維度為:[batch_size,input_size]#如果是第一層每個時刻詞語的輸入,則這個input_size 就是 embedding_size,就等于詞向量的維度;# 所以 此時 input_depth,就是input_sizeinput_depth = inputs_shape[1].value# h_depth 就是 Hidden_size,隱藏層的維度h_depth = self._num_units# self._kernel == W;則此時 W的維度 為【input_size + Hidden_size,4* Hidden_size】# 此處定義四個 W 和 B,是為了,一次就把 i,j,f,o 計算出來;相當于圖中的 ft,it,ct‘,otself._kernel = self.add_variable(_WEIGHTS_VARIABLE_NAME,shape=[input_depth + h_depth, 4 * self._num_units])# 此時的B的維度為【4 * Hidden_size】self._bias = self.add_variable(_BIAS_VARIABLE_NAME,shape=[4 * self._num_units],initializer=init_ops.zeros_initializer(dtype=self.dtype))self.built = Truedef call(self, inputs, state):"""Long short-term memory cell (LSTM).Args:inputs: `2-D` tensor with shape `[batch_size, input_size]`.state: An `LSTMStateTuple` of state tensors, each shaped`[batch_size, num_units]`, if `state_is_tuple` has been set to`True`. Otherwise, a `Tensor` shaped`[batch_size, 2 * num_units]`.Returns:A pair containing the new hidden state, and the new state (either a`LSTMStateTuple` or a concatenated state, depending on`state_is_tuple`)."""sigmoid = math_ops.sigmoidone = constant_op.constant(1, dtype=dtypes.int32)# Parameters of gates are concatenated into one multiply for efficiency.# 每一層的第0時刻的 c 和 h,元素全部初始化為0;if self._state_is_tuple:c, h = stateelse:c, h = array_ops.split(value=state, num_or_size_splits=2, axis=one)# 此時刻的 input:Xt 和 上一時刻的輸出:Ht-1,進行結合;# inputs shape : [batch_size,input_size],第一層的時候,input_size,就相當于 embedding_size# 結合后的維度為【batch_size,input_size + Hidden_size】,W的維度為【input_size + Hidden_size,4*hidden_size】# 兩者進行矩陣相乘后的維度為:【batch_size,4*hidden_size】gate_inputs = math_ops.matmul(array_ops.concat([inputs, h], 1), self._kernel)# B 的shape 為:【4 * Hidden_size】,[Xt,Ht-1] * W 計算后的shape為:[batch_size, 4 * Hidden_size]# nn_ops.bias_add,這個函數的計算方法是,讓每個 batch 得到的值,都加上這個 B;# 這一步,加上B后,得到的是,i,j,f,o 的結合, [Xt,Ht-1] * W + B,得到的 shape 還是: [batch_size, 4 * Hidden_size]# 加上偏置B后的維度為:【batch_size,4 * Hidden_size】gate_inputs = nn_ops.bias_add(gate_inputs, self._bias)# i = input_gate, j = new_input, f = forget_gate, o = output_gate# 從以上的矩陣相乘后,分割出來四部分,就是 i,j,f,o的值;# 每個的維度為【batch_size,Hidden_size】i, j, f, o = array_ops.split(value=gate_inputs, num_or_size_splits=4, axis=one)forget_bias_tensor = constant_op.constant(self._forget_bias, dtype=f.dtype)# Note that using `add` and `multiply` instead of `+` and `*` gives a# performance improvement. So using those at the cost of readability.add = math_ops.add# 此處加上遺忘的 bias,選擇遺忘元素;# 以下計算是:對應元素相乘:因為四個參數的維度都是【batch_size,hidden_size】,計算后維度不變;# new_c = c*sigmoid(f+bias) + sigmoid(i)*tanh(o)# 計算后的維度為【batch_size,hidden_size】multiply = math_ops.multiplynew_c = add(multiply(c, sigmoid(add(f, forget_bias_tensor))),multiply(sigmoid(i), self._activation(j)))# 以下計算是:對應元素相乘:因為2個參數的維度都是【batch_size,hidden_size】,計算后維度不變;#new_h = sigmoid(o) * tanh(new_c)new_h = multiply(self._activation(new_c), sigmoid(o))# 計算后的維度是(值不相等):new_c == new_h == 【batch_size,hidden_size】if self._state_is_tuple:new_state = LSTMStateTuple(new_c, new_h)else:new_state = array_ops.concat([new_c, new_h], 1)# new_h:最后一個時刻的H,new_state:最后一個時刻的 H和C;循環執行該函數,執行 num_step次(即 最大的步長),則該層計算完全;# 此時的 new_c 和 new_h,作為下一時刻的輸入,new_h 和下一時刻的,Xt+1 進行連接,連接后的維度為,【batch_size,input_size + Hidden_size】# 如果還有下一層的話,那么此刻的 new_h,變身為下一時刻的 Xtreturn new_h, new_state在知乎上,找到一個例子非常形象,
RecurrentNNs的結構圖(RNN與LSTM等同)我認為應該這樣畫,在理解上才會更清晰些,對比MLP,也一目了然。(自己畫的為了簡約,只畫了4個time-steps )……
看圖。每個時序 的輸入 我是一次time_step一張input tensor,隱狀態 也就代表了一張MLP的hidden layer的一個cell。輸出 理解無異。注意,紅色的箭頭指向indicates the tensor’s flow at time-sequential order。再結合一個操作實例說明。如果我有一條長文本,我給句子事先分割好句子,并且進行tokenize, dictionarize,接著再由look up table 查找到embedding,將token由embedding表示,再對應到上圖的輸入。流程如下:
step1, raw text:
接觸LSTM模型不久,簡單看了一些相關的論文,還沒有動手實現過。然而至今仍然想不通LSTM神經網絡究竟是怎么工作的。……
step2, tokenize (中文得分詞): sentence1: 接觸 LSTM 模型 不久 ,簡單 看了 一些 相關的 論文 , 還 沒有 動手 實現過 。 sentence2: 然而 至今 仍然 想不通 LSTM 神經網絡 究竟是 怎么 工作的。 ……
step3, dictionarize:
sentence1: 1 34 21 98 10 23 9 23
sentence2: 17 12 21 12 8 10 13 79 31 44 9 23
……
step4, padding every sentence to fixed length: sentence1: 1 34 21 98 10 23 9 23 0 0 0 0 0 sentence2: 17 12 21 12 8 10 13 79 31 44 9 23 0 ……
step5, mapping token to an embeddings:
sentence1:
[0.3410.1330.011...0.4350.0810.501...0.0130.9580.121...............]\begin{bmatrix} 0.341 & 0.133 & 0.011 & ... \\ 0.435 & 0.081 & 0.501 & ...\\ 0.013& 0.958 & 0.121 & ...\\ ...& ... & ... & ... \end{bmatrix}?????0.3410.4350.013...?0.1330.0810.958...?0.0110.5010.121...?............??????,每一列代表一個詞向量,詞向量維度自行確定;矩陣列數固定為time_step length。
sentence2:
……
step6, feed into RNNs as input: 假設 一個RNN的time_step 確定為 ,則padded sentence length(step5中矩陣列數)固定為 。一次RNNs的run只處理一條sentence。每個sentence的每個token的embedding對應了每個時序 的輸入 。一次RNNs的run,連續地將整個sentence處理完。
step7, get output:
看圖,每個time_step都是可以輸出當前時序 t 的隱狀態 h_{i}^{t} ;但整體RNN的輸出 o_{i}^{t} 是在最后一個time_step t=l 時獲取,才是完整的最終結果。
step8, further processing with the output:我們可以將output根據分類任務或回歸擬合任務的不同,分別進一步處理。比如,傳給cross_entropy&softmax進行分類……或者獲取每個time_step對應的隱狀態 ,做seq2seq 網絡……或者搞創新……
總結
以上是生活随笔為你收集整理的深度学习笔记2:关于LSTM神经网络输入输出的理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle工作流错误,工作流错误处理
- 下一篇: oracle job 与存储过程,应用o