從零實(shí)現(xiàn)深度學(xué)習(xí)框架系列:RNN實(shí)戰(zhàn)篇(附完整代碼)
來源:投稿 作者:175
編輯:學(xué)姐
鐺鐺!上期我們學(xué)習(xí)了RNN的理論部分,實(shí)戰(zhàn)部分這就來了!
本文我們來看如何實(shí)現(xiàn)它,包括堆疊RNN和雙向RNN。從而理解它們的原理。最后看一個(gè)應(yīng)用到詞性標(biāo)注任務(wù)的實(shí)戰(zhàn)。
為了方便同學(xué)們學(xué)習(xí),學(xué)姐也整理了部分AI必讀論文資料放在這里
關(guān)注【學(xué)姐帶你玩AI】公眾號(hào),回復(fù)“500”免費(fèi)領(lǐng)的哈~
RNNCell
首先實(shí)現(xiàn)單時(shí)間步RNN計(jì)算類,這是一個(gè)公共類:
激活函數(shù)支持tanh和relu,這只是單時(shí)間步的RNN計(jì)算,RNN模型就是基于它來實(shí)現(xiàn)的。
RNN
下面來實(shí)現(xiàn)簡(jiǎn)單RNN。
從參數(shù)可以看到,我們支持多層RNN,同時(shí)在多層RNN之間經(jīng)過了一層Dropout。
為了簡(jiǎn)化實(shí)現(xiàn),將batch維度放到維度1。
由于包含多層,每層含有不同的隱藏狀態(tài),所以需要按層數(shù)來拆分h。

多層的情況下,需要在合適的位置增加Dropout。比如上圖的例子中,在RNN1和RNN2以及RNN2和RNN3的連接處增加Dropout。
雙向RNN

雙向RNN其實(shí)就是多了另一個(gè)反方向處理的RNN,因此我們首先增加新的用于處理反序輸入的RNN:
最簡(jiǎn)單的方法,就是將輸入逆序,然后依照正向過程重新,重新跑一遍反向RNN過程。但這樣會(huì)有重復(fù)代碼,因此我們把RNN沿著某個(gè)方向的運(yùn)算過程抽成一個(gè)函數(shù)。
我們這里輸出的維度和PyTorch保持一致。那么其中的one_directional_op是怎么實(shí)現(xiàn)的呢?
這里要注意的是output = F.flip(output, 0)將輸出按時(shí)間步維度逆序,使得時(shí)間步t=0上,是看了整個(gè)序列的結(jié)果。
最后我們通過詞性標(biāo)注任務(wù)實(shí)戰(zhàn)來應(yīng)用我們的RNN。
詞性標(biāo)注實(shí)戰(zhàn)
詞性標(biāo)注任務(wù)可以看成是多類別文本分類問題,我們使用NLTK提供的賓州樹庫(Penn Treebank)樣例數(shù)據(jù),首先加載詞性標(biāo)注語料庫:
我們采用前3000句作為訓(xùn)練數(shù)據(jù),其余的作為測(cè)試數(shù)據(jù)。然后實(shí)現(xiàn)我們的數(shù)據(jù)集類:
為了對(duì)齊批次內(nèi)數(shù)據(jù)的長度,需要對(duì)輸入序列和輸出序列進(jìn)行補(bǔ)齊,同時(shí)用mask記錄了哪些是經(jīng)過補(bǔ)齊的標(biāo)記。
然后基于我們上面實(shí)現(xiàn)的RNN來實(shí)現(xiàn)該詞性標(biāo)注分類模型,這里同樣也叫RNN:
這里在序列標(biāo)注任務(wù)中,需要使用序列全部狀態(tài)的隱藏層,存儲(chǔ)在變量output中。
最后,在訓(xùn)練和預(yù)測(cè)階段,需要使用mask來保證僅對(duì)有效標(biāo)記求損失、對(duì)正確預(yù)測(cè)結(jié)果以及總的標(biāo)記計(jì)數(shù)。
訓(xùn)練代碼如下:
我們通過model.train()來model.eval()來控制需不需要進(jìn)行Dropout。最終,在雙向RNN中訓(xùn)練了10個(gè)批次,結(jié)果為:
由于電腦上沒有GPU,因此速度較慢,就只訓(xùn)練了10個(gè)批次,看起來效果還不錯(cuò),測(cè)試集上的準(zhǔn)確率達(dá)到了70%。
完整代碼:
https://github.com/nlp-greyfoss/metagrad
參考
Speech and Language Processing
自然語言處理:基于預(yù)訓(xùn)練模型的方法
https://nn.labml.ai/lstm/index.html