RNN Tutorial(1)

主要採用CS231n RNN部分的slide和CS224 RNN部分的note,綜合了兩者的說法,盡量保證內容完整,清晰,邏輯連貫,因此部分內容使用原paper的說法,以及Tensorflow的tutorial部分。內容以自然語言處理為主,以機器視覺為輔。

主要作用:較完整的RNN入門;相關內容的查漏補缺;鼓勵通過做筆記來紮實學習的方法。

導引:

1.RNN基礎模型:

1.1Recurrent Neural Networks(RNN)

1.2LSTM and GRU

1.3語言模型 Language Models

2.RNN在NLP中應用:

2.1翻譯模型(NMT)

2.2Deep Bidirectional RNNs

2.3Attention 機制

3.與視覺相關模型

3.1圖像描述 Image Captioning

3.2問答系統 Question Answering和VQA

其中1、2、3分別為一節,每節一篇文章。

以語言模型作為問題的出發點:由m個詞{w_1,…,w_n}組成的句子的概率:

按照傳統的觀點看,要建模和計算之前所有詞的條件概率是很困難的,所以以前常採用當前詞僅與前一個詞有關(Markov假設),或者當前詞僅與固定長度(窗口)的詞有關:

然而在現實應用時,這種模型具有局限性。

Recurrent Neural Networks(RNN)

不像之前有限窗口的方法,RNN可以利用之前所有詞的信息:RNN使用狀態(state)變數來儲存之前的信息,每次計算後,狀態會更新,吸收新的詞的信息。因此理論上RNN能利用任意長度的序列信息,然而實際上RNN只能利用有限長的先前信息(之後詳細討論)。

RNN的基礎結構:

上圖的摺疊表示的RNN(左)等價於展開表示(右)

計算過程

每一個時刻t,對當前詞x_t和先前狀態s_{t-1}進行計算:s_t=f(Ux_t+Ws_{t-1})得到新的狀態s_tf為一激活函數,如sigmoid,tanh,relu等,可以看出新狀態s_t與之前狀態s_{t-1}和當前詞x_t都有關,因此其與之前所有詞相關,儲存了所有詞的信息。注意點:

  • x_t為當前輸入,在NLP任務中,使用one-hot的向量表示,或者使用詞向量表示。而在視覺任務中,可以是圖像經過CNN提取的特徵。
  • 初始狀態s_0,常取零向量。
  • 網路參數W,U,V,不隨時間改變,被整個序列共用,因此可以寫成摺疊表示來簡潔表達結構。
  • 額外的,可以計算得到當前輸出o_t=softmax(Vs_t),在語言模型中,表示根據之前詞和當前詞的信息,預測下一詞的概率分布:是對p(w_t|w_1,...,w_{t-1})的估計。但在一些任務(如機器翻譯)中,就不需要這種輸出。

LSTM and GRU

梯度彌散問題

RNN的本意就是希望能保持任意長度的信息,如"Jane walked into the room. John walked in too. It was late in the day, and everyone was walking home after a long day at work. Jane said hi to ___",要回答出"John",需要RNN有長時的記憶。

然而使用之前介紹的vanilla RNN,會面臨梯度彌散的問題(詳細證明過程見cs224):這是由於每次梯度沿序列反向傳播時會乘上一個W:frac{partial s_t}{partial s_{t-1}}=W^Tdiag(f),當W的值很小時,多步反向傳播後梯度就變得很小,這就說明了多步之前的信息只能起很小的作用;反之當W的值很大時,會導致反向傳播時梯度越來越大(梯度爆炸),不利於網路訓練。

而這種梯度彌散的問題對於網路模型的訓練也是一個問題(見cs231n Lecture 6),為了解決深網路的梯度彌散問題:1.使用更好的初始化方法,而非零初始或簡單隨機初始。2.使用Relu,而非sigmoid。3.使用batch normalization。但這三種方法只能解決一般前向網路的梯度彌散,對於RNN的問題,還是需要對結構進行改良,即使用LSTM或GRU。

而梯度爆炸的情況可以使用clipping的方法解決。

Long Short Term Memory(LSTM)

[Hochreiter et al., 1997]為了解決這個問題,提出了一種改進結構,稱為LSTM。之前RNN只有一個狀態變數h_t,而LSTM還引入了一個新的cell,稱為memory cell,記為c_t,它包含了之前所有詞的總信息,而且我們對它進行盡量簡單的運算,來保證梯度能更容易反向傳播。

LSTM的組成結構和運算過程:

在vanilla RNN,我們僅僅對輸入x_t,h_{t-1}進行一次線性計算和一次非線性激活函數:s_t=f(Ux_t+Ws_{t-1})

在LSTM中我們對輸入x_t,h_{t-1}則進行四次線性計算以及分別的非線性激活函數,得到四個h維輸出:i,f,o,g(ortilde c_t),

利用這四個輸出可以計算得到新的memory cellc_t

含義和作用為:

  • tilde c_tNew memory cell:表示根據過去狀態h_{t-1}和當前詞x_t整合的新記憶雛形,之後會根據i_tf_t,來決定最後的新記憶c_t
  • i_tInput gate:用來決定新生成的記憶tilde c_t是否要寫入最後的新記憶c_t中,寫入多少,因此與tilde c_t進行element-wise乘
  • f_tForget gate:用來決定舊的記憶c_{t-1}應該遺忘多少,因此與c_{t-1}進行element-wise乘
  • o_tOutput gate:用來決定新的記憶c_t應該在新的狀態h_t中表現多少,如果approx 0,說明它認為下一個詞的信息提取tilde c_{t+1},不依賴於之前的信息。

i,f,o都是gate,決定多少,取值應該在0,1之間,因此用sigmoid激活函數。而g(ortilde c_t)是新的記憶變數,因此用tanh函數。

分析:

因此LSTM中的序列信息由兩個變數攜帶:memory cellc_t和 hidden stateh_t,可以認為c_t是攜帶長時信息的,對它的操作盡量簡單:只有element-wise乘和加法,因此gradient在c_t能更容易地反向傳播;而h_t和在RNN中的作用差不多,可以認為是從c_t誘導出,受當前狀態控制的短時記憶,用來處理新的信息。

通過只有element-wise乘和加法,梯度能在整個序列中反向傳播,這一點和ResNet很像。因此LSTM能保留長時的信息,更好地處理當前詞的選擇。

Gated Recurrent Units(GRU)

[Learning phrase representations using rnn encoder-decoder for statistical machine translation, Cho et al. 2014]提出一種更簡潔的結構,稱為GRU,與LSTM原理類似(只用element-wise乘和加法更新memory),可以使梯度更容易的反向傳播。與LSTM不同的是GRU只使用了一個hidden state,因為可以看到LSTM中的h_t變數並沒有更多的信息,完全可以只用c_t來傳遞信息。

一個GRU單元的內部結構:

GRU的計算過程:

其中只使用了兩個gate(相比於LSTM的4個gate),

闡述上式的含義:

  • r_tReset gate:用來決定之前的狀態h_{t-1}對於當前詞x_t的總結tilde h_t的是否有作用。如果r_t的取值很小approx 0,那說明之前的狀態h_{t-1}與當前詞x_t幾乎無關,那麼新的總結應該只提取新詞x_t的信息,而丟棄之前的無用信息,這樣能得到更緊緻的表示。(其作用與LSTM的Output gateo_t類似)
  • z_tUpdate gate:用來決定當前的總結tilde h_t有多少值得傳遞到下一狀態。如果z_t取值很大approx 1,那麼h_t幾乎為上一狀態h_{t-1},說明當前總結不需要傳遞下去,即之後詞的處理無需當前總結的信息。如果z_t取值很小approx 0,那麼新總結幾乎直接傳給下一狀態,說明當前詞的總結信息對之後詞的處理有價值。

分析:

因為採用的element-wise乘來使用兩個gate,因此在h_t中的每個分量unit都有各自的Reset gate和Update gate(即r_t,z_t的分量),那麼這些unit自動學會保持多少的時間跨度:那些保持短時記憶的unit,它們的Reset gate會一直active(r_t approx1,需要短時工作記憶來做當前總結);那些保持長時記憶的unit,它們的Update gate會一直active(z_t approx 1,記憶不會頻繁更新,保證了記憶的持久性)。因此這種自動調控時長的hidden unit就把LSTM中人為控制長短時的兩個cellc_t,h_t整合到一起了。

在GRU paper中的實驗部分可以看出這種具有實際意義的gate單元會比沒有意義的tanh unit效果好。

語言模型Language Models

然後在這個概率分布上採樣就能得到一句真實話的樣本。為了達到這個目的,優化RNN時,我們需要真實句子的概率最大化,即提高最大擬然概率,即把負的擬然概率作為loss:

將下式作為t時刻的loss function:large J^{(t)}=-log p_{target_t},使用RNN時,p_{target_t}o_t的第target_tx_{t+1}所對應的標號)個分量。

那麼一句T長度的話的loss為:large J=-frac{1}{T}sum_{1}^TJ^{(t)}=-frac{1}{T}sum_{1}^Tlog p_{target_t}

使用梯度下降方法(SGD)來最小化loss function即可。

使用perplexity來作為衡量標準:perplexity=2^J。perplexity值越低代表有更多的信心預測下一個詞。

需要說明的是:在訓練時,RNN的輸入為數據集中的一句話(實際編程時,是batch句話),即把一句話樣本中的每一個詞依次輸入到RNN中,而RNN每輸入一個詞都會對下一個詞做一次預測,最後把這句話預測的所有loss求和得到總loss。但在test時,RNN的初始輸入為零狀態和起始標誌詞,每一次也會計算下一個詞的預測,然後在預測概率上採樣得到下一個詞,將採樣詞作為下一個輸入,最後輸出結束標誌詞。

註:在語言模型的test中上圖應該使用單詞而非字母。

noise-contrastive estimation (NCE)

在RNN輸出單詞最後一步,我們使用softmax來輸出一個|V|維向量(|V|為單詞庫中單詞總數),代表了每個單詞的概率,這樣就能用maximum likelihood (ML)來優化模型:J^{(t)}=-log p_{target_t},設h={w_1,…,w_{t-1}}為之前的單詞,我們需要預測的下個單詞w_t的概率分布為:

注意到分母中需要對單詞庫里所有的單詞的RNN輸出值進行求和,因此每預測一個詞我們需要計算所有詞的得分,並做歸一化,這在單詞庫很大時,計算開銷十分大。

noise-contrastive estimation (NCE)就提出用二分類問題代替原多分類問題:因為我們使用softmax計算概率只是為了計算ML的lossJ,而J^{(t)}=-log p_{target_t},可以看出我們只關心正確答案的概率,而不使用其他詞的概率值,因此其他單詞的值只是用來做歸一化約束,換句話說是作為懲罰項,鼓勵正確詞的值明顯高於其他詞,如果沒有歸一化約束,那麼所有詞的值就都很高,就沒有意義了。

明白了softmax只需要正確答案的概率,而其他值是作為懲罰項後,我們可以把正確詞作為一類,而隨機選k個其他值作為另一類,那麼就轉換為二分類問題,而且只需要計算k個值,而非所有詞的值:

NEG loss(或NCE loss)中Q_{theta}表示把w_t,h作為輸入RNN的二分類器:Q_{theta}(D=1|w_t,h)=sigmoid(RNN(w_t,h)),輸出w_t的概率值,所以loss第一項就是計算了正確答案的概率,讓其變高。

而第二項是隨機取k個其他詞作為負類,Q_{theta}(D=0|tilde w,h)=sigmoid(-RNN(tilde w,h)),作為懲罰項,要被分入這類的概率儘可能低。由此我們只需要計算k個值,而非整個詞庫|V|個值。

上述過程只要使用tensorflow的tf.nn.nce_loss()就可以直接實現。

樣例:

我們可以用語言模型來生成一本代數幾何書,只需要數據集是很多本代數幾何書的latex文本。(github項目)

如果數據集是C語言文本,那我們可以用來生成C語言代碼:

雖然這些都沒有實際的含義,但是至少看起像是代數幾何的用語或C語言代碼(至少它知道定理的proof常常讓你當練習做(笑)),說明RNN的語言模型能把握文本內在的語言結構(雖然無法把握數學或代碼含義)。

推薦閱讀:

阿里知識圖譜首次曝光:每天千萬級攔截量,億級別全量智能審核
通俗講解平方損失函數平方形式的數學解釋?
智能時代的怪獸四:語言的運算(上)
cs.CL weekly 2016.08.29-2016.09.02
Word2Vec如何解決多義詞的問題?

TAG:自然语言处理 | 机器学习 |