零基礎入門深度學習(五):長短時記憶網絡
在上一篇文章《零基礎入門深度學習(4):循環神經網絡》中,我們介紹了循環神經網絡以及它的訓練算法。我們也介紹了循環神經網絡很難訓練的原因,這導致了它在實際應用中,很難處理長距離的依賴。在本文中,我們將介紹一種改進之後的循環神經網絡:長短時記憶網絡(Long Short Term Memory Network, LSTM),它成功地解決了原始循環神經網絡的缺陷,成為當前最流行的RNN,在語音識別、圖片描述、自然語言處理等許多領域中成功應用。
但不幸的一麵是,LSTM的結構很複雜,因此,我們需要花上一些力氣,才能把LSTM以及它的訓練算法弄明白。在搞清楚LSTM之後,我們再介紹一種LSTM的變體:GRU (Gated Recurrent Unit)。 它的結構比LSTM簡單,而效果卻和LSTM一樣好,因此,它正在逐漸流行起來。最後,我們仍然會動手實現一個LSTM。
長短時記憶網絡是啥
我們首先了解一下長短時記憶網絡產生的背景。回顧一下中推導的,誤差項沿時間反向傳播的公式:
梯度消失到底意味著什麼?在《零基礎入門深度學習(4):循環神經網絡》中我們已證明,權重數組W最終的梯度是各個時刻的梯度之和,即:
假設某輪訓練中,各時刻的梯度以及最終的梯度之和如下圖:
我們就可以看到,從上圖的t-3時刻開始,梯度已經幾乎減少到0了。那麼,從這個時刻開始再往之前走,得到的梯度(幾乎為零)就不會對最終的梯度值有任何貢獻,這就相當於無論t-3時刻之前的網絡狀態h是什麼,在訓練中都不會對權重數組W的更新產生影響,也就是網絡事實上已經忽略了t-3時刻之前的狀態。這就是原始RNN無法處理長距離依賴的原因。
既然找到了問題的原因,那麼我們就能解決它。從問題的定位到解決,科學家們大概花了7、8年時間。終於有一天,Hochreiter和Schmidhuber兩位科學家發明出長短時記憶網絡,一舉解決這個問題。
其實,長短時記憶網絡的思路比較簡單。原始RNN的隱藏層隻有一個狀態,即h,它對於短期的輸入非常敏感。那麼,假如我們再增加一個狀態,即c,讓它來保存長期的狀態,那麼問題不就解決了麼?如下圖所示:
新增加的狀態c,稱為單元狀態(cell state)。我們把上圖按照時間維度展開:
LSTM的關鍵,就是怎樣控製長期狀態c。在這裏,LSTM的思路是使用三個控製開關。第一個開關,負責控製繼續保存長期狀態c;第二個開關,負責控製把即時狀態輸入到長期狀態c;第三個開關,負責控製是否把長期狀態c作為當前的LSTM的輸出。三個開關的作用如下圖所示:
接下來,我們要描述一下,輸出h和單元狀態c的具體計算方法。
長短時記憶網絡的前向計算
前麵描述的開關是怎樣在算法中實現的呢?這就用到了門(gate)的概念。門實際上就是一層全連接層,它的輸入是一個向量,輸出是一個0到1之間的實數向量。假設W是門的權重向量,是偏置項,那麼門可以表示為:
我們先來看一下遺忘門:
下圖顯示了遺忘門的計算:
接下來看看輸入門:
上式中,Wi是輸入門的權重矩陣,bi是輸入門的偏置項。下圖表示了輸入門的計算:
下圖表示輸出門的計算:
LSTM最終的輸出,是由輸出門和單元狀態共同確定的:
下圖表示LSTM最終輸出的計算:
式1到式6就是LSTM前向計算的全部公式。至此,我們就把LSTM前向計算講完了。
長短時記憶網絡的訓練
熟悉我們這個係列文章的同學都清楚,訓練部分往往比前向計算部分複雜多了。LSTM的前向計算都這麼複雜,那麼,可想而知,它的訓練算法一定是非常非常複雜的。現在隻有做幾次深唿吸,再一頭紮進公式海洋吧。
LSTM訓練算法框架
LSTM的訓練算法仍然是反向傳播算法,對於這個算法,我們已經非常熟悉了。主要有下麵三個步驟:
關於公式和符號的說明
首先,我們對推導中用到的一些公式、符號做一下必要的說明。
接下來的推導中,我們設定gate的激活函數為sigmoid函數,輸出的激活函數為tanh函數。他們的導數分別為:
從上麵可以看出,sigmoid和tanh函數的導數都是原函數的函數。這樣,我們一旦計算原函數的值,就可以用它來計算出導數的值。
誤差項沿時間的反向傳遞
下麵,我們要把式7中的每個偏導數都求出來。根據式6,我們可以求出:
根據式4,我們可以求出:
因為:
我們很容易得出:
將上述偏導數帶入到式7,我們得到:
式8到式12就是將誤差沿時間反向傳播一個時刻的公式。有了它,我們可以寫出將誤差項向前傳遞到任意k時刻的公式:
將誤差項傳遞到上一層
我們假設當前為第l層,定義l-1層的誤差項是誤差函數對l-1層加權輸入的導數,即:
式14就是將誤差傳遞到上一層的公式。
權重梯度的計算
對於的權重梯度,我們知道它的梯度是各個時刻梯度之和(證明過程請參考文章),我們首先求出它們在t時刻的梯度,然後再求出他們最終的梯度。
我們已經求得了誤差項,很容易求出t時刻的
:
將各個時刻的梯度加在一起,就能得到最終的梯度:
對於偏置項的梯度,也是將各個時刻的梯度加在一起。下麵是各個時刻的偏置項梯度:
下麵是最終的偏置項梯度,即將各個時刻的偏置項梯度加在一起:
對於的權重梯度,隻需要根據相應的誤差項直接計算即可:
以上就是LSTM的訓練算法的全部公式。因為這裏麵存在很多重複的模式,仔細看看,會發覺並不是太複雜。
當然,LSTM存在著相當多的變體,讀者可以在互聯網上找到很多資料。因為大家已經熟悉了基本LSTM的算法,因此理解這些變體比較容易,因此本文就不再贅述了。
長短時記憶網絡的實現
在下麵的實現中,LSTMLayer的參數包括輸入維度、輸出維度、隱藏層維度,單元狀態維度等於隱藏層維度。gate的激活函數為sigmoid函數,輸出的激活函數為tanh。
激活函數的實現
我們先實現兩個激活函數:sigmoid和tanh。
LSTM初始化
和前兩篇文章代碼架構一樣,我們把LSTM的實現放在LstmLayer類中。
根據LSTM前向計算和方向傳播算法,我們需要初始化一係列矩陣和向量。這些矩陣和向量有兩類用途,一類是用於保存模型參數,例如;另一類是保存各種中間計算結果,以便於反向傳播算法使用,它們包括
,以及各個權重對應的梯度。
在構造函數的初始化中,隻初始化了與forward計算相關的變量,與backward相關的變量沒有初始化。這是因為構造LSTM對象的時候,我們還不知道它未來是用於訓練(既有forward又有backward)還是推理(隻有forward)。
前向計算的實現
forward方法實現了LSTM的前向計算:
從上麵的代碼我們可以看到,門的計算都是相同的算法,而門和的計算僅僅是激活函數不同。因此我們提出了calc_gate方法,這樣減少了很多重複代碼。
反向傳播算法的實現
backward方法實現了LSTM的反向傳播算法。需要注意的是,與backword相關的內部狀態變量是在調用backward方法之後才初始化的。這種延遲初始化的一個好處是,如果LSTM隻是用來推理,那麼就不需要初始化這些變量,節省了很多內存。
算法主要分成兩個部分,一部分使計算誤差項:
另一部分是計算梯度:
梯度下降算法的實現
下麵是用梯度下降算法來更新權重:
梯度檢查的實現
和RecurrentLayer一樣,為了支持梯度檢查,我們需要支持重置內部狀態:
最後,是梯度檢查的代碼:
我們隻對做了檢查,讀者可以自行增加對其他梯度的檢查。下麵是某次梯度檢查的結果:
GRU
前麵我們講了一種普通的LSTM,事實上LSTM存在很多變體,許多論文中的LSTM都或多或少的不太一樣。在眾多的LSTM變體中,GRU (Gated Recurrent Unit)也許是最成功的一種。它對LSTM做了很多簡化,同時卻保持著和LSTM相同的效果。因此,GRU最近變得越來越流行。
GRU對LSTM做了兩個大改動:
-
將輸入門、遺忘門、輸出門變為兩個門:更新門(Update Gate)Zt和重置門(Reset Gate)rt。
-
將單元狀態與輸出合並為一個狀態:h。
GRU的前向計算公式為:
下圖是GRU的示意圖:
GRU的訓練算法比LSTM簡單一些,留給讀者自行推導,本文就不再贅述了。
小結
至此,LSTM——也許是結構最複雜的一類神經網絡——就講完了,相信拿下前幾篇文章的讀者們搞定這篇文章也不在話下吧!現在我們已經了解循環神經網絡和它最流行的變體——LSTM,它們都可以用來處理序列。但是,有時候僅僅擁有處理序列的能力還不夠,還需要處理比序列更為複雜的結構(比如樹結構),這時候就需要用到另外一類網絡:遞歸神經網絡(Recursive Neural Network),巧合的是,它的縮寫也是RNN。
原文發布時間為:2011-01-20
本文來自雲棲社區合作夥伴DBAplus
最後更新:2017-05-15 17:33:45