【DL學習筆記】3:循環神經網絡(Recurrent Neural Network)

1 序列數據表示

1.1 簡述

語音、文字等有先後順序,屬於序列數據,將序列據表達爲能處理的形式就叫Sequence Representation。如對文字而言,PyTorch中沒有對string的支持,所以要將其表示成數值形式,相應的方法就是Word Embedding

通常將一個序列表示爲[元素數、每個元素的向量長]的Tensor形式:
[seq_len, feature_len] [seq\_len, \ feature\_len]

注意,這個表示方法對不同的具體序列數據的類型有不同的具體解釋,上面的seq_len既是序列的長度,也是序列的特徵的數量,因爲序列中每個元素就是序列的一個特徵,所以也可以理解成feature_num

1.1.1 文本數據

例如,每句話有5個單詞,每個單詞表示爲長度100的詞向量,那麼一句話表示爲Tensor後的shape就是[5,100]了。

1.1.2 數值數據

例如,房價時序數據,有100個月的房價,每個月的房價是一個數值,那麼一個房價時序數據的Tensor的shape就應該是[100,1]。房價本身就是一個可以處理的數值數據,也就不需要做embedding了。

1.1.3 數字圖像數據

數字圖像數據可以用前面的CNN處理,但是也可以從掃描數字圖像的角度來處理。看圖片可以是逐行掃描的,那麼圖片從上到下就成了一個序列數據,每行是序列中的一個特徵。如28*28的圖像數據,每次掃描一行作爲序列中的一個元素,其作爲序列數據的Tensor的shape就是[28,28](28個特徵,每個特徵是28個像素值組成的向量)。

不過強行這樣處理也往往沒有CNN那樣做得更好。

1.2 加入batch size後的序列數據

爲了訓練效率,每次輸入的不一定是一個序列樣本,可以是多個序列樣本,也就要在Tensor里加入一個batch size的維度b,這樣輸入的Tensor的shape可以有下面兩種表示法:

  1. [seq_len, b, feature_len][seq\_len, \ b, \ feature\_len]
  2. [b, seq_len, feature_len][b, \ seq\_len, \ feature\_len]

其中b就是一次送入多少個序列。這兩種方式中前面一種是比較常用的,因爲最外層循環的就應該是一個個特徵(特徵數即序列長度)。如給出三條房價曲線,都是100個月的房價,那麼就是月份編號從0到99,樣本從0到2,房價維度是1。

具體使用哪種方式存儲數據,在PyTorch中只要使用batch_first標誌位即可。

2 循環神經網絡

循環(Recurrent)神經網絡將序列數據時間上展開處理。以下簡稱循環網絡,爲了避免和遞歸神經網絡混淆,不使用RNN這個簡稱。循環網絡可以用於序列數據的分析、生成和轉換。

2.1 共享參數的引入

對於序列數據最直觀的處理方法是對序列中的每個特徵單獨處理,如下圖,是對一句話的每個詞向量用獨立的線性層處理,最後再將結果聚合:
在這裏插入圖片描述
但這樣的處理方式會帶來兩個問題:

  1. 參數量太大,因爲序列可能很長(如長句子序列會有很多單詞)
  2. 沒有使用前面時刻的語境信息(如句子"喜歡"和"不 喜歡"意思是不一樣的)

可以採用權值共享的方式,所有的特徵共享權值矩陣WW和偏置bb
在這裏插入圖片描述
這樣可以解決上面的第一個問題,但是這樣做對語境信息處理得還不好。

2.2 循環網絡的結構

循環網絡和傳統的前饋式網絡的區別就是,循環網絡的輸入是分散在各個時刻上的,而不是一次性輸入的。
在這裏插入圖片描述
圖中所有的A都是同一個處理單元,裏面的運算是一樣的,參數也是共享的。hi1h_{i-1}保存了網絡在ii時刻之前學習到的語境信息,具體地,每層綜合之前信息和當前輸入計算得到hih_i
hi=Tanh(Uhi1+Wxi+b) h_i = Tanh(U\cdot h_{i-1} + W\cdot x_i + b)

其中Uhi1U\cdot h_{i-1}部分即是對之前的信息的處理,而Wxi+bW\cdot x_i + b則是對新輸入的特徵xix_i的處理。這就需要一個hh單元用來保存語境信息,最開始可以將其初始化爲全0的Tensor。


而這一層的輸出yiy_i是可選的,有的循環網絡中有輸出,有的可以沒有輸出。最簡單的方式就是用感知器的方式,對hih_i進行一個線性層處理再激活一下:
yi=σ(Wohi+bo) y_i = \sigma(W_o \cdot h_i + b_o)

這樣的循環網絡叫Elman網絡,目前用得較少了。

3 循環網絡下的語言模型

3.1 n-gram模型

如對句子S=w1w2...wnS=w_1w_2...w_n,估算句子出現概率P(S)P(S),可以將其用條件概率分解:
P(S)=P(w1w2...wn)=P(w1)P(w2  w1)...P(wn  w1w2...wn1) \begin{aligned} P(S) &= P(w_1w_2...w_n) \\ &= P(w_1)P(w_2 \ | \ w_1)...P(w_n \ | \ w_1w_2...w_{n-1}) \end{aligned}

可見其中存在P(wn  w1w2...wn1)P(w_n \ | \ w_1w_2...w_{n-1})這樣的項,這樣的項條件部分太多,在現實中沒法做到準確的估計,可以在一定程度上進行獨立性假設,n元文法(n-gram)模型就是解決類似的問題,例如:
P(wn  w1w2...wn1)P(wn  w1w2) P(w_n \ | \ w_1w_2...w_{n-1}) \approx P(w_n \ | \ w_1w_2)

即是認爲一個詞的出現僅依賴於前面的兩個詞,這樣就是三元文法(Tri-gram)模型。

3.2 困惑度(Preplexity)

困惑度是評價訓練好的語言模型的一種理論方法,對測試集中的數據D=w1w2...wnD=w_1w_2...w_n,困惑度的計算方法是:
PPL(D)=21Ninlog2p(wi) PPL(D) = 2^{- \frac{1}{N} \sum_{i}^n log_2p(w_i)}

困惑度本質上是測試數據上的經驗分佈pp和模型上的概率分佈qq的交叉熵:
H(p~,q)=xp(x~)log2q(x) H(\tilde{p}, q) = - \sum_{x} p(\tilde{x}) \cdot log_2q(x)

3.3 循環網絡的語言模型結構

使用循環網絡構建語言模型,輸入和輸出都是自然語言的詞,上一時刻的輸出直接作爲下一時刻的輸入:
在這裏插入圖片描述
圖中特殊詞<bos>表示begin of sentence,相應的還有<eos>在句子結尾。


在語言模型中,因爲每個時刻要輸出的yiy_i需要是一個概率分佈,以來表達輸出的是每個詞的概率,所以不能再像前面的Elman網絡那樣使用sigmoid激活,而是採用softmax來將輸出歸一化成多類的概率向量的形式
yi=softmax(Vhi+bo) y_i = softmax(V \cdot h_i + b_o)

在這裏插入圖片描述
在類別只有兩類的時候,softmax和sigmoid是等價的,但是顯然語言中的詞彙有很多很多,不止兩類。

4 循環網絡的參數訓練——BPTT算法

BPTT算法,即梯度在循環網絡中按時間線回傳,以更新網絡中的參數。

4.1 每個時刻單獨優化(舊的方法)

要進行參數訓練,可以用SGD,這就要爲循環網絡定義一個損失函數。以前面的語言模型爲例,最開始時刻的輸入是y0y_0,輸出是y1y_1(會作爲下一時刻的輸入),那麼想要最大化的就是在參數θ\theta情形下,輸入y0y_0時輸出爲y1y_1的概率,即最大化P(y1  y0,θ)P(y_1 \ | \ y_0,\theta),所以可以將損失函數定義爲對數似然度的相反數
J(y1,y0)=log P(y1  y0,θ)J(y_1,y_0)=-log \ P(y_1 \ | \ y_0,\theta)

在訓練時就是去優化模型的參數θ\theta使得損失函數越小越好:
θ=argminθJ(y1,y0)\theta^* = argmin_{\theta} J(y_1,y_0)


李沐老師在這裏特別提到,對softmax本身求梯度比較複雜,但和對數似然函數的損失函數複合之後(softmax log-loss)的梯度的梯度卻比較簡單:

Jxi={p(yt)1,ytlabelp(yt)0,yt=label \begin{aligned} \frac{\partial J}{\partial x_i} = \begin{cases} p(y_t)-1, y_t \neq label \\ p(y_t)-0, y_t = label \end{cases} \end{aligned}

它就等於期望輸出這個詞的概率,減去當前是否是輸出這個詞。也就是說在梯度下降中的調整正好是當前值和目標值的差。


在最初的循環網絡中,計算損失和計算梯度都是按時刻依次計算的 ,先算第一個時刻再算第二個時刻。而從第二個時刻開始,就會開始涉及上一時刻的計算圖,如下圖:
在這裏插入圖片描述
這時還要將梯度從y1y_1傳到y0y_0的部分,來更新網絡參數,所以這個算法叫BPTT(Back Propagation Through Time),即是經過時間線來反向傳播。

同理,在第三個時刻,有:
J(y3  y2,y1,y0)=log P(y2  y2,y1,y0,θ)J(y_3 \ | \ y_2,y_1,y_0)=-log \ P(y_2 \ | \ y_2,y_1,y_0,\theta)

4.2 整體優化(新的方法)

有了計算圖工具,不用一個時刻一個時刻的分別計算梯度,可以把整個計算圖做完,把所有的損失加在一起,求出一個總的損失再整體優化循環網絡

在這裏插入圖片描述
以圖中序列長爲3爲例,總的損失爲:
J(y3  y2,y1,y0)=J(y1  y0)+J(y2  y0,y1)+J(y3  y0,y1,y2) J(y_3 \ | \ y_2,y_1,y_0)=J(y_1 \ | \ y_0) + J(y_2 \ | \ y_0,y_1) + J(y_3 \ | \ y_0,y_1,y_2)

優化模型參數θ\theta以最小化損失:
θ=argminθJ(y3,y2,y1  y0,θ)\theta^* = argmin_{\theta} J(y_3,y_2,y_1 \ | \ y_0,\theta)

最終的優化結果和4.1中的單獨優化的結果是等價的。但這樣可以簡化程序(把計算圖搭建完一步計算梯度)、提高效率4.1中的方法較前面的層要更新很多次,但用現在的方法只要更新一次)。

5 梯度消失/梯度爆炸問題

循環網絡訓練過程中,把梯度從最後的時刻傳到最前面的時刻,要經過的傳導路徑很長,也就是鏈式法則展開後是非常多的偏導數相乘,如前面的序列長爲3的例子中:
Jy0=Jh3h3h2h2h1h1y0 \frac{\partial J}{\partial y_0} = \frac{\partial J}{\partial h_3} \cdot \frac{\partial h_3}{\partial h_2} \cdot \frac{\partial h_2}{\partial h_1} \cdot \frac{\partial h_1}{\partial y_0}

所以就可能導致梯度消失和梯度爆炸問題,後面會學習的LSTM就較好的解決了這個問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章