循環神經網絡(RNN)入門介紹


 

循環神經⽹絡是為更好地處理時序信息而設計的。它引⼊狀態變量來存儲過去的信息,並⽤其與當前的輸⼊共同決定當前的輸出。
循環神經⽹絡常⽤於處理序列數據,如⼀段⽂字或聲⾳、購物或觀影的順序,甚⾄是圖像中的⼀⾏或⼀列像素。因此,循環神經⽹絡有着極為⼴泛的實際應⽤,如語⾔模型、⽂本分類、機器翻譯、語⾳識別、圖像分析、⼿寫識別和推薦系統。

引入

對於2句話,都有Taipei這個詞,但是一個是目的地一個是出發地

如果神經網絡有記憶力,能夠根據上下文對同樣的input詞匯產生不同的輸出,我們就能解決這個問題

像下面兩句話,同樣輸入Taipei,一個輸出“目的地”,一個輸出“出發地”

arrive Taipei on November 2

leave Taipei on November 2

RNN

Recurrent Neural Network(RNN)就是一個有記憶的神經網絡,在隱藏層中的每個隱藏單元產生Output的時候,都會存到Memory

下一次,當有input的時候,這個隱藏單元不僅會考慮input的值,還會考慮存在memory中的值

 

例子

下面舉一個例子,假設圖中的RNN的所有weight都是1,沒有bias,激活函數都是linear

我們的input是一個下圖中的sequence

在我們使用練RNN的時候,我需要先要給memory初始值,比如這里的 0 0

現在輸入[1, 1],隱藏單元還會考慮內存中的0,0,因為我們weight都是1,所以隱藏層神經元的都是 0 + 0 + 1 + 1 =2

由於權重都是1,所以這時候網絡的輸出是[4, 4]

另外,剛剛隱藏層的輸出[2,2]還會被存到memory中去

這時候如果我們的輸入是[1,1],隱藏層的輸出就變為了[6, 6]。因為memory中存着[2,2],輸入是[1,1],權重都為1,所以每一個隱藏層神經元的輸出為1 + 1 + 2 + 2 = 6

這時候網絡的輸出是[12, 12]。可以看到如果memory中的值不一樣,我們同樣的輸入會產生不同的輸出

當我們的輸入是[2,2]的時候,memory中的值是[6,6],跟前面的步驟一樣,我們可以得到輸出是[32, 32]。我們輸入sequence的輸出結果如下

調換輸入sequence中的順序,我的輸出也會不一樣,即RNN考慮了輸入sequence的順序

例子 

讓我們回到一開始講的兩句話,看看RNN是怎么識別Taipei的不同含義(目的地or出發地)

arrive Taipei on November 2

leave Taipei on November 2

首先,網絡接收第一個單詞(arrive)的輸入,經過網絡得到一個輸出,並保存隱藏層的輸出。

然后接收第二個單詞(Taipei)的輸入,經過網絡得到輸出,……,用同樣的網絡結構不斷重復這個行為

所以當兩句不同的話輸入的時候,一個Taipei前面是leave,一個是arrive,而這兩個的vector是不一樣的,所以存在memory中的值不同,這樣就會得到不同的輸出

更一般地,我們的網絡可以不止有一個隱藏層,而是有許多個隱藏層,每個單詞輸入的時候,各個隱藏層都會考慮之前存在memory中的值

 

兩種RNN

我們前面看到的RNN是隱藏層的輸出存起來,當下一個sequence輸入的時候,再讀取來,這個是Elman Network

還有另一種RNN——Jordan Network,它把網絡的整個輸出存到memory中,下一個sequence輸入時再讀出來

隱藏層是沒有target,而output有,所以Jordan Network一般會有比較好的表現

雙向RNN(bidirectional RNN)

我們的RNN可以是雙向的,我們輸入一個句子的時候,它會從句首讀到句尾,但讀取的方向可以反過來,從句尾讀到句首,如上圖中第一個結構。

我們可以同時訓練一個正向的RNN和一個逆向的RNN,把這兩個RNN的隱藏層輸出拿出來,接給一個輸出層

用雙向RNN的好處是網絡可以學習到更多的上下文信息,正向或者反向RNN能學習到的內容有限,只能學習到一半的信息

LSTM

原理

Long Short-Term Memory(LSTM),長短期記憶網絡。

前面提到的Memory是最單純的,我們隨時可以把值存到Memory中去,也可以隨時把值從中讀出來。

但LSTM不是這樣,它有3個gate:

  • 1)input gate
  • 2)ouput gate
  • 3)forget gate

當網絡中的其他部分的output想要寫入memory中的時候,需要通過一個input gate,input gate打開的時候才能夠把值寫到里面,如果Input gate是關閉的,那么就不能把值寫到里面。這個input gate是打開還是關閉是由網絡自己學習的,學習什么時候打開input gate,什么時候關閉。

輸出的地方也有一個output gate,這個這個output gate決定外界能否從memory中讀取值。跟input gate一樣,output gate的開關也由網絡自己學習。

 forget gate決定什么時候memory cell要把memory中的內容忘掉,這個也由網絡自己學習

 整個網絡的結構如下圖所示,我們可以把LSTM看成有4個輸入,1個輸出的特殊神經網絡。這4個值分別是

  • 想存進memory的值
  • 操控input gate的信號
  • 操控output gate的信號
  • 操控forget gate的信號

下面是LSTM更詳細的結構

其中

  • $z$ 是要存到memory中的輸入
  • $z_{i}$ 是操控input gate的信號,是一個數值
  • $z_{f}$ 是操控forget gate的信號,是一個數值
  • $z_{o}$ 是操控output gate的信號,是一個數值
  • $a$ 是輸出
  • memory cell中的初始值是$c$

4個輸入經過gate的時候常用的激活函數是 sigmoid,因為它的值在0到1之間,1代表gate打開,0代表關閉。

下面我們看看網絡是怎么輸入輸出的,首先 $z_{i}$ 和 $z$ 經過神經元得到$g(z)$$f(z_{i})$,將兩者相乘

$z_{f}$ 經過神經元得到 $f(z_{f})$ ,取出memory中的值$c$,相乘得到$cf(z_{f})$

然后將上面得到的兩個相乘的結果相加,得到$c'$$c'$就是新的要存入memory中的值

從上面的式子可以看到,$f(z_{i})$是控制$g(z)$能否輸入的關卡,$f(z_{f})$是控制是否取出memory中的值

如果$f(z_{i})$為0,那么它們相乘為0,跟沒有輸入是一樣的,如果$f(z_{i})$是1,那么相乘會得到$g(z)$本身

$f(z_{f})$是也是同樣的工作原理,如果$f(z_{f})$是1,那么$c$會通過,表示之前存的值還是記得。如果$f(z_{f})$是0,那么相乘得到0,相當於之前的值不記得

可以看出,forget gate打開代表記得,關閉代表忘記

接着$c'$通過$h$,得到$h(c')$,讓$f(z_{o})$$h(c')$相乘,得到輸出

原理也是一樣的,$f(z_{o})$是1,等於$h(c')$可以通過output gate,否則為0,存在memory中的值無法讀取出來

例子

假設我們的網絡有以下性質:

現在的網絡只有一個memory cell,我們的input都是3維的vector,輸出都是1維的vector。

$x_{2}$中的值為1時,$x_{1}$中的值就會被寫到memory中

$x_{2}$中的值為-1時,memory中的值就會被遺忘

$x_{3}$中的值為1時,我們的output gate才會輸出

memory中的初始值為0

那么根據網絡的性質,我們可以得到的輸出和memory中的值如下圖所示,其中藍色方塊代表memory中的值

具體地來看,假設我們網絡的weight如下圖,4個輸入都由我們輸入的向量乘上weight再加上bias得到。

假設網絡中$g$$h$現在是linear激活函數,我們輸入[3,1,0]時,根據我們剛剛介紹的過程,網絡的輸入輸出過程如下圖所示

輸入[4,1,0]和[2,0,0]時,結果如下圖所示,網絡依然沒有輸出

當輸入[1,0,1]的時候,output gate 的值是1,所以memory中的值會取出。輸入是[3,-1,0]的時候,forget gate的值是0,memory中的內容會被忘記

LSTM結構

我們上面談到的LSTM,其實可以變成下面這種形式

但是通常LSTM並不是這樣的,它還會把輸出作為下一個輸入的一部分,還會加一個“peephole”,它會把存在memory中的值也作為下一個輸入的一部分

LSTM通常也不會有一層,可能會有多層

下面我們看看這種LSTM結構是如何計算的,它可以簡化成下面這種形式,它有3個輸入,分別是$x^{t}$$c^{t-1}$$h^{t-1}$

這時候我們的3個輸入分別乘以各自的權重,其中$c^{t-1}$的權重矩陣是對角矩陣,這是為了減少訓練的參數。

像前面介紹的LSTM一樣,我們其他門的輸入如下圖所示,各部分的的權重矩陣不一樣的,

memory cell 中要存入的內容如下,$\odot $是 element wise 乘法(對應元素相乘)。

輸出的$h^{t}$$y^{t}$如下

最終我們得到的 LSTM 的一個 block 就像下面的樣子

GRU

表現上看,GRU會輸入一個$h^{t-1}$,輸出一個$h^{t}$,輸入一個$x^{t}$,輸出一個$y^{t}$。看起來很像RNN,其實它更像LSTM。GRU里面這個$h^{t}$的作用更像是LSTM中$c^{t}$的作用。GRU中我們可以看作是 forget gate 和 input gate 是聯動的LSTM,一個開另一個就要關。

GRU(gated recurrent unit)門控循環單元,它引入了reset gate(重置門)update gate(更新門)的概念,類似LSTM。

相較於LSTM,它只有2個gate,所以它的參數量比較少,它的思想是:舊的不去,新的不來要把 memory 中的值清掉,才能放進新的值
重置門的輸入包括當前時間的輸入$X_{t}$和 memory 中的值$H_{t-1}$經過 sigmoid 激活函數得到輸出$R_{t}$$Z_{t}$

重置門和更新門的計算公式如下,其中$W$是weight

然后我們讓重置門的輸出$R_{t}$ 與 memory 中的值$H_{t-1}$相乘,如果結果是0,則將 memory 中的值重置為0

跟LSTM中一樣,輸入$X_{t}$跟權重$W_{xh}$相乘,然后把這個結果跟上面相乘的結果相加。 

得到一個候選值$\tilde{H_{t}}$,其中內容是:

從上⾯這個公式可以看出,重置門控制了上⼀時間步的 memory 如何流⼊當前時間步的候選值

上⼀時間步的 memory 可能包含了時間序列截⾄上⼀時間步的全部歷史信息。因此,重置⻔可以⽤來丟棄與預測⽆關的歷史信息

然后用當前更新門$Z_{t}$來對上⼀時間步的 memory $H_{t-1}$和當前時間步的候選值$\tilde{H_{t}}$做組合,得到新的要存入 memory 中的值:

更新門$Z_{t}$取值為0的時候,新的 memory 只跟候選值有關,而候選值又是經過激活函數$tanh$輸出的,所以在-1和1之間。

當更新門$Z_{t}$為1的時候,新 memory 同等於上一時間點的 memory ,即不執行更新操作。

如果更新門$Z_{t}$$t'$$t$$t'$ < $t$之間一直是1,那么$t'$$t$之間輸入的信息幾乎沒有流入 memory 中,一直保持較早的memory。

RNN網絡學習過程

損失函數

我們訓練網絡的時候,我們需要定義一個損失函數。在RNN中,我們需要怎么定義損失函數?

假設我們現在要做slot filling,我們會有訓練數據,數據是一些sentence,然后需要給sentence一些label,告訴我們不同的詞屬於不同的slot

然后把單詞輸入到網絡中,RNN會得到一個輸出$y^{1}$,接下來這個$y^{1}$會和一個 reference vector 算它的 cross entropy 。

當我們輸入是單詞arrive的時候,$y^{1}$應該跟“other”對應的reference vector距離越近越好,這個reference vector的長度就是我們slot的數目,后面輸入的單詞同理。

 我們損失函數就是:每一個時間點的RNN的outputreference vector 的 cross entropy ,這就是我們要優化的函數。

訓練過程

訓練也是使用梯度下降來更新參數,我們已經有了損失函數,我們要更新網絡中某個參數$w$的公式如下

但是為了計算方便,開發了一套算法,這套算法是BackPropagation(反向傳播)的進階版,叫做BackPropagation through time(BPTT)

它跟BackPropagation是很類似的,只是RNN在time sequence上做運算,所以需要考慮時間的信息

RNN的訓練是比較困難的,一般我們都希望我們的學習曲線像下圖中的藍色曲線一樣,但我們訓練RNN的時候,有時候會看到綠色這條線,訓練曲線劇烈抖動

這是因為RNN的error surface(total loss對參數的變化)對參數的變化是非常崎嶇的,error surface有些地方很平坦,有些地方很陡峭。

如下圖我們,假設我們的起始位置左下方的橙色點,第一次更新參數讓我的 loss 上升,來到右邊的橙色點

第二次更新參數的時候,我們的學習率剛好讓我們來到了左上方的橙色點,這時候loss就突然增加

 可以用Clipping解決這一問題,當梯度大於某個閾值的時候,就不要讓梯度等於閾值

在RNN中,激活函數使用ReLU的表現通常比sigmoid差

那為什會這樣?我們可以用一個直觀的例子來看梯度的大小是怎么樣的:就是我們把某一個參數做小小的變化,看網絡的輸出變化有多大。就能測出這個參數梯度的大小。

比如下面這個例子:

我們有一個簡單的RNN,輸入輸出的weight都是1,memory接到隱層單元的weight是$w$,隱層單元激活函數是linear,假設我們輸入[1,0,...,0],那我們的輸出是$w^{999}$

 我們改變$w$的值,對網絡的輸出有多大影響,假設此時$w$為1,那么$w$增加0.1,網絡的輸出會增加特別多,所以$w$在1的地方特別大的梯度

如果我們的學習率設小,可以彌補這種缺陷嗎?我們來看一下$w$為0.99和0.1的情況,可以發現輸出都是0,說明在0.99的地方梯度變得很小。

所以學習率的設置不能改變這種局面,因為我們的梯度時大時小

RNN訓練的問題在於:RNN把同樣的東西在 transitio的時候反復使用,即memory接到隱藏層的權重$w$

 有什么技巧可以幫助我們解決RNN的這一問題?

  • LSTM可以解決梯度消失的問題,但不能解決梯度爆炸的問題。所以我們可以把學習率設置小一點。
    • 跟普通 RNN 相比,LSTM 在處理 memory 中的操作是不一樣的。RNN中每個時間點都會更新memory中的值。LSTM中,它將 memory 乘上一個值再和輸入相加的放到memory cell中。
    • RNN中如果memory中的值清零了,那weight的影響就不存在了。weight 如果影響 LSTM中 memory 的值的話,這個影響會一直持續到 memory 中的內容被忘掉。一般我們需要給 forget gate 一個較大的 bias,讓它在少數情況下才會清零。

RNN的輸入和輸出形式

One to One:輸入和輸出都是一個vector。

One to Many:輸入是vector,輸出是vector sequence

Many to One:輸入是一個vector sequence,輸出是一個vector。比如情感分析,輸出哪些文章是正類,哪些是負類;比如文檔關鍵詞提取;

Many to Many:輸入輸出都是vector sequence。輸出sequence比輸入sequence少的例子:比如語音識別任務,輸入是聲音信號(每隔一小段時間用vector表示),輸出是character sequence。通常是好多個vector對應到一個character。

Sqe2Seq簡介 

要理解word sequence的含義,單詞在句子的位置信息是不能忽略的。下面兩句話有同樣的單詞,但單詞的位置不同,含義也完全相反

但是如果用 bag-of-word 來描述的話,它們是完全一樣的。

我們可以用Seq2Seq Auto-encoder 來考慮 word sequence 有順序的情況,把一個 document 變成一個 vector

我們輸入一個word sequence,通過一個RNN,把它變成一個embedding vector,再把這個 embedding vector 當做 Decoder 的輸入,讓 Decoder 造回一個一模一樣的句子

如果RNN可以做到這一件事,那么 encoder 的這個 vector 就代表 input sequence 里面重要的信息。所以 Decoder 才能根據 Encoder 的 vector,把這個序列 decode 回來。

訓練Seq2Seq我們不需要labeled data,只需要有大量的文章,然后輸入到網絡中。

它也可以是 hierarchy 結構,我們先得到每個句子的vector,然后把這些vector加起來,變成一整個 document higher-level 的 vector。再用這個higher-level  vector產生一串 sentence vector,再根據每一個 sentence  vector解回 word sequence。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM