在很長一段時間裡,我一直忙於尋找一個實現LSTM網絡的好教程。它們似乎很複雜,而且在此之前我從來沒有使用它們做過任何東西。在網際網路上快速搜索並沒有什麼幫助,因為我找到的都是一些幻燈片。
幸運地是,我參加了Kaggle EEG 競賽,而且我認為使用LSTM很有意思,最後還理解了它的工作原理。這篇文章基於我的解決方案,使用的是AndrejKarpathy的char-rnn代碼,這也是我強烈推薦給大家的。
RNN誤區我感覺有一件很重要的事情一直未被大家充分強調過(而且這也是我為什麼不能使用RNN做我想做的事情的主要原因)。RNN和前饋神經網絡並沒有很大不同。最容易實現RNN的一種方法就是像前饋神經網絡使用部分輸入到隱含層,以及一些來自隱含層的輸出。在網絡中沒有任何神奇的內部狀態。它作為輸入的一部分。
RNN的整體結構與前饋網絡的結構非常相似
LSTM回顧本節內容將僅覆蓋LSTM的正式定義。有很多其它的好博文,都詳細地描述了你該如何設想並思考這些等式。
LSTM有多種變換形式,但我們只講解一個簡單的。一個Cell由三個Gate(input、forget、output)和一個cell單元組成。Gate使用一個sigmoid激活函數,而input和cell state通常會使用tanh來轉換。LSTM 的cell可以使用下列的等式來定義:
Gates:
輸入變換:
狀態更新:
使用圖片描述類似下圖:
由於門控機制,Cell可以在工作時保持一段時間的信息,並在訓練時保持內部梯度不受不利變化的幹擾。Vanilla LSTM 沒有forget gate,並在更新期間添加無變化的cell狀態(它可以看作是一個恆定的權值為1的遞歸連結),通常被稱為一個Constant Error Carousel(CEC)。這樣命名是因為它解決了在RNN訓練時一個嚴重的梯度消失和梯度爆炸問題,從而使得學習長期關係成為可能。
建立你自己的LSTM層這篇教程的代碼使用的是Torch7。如果你不了解它也不必擔心。我會詳細解釋的,所以你可以使用你喜歡的框架來實現相同的算法。
該網絡將作為nngraph.gModule模塊來實現,基本上表示我們定義的一個由標準nn模塊組成的神經網絡計算圖。我們需要以下幾層:
nn.Identity() - 傳遞輸入(用來存放輸入數據)nn.Dropout(p) - 標準的dropout模塊(以1-p的概率丟棄一部分隱層單元)nn.Linear(in, out) - 從in維到out維的一個仿射變換nn.Narrow(dim, start, len) - 在第dim方向上選擇一個子向量,下標從start開始,長度為lennn.Sigmoid() - 應用sigmoid智能元素nn.Tanh() - 應用tanh智能元素nn.CMulTable() - 輸出張量(tensor)的乘積nn.CAddTable() - 輸出張量的總和輸入首先,讓我們來定義輸入形式。在lua中類似數組的對象稱為表,這個網絡將接受一個類似下面的這個張量表。
Identity模塊只將我們提供給網絡的輸入複製到圖中。
計算gate值為了加快我們的實現,我們會同時運用整個LSTM層轉換。
locali2h=nn.Linear(input_size,4*rnn_size)(input)-- input to hiddenlocalh2h=nn.Linear(rnn_size,4*rnn_size)(prev_h)-- hidden to hiddenlocalpreactivations=nn.CAddTable()({i2h,h2h})-- i2h + h2h如果你不熟悉nngraph,你也許會覺得奇怪,在上一小節我們建立的inputs屬於nn.Module,這裡怎麼已經用圖節點調用一次了。事實上發生的是,第二次調用把nn.Module轉換為nngraph.gModule,並且參數指定了該節點在圖中的父節點。
preactivations輸出一個向量,該向量由輸入和前隱藏狀態的一個線性變換生成。這些都是原始值,用來計算gate 激活函數和cell輸出。這個向量被分為四個部分,每一部分的大小為rnn_size。第一部分將用於in gates,第二部分用於forget gate,第三部分用於out gate,而最後一個作為cell input(因此各個gate的下標和cell數量i的輸入為{i, rnn_size+i, 2⋅rnn_size+i, 3⋅rnn_size+i})。
接下來,我們必須運用非線性,但是儘管所有的gate使用的都是sigmoid,我們仍使用tanh對輸入進行預激活處理。正因為這個,我們將會使用兩個nn.Narrow模塊,這會選擇預激活向量中合適的部分。
-- gates在非線性操作之後,我們需要增加更多的nn.Narrow,然後我們就完成了gates。
localin_gate=nn.Narrow(2,1,rnn_size)(all_gates)有了計算好的gate值,接下來我們可以計算當前的Cell狀態了。所有的這些需要的是兩個nn.CMulTable模塊(一個用於,一個用於),並且nn.CAddTable用於把它們加到當前的cell狀態上。
-- previous cell state contribution最後,是時候來實現hidden 狀態計算了。這是最簡單的部分,因為它僅僅是把tanh應用到當前的cell 狀態(nn.Tanh)並乘上output gate(nn.CMulTable)。
localc_transform=nn.Tanh()(next_c)現在,如果你想要導出整張圖作為一個獨立的模塊,你可以使用下列代碼把它封裝起來:
-- module outputs
outputs={}
table.insert(outputs,next_c)
table.insert(outputs,next_h)
-- packs the graph into a convenient module with standard API (:forward(), :backward())
returnnn.gModule(inputs,outputs)
LSTM layer實現可以在這裡獲得。你也可以這樣使用它:
th> LSTM= require 'LSTM.lua'為了製作一個多層LSTM網絡,你可以在for循環中請求後續層,用上一層的next_h作為下一層的輸入。你可以查看這個例子。
訓練最後,如果你感興趣,請留個評論吧,我會試著擴展這篇文章!
結束語確實是這樣!當你理解怎樣處理隱藏層的時候,實現任何RNN都會很容易。僅僅把一個常規MLP層放到頂部,然後連接多個層並且把它和最後一層的隱藏層相連,你就完成了。
如果你有興趣的話,下面還有幾篇關於RNN的好論文:
原文連結:LSTM implementation explained(編譯/劉帝偉 審校/趙屹華、朱正貴、李子健 責編/周建丁)
譯者簡介: 劉帝偉,中南大學軟體學院在讀研究生,關注機器學習、數據挖掘及生物信息領域。
連結:深入淺出LSTM神經網絡
1. 加入CSDN人工智慧用戶微信群,交流人工智慧相關技術,加微信號「jianding_zhou」或掃下方二維碼,由工作人員加入。請註明個人信息和入群需求,並在入群後按此格式改群名片:機構名-技術方向-姓名/暱稱。
2. 加入CSDN 人工智慧技術交流QQ群,請搜索群號加入:465538150。同上註明信息。
3. CSDN高端專家微信群,採取受邀加入方式,不懼高門檻的請加微信號「jianding_zhou」或掃描下方二維碼,PS:請務必帶上你的BIO。
本文為CSDN編譯整理,未經允許不得轉載,如需轉載請聯繫market#csdn.net(#換成@)