零基礎入門深度學習(5) - 循環神經網絡

往期回顧

在前面的文章系列文章中,咱們介紹了全鏈接神經網絡和卷積神經網絡,以及它們的訓練和使用。他們都只能單獨的取處理一個個的輸入,前一個輸入和後一個輸入是徹底沒有關係的。可是,某些任務須要可以更好的處理序列的信息,即前面的輸入和後面的輸入是有關係的。好比,當咱們在理解一句話意思時,孤立的理解這句話的每一個詞是不夠的,咱們須要處理這些詞鏈接起來的整個序列;當咱們處理視頻的時候,咱們也不能只單獨的去分析每一幀,而要分析這些幀鏈接起來的整個序列。這時,就須要用到深度學習領域中另外一類很是重要神經網絡:循環神經網絡(Recurrent Neural Network)。RNN種類不少,也比較繞腦子。不過讀者不用擔憂,本文將一如既往的對複雜的東西剝繭抽絲,幫助您理解RNNs以及它的訓練算法,並動手實現一個循環神經網絡。python

 

語言模型

RNN是在天然語言處理領域中最早被用起來的,好比,RNN能夠爲語言模型來建模。那麼,什麼是語言模型呢?git

咱們能夠和電腦玩一個遊戲,咱們寫出一個句子前面的一些詞,而後,讓電腦幫咱們寫下接下來的一個詞。好比下面這句:github

我昨天上學遲到了,老師批評了____。算法

咱們給電腦展現了這句話前面這些詞,而後,讓電腦寫下接下來的一個詞。在這個例子中,接下來的這個詞最有多是『我』,而不太多是『小明』,甚至是『吃飯』。數組

語言模型就是這樣的東西:給定一個一句話前面的部分,預測接下來最有可能的一個詞是什麼。網絡

語言模型是對一種語言的特徵進行建模,它有不少不少用處。好比在語音轉文本(STT)的應用中,聲學模型輸出的結果,每每是若干個可能的候選詞,這時候就須要語言模型來從這些候選詞中選擇一個最可能的。固然,它一樣也能夠用在圖像到文本的識別中(OCR)。app

使用RNN以前,語言模型主要是採用N-Gram。N能夠是一個天然數,好比2或者3。它的含義是,假設一個詞出現的機率只與前面N個詞相關。咱們以2-Gram爲例。首先,對前面的一句話進行切詞:dom

我 昨天 上學 遲到 了 ,老師 批評 了 ____。python2.7

若是用2-Gram進行建模,那麼電腦在預測的時候,只會看到前面的『了』,而後,電腦會在語料庫中,搜索『了』後面最可能的一個詞。無論最後電腦選的是否是『我』,咱們都知道這個模型是不靠譜的,由於『了』前面說了那麼一大堆其實是沒有用到的。若是是3-Gram模型呢,會搜索『批評了』後面最可能的詞,感受上比2-Gram靠譜了很多,但仍是遠遠不夠的。由於這句話最關鍵的信息『我』,遠在9個詞以前!ide

如今讀者可能會想,能夠提高繼續提高N的值呀,好比4-Gram、5-Gram.......。實際上,這個想法是沒有實用性的。由於咱們想處理任意長度的句子,N設爲多少都不合適;另外,模型的大小和N的關係是指數級的,4-Gram模型就會佔用海量的存儲空間。

因此,該輪到RNN出場了,RNN理論上能夠往前看(日後看)任意多個詞。

 

循環神經網絡是啥

循環神經網絡種類繁多,咱們先從最簡單的基本循環神經網絡開始吧。

 

基本循環神經網絡

下圖是一個簡單的循環神經網絡如,它由輸入層、一個隱藏層和一個輸出層組成:

納尼?!相信第一次看到這個玩意的讀者心裏和我同樣是崩潰的。由於循環神經網絡實在是太難畫出來了,網上全部大神們都不得不用了這種抽象藝術手法。不過,靜下心來仔細看看的話,其實也是很好理解的。若是把上面有W的那個帶箭頭的圈去掉,它就變成了最普通的全鏈接神經網絡。x是一個向量,它表示輸入層的值(這裏面沒有畫出來表示神經元節點的圓圈);s是一個向量,它表示隱藏層的值(這裏隱藏層面畫了一個節點,你也能夠想象這一層實際上是多個節點,節點數與向量s的維度相同);U是輸入層到隱藏層的權重矩陣(讀者能夠回到第三篇文章零基礎入門深度學習(3) - 神經網絡和反向傳播算法,看看咱們是怎樣用矩陣來表示全鏈接神經網絡的計算的);o也是一個向量,它表示輸出層的值;V是隱藏層到輸出層的權重矩陣。那麼,如今咱們來看看W是什麼。循環神經網絡的隱藏層的值s不只僅取決於當前此次的輸入x,還取決於上一次隱藏層的值s。權重矩陣 W就是隱藏層上一次的值做爲這一次的輸入的權重。

若是咱們把上面的圖展開,循環神經網絡也能夠畫成下面這個樣子:

如今看上去就比較清楚了,這個網絡在t時刻接收到輸入以後,隱藏層的值是,輸出值是。關鍵一點是,的值不只僅取決於,還取決於。咱們能夠用下面的公式來表示循環神經網絡的計算方法:

 

 

式式

 

式1是輸出層的計算公式,輸出層是一個全鏈接層,也就是它的每一個節點都和隱藏層的每一個節點相連。V是輸出層的權重矩陣,g是激活函數。式2是隱藏層的計算公式,它是循環層。U是輸入x的權重矩陣,W是上一次的值做爲這一次的輸入的權重矩陣,f是激活函數。

從上面的公式咱們能夠看出,循環層和全鏈接層的區別就是循環層多了一個權重矩陣 W。

若是反覆把式2帶入到式1,咱們將獲得:

 

 

 

 

從上面能夠看出,循環神經網絡的輸出值,是受前面歷次輸入值、...影響的,這就是爲何循環神經網絡能夠往前看任意多個輸入值的緣由。

 

雙向循環神經網絡

對於語言模型來講,不少時候光看前面的詞是不夠的,好比下面這句話:

個人手機壞了,我打算____一部新手機。

能夠想象,若是咱們只看橫線前面的詞,手機壞了,那麼我是打算修一修?換一部新的?仍是大哭一場?這些都是沒法肯定的。但若是咱們也看到了橫線後面的詞是『一部新手機』,那麼,橫線上的詞填『買』的機率就大得多了。

在上一小節中的基本循環神經網絡是沒法對此進行建模的,所以,咱們須要雙向循環神經網絡,以下圖所示:

當遇到這種從將來穿越回來的場景時,不免處於懵逼的狀態。不過咱們仍是能夠用屢試不爽的老辦法:先分析一個特殊場景,而後再總結通常規律。咱們先考慮上圖中,的計算。

從上圖能夠看出,雙向卷積神經網絡的隱藏層要保存兩個值,一個A參與正向計算,另外一個值A'參與反向計算。最終的輸出值取決於。其計算方法爲:

 

 

 

 

則分別計算:

 

 

 

 

如今,咱們已經能夠看出通常的規律:正向計算時,隱藏層的值有關;反向計算時,隱藏層的值有關;最終的輸出取決於正向和反向計算的加和。如今,咱們仿照式1和式2,寫出雙向循環神經網絡的計算方法:

 

 

 

 

從上面三個公式咱們能夠看到,正向計算和反向計算不共享權重,也就是說U和U'、W和W'、V和V'都是不一樣的權重矩陣。

 

深度循環神經網絡

前面咱們介紹的循環神經網絡只有一個隱藏層,咱們固然也能夠堆疊兩個以上的隱藏層,這樣就獲得了深度循環神經網絡。以下圖所示:

咱們把第i個隱藏層的值表示爲,則深度循環神經網絡的計算方式能夠表示爲:

 

 

 

 

 

循環神經網絡的訓練

 

循環神經網絡的訓練算法:BPTT

BPTT算法是針對循環層的訓練算法,它的基本原理和BP算法是同樣的,也包含一樣的三個步驟:

  1. 前向計算每一個神經元的輸出值;
  2. 反向計算每一個神經元的偏差項值,它是偏差函數E對神經元j的加權輸入的偏導數;
  3. 計算每一個權重的梯度。

最後再用隨機梯度降低算法更新權重。

循環層以下圖所示:

 

前向計算

使用前面的式2對循環層進行前向計算:

 

 

 

 

注意,上面的都是向量,用黑體字母表示;而U、V是矩陣,用大寫字母表示。向量的下標表示時刻,例如,表示在t時刻向量s的值。

咱們假設輸入向量x的維度是m,輸出向量s的維度是n,則矩陣U的維度是,矩陣W的維度是。下面是上式展開成矩陣的樣子,看起來更直觀一些:

 

 

 

 

在這裏咱們用手寫體字母表示向量的一個元素,它的下標表示它是這個向量的第幾個元素,它的上標表示第幾個時刻。例如,表示向量s的第j個元素在t時刻的值。表示輸入層第i個神經元到循環層第j個神經元的權重。表示循環層第t-1時刻的第i個神經元到循環層第t個時刻的第j個神經元的權重。

 

偏差項的計算

BTPP算法將第l層t時刻的偏差項值沿兩個方向傳播,一個方向是其傳遞到上一層網絡,獲得,這部分只和權重矩陣U有關;另外一個是方向是將其沿時間線傳遞到初始時刻,獲得,這部分只和權重矩陣W有關。

咱們用向量表示神經元在t時刻的加權輸入,由於:

 

 

 

 

所以:

 

 

 

 

咱們用a表示列向量,用表示行向量。上式的第一項是向量函數對向量求導,其結果爲Jacobian矩陣:

 

 

 

 

同理,上式第二項也是一個Jacobian矩陣:

 

 

 

 

其中,diag[a]表示根據向量a建立一個對角矩陣,即

 

 

 

 

最後,將兩項合在一塊兒,可得:

 

 

 

 

上式描述了將沿時間往前傳遞一個時刻的規律,有了這個規律,咱們就能夠求得任意時刻k的偏差項

 

 

 

式3就是將偏差項沿時間反向傳播的算法。

循環層將偏差項反向傳遞到上一層網絡,與普通的全鏈接層是徹底同樣的,這在前面的文章零基礎入門深度學習(3) - 神經網絡和反向傳播算法中已經詳細講過了,在此僅簡要描述一下。

循環層的加權輸入與上一層的加權輸入關係以下:

 

 

 

 

上式中是第l層神經元的加權輸入(假設第l層是循環層);是第l-1層神經元的加權輸入;是第l-1層神經元的輸出;是第l-1層的激活函數。

 

 

 

 

因此,

 

 

 

式4就是將偏差項傳遞到上一層算法。

 

權重梯度的計算

如今,咱們終於來到了BPTT算法的最後一步:計算每一個權重的梯度。

首先,咱們計算偏差函數E對權重矩陣W的梯度

上圖展現了咱們到目前爲止,在前兩步中已經計算獲得的量,包括每一個時刻t 循環層的輸出值,以及偏差項

回憶一下咱們在文章零基礎入門深度學習(3) - 神經網絡和反向傳播算法介紹的全鏈接網絡的權重梯度計算算法:只要知道了任意一個時刻的偏差項,以及上一個時刻循環層的輸出值,就能夠按照下面的公式求出權重矩陣在t時刻的梯度

 

 

 

在式5中,表示t時刻偏差項向量的第i個份量;表示t-1時刻循環層第i個神經元的輸出值。

咱們下面能夠簡單推導一下式5。

咱們知道:

 

 

 

 

由於對W求導與無關,咱們再也不考慮。如今,咱們考慮對權重項求導。經過觀察上式咱們能夠看到只與有關,因此:

 

 

 

 

按照上面的規律就能夠生成式5裏面的矩陣。

咱們已經求得了權重矩陣W在t時刻的梯度,最終的梯度是各個時刻的梯度之和:

 

 

 

式6就是計算循環層權重矩陣W的梯度的公式。

----------數學公式超高能預警----------

前面已經介紹了的計算方法,看上去仍是比較直觀的。然而,讀者也許會困惑,爲何最終的梯度是各個時刻的梯度之和呢?咱們前面只是直接用了這個結論,實際上這裏面是有道理的,只是這個數學推導比較繞腦子。感興趣的同窗能夠仔細閱讀接下來這一段,它用到了矩陣對矩陣求導、張量與向量相乘運算的一些法則。

咱們仍是從這個式子開始:

 

 

 

 

由於與W徹底無關,咱們把它看作常量。如今,考慮第一個式子加號右邊的部分,由於W和都是W的函數,所以咱們要用到大學裏面都學過的導數乘法運算:

 

 

 

 

所以,上面第一個式子寫成:

 

 

 

 

咱們最終須要計算的是

 

 

 

咱們先計算式7加號左邊的部分。是矩陣對矩陣求導,其結果是一個四維張量(tensor),以下所示:

 

 

 

 

接下來,咱們知道,它是一個列向量。咱們讓上面的四維張量與這個向量相乘,獲得了一個三維張量,再左乘行向量,最終獲得一個矩陣:

 

 

 

 

接下來,咱們計算式7加號右邊的部分:

 

 

 

 

因而,咱們獲得了以下遞推公式:

 

 

 

 

這樣,咱們就證實了:最終的梯度是各個時刻的梯度之和。

----------數學公式超高能預警解除----------

同權重矩陣W相似,咱們能夠獲得權重矩陣U的計算方法。

 

 

 

式8是偏差函數在t時刻對權重矩陣U的梯度。和權重矩陣W同樣,最終的梯度也是各個時刻的梯度之和:

 

 

 

 

具體的證實這裏就再也不贅述了,感興趣的讀者能夠練習推導一下。

 

RNN的梯度爆炸和消失問題

不幸的是,實踐中前面介紹的幾種RNNs並不能很好的處理較長的序列。一個主要的緣由是,RNN在訓練中很容易發生梯度爆炸和梯度消失,這致使訓練時梯度不能在較長序列中一直傳遞下去,從而使RNN沒法捕捉到長距離的影響。

爲何RNN會產生梯度爆炸和消失問題呢?咱們接下來將詳細分析一下緣由。咱們根據式3可得:

 

 

 

 

上式的定義爲矩陣的模的上界。由於上式是一個指數函數,若是t-k很大的話(也就是向前看很遠的時候),會致使對應的偏差項的值增加或縮小的很是快,這樣就會致使相應的梯度爆炸和梯度消失問題(取決於大於1仍是小於1)。

一般來講,梯度爆炸更容易處理一些。由於梯度爆炸的時候,咱們的程序會收到NaN錯誤。咱們也能夠設置一個梯度閾值,當梯度超過這個閾值的時候能夠直接截取。

梯度消失更難檢測,並且也更難處理一些。總的來講,咱們有三種方法應對梯度消失問題:

  1. 合理的初始化權重值。初始化權重,使每一個神經元儘量不要取極大或極小值,以躲開梯度消失的區域。
  2. 使用relu代替sigmoid和tanh做爲激活函數。原理請參考上一篇文章零基礎入門深度學習(4) - 卷積神經網絡的激活函數一節。
  3. 使用其餘結構的RNNs,好比長短時記憶網絡(LTSM)和Gated Recurrent Unit(GRU),這是最流行的作法。咱們將在之後的文章中介紹這兩種網絡。
 

RNN的應用舉例——基於RNN的語言模型

如今,咱們介紹一下基於RNN語言模型。咱們首先把詞依次輸入到循環神經網絡中,每輸入一個詞,循環神經網絡就輸出截止到目前爲止,下一個最可能的詞。例如,當咱們依次輸入:

我 昨天 上學 遲到 了

神經網絡的輸出以下圖所示:

其中,s和e是兩個特殊的詞,分別表示一個序列的開始和結束。

 

向量化

咱們知道,神經網絡的輸入和輸出都是向量,爲了讓語言模型可以被神經網絡處理,咱們必須把詞表達爲向量的形式,這樣神經網絡才能處理它。

神經網絡的輸入是詞,咱們能夠用下面的步驟對輸入進行向量化:

  1. 創建一個包含全部詞的詞典,每一個詞在詞典裏面有一個惟一的編號。
  2. 任意一個詞均可以用一個N維的one-hot向量來表示。其中,N是詞典中包含的詞的個數。假設一個詞在詞典中的編號是i,v是表示這個詞的向量,是向量的第j個元素,則:
 

 

 

 

上面這個公式的含義,能夠用下面的圖來直觀的表示:

使用這種向量化方法,咱們就獲得了一個高維、稀疏的向量(稀疏是指絕大部分元素的值都是0)。處理這樣的向量會致使咱們的神經網絡有不少的參數,帶來龐大的計算量。所以,每每會須要使用一些降維方法,將高維的稀疏向量轉變爲低維的稠密向量。不過這個話題咱們就再也不這篇文章中討論了。

語言模型要求的輸出是下一個最可能的詞,咱們可讓循環神經網絡計算計算詞典中每一個詞是下一個詞的機率,這樣,機率最大的詞就是下一個最可能的詞。所以,神經網絡的輸出向量也是一個N維向量,向量中的每一個元素對應着詞典中相應的詞是下一個詞的機率。以下圖所示:

 

Softmax層

前面提到,語言模型是對下一個詞出現的機率進行建模。那麼,怎樣讓神經網絡輸出機率呢?方法就是用softmax層做爲神經網絡的輸出層。

咱們先來看一下softmax函數的定義:

 

 

 

 

這個公式看起來可能很暈,咱們舉一個例子。Softmax層以下圖所示:

從上圖咱們能夠看到,softmax layer的輸入是一個向量,輸出也是一個向量,兩個向量的維度是同樣的(在這個例子裏面是4)。輸入向量x=[1 2 3 4]通過softmax層以後,通過上面的softmax函數計算,轉變爲輸出向量y=[0.03 0.09 0.24 0.64]。計算過程爲:

 

 

 

 

咱們來看看輸出向量y的特徵:

  1. 每一項爲取值爲0-1之間的正數;
  2. 全部項的總和是1。

咱們不難發現,這些特徵和機率的特徵是同樣的,所以咱們能夠把它們看作是機率。對於語言模型來講,咱們能夠認爲模型預測下一個詞是詞典中第一個詞的機率是0.03,是詞典中第二個詞的機率是0.09,以此類推。

 

語言模型的訓練

可使用監督學習的方法對語言模型進行訓練,首先,須要準備訓練數據集。接下來,咱們介紹怎樣把語料

我 昨天 上學 遲到 了

轉換成語言模型的訓練數據集。

首先,咱們獲取輸入-標籤對:

輸入 標籤
s
昨天
昨天 上學
上學 遲到
遲到
e

而後,使用前面介紹過的向量化方法,對輸入x和標籤y進行向量化。這裏面有意思的是,對標籤y進行向量化,其結果也是一個one-hot向量。例如,咱們對標籤『我』進行向量化,獲得的向量中,只有第2019個元素的值是1,其餘位置的元素的值都是0。它的含義就是下一個詞是『我』的機率是1,是其它詞的機率都是0。

最後,咱們使用交叉熵偏差函數做爲優化目標,對模型進行優化。

在實際工程中,咱們可使用大量的語料來對模型進行訓練,獲取訓練數據和訓練的方法都是相同的。

 

交叉熵偏差

通常來講,當神經網絡的輸出層是softmax層時,對應的偏差函數E一般選擇交叉熵偏差函數,其定義以下:

 

 

 

 

在上式中,N是訓練樣本的個數,向量是樣本的標記,向量是網絡的輸出。標記是一個one-hot向量,例如,若是網絡的輸出,那麼,交叉熵偏差是(假設只有一個訓練樣本,即N=1):

 

 

 

 

咱們固然能夠選擇其餘函數做爲咱們的偏差函數,好比最小平方偏差函數(MSE)。不過對機率進行建模時,選擇交叉熵偏差函數更make sense。具體緣由,感興趣的讀者請閱讀參考文獻7

 

RNN的實現

完整代碼請參考GitHub: https://github.com/hanbt/learn_dl/blob/master/rnn.py (python2.7)

爲了加深咱們對前面介紹的知識的理解,咱們來動手實現一個RNN層。咱們複用了上一篇文章零基礎入門深度學習(4) - 卷積神經網絡中的一些代碼,因此先把它們導入進來。

 
  1. import numpy as np
  2. from cnn import ReluActivator, IdentityActivator, element_wise_op

咱們用RecurrentLayer類來實現一個循環層。下面的代碼是初始化一個循環層,能夠在構造函數中設置卷積層的超參數。咱們注意到,循環層有兩個權重數組,U和W。

 
  1. class RecurrentLayer(object):
  2. def __init__(self, input_width, state_width,
  3. activator, learning_rate):
  4. self.input_width = input_width
  5. self.state_width = state_width
  6. self.activator = activator
  7. self.learning_rate = learning_rate
  8. self.times = 0 # 當前時刻初始化爲t0
  9. self.state_list = [] # 保存各個時刻的state
  10. self.state_list.append(np.zeros(
  11. (state_width, 1))) # 初始化s0
  12. self.U = np.random.uniform(-1e-4, 1e-4,
  13. (state_width, input_width)) # 初始化U
  14. self.W = np.random.uniform(-1e-4, 1e-4,
  15. (state_width, state_width)) # 初始化W

在forward方法中,實現循環層的前向計算,這部分比較簡單。

 
  1. def forward(self, input_array):
  2. '''
  3. 根據『式2』進行前向計算
  4. '''
  5. self.times += 1
  6. state = (np.dot(self.U, input_array) +
  7. np.dot(self.W, self.state_list[-1]))
  8. element_wise_op(state, self.activator.forward)
  9. self.state_list.append(state)

在backword方法中,實現BPTT算法。

 
  1. def backward(self, sensitivity_array,
  2. activator):
  3. '''
  4. 實現BPTT算法
  5. '''
  6. self.calc_delta(sensitivity_array, activator)
  7. self.calc_gradient()
  8. def calc_delta(self, sensitivity_array, activator):
  9. self.delta_list = [] # 用來保存各個時刻的偏差項
  10. for i in range(self.times):
  11. self.delta_list.append(np.zeros(
  12. (self.state_width, 1)))
  13. self.delta_list.append(sensitivity_array)
  14. # 迭代計算每一個時刻的偏差項
  15. for k in range(self.times - 1, 0, -1):
  16. self.calc_delta_k(k, activator)
  17. def calc_delta_k(self, k, activator):
  18. '''
  19. 根據k+1時刻的delta計算k時刻的delta
  20. '''
  21. state = self.state_list[k+1].copy()
  22. element_wise_op(self.state_list[k+1],
  23. activator.backward)
  24. self.delta_list[k] = np.dot(
  25. np.dot(self.delta_list[k+1].T, self.W),
  26. np.diag(state[:,0])).T
  27. def calc_gradient(self):
  28. self.gradient_list = [] # 保存各個時刻的權重梯度
  29. for t in range(self.times + 1):
  30. self.gradient_list.append(np.zeros(
  31. (self.state_width, self.state_width)))
  32. for t in range(self.times, 0, -1):
  33. self.calc_gradient_t(t)
  34. # 實際的梯度是各個時刻梯度之和
  35. self.gradient = reduce(
  36. lambda a, b: a + b, self.gradient_list,
  37. self.gradient_list[0]) # [0]被初始化爲0且沒有被修改過
  38. def calc_gradient_t(self, t):
  39. '''
  40. 計算每一個時刻t權重的梯度
  41. '''
  42. gradient = np.dot(self.delta_list[t],
  43. self.state_list[t-1].T)
  44. self.gradient_list[t] = gradient

有意思的是,BPTT算法雖然數學推導的過程很麻煩,可是寫成代碼卻並不複雜。

在update方法中,實現梯度降低算法。

 
  1. def update(self):
  2. '''
  3. 按照梯度降低,更新權重
  4. '''
  5. self.W -= self.learning_rate * self.gradient

上面的代碼不包含權重U的更新。這部分實際上和全鏈接神經網絡是同樣的,留給感興趣的讀者本身來完成吧。

循環層是一個帶狀態的層,每次forword都會改變循環層的內部狀態,這給梯度檢查帶來了麻煩。所以,咱們須要一個reset_state方法,來重置循環層的內部狀態。

 
  1. def reset_state(self):
  2. self.times = 0 # 當前時刻初始化爲t0
  3. self.state_list = [] # 保存各個時刻的state
  4. self.state_list.append(np.zeros(
  5. (self.state_width, 1))) # 初始化s0

最後,是梯度檢查的代碼。

 
  1. def gradient_check():
  2. '''
  3. 梯度檢查
  4. '''
  5. # 設計一個偏差函數,取全部節點輸出項之和
  6. error_function = lambda o: o.sum()
  7. rl = RecurrentLayer(3, 2, IdentityActivator(), 1e-3)
  8. # 計算forward值
  9. x, d = data_set()
  10. rl.forward(x[0])
  11. rl.forward(x[1])
  12. # 求取sensitivity map
  13. sensitivity_array = np.ones(rl.state_list[-1].shape,
  14. dtype=np.float64)
  15. # 計算梯度
  16. rl.backward(sensitivity_array, IdentityActivator())
  17. # 檢查梯度
  18. epsilon = 10e-4
  19. for i in range(rl.W.shape[0]):
  20. for j in range(rl.W.shape[1]):
  21. rl.W[i,j] += epsilon
  22. rl.reset_state()
  23. rl.forward(x[0])
  24. rl.forward(x[1])
  25. err1 = error_function(rl.state_list[-1])
  26. rl.W[i,j] -= 2*epsilon
  27. rl.reset_state()
  28. rl.forward(x[0])
  29. rl.forward(x[1])
  30. err2 = error_function(rl.state_list[-1])
  31. expect_grad = (err1 - err2) / (2 * epsilon)
  32. rl.W[i,j] += epsilon
  33. print 'weights(%d,%d): expected - actural %f - %f' % (
  34. i, j, expect_grad, rl.gradient[i,j])

須要注意,每次計算error以前,都要調用reset_state方法重置循環層的內部狀態。下面是梯度檢查的結果,沒問題!

 

小節

至此,咱們講完了基本的循環神經網絡、它的訓練算法:BPTT,以及在語言模型上的應用。RNN比較燒腦,相信拿下前幾篇文章的讀者們搞定這篇文章也不在話下吧!然而,循環神經網絡這個話題並無完結。咱們在前面說到過,基本的循環神經網絡存在梯度爆炸和梯度消失問題,並不能真正的處理好長距離的依賴(雖然有一些技巧能夠減輕這些問題)。事實上,真正獲得普遍的應用的是循環神經網絡的一個變體:長短時記憶網絡。它內部有一些特殊的結構,能夠很好的處理長距離的依賴,咱們將在下一篇文章中詳細的介紹它。如今,讓咱們稍事休息,準備挑戰更爲燒腦的長短時記憶網絡吧。

 

參考資料

  1. RECURRENT NEURAL NETWORKS TUTORIAL
  2. Understanding LSTM Networks
  3. The Unreasonable Effectiveness of Recurrent Neural Networks
  4. Attention and Augmented Recurrent Neural Networks
  5. On the difficulty of training recurrent neural networks, Bengio et al.
  6. Recurrent neural network based language model, Mikolov et al.
  7. Neural Network Classification, Categorical Data, Softmax Activation, and Cross Entropy Error, McCaffrey
  8. 轉載自:https://zybuluo.com/hanbingtao/note/541458
相關文章
相關標籤/搜索