从前馈到反馈:解析循环神经网络(RNN)及其tricks
好像已經(jīng)有兩周沒有更新啦。最后這幾天都不敢打開訂閱號后臺了,怕一打開發(fā)現(xiàn)掉了幾百個粉絲的話就難過死了T_T。然而小夕發(fā)現(xiàn)你們并沒有離開,感動的差點哭出來,都感覺再不認(rèn)真寫一篇文章就太對不起大家的等待啦。
而這兩周,經(jīng)歷的事情蠻多的。為了湊下一季的房租,接了個私活,要死要活的做完了QAQ。而且還發(fā)現(xiàn)了一個特別好的學(xué)習(xí)平臺,閉關(guān)修煉了一周,改天跟你們分享一下~下面開始正文啦,不要太激動哦。
為什么需要反饋?
?
在上一篇《前饋網(wǎng)絡(luò)與卷積網(wǎng)絡(luò)》中,小夕講解了前饋網(wǎng)絡(luò)中的戰(zhàn)斗機——卷積神經(jīng)網(wǎng)絡(luò)(CNN)更關(guān)注局部特征的提取,比如在句子級情感分類的任務(wù)中,往往找出句子中可以表現(xiàn)情感的情感詞就能完成這個句子的情感極性分類,而情感詞之前的詞并不會對決策結(jié)果起到多大的影響。還是習(xí)慣性的舉出栗子????:
?
比如?“夕小瑤 今天 不開心 了。”?
為了判斷這個句子的情感極性,只需要讓分類器能識別出“不開心”是個負(fù)極性的詞,其他詞是偏中性詞就可以了。而“不開心”前面的中性詞是“今天”還是“有時”,對“不開心”的情感極性并不會有多大的影響,也不會對整個句子的情感極性有多大影響。因此,當(dāng)我們把該句子中的各個詞條依次輸入給模型去分類時,并不需要去“瞻前顧后”,因此使用一個關(guān)注局部的前饋神經(jīng)網(wǎng)絡(luò)往往表現(xiàn)更佳。而從最近三四年的論文來看,在句子級情感分類問題上,也確實是卷積網(wǎng)絡(luò)比遞歸網(wǎng)絡(luò)和循環(huán)網(wǎng)絡(luò)更容易引領(lǐng)state-of-arts。
?
然而,還有一些任務(wù)的labels之間會有很強的相關(guān)性,比如命名實體識別(NER)任務(wù),我們想要識別出文本中的地址時:
?
“據(jù)報道,在 2016年,有 一只 可愛的 小狗狗 在 北京市 海淀區(qū) 番茄貓咪 小區(qū) 失蹤。”(什么鬼栗子)
?
這句話中的地址是“北京市 海淀區(qū) 番茄 貓咪 小區(qū)”,因此我們的目標(biāo)是給這幾個詞條貼上“這是地址”的標(biāo)簽,給其他詞條貼上“這不是地址”的標(biāo)簽。
?
顯然,如果用CNN去做的話,很容易把“番茄”識別成非地址詞,畢竟想要識別出來它的話,就要同時考慮“海淀區(qū)”“貓咪”“小區(qū)”這三個相鄰的上下文詞條,也就是說CNN的卷積濾波器的長度要最少達到4才有較大的可能性正確標(biāo)注這個句子的labels。而對更長的地址,那代價就更大了,總不能無限增長濾波器的長度吶。所以一個更靠譜的辦法是讓模型在當(dāng)前位置的輸出再反饋給模型,來幫助模型做下一位置的決策!
這樣的有反饋單元的模型在做當(dāng)前位置的決策的時候,會同時考慮前面所有位置/時間點的情況(直接考慮上一時間點,又由于上一時間點也考慮了上上時間點,因此間接考慮了上一時間點前面所有的時間點),因此有反饋單元的模型在判斷“番茄”是不是地址時,它通過前面積累的 “據(jù)報道,在2016年,有 一只 可愛的 小狗狗 在 北京市 海淀區(qū)” 發(fā)現(xiàn)下一個詞 “番茄” 是非地址詞的概率并不大(因為訓(xùn)練集中很少會出現(xiàn)主語+“在”+地址1+地址2+非地址名詞+...的情況,倒是經(jīng)常出現(xiàn)主語+“在”+地址1+地址2+地址3+...的情況),從而可以完成正確決策。
?
相比之下,在卷積網(wǎng)絡(luò)中,由于它視野有限,不瞻前顧后,所以它更可能覺得“地址+非地址名詞”非常正常,因為比如“北京糖葫蘆”,“美國 貓”也很常見嘛~進而導(dǎo)致了錯誤決策。這也是為什么在該任務(wù)中,有反饋單元的神經(jīng)網(wǎng)絡(luò)比前饋網(wǎng)絡(luò)更容易成為state-of-art。
?
數(shù)學(xué)上如何描述反饋?
?
顯然這樣模型就不再是前饋的了,而是將模型的輸出也作為輸入來丟進模型。回顧一下,前饋網(wǎng)絡(luò)的時候簡單的前饋公式是這樣的:
?
O=f( X * W+b )
?
其中W和b是模型的參數(shù),X是當(dāng)前的輸入,f(·)是激活函數(shù),*是矩陣乘法,O是當(dāng)前的輸出。即輸出 等于 輸入經(jīng)過線性與非線性映射后的結(jié)果。
?
加上反饋單元后,公式就變成了:
?
Ot=f(X * W + Ot-1 * V+ b)
?
其中W、V、b是模型的參數(shù),下標(biāo)t代表當(dāng)前的序列位置/時間點,t-1代表上個位置/上個時間點,X是當(dāng)前的輸入,f(·)是激活函數(shù),*是矩陣乘法,O是模型輸出。
?
簡單的加上反饋單元的這個模型,就叫做循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN,R=Recurrent)。這也是后續(xù)LSTM、GRU等門限循環(huán)網(wǎng)絡(luò)的基礎(chǔ)模型。
?
RNN是淺層的還是深層的?
?
從前向過程來看,貌似是淺層的。比如1000層的前饋神經(jīng)網(wǎng)絡(luò),在做“xxxxx”的命名實體識別這個前文例子的時候,是每一個詞條都要跑一遍1000層的網(wǎng)絡(luò)才能出這個詞條的標(biāo)注結(jié)果。而簡單的循環(huán)網(wǎng)絡(luò)來說,跑一層就會出一個標(biāo)注結(jié)果。因此看起來是淺層的。然而,在計算序列的后幾個位置的label的時候,顯然也積累了前面所有位置的計算結(jié)果,因此這樣看又是深層的。
?
從反向過程看,也就是從優(yōu)化的角度來看,RNN是深層的。因為在進行每一次誤差反向傳播的時候,誤差要從前饋網(wǎng)絡(luò)的第1000層開始逐層傳回第一層,誤差在循環(huán)網(wǎng)絡(luò)中也要從序列的末端輸出一直傳回到序列的首端。因此序列是1000長度的話,在RNN中誤差就相當(dāng)于傳遞了1000層。
?
這個爭論也被公認(rèn)為是爭論。小夕就不參與討論了。我們只關(guān)注這樣會帶來什么特殊的問題。
?
RNN的問題
?
首先,從淺層的角度去看RNN的前向過程,就可以認(rèn)為它只有一層權(quán)重矩陣W(我們先不管V)。由此可見從深層的角度去看RNN的前向過程,就可以認(rèn)為RNN是各個層的權(quán)重矩陣相同的深層網(wǎng)絡(luò)。我們忽略V和激活函數(shù),就可以近似的認(rèn)為網(wǎng)絡(luò)一共有T層(T等于序列的長度),那么第t層的輸出就是連乘t次W,也就是Wt!
在《線性代數(shù)(二)》中,小夕講過矩陣可以用它的特征值矩陣和特征向量矩陣去近似,即
W≈Vdiag(λ)V-1
所以Wt就可以展開成(Vdiag(λ)V-1)(Vdiag(λ)V-1)...,約掉中間的一堆V-1V,就是
Wt=Vdiag(λ)tV-1
也就是說,特征值矩陣中的每個特征值都會隨著t的增大發(fā)生指數(shù)級變化!所以某個特征值大于1時,就容易導(dǎo)致這個維度的值爆炸性增長;當(dāng)特征值小于1時,會就會導(dǎo)致這個維度的值指數(shù)級速度衰減為0!
?
前向過程如此,誤差反向傳播的過程也必然近似為輸出層的誤差會乘以Wt來得到倒數(shù)第t層的梯度,然而由于剛才講的各個維度不是指數(shù)級衰減就是指數(shù)級爆炸,很容易看出當(dāng)你去更新RNN的靠前的層的時候(即離著輸出層較遠的那些層,或者說與序列末端離得遠的位置),計算出的梯度要么大的嚇人,要么小的忽略。小的忽略的值不會對W的更新有任何指導(dǎo)作用,大的嚇人的值一下子就把W中的某些值弄的大的嚇人了,害得前面的優(yōu)化都白做了...因此這顯然是不靠譜的。哦對了,這個現(xiàn)象叫做梯度消失和梯度爆炸。
?
解決方法呢?
?
一個很簡單的想法是進行梯度截斷,我們在優(yōu)化RNN的參數(shù)的時候,給剃度值設(shè)置一個上限,免得發(fā)生爆炸摧毀我們之前積累的結(jié)果。但是這樣顯然就會導(dǎo)致模型難以再顧及很靠前的歷史信息了(梯度都被大幅閹割或者自己消失了),因此理論上RNN可以保存任意長的歷史信息來輔助當(dāng)前時間點的決策,然而由于在優(yōu)化時(訓(xùn)練RNN時),梯度無法準(zhǔn)確合理的傳到很靠前的時間點,因此RNN實際上只能記住并不長的序列信息(在NLP中,經(jīng)驗上認(rèn)為序列大于30的時候,RNN基本記不住多于30的部分,而從多于10的位置開始就變得記性很差了),因此RNN相比前饋網(wǎng)絡(luò),可以記住的歷史信息要長一些,但是無法記住長距離的信息(比如段落級的序列甚至篇章級的序列,用RNN就雞肋了)。
?
使用梯度截斷的trick可以減輕參數(shù)矩陣連乘帶來的問題。同樣,如前所述,由于前向過程也會對參數(shù)矩陣進行連乘,顯然乘的次數(shù)多了,就會容易產(chǎn)生很大的值(包括很大的正值與很小的負(fù)值),而很大的正值或者很小的負(fù)值會在反向傳播時丟給激活函數(shù)的導(dǎo)數(shù)(忘了的回去復(fù)習(xí)《BP算法過程》),因此:
?
對于sigmoid的導(dǎo)數(shù)σ′(z) = σ(z)(1-σ(z))(有疑問的自己推導(dǎo)一下)來說,顯然無論輸入的z是超大正值也好,超小負(fù)值也好,都會導(dǎo)致其導(dǎo)數(shù)接近0!因此直接導(dǎo)致梯度傳到這里后就消失了!而ELU?的導(dǎo)數(shù)和ReLU的導(dǎo)數(shù)則不會有這個問題。因此在RNN中,更要注意一般情況下不要使用sigmoid作為激活函數(shù)!
?
好了。標(biāo)準(zhǔn)的RNN講完了。我們發(fā)現(xiàn)RNN的想法很簡單,直接引入了反饋單元來記憶歷史信息。然而由于訓(xùn)練時的梯度爆炸與消失,導(dǎo)致其難以學(xué)習(xí)遠距離歷史信息(或者說與當(dāng)前時間點的遠距離依賴關(guān)系),因此要注意使用梯度截斷的trick來處理優(yōu)化過程,同時要避開sigmoid這類容易帶來梯度飽和的激活函數(shù)。那么如何讓循環(huán)神經(jīng)網(wǎng)絡(luò)去學(xué)習(xí)長距離依賴關(guān)系呢?期待小夕的長短時記憶網(wǎng)絡(luò)(LSTM)吧!
總結(jié)
以上是生活随笔為你收集整理的从前馈到反馈:解析循环神经网络(RNN)及其tricks的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 拿下字节offer,这些面试题命中率高达
- 下一篇: 再介绍一篇Contrastive Sel