從前饋到反饋:解析迴圈神經網路(RNN)及其tricks

夕小瑤發表於2018-07-26
從前饋到反饋:解析迴圈神經網路(RNN)及其tricks

為什麼需要反饋?

在上一篇《前饋網路與卷積網路》中,小夕講解了前饋網路中的戰鬥機——卷積神經網路(CNN)更關注區域性特徵的提取,比如在句子級情感分類的任務中,往往找出句子中可以表現情感的情感詞就能完成這個句子的情感極性分類,而情感詞之前的詞並不會對決策結果起到多大的影響。還是習慣性的舉出栗子?:

比如 “夕小瑤 今天 不開心 了。” 

為了判斷這個句子的情感極性,只需要讓分類器能識別出“不開心”是個負極性的詞,其他詞是偏中性詞就可以了。而“不開心”前面的中性詞是“今天”還是“有時”,對“不開心”的情感極性並不會有多大的影響,也不會對整個句子的情感極性有多大影響。因此,當我們把該句子中的各個詞條依次輸入給模型去分類時,並不需要去“瞻前顧後”,因此使用一個關注區域性的前饋神經網路往往表現更佳。而從最近三四年的論文來看,在句子級情感分類問題上,也確實是卷積網路比遞迴網路和迴圈網路更容易引領state-of-arts。

然而,還有一些任務的labels之間會有很強的相關性,比如命名實體識別(NER)任務,我們想要識別出文字中的地址時:

“據報導,在 2016年,有 一隻 可愛的 小狗狗 在 北京市 海淀區 番茄貓咪 小區 失蹤。”(什麼鬼栗子)

這句話中的地址是“北京市 海淀區 番茄 貓咪 小區”,因此我們的目標是給這幾個詞條貼上“這是地址”的標籤,給其他詞條貼上“這不是地址”的標籤。

顯然,如果用CNN去做的話,很容易把“番茄”識別成非地址詞,畢竟想要識別出來它的話,就要同時考慮“海淀區”“貓咪”“小區”這三個相鄰的上下文詞條,也就是說CNN的卷積濾波器的長度要最少達到4才有較大的可能性正確標註這個句子的labels。而對更長的地址,那代價就更大了,總不能無限增長濾波器的長度吶。所以一個更靠譜的辦法是讓模型在當前位置的輸出再反饋給模型,來幫助模型做下一位置的決策!

這樣的有反饋單元的模型在做當前位置的決策的時候,會同時考慮前面所有位置/時間點的情況(直接考慮上一時間點,又由於上一時間點也考慮了上上時間點,因此間接考慮了上一時間點前面所有的時間點),因此有反饋單元的模型在判斷“番茄”是不是地址時,它通過前面積累的 “據報導,在2016年,有 一隻 可愛的 小狗狗 在 北京市 海淀區” 發現下一個詞 “番茄” 是非地址詞的概率並不大(因為訓練集中很少會出現主語+“在”+地址1+地址2+非地址名詞+...的情況,倒是經常出現主語+“在”+地址1+地址2+地址3+...的情況),從而可以完成正確決策。

相比之下,在卷積網路中,由於它視野有限,不瞻前顧後,所以它更可能覺得“地址+非地址名詞”非常正常,因為比如“北京糖葫蘆”,“美國 貓”也很常見嘛~進而導致了錯誤決策。這也是為什麼在該任務中,有反饋單元的神經網路比前饋網路更容易成為state-of-art。

數學上如何描述反饋?

顯然這樣模型就不再是前饋的了,而是將模型的輸出也作為輸入來丟進模型。回顧一下,前饋網路的時候簡單的前饋公式是這樣的:

O=f( X * W+b )

其中W和b是模型的引數,X是當前的輸入,f(·)是啟用函式,*是矩陣乘法,O是當前的輸出。即輸出 等於 輸入經過線性與非線性對映後的結果。

加上反饋單元后,公式就變成了:

Ot=f(X * W + Ot-1 * V+ b)

其中W、V、b是模型的引數,下標t代表當前的序列位置/時間點,t-1代表上個位置/上個時間點,X是當前的輸入,f(·)是啟用函式,*是矩陣乘法,O是模型輸出。

簡單的加上反饋單元的這個模型,就叫做迴圈神經網路(RNN,R=Recurrent)。這也是後續LSTM、GRU等門限迴圈網路的基礎模型。

RNN是淺層的還是深層的?

從前向過程來看,貌似是淺層的。比如1000層的前饋神經網路,在做“xxxxx”的命名實體識別這個前文例子的時候,是每一個詞條都要跑一遍1000層的網路才能出這個詞條的標註結果。而簡單的迴圈網路來說,跑一層就會出一個標註結果。因此看起來是淺層的。然而,在計算序列的後幾個位置的label的時候,顯然也積累了前面所有位置的計算結果,因此這樣看又是深層的。

從反向過程看,也就是從優化的角度來看,RNN是深層的。因為在進行每一次誤差反向傳播的時候,誤差要從前饋網路的第1000層開始逐層傳回第一層,誤差在迴圈網路中也要從序列的末端輸出一直傳回到序列的首端。因此序列是1000長度的話,在RNN中誤差就相當於傳遞了1000層。

這個爭論也被公認為是爭論。小夕就不參與討論了。我們只關注這樣會帶來什麼特殊的問題。

RNN的問題

首先,從淺層的角度去看RNN的前向過程,就可以認為它只有一層權重矩陣W(我們先不管V)。由此可見從深層的角度去看RNN的前向過程,就可以認為RNN是各個層的權重矩陣相同的深層網路。我們忽略V和啟用函式,就可以近似的認為網路一共有T層(T等於序列的長度),那麼第t層的輸出就是連乘t次W,也就是Wt

《線性代數(二)》中,小夕講過矩陣可以用它的特徵值矩陣和特徵向量矩陣去近似,即

W≈Vdiag(λ)V-1

所以Wt就可以展開成(Vdiag(λ)V-1)(Vdiag(λ)V-1)...,約掉中間的一堆V-1V,就是

Wt=Vdiag(λ)tV-1

也就是說,特徵值矩陣中的每個特徵值都會隨著t的增大發生指數級變化!所以某個特徵值大於1時,就容易導致這個維度的值爆炸性增長;當特徵值小於1時,會就會導致這個維度的值指數級速度衰減為0!

前向過程如此,誤差反向傳播的過程也必然近似為輸出層的誤差會乘以Wt來得到倒數第t層的梯度,然而由於剛才講的各個維度不是指數級衰減就是指數級爆炸,很容易看出當你去更新RNN的靠前的層的時候(即離著輸出層較遠的那些層,或者說與序列末端離得遠的位置),計算出的梯度要麼大的嚇人,要麼小的忽略。小的忽略的值不會對W的更新有任何指導作用,大的嚇人的值一下子就把W中的某些值弄的大的嚇人了,害得前面的優化都白做了...因此這顯然是不靠譜的。哦對了,這個現象叫做梯度消失梯度爆炸

解決方法呢?

一個很簡單的想法是進行梯度截斷,我們在優化RNN的引數的時候,給剃度值設定一個上限,免得發生爆炸摧毀我們之前積累的結果。但是這樣顯然就會導致模型難以再顧及很靠前的歷史資訊了(梯度都被大幅閹割或者自己消失了),因此理論上RNN可以儲存任意長的歷史資訊來輔助當前時間點的決策,然而由於在優化時(訓練RNN時),梯度無法準確合理的傳到很靠前的時間點,因此RNN實際上只能記住並不長的序列資訊(在NLP中,經驗上認為序列大於30的時候,RNN基本記不住多於30的部分,而從多於10的位置開始就變得記性很差了),因此RNN相比前饋網路,可以記住的歷史資訊要長一些,但是無法記住長距離的資訊(比如段落級的序列甚至篇章級的序列,用RNN就雞肋了)。

使用梯度截斷的trick可以減輕引數矩陣連乘帶來的問題。同樣,如前所述,由於前向過程也會對引數矩陣進行連乘,顯然乘的次數多了,就會容易產生很大的值(包括很大的正值與很小的負值),而很大的正值或者很小的負值會在反向傳播時丟給啟用函式的導數(忘了的回去複習《BP演算法過程》),因此:

對於sigmoid的導數σ′(z) = σ(z)(1-σ(z))(有疑問的自己推導一下)來說,顯然無論輸入的z是超大正值也好,超小負值也好,都會導致其導數接近0!因此直接導致梯度傳到這裡後就消失了!而tanh的導數和ReLU的導數則不會有這個問題。因此在RNN中,更要注意一般情況下不要使用sigmoid作為啟用函式

好了。標準的RNN講完了。我們發現RNN的想法很簡單,直接引入了反饋單元來記憶歷史資訊。然而由於訓練時的梯度爆炸與消失,導致其難以學習遠距離歷史資訊(或者說與當前時間點的遠距離依賴關係),因此要注意使用梯度截斷的trick來處理優化過程,同時要避開sigmoid這類容易帶來梯度飽和的啟用函式。那麼如何讓迴圈神經網路去學習長距離依賴關係呢?期待小夕的長短時記憶網路(LSTM)吧!

相關文章