基於Keras的RNN原理及圖解


本文翻譯自:RNN with Keras: Understanding computations

經過查找,覺得這篇博客將得比較清楚,所以進行了搬運。

Author:Gqq

本教程通過跟蹤和理解每個模型執行的計算,重點介紹了常見RNN算法的結構。 它適用於任何已經了解一般深度學習工作流程,但又無需事先了解RNN的人。 如果您真的從未聽說過RNN,可以先閱讀Christopher Olah的這篇文章。

本篇文章着重於逐步理解每個模型中的計算,而無需注意訓練有用的東西。 它用Keras代碼說明,分為五個部分:

  • TimeDistributed component
  • Simple RNN
  • Simple RNN with two hidden layers
  • LSTM
  • GRU

這篇文章的配套源代碼在這里

前饋神經網絡上RNN的核心思想是按順序讀取輸入。在RNN中,輸入x用t索引並順序處理。索引t可以代表時間序列的時間,也可以代表NLP任務的句子位置。使用隱藏變量可以隨時間存儲,更新和傳輸信息。

簡單的RNN是一種隨時間保持和更新信息的簡單方法。在A,B和C部分中對此進行了逐步描述。這種模型很有效,但對於長期依賴的序列很難訓練。主要問題是由梯度消失問題引起的。深度學習書的10.7節中詳細介紹了此問題。

引入了門控RNN來規避梯度消失問題。在D部分(LSTM)和E部分(GRU)中描述了兩種流行的門控RNN。主要思想是通過引入門來控制信息流。您可以在這里找到其運行原因的見解(如果您有更好的參考,請分享)。

請注意,即使是門控RNN,也具有計算和可解釋性問題,並且不是顯式的內存。解決這些問題的一種有前途的方法是注意力機制。此處提供概述(另請參閱本篇本篇短篇文章)。

Part A:TimeDistributed component 解釋

一個非常簡單的網絡。讓我們從一維輸入和輸出開始。在Keras中,命令行:

Dense(activation='sigmoid', units=1)

上式對應的數學方程式:

\[y = \sigma(W_yx+b_y) \]

輸入\(x\)和輸出\(y\)是一維的,因此權重使得\(W_y∈\R\)\(b_y∈\R\)。 輸出層的確是一維的,因為我們在前一個命令行中將unit = 1。 下圖可以表示該方程式(對bias項\(b_y\)進行了屏蔽更容易理解):

維度為1的TimeDistributed wrapperTimeDistributed wrapper在每個時間步均應用相同的層。 例如,對於T = 6的一維輸入和輸出,輸入用\((x_0,...,x_5)∈\R^6\)表示,輸出用\((y_0,...,y_5)∈\R^6\)表示。 然后,該模型為:

TimeDistributed(Dense(activation='sigmoid', units=1), input_shape=(None, 1))

對應於等式:

\[y_t=\sigma(W_yx_t+b_y) \]

應用於每個\(t, t∈\{0,…5\}\)。注意,每個\(t\)使用的\(W_y∈\R\)\(b_y∈\R\)都相同(同一組參數)。在上一個命令行中,input_shape =(None,1)表示輸入層是形狀為T×1的數組,而unit = 1表示每個t時刻輸出層為1個單元。 該模型可以用下圖表示:

在實踐中的輸入和輸出形狀。 輸入通常具有\((N,T,m)\)的形狀,其中\(N\)是樣本大小,\(T\)是時間大小,\(m\)是每個輸入向量的維數。 輸出的形狀為\((N,T,m')\),其中\(m'\)是每個輸出向量的維數。 在前面的示例中,我們選擇了\(T = 6\)\(m = 1\)\(m'= 1\)

新輸入的預測。 給定一個接受形狀\((N,T,m)\)輸入訓練的模型,我們可以為模型輸入形狀\((k,l,m)\)的新輸入。

在前面的示例中,我們可以選擇例如:

new_input = np.array([[[1],[0.8],[0.6],[0.2],[1],[0],[1],[1]]])
new_input.shape # (1, 8, 1)
print(model.predict(new_input))

具有更高尺寸的TimeDistributed的完整示例。 令\(N=256,T=6,m=2,m′=3\)。 訓練輸入的形狀為\((256,6,2)\),訓練輸出的形狀為\((256,6,3)\)

該模型的構建和訓練如下:

dim_in = 2
dim_out = 3
model=Sequential()
model.add(TimeDistributed(Dense(activation='sigmoid', units=dim_out), # target is dim_out-dimensional
                          input_shape=(None, dim_in))) # input is dim_in-dimensional
model.compile(loss = 'mse', optimizer = 'rmsprop')
model.fit(x_train, y_train, epochs = 100, batch_size = 32)

形狀\((k,l,m)\)的新輸入的輸出可以預測如下:

new_input = model.predict(np.array([[[1,1]]]))
new_input.shape # (1, 1, 2), which is a valid shape for this model
print(model.predict(new_input))
# [[[ 0.70669621  0.70633912  0.65635538]]]
# output is (1, 1, 3) as expected.
# Note that each column has been trained differently

通過獲取\(x_t\)二維矢量,計算\(W_yx_t + b_y\),然后將\(Sigmoid\)應用於每個分量,可以詳細了解此計算。

W_y = model.get_weights()[0] # this is a (2,3) matrix
b_y = model.get_weights()[1] # this is a (3,1) vector
# At each time, we have a dense neural network 
# (without hidden layer) from 2+1 inputs to 3 outputs.
# On the whole, there are 9 parameters 
# (the same parameters are used at each time).

[[sigmoid(y)
  for y in np.dot(x,W_y) + b_y] # like doing X * beta
  for x in [[1,1]]]
# We obtain the same results as with 'model.predict'

利用\(W_y = \begin{bmatrix} 0.76 & 0.68 & 0.66 \\ 0.92 & 0.99 & 0.52 \end{bmatrix}\)\(b_y = \begin{bmatrix} -0.80 \\ -0.79 \\ -0.54 \end{bmatrix}\)\(x_t = \begin{bmatrix} 1 \\ 1 \end{bmatrix}\)。所以\(W_y^\intercal x_t + b_y = \begin{bmatrix} 0.88 \\ 0.88 \\ 0.65 \end{bmatrix}\),通過\(sigmoid\)函數后得到\(y_t = \begin{bmatrix} 0.71 \\ 0.71 \\ 0.66 \end{bmatrix}\)

Part B:simple RNN 解釋

簡單RNN是神經網絡保持信息隨時間變化的最簡單方法。 信息存儲在隱藏變量\(h\)中,並根據新的輸入每次更新。 可以將簡單RNN連接到時間分布組件(time distributed component ),以形成1990年推出的Elman網絡。時間分布組件允許計算隱藏變量的輸出。 我們將在這一部分中描述這個完整的網絡。

網絡說明。在Keras中,命令行:

dim_in=3; dim_out=2; nb_units=5;
model=Sequential()
model.add(SimpleRNN(input_shape=(None, dim_in), 
                    return_sequences=True, 
                    units=nb_units))
model.add(TimeDistributed(Dense(activation='sigmoid',
                                units=dim_out)))

對應於數學方程式(對於所有時間\(t\)):

\[\begin{align}h_t =& \sigma(W_x x_t + W_h h_{t-1} + b_h), \\y_t =& \sigma(W_y h_t + b_y).\end{align} \]

和以前一樣,訓練輸入的形狀為\((N,T,m)\),訓練輸出的形狀為\((N,T,m')\)。 在該示例中,我們取\(m = 3\)\(m'= 2\),則\(x_t\)是二維矢量,而\(y_t\)是三維矢量。 我們選擇的\(units= 5\),所以\(h_t\)是一個五維向量。 詳細來說,SimpleRNN代碼行從\((x_0,...,x_T)\)(和初始\(h_{-1}\))計算完整序列\((h_0,...,h_T)\), TimeDistributed代碼行從\((h_0,...,h_T)\)計算序列\((y_0,...,y_T)\)。這些等式可用下圖表示:

Snipaste_2021-04-28_20-12-01

此圖顯示了網絡的一個臨時步驟,解釋了如何從\(x_t\)\(h_{t-1}\)計算\(h_t\)\(y_t\)

仍然需要選擇隱藏變量的初始值\(h_{-1}\),我們采用空向量:\(h_{-1} =(0,0,0,0,0)^⊺\)

簡單RNN的完整示例。\(N=256,T=6,m=2,m′=3\)。 訓練輸入的形狀為\((256,6,2)\),訓練輸出的形狀為\((256,6,3)\)

該模型的構建和訓練如下:

dim_in = 2; dim_out = 3; nb_units = 5
model=Sequential()
model.add(SimpleRNN(input_shape=(None, dim_in), 
                    return_sequences=True, 
                    units=nb_units))
model.add(TimeDistributed(Dense(activation='sigmoid', units=dim_out)))
model.compile(loss = 'mse', optimizer = 'rmsprop')
model.fit(x_train, y_train, epochs = 100, batch_size = 32)

訓練有素的網絡的權重為:

W_x = model.get_weights()[0] # W_x a (3,5) matrix
W_h = model.get_weights()[1] # W_h a (5,5) matrix
b_h = model.get_weights()[2] # b_h a (5,1) vector
W_y = model.get_weights()[3] # W_y a (5,2) matrix
b_y = model.get_weights()[4] # b_y a (2,1) vector

要預測形狀為\((k, l, m)\)的新輸入的輸出。我們采取形狀\((1, 3, 3)\)並讓:\(x0 =(4,2,1)^T\)\(x1 =(1,1,1 )^T\)\(x2 =(1,1,1)^T\)。該模型預測此系列的輸出:

new_input = [[4,2,1], [1,1,1], [1,1,1]]
print(model.predict(np.array([new_input])))
# [[[ 0.79032147  0.42571515]
#   [ 0.59781438  0.55316663]
#   [ 0.87601596  0.86248338]]]
# (1, 3, 2)

通過從\(x_0\)\(h_{-1}\)計算\(h_0\)可以手動檢索此結果。 然后從\(h_0\)\(y_0\); 然后從\(x_1\)\(h_0\)得到\(h_1\); 然后從\(h_1\)得到\(y_1\); 然后從\(x_2\)\(h_1\)中得到\(h_2\); 然后從\(h_2\)得出\(y_2\)隨附代碼的B部分對此進行了詳細說明。

Part C:兩層隱藏層Simple RNN解釋

在Part B中,我們連接了SimpleRNN層和TimeDistributed層,以形成一個具有一個隱藏層的Elman網絡。堆疊另一個SimpleRNN層很容易。 此Keras代碼:

dim_in = 3; dim_out = 2
model=Sequential()
model.add(SimpleRNN(input_shape=(None, dim_in), 
                    return_sequences=True, 
                    units=5))
model.add(SimpleRNN(input_shape=(None,4), 
                    return_sequences=True, 
                    units=7))
model.add(TimeDistributed(Dense(activation='sigmoid',
                                units=dim_out)))

對應於數學方程式(對於所有時間\(t\)):

\[\begin{align}h_t =& \sigma(W_x x_t + W_h h_{t-1} + b_h), \\h'_t =& \sigma(W'_x h_t + W'_h h'_{t-1} + b'_h), \\y_t =& \sigma(W_y h'_t + b_y).\end{align} \]

並由下圖表示(顯示了兩個時間步驟,以幫助理解所有對象如何相互連接):

Snipaste_2021-04-28_20-44-09

在該示例中,在每個時間\(t,x_t,h_t,h'_t,y_t\)分別是大小為2、5、7、3的向量。

權重矩陣的形狀和手動計算在伴隨代碼的C部分中進行了詳細說明。

Part D:LSTM 解釋

SimpleRNN層到LSTM。在Part B中,我們使用SimpleRNN層更新了隱藏變量\(h\),即根據\((x_t,h_{t-1})\)計算\(h_t\)。在時間\(t\)處隔離的該層表示如下:

Snipaste_2021-04-28_20-51-34

長短期記憶(LSTM)網絡用LSTM層代替了SimpleRNN層。 LSTM層接受3個輸入\((x_t, h_{t-1}, c_{t-1})\),並在每個步驟 \(t\) 輸出一對\((h_t,c_t)\)\(h\)是隱藏變量,\(c\)稱為細胞變量。 這種網絡已於1997年引入。在Keras中,命令行:

LSTM(input_shape=(None, dim_in), 
                    return_sequences=True, 
                    units=nb_units,
                    recurrent_activation='sigmoid',
                    activation='tanh')

相應的公式如下:

\[\begin{align}i_t =& \sigma(W_{ix} x_t + W_{ih} h_{t-1} + b_i) \\f_t =& \sigma(W_{fx} x_t + W_{fh} h_{t-1} + b_f) \\\tilde{c}_t =& \tanh(W_{cx} x_t + W_{ch} h_{t-1} + b_c) \\o_t =& \sigma(W_{ox} x_t + W_{oh} h_{t-1} + b_o) \\ \\c_t =& f_t c_{t-1} + i_t \tilde{c}_t \\h_t =& o_t \tanh(c_t)\end{align} \]

(具有用於\(c_{-1}\)\(h_{-1}\)的空向量),並由下圖表示:

Snipaste_2021-04-28_21-04-13

在此設置中,\(f_t\)代表遺忘變量(控制\(c_{t-1}\)保留多少信息),\(\widetilde{c}_t\)代表要保存的新信息,並由輸入變量\(i_t\)加權(控制\(\widetilde{c}_t\)保留多少信息)。 這些變量的組合形成細胞單元變量\(c_t\)。最后,\(o_t\)表示輸出變量(控制\(tanh c_t\)信息保留多少到\(h_t\)中)。

矩陣的解釋。了解所有矩陣的組織方式可能會造成混淆。讓我們假設\(dim_{in} = 7\)\(nb_{units }=13\)。輸入向量\(x_t\)的長度為7,隱藏向量\(h_t\)和細胞向量\(c_t\)的長度均為13。

  • 矩陣\(W_{ix},W_{fx},W_{cx},W_{ox}\)都為\(7×13\)的形狀,因為它們都將乘以\(x_t\)
  • 矩陣\(W_{ih},W_{fh},W_{cx},W_{oh}\)都為\(13×13\)的形狀,因為它們都乘以\(h_t\)
  • 偏差\(b_i,b_j,b_c,b_o\)具有13的長度

因此,向量\(i_t,f_t,\widetilde{c}_t,o_t\)都具有13的長度

在LSTM的Keras實現中,\(W_x\)\(W_h\)定義如下:

  • \(W_x\)\(W_{ix}\)\(W_{fx}\)\(W_{cx}\)\(W_{ox}\)的串聯,形成\(7×52\)矩陣
  • \(W_h\)\(W_{ih}\)\(W_{fh}\)\(W_{ch}\)\(W_{oh}\)的串聯,得出\(13×52\)的矩陣
  • \(b_h\)\(b_i\)\(b_f\)\(b_c\)\(b_o\)的連接,得出長度為\(52\)的向量

利用這些符號,我們可以首先計算原始向量\(W_xx_t+W_hh_{t-1}+b_h\),長度為\(52\),然后對其進行切割並應用激活函數來獲取\(i_t,f_t,\widetilde{c}_t,o_t\)

請注意,在Christopher Olah報告中,\(W_i\)\(W_f\)\(W_c\)\(W_o\)的定義如下:

  • \(W_i\)\(W_{ih}\)\(W_{ix}\)的拼接
  • \(W_f\)\(W_{fh}\)\(W_{f x}\)的拼接
  • \(W_c\)\(W_{ch}\)\(W_{c x}\)的拼接
  • \(W_o\)\(W_{oh}\)\(W_{o x}\)的拼接

將LSTM層與后續層連接。網絡的其余部分都像以前一樣工作。在Keras,我們讓:

model=Sequential()
model.add(LSTM(input_shape=(None, dim_in), 
                    return_sequences=True, 
                    units=nb_units,
                    recurrent_activation='sigmoid',
                    activation='tanh'))
model.add(TimeDistributed(Dense(activation='sigmoid',
                                units=dim_out)))

詳細而言,LSTM代碼行從\((x_0,...,x_T)(初始化h_{-1}和c_{-1})\)計算完整序列\((h_0,...,h_T)\)\((c_0,...,c_T)\) TimeDistributed代碼行從\((h_0,...,h_T)\)計算序列\((y_0,...,y_T)\)和(請注意,細胞變量\(c\)在LSTM內部使用,但在隨后的層中不使用,與隱藏變量\(h\)相反)。

權重矩陣的形狀和手動計算在隨附代碼的Part D中進行了詳細說明。

Part E:GRU 解釋

門控循環單元(GRU)是2014年推出的LSTM的一種流行替代方法。顯然,它們具有與LSTM類似的結果,但需要訓練的參數較少(GRU的權重為3組,而LSTM的權重為4組)

GRU層在每個步驟\(t\)取輸入\((x_t,h_{t-1})\)並輸出\(h_t\)。在Keras中,命令行:

GRU(input_shape=(None, dim_in), 
    return_sequences=True, 
    units=nb_units,
    recurrent_activation='sigmoid',
    activation='tanh')

對應於等式:

\[\begin{align}z_t =& \sigma(W_{zx} x_t + W_{zh} h_{t-1} + b_z) \\r_t =& \sigma(W_{rx} x_t + W_{rh} h_{t-1} + b_r) \\\tilde{h}_t =& \tanh(W_{ox} x_t + W_{oh} r_t h_{t-1} + b_o) \\ \\h_t =& z_t h_{t-1} + (1 - z_t) \tilde{h}_t\end{align} \]

(對於\(h_{-1}\)具有空向量),並由下圖表示:

Snipaste_2021-04-28_23-48-40

在這種設置下,\(z_t\)的作用類似於遺忘變量(控制保留\(h_{t-1}\)\(\widetilde{h}_t\)的信息量),\(r_t\)是遞歸變量(控制\(h_{t- 1}\)的加權方式),\(\widetilde{h}_t\)表示要保存的新信息(隨后由$z_ t $加權)。

矩陣的解釋。和以前一樣,我們假設\(dim_{in} = 7\)\(nb_{units }= 13\),所以\(x_t\)的長度為7,\(h_t\)的長度為13。

  • \(W_{zx},W_{rx},W_{ox}\)形狀為\(7×13\),它們都與\(x_t\)相乘
  • \(W_{zh},W_{rh},W_{oh}\)形狀為\(13×13\),它們都與\(h_t\)相乘
  • 偏差\(b_z,b_r,b_o\)都有相同的長度為13

因此,向量\(z_t,r_t,\widetilde{h}_t\)長度都為13

在LSTM的Keras實現中,\(W_x\)\(W_h\)定義如下:

  • \(Wx\)\(W_{zx},W_{rx},W_{ox}\)的聯接,形成\(7×39\)的矩陣
  • \(W_h\)\(W_{zh},W_{rh},W_{oh}\)的聯接,形成\(13×39\)的矩陣
  • \(b_h\)\(b_z,b_r,b_o\)的聯接,計算得到長度為39的向量

用戶手冊對Part E進行了詳細介紹,與LSTM相比更不直接。

References


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM