RNN:(Recurrent Neural Networks)循環神經網絡
- 第t層神經元的輸入,除了其自身的輸入xt,還包括上一層神經元的隱含層輸出st−1
- 每一層的參數U,W,V都是共享的
lstm:長短時記憶網絡,是一種改進后的循環神經網絡,可以解決RNN無法處理的長距離依賴問題。
原始 RNN 的隱藏層只有一個狀態,即h,它對於短期的輸入非常敏感。再增加一個狀態,即c,讓它來保存長期的狀態,稱為單元狀態(cell state)。
按照時間維度展開如下所示:
在t時刻,lstm的輸入有三個:當前時刻的網絡的輸入值、上時刻lstm的輸出值、以及上一時刻的單元狀態;lstm的輸出有兩個:當前時刻lstm的輸出值、和當前時刻的單元狀態。使用三個控制開關控制長期狀態c:
在算法中利用門實現三個狀態的功能:
門就是一個全連接層,輸入的是一個向量,輸出是一個0到1之間的實數向量。
門控制的原理:用門的輸出向量按照元素乘以我們需要控制的那個向量,門的輸出不是0就是1,0乘以任何向量都是0代表不通過,1乘以任何向量不會發生改變。
遺忘門的計算方式:
遺忘門:決定了上一時刻的單元狀態c_t-1有多少保留到了c_t當前狀態,Wf 是遺忘門的權重矩陣,[ht-1,xt]表示將兩個變量拼接起來,bf是遺忘門的偏置項,是sigmoid函數。
輸入門的計算:
輸入門:決定了當前時刻網絡的輸入x_t有多少保存到單元狀態c_t.
根據上一次的輸出和本次輸入計算當前輸入的單元狀態:
當前時刻的單元狀態c_t的計算由上一次的單元狀態c_t-1乘以按元素乘以遺忘門ft,在用當前輸入的單元狀態c_t乘以輸入門i_t,將兩個積加和,可以將長期記憶和當前記憶結合起來形成新的單元狀態。由於遺忘門的控制可以保存很久很久的信息。由於輸入門的控制可以避免無關緊要的內容進入記憶。
目標是要學習8組參數:
權重矩陣是由兩個矩陣拼接而成的。誤差項是沿時間的反向傳播,定義t時刻的誤差項:
權重矩陣計算公式如下:
總體流程總結:
原始輸入循環體的是當前輸入和上前一步的輸出
,以及上一步的狀態
,
,
先遇到遺忘門(forget gate):
經過遺忘門的函數之后產生一個0到1之間的輸出,代表遺忘多少之前的狀態
,當
為0時代表全部遺忘,1代表完全保持。
另外一條路線上,,
又會遇見輸入門(input gate),輸入門會決定記憶哪些值:
另外同時經過函數會產生一個新的狀態
:
這個時候,由,
,
,
就可以決定循環體的當前狀態
了:
有了當前的狀態,自然就可以去輸出門(output gate)了:
從上面的公式,我們容易發現,每個門的形態是一樣的,都是通過函數作用於當前的輸入
和前一時刻的輸出
產生一個0到1的數值,以此來決定通過多少信息。
GRU:gate recurrent unit ,門控循環單元(GRU)。GRU 旨在解決標准 RNN 中出現的梯度消失問題。GRU 也可以被視為 LSTM 的變體。
GRU 背后的原理與 LSTM 非常相似,即用門控機制控制輸入、記憶等信息而在當前時間步做出預測,表達式由以下給出:

GRU 有兩個門,即一個重置門(reset gate)和一個更新門(update gate)。從直觀上來說,重置門決定了如何將新的輸入信息與前面的記憶相結合,更新門定義了前面記憶保存到當前時間步的量。如果我們將重置門設置為 1,更新門設置為 0,那么我們將再次獲得標准 RNN 模型。使用門控機制學習長期依賴關系的基本思想和 LSTM 一致,但還是有一些關鍵區別:
- GRU 有兩個門(重置門與更新門),而 LSTM 有三個門(輸入門、遺忘門和輸出門)。
- GRU 並不會控制並保留內部記憶(c_t),且沒有 LSTM 中的輸出門。
- LSTM 中的輸入與遺忘門對應於 GRU 的更新門,重置門直接作用於前面的隱藏狀態。
- 在計算輸出時並不應用二階非線性
1.更新門
在時間步 t,我們首先需要使用以下公式計算更新門 z_t:

其中 x_t 為第 t 個時間步的輸入向量,即輸入序列 X 的第 t 個分量,它會經過一個線性變換(與權重矩陣 W(z) 相乘)。h_(t-1) 保存的是前一個時間步 t-1 的信息,它同樣也會經過一個線性變換。更新門將這兩部分信息相加並投入到 Sigmoid 激活函數中,因此將激活結果壓縮到 0 到 1 之間。以下是更新門在整個單元的位置與表示方法。更新門幫助模型決定到底要將多少過去的信息傳遞到未來,或到底前一時間步和當前時間步的信息有多少是需要繼續傳遞的。這一點非常強大,因為模型能決定從過去復制所有的信息以減少梯度消失的風險。我們隨后會討論更新門的使用方法,現在只需要記住 z_t 的計算公式就行。
2. 重置門
本質上來說,重置門主要決定了到底有多少過去的信息需要遺忘,我們可以使用以下表達式計算:

該表達式與更新門的表達式是一樣的,只不過線性變換的參數和用處不一樣而已。下圖展示了該運算過程的表示方法。如前面更新門所述,h_(t-1) 和 x_t 先經過一個線性變換,再相加投入 Sigmoid 激活函數以輸出激活值。
3. 當前記憶內容
現在我們具體討論一下這些門控到底如何影響最終的輸出。在重置門的使用中,新的記憶內容將使用重置門儲存過去相關的信息,它的計算表達式為:

輸入 x_t 與上一時間步信息 h_(t-1) 先經過一個線性變換,即分別右乘矩陣 W 和 U。
計算重置門 r_t 與 Uh_(t-1) 的 Hadamard 乘積,即 r_t 與 Uh_(t-1) 的對應元素乘積。因為前面計算的重置門是一個由 0 到 1 組成的向量,它會衡量門控開啟的大小。例如某個元素對應的門控值為 0,那么它就代表這個元素的信息完全被遺忘掉。該 Hadamard 乘積將確定所要保留與遺忘的以前信息。
將這兩部分的計算結果相加再投入雙曲正切激活函數中。
4. 當前時間步的最終記憶
在最后一步,網絡需要計算 h_t,該向量將保留當前單元的信息並傳遞到下一個單元中。在這個過程中,我們需要使用更新門,它決定了當前記憶內容 h'_t 和前一時間步 h_(t-1) 中需要收集的信息是什么。這一過程可以表示為:

z_t 為更新門的激活結果,它同樣以門控的形式控制了信息的流入。z_t 與 h_(t-1) 的 Hadamard 乘積表示前一時間步保留到最終記憶的信息,該信息加上當前記憶保留至最終記憶的信息就等於最終門控循環單元輸出的內容。
雙向RNN:Bidirectional RNN(雙向RNN)假設當前t的輸出不僅僅和之前的序列有關,並且 還與之后的序列有關,例如:預測一個語句中缺失的詞語那么需要根據上下文進 行預測;Bidirectional RNN是一個相對簡單的RNNs,由兩個RNNs上下疊加在 一起組成。輸出由這兩個RNNs的隱藏層的狀態決定。有些情況下,當前的輸出不只依賴於之前的序列元素,還可能依賴之后的序列元素; 比如做完形填空,機器翻譯等應用。
# 開始網絡構建
# 1. 輸入的數據格式轉換 # X格式:[batch_size, time_steps, input_size] X = tf.reshape(_X, shape=[-1, timestep_size, input_size]) # 單層LSTM RNN # 2. 定義Cell lstm_cell_fw = tf.nn.rnn_cell.LSTMCell(num_units=hidden_size, reuse=tf.get_variable_scope().reuse) gru_cell_bw = tf.nn.rnn_cell.GRUCell(num_units=hidden_size, reuse=tf.get_variable_scope().reuse) # 3. 單層的RNN網絡應用 init_state_fw = lstm_cell_fw.zero_state(batch_size, dtype=tf.float32) init_state_bw = gru_cell_bw.zero_state(batch_size, dtype=tf.float32) # 3. 動態構建雙向的RNN網絡 """ bidirectional_dynamic_rnn( cell_fw: 前向的rnn cell , cell_bw:反向的rnn cell , inputs:輸入的序列 , sequence_length=None , initial_state_fw=None:前向rnn_cell的初始狀態 , initial_state_bw=None:反向rnn_cell的初始狀態 , dtype=None , parallel_iterations=None , swap_memory=False, time_major=False, scope=None) API返回值:(outputs, output_states) => outputs存儲網絡的輸出信息,output_states存儲網絡的細胞狀態信息 outputs: 是一個二元組, (output_fw, output_bw)構成,output_fw對應前向的rnn_cell的執行結果,結構為:[batch_size, time_steps, output_size];output_bw對應反向的rnn_cell的執行結果,結果和output_bw一樣 output_states:是一個二元組,(output_state_fw, output_state_bw) 構成,output_state_fw和output_state_bw是dynamic_rnn API輸出的狀態值信息 """ outputs, states = tf.nn.bidirectional_dynamic_rnn( cell_fw=lstm_cell_fw, cell_bw=gru_cell_bw, inputs=X, initial_state_fw=init_state_fw, initial_state_bw=init_state_bw) output_fw = outputs[0][:, -1, :] output_bw = outputs[1][:, -1, :] output = tf.concat([output_fw, output_bw], 1)
深度RNN
Deep Bidirectional RNN(深度雙向RNN)類似Bidirectional RNN,區別在於每一步的輸入有多層網絡,這樣的話該網絡便具有更加強大的表達能力和學習 能力,但是復雜性也提高了,同時需要訓練更多的數據。
#多層
def lstm_call(): cell = tf.nn.rnn_cell.LSTMCell(num_units=hidden_size,reuse=tf.get_variable_scope().reuse) return tf.nn.rnn_cell.DropoutWrapper(cell,output_keep_prob=keep_prob) mlstm_cell = tf.nn.rnn_cell.MultiRNNCell(cells=[lstm_call() for i in range(layer_num)]) inint_state = mlstm_cell.zero_state(batch_size,tf.float32) output,state = tf.nn.dynamic_rnn(mlstm_cell,inputs=X,initial_state=inint_state) output = output[:,-1,:]