1 什么是 Seq2Seq ?
Seq2Seq 是一個 Encoder-Decoder 結(jié)構(gòu)的神經(jīng)網(wǎng)絡,它的輸入是一個序列(Sequence),輸出也是一個序列(Sequence),因此而得名 “Seq2Seq”。在 Encoder 中,將可變長度的序列轉(zhuǎn)變?yōu)楣潭ㄩL度的向量表達,Decoder 將這個固定長度的向量轉(zhuǎn)換為可變長度的目標的信號序列。
如下是 Seq2Seq 模型工作的流程:
最基礎(chǔ)的 Seq2Seq模型 包含了三個部分(上圖有一部分沒有顯示的標明),即 Encoder、Decoder 以及連接兩者的中間狀態(tài)向量 C,Encoder通過學習輸入,將其編碼成一個固定大小的狀態(tài)向量 C(也稱為語義編碼),繼而將 C 傳給Decoder,Decoder再通過對狀態(tài)向量 C 的學習來進行輸出對應的序列。
當然,在模型的訓練階段,工作方式如下所示:
這里的每一個 Box 代表了一個 RNN 單元,通常是 LSTM 或者 GRU 。其實,Basic Seq2Seq 是有很多弊端的,首先 Encoder 將輸入編碼為固定大小狀態(tài)向量(hidden state)的過程實際上是一個“信息有損壓縮”的過程。如果信息量越大,那么這個轉(zhuǎn)化向量的過程對信息造成的損失就越大。同時,隨著 sequence length的增加,意味著時間維度上的序列很長,RNN 模型也會出現(xiàn)梯度彌散。最后,基礎(chǔ)的模型連接 Encoder 和 Decoder 模塊的組件僅僅是一個固定大小的狀態(tài)向量,這使得Decoder無法直接去關(guān)注到輸入信息的更多細節(jié)。由于 Basic Seq2Seq 的種種缺陷,隨后引入了 Attention 的概念以及 Bi-directional encoder layer 等,能夠取得更好的表現(xiàn)。
總結(jié)起來說,基礎(chǔ)的 主要包括 Encoder,Decoder,以及連接兩者的固定大小的 State Vector。
2 Attention
seq2seq 模型雖然強大,但如果僅僅是單一使用的話,效果會大打折扣。注意力模型就是基于 Encoder-Decoder 框架下的一種模擬 Human 注意力直覺的一種模型。
人腦的注意力機制本質(zhì)上是一種注意力分配的模型,比如說我們在閱讀一篇論文的時候,在某個特定時刻注意力肯定只會在某一行文字描述,在看到一張圖片時,我們的注意力肯定會聚焦于某一局部。隨著我們的目光移動,我們的注意力肯定又聚焦到另外一行文字,另外一個圖像局部。所以,對于一篇論文、一張圖片,在任意一時刻我們的注意力分布是不一樣的。這便是著名的注意力機制模型的由來。
早在計算機視覺目標檢測相關(guān)的內(nèi)容學習時,我們就提到過注意力機制的思想,目標檢測中的 Fast R-CNN 利用 RoI(興趣區(qū)域)來更好的執(zhí)行檢測任務,其中 RoI 便是注意力模型在計算機視覺上的應用。
注意力模型的使用更多是在自然語言處理領(lǐng)域,在機器翻譯等序列模型應用上有著更為廣泛的應用。在自然語言處理中,注意力模型通常是應用在經(jīng)典的 Encoder-Decoder 框架下的,也就是 RNN 中著名的 N vs M 模型,seq2seq 模型正是一種典型的 Encoder-Decoder 框架。
Encoder-Decoder 作為一種通用框架,在具體的自然語言處理任務上還不夠精細化。換句話說,單純的Encoder-Decoder 框架并不能有效的聚焦到輸入目標上,這使得像 seq2seq 的模型在獨自使用時并不能發(fā)揮最大功效。比如說在上圖中,編碼器將輸入編碼成上下文變量 C,在解碼時每一個輸出 Y 都會不加區(qū)分的使用這個 C 進行解碼。而注意力模型要做的事就是根據(jù)序列的每個時間步將編碼器編碼為不同 C,在解碼時,結(jié)合每個不同的 C 進行解碼輸出,這樣得到的結(jié)果會更加準確,如下所示:
簡單的注意力模型通常有以上三個公式來描述:
1)計算注意力得分
2)進行標準化處理
3)結(jié)合注意力得分和隱狀態(tài)值計算上下文狀態(tài) C 。
其中 u 為解碼中某一時間步的狀態(tài)值,也就是匹配當前任務的特征向量,vi 是編碼中第 i 個時間步的狀態(tài)值,a() 為計算 u 和 vi 的函數(shù)。a()通常可以取以下形式:
Attention works by first, calculating an attention vector, , that is the length of the source sentence. The attention vector has the property that each element is between 0 and 1, and the entire vector sums to 1. We then calculate a weighted sum of our source sentence hidden states,
, to get a weighted source vector,
.
We calculate a new weighted source vector every time-step when decoding, using it as input to our decoder RNN as well as the linear layer to make a prediction.
Attention模型的出現(xiàn)是上述的seq2seq模型存在缺陷,即無論之前的encoder的context有多長,包含多少信息量,最終都要被壓縮成一個幾百維的vector。這意味著context越大,decoder的輸入之一的last state 會丟失越多的信息。對于機器翻譯問題,意味著輸入sentence長度增加后,最終decoder翻譯的結(jié)果會顯著變差。
Attention 注意力機制提供了一個可以和遠距離單詞保持聯(lián)系的方式, 解決了一個 vector 保存信息不足的問題。
Attention實質(zhì)上是一種 content-based addressing 的機制,即從網(wǎng)絡中某些狀態(tài)集合中選取與給定狀態(tài)較為相似的狀態(tài),進而做后續(xù)的信息抽取;
說人話就是: 首先根據(jù) Encoder 和 Decoder 的特征計算權(quán)值,然后對Encoder的特征進行加權(quán)求和,作為Decoder的輸入,其作用是將Encoder的特征以更好的方式呈獻給Decoder,即:并不是所有 context 都對下一個狀態(tài)的生成產(chǎn)生影響,Attention 就是選擇恰當?shù)腸ontext用它生成下一個狀態(tài)。
圖中, 明確的標出了,最后一個state包含了整個句子的信息。 但是問題是,一個 vector 是無法包含這么多信息的。 這樣勢必導致比較遠的單詞信息的丟失。 另一個優(yōu)化上的問題是, 求微分時,較遠的單詞會受到 diminishing gradient 的影響, 導致失去了long term dependency 的關(guān)系。
Attention 注意力機制提供了一個可以和遠距離單詞保持聯(lián)系的方式, 解決了一個vector保存信息不足的問題。
注意力機制的計算發(fā)生在decoder 的每一個步驟, 包含了四個步驟。首先decoder state 和encoder 所有的source state 進行softmax, 計算, 算出attention weights. 比如Fig. 6 里的 "the" hidden state, 就去和原文中的"les pauvres sont demunis"對應的state 進行softmax 計算?;谶@個attention weights (attention distribution 在Fig. 6里), 我們算出一個上下文向量(attention output)。 這個向量是通過加權(quán)平均的 source state.attention output 再和 decoder hidden state 拼接起來, 最后算出y2. 下面的公式總結(jié)了這三步計算過程。
3 小任務:反轉(zhuǎn)一個變長序列
設(shè)計網(wǎng)絡結(jié)構(gòu),反轉(zhuǎn)一個變長序列(最大長度N=20),即246910000反轉(zhuǎn)為196420000,其中1-9為需要反轉(zhuǎn)的有效字符,0為補位字符
考核點:
- 序列長度很長時,如何記住前序信息
- output structure是序列的優(yōu)化方法
Constraints:
- 結(jié)構(gòu)設(shè)計,不用調(diào)參,固定 batch=32, lr=0.02, optimizer=Adam, epoch=1
- 將序列中的數(shù)字映射到8維空間作為輸入
- 禁止直接將 input 與 output 層相連
3.1 解決思路
對于這樣一個回文序列問題,使用神經(jīng)網(wǎng)絡來做的話首當其沖需要考慮的就是序列長度的問題,因為回文序列的 Long Term Dependency 很遠,長度一旦超過網(wǎng)絡的學習能力的 capacity 之后,神經(jīng)網(wǎng)絡便學習不到相應的關(guān)系,進而就會產(chǎn)生錯誤的結(jié)果。
Seq2Seq 模型很適合解決這樣的問題,特別是引入 Attention 機制后,記住前序信息的能力有明顯的增強。另外,針對該問題的特點,相信使用雙向的循環(huán)神經(jīng)網(wǎng)絡(BiLSTM or BiGRU)會有更好的表現(xiàn)。
當然,還有一種思路就是純粹使用 Attention 來做,摒棄掉 RNN 部分,結(jié)合使用 Position Embedding 來“記住”序列的順序或者位置信息。
至于 output structure是序列的優(yōu)化方法
這一句想到現(xiàn)在也沒有理解其含義...
3.2 模型實現(xiàn)
那么,現(xiàn)在就開始動起手來實現(xiàn)吧!
3.2.1 數(shù)據(jù)預處理
在神經(jīng)網(wǎng)絡中,對于文本的數(shù)據(jù)預處理無非是將文本轉(zhuǎn)化為模型可理解的數(shù)字,這里都比較熟悉,不作過多解釋。但在這里我們需要加入以下四種字符,<PAD>主要用來進行字符補全,<EOS>和<GO>都是用在Decoder端的序列中,告訴解碼器句子的起始與結(jié)束,<UNK>則用來替代一些未出現(xiàn)過的詞或者低頻詞。
<PAD>: 補全字符。
<GO>/<SOS>: 解碼器端的句子起始標識符。
<EOS>: 解碼器端的句子結(jié)束標識符。
<UNK>: 低頻詞或者一些未登陸詞等。
我們首先需要對target端的數(shù)據(jù)進行一步預處理。在我們將target中的序列作為輸入給Decoder端的RNN時,序列中的最后一個字母(或單詞)其實是沒有用的。我們來用下圖解釋:
我們此時只看右邊的Decoder端,可以看到我們的target序列是[<go>, W, X, Y, Z, <eos>],其中<go>,W,X,Y,Z是每個時間序列上輸入給RNN的內(nèi)容,我們發(fā)現(xiàn),<eos>并沒有作為輸入傳遞給RNN。因此我們需要將target中的最后一個字符去掉,同時還需要在前面添加<go>標識,告訴模型這代表一個句子的開始。
這個圖代表我們的predict階段,在這個階段,我們沒有target data,這個時候前一階段的預測結(jié)果就會作為下一階段的輸入。
當然,predicting雖然與training是分開的,但他們是會共享參數(shù)的,training訓練好的參數(shù)會供predicting使用。
目前為止我們已經(jīng)完成了整個模型的構(gòu)建,但還沒有構(gòu)造batch函數(shù),batch函數(shù)用來每次獲取一個batch的訓練樣本對模型進行訓練。
在這里,我們還需要定義另一個函數(shù)對batch中的序列進行補全操作。這是啥意思呢?我們來看個例子,假如我們定義了batch=2,里面的序列分別是
[['h', 'e', 'l', 'l', 'o'],
['w', 'h', 'a', 't']]
那么這兩個序列的長度一個是5,一個是4,變長的序列對于RNN來說是沒辦法訓練的,所以我們這個時候要對短序列進行補全,補全以后,兩個序列會變成下面的樣子:
[['h', 'e', 'l', 'l', 'o'],
['w', 'h', 'a', 't', '<PAD>']]
這樣就保證了我們每個batch中的序列長度是固定的。
To train we run the input sentence through the encoder, and keep track of every output and the latest hidden state. Then the decoder is given the <SOS> token as its first input, and the last hidden state of the encoder as its first hidden state.
"Teacher forcing" is the concept of using the real target outputs as each next input, instead of using the decoder's guess as the next input. Using teacher forcing causes it to converge faster but when the trained network is exploited, it may exhibit instability http://minds.jacobs-university.de/sites/default/files/uploads/papers/ESNTutorialRev.pdf.
關(guān)于 <SOS>和<EOS> : 它們都是用在 Decoder 端的序列中,告訴解碼器句子的起始與結(jié)束。所以,target 序列需要加上 <SOS>
在 seq2seq 模型的預測流程中,Encoder 需要依靠 <EOS> 來判斷序列的結(jié)束,所以對于 source 序列需要在結(jié)尾加上 <EOS> 。
在 seq2seq 模型的訓練流程中,
target sequence 加上 <EOS>,train decoder input + <SOS>
predic decoder <SOS>
作為補充,參考一些案例代碼,同時也實現(xiàn)了 TensorFlow 版本。
Github Repository:Seq2Seq-Attention
3.2 思考與改進
Encoder-Decoder 模型確實
PyTorch 相關(guān) API 概述
PyTorch 中的詞嵌入是通過函數(shù) nn.Embedding(m, n) 來實現(xiàn)的,其中 m 表示所有的單詞數(shù)目,n 表示詞嵌入的維度。
nn.GRU(hidden_size, hidden_size)
nn.Linear(hidden_size, output_size)
nn.LogSoftmax(dim=1)
F.relu(output)
編程實現(xiàn)的知識點
Python Dict get
dict.get(key, default=None)
params:
- key: 字典中需要查找的鍵。
- default: 如果指定鍵的值不存在時候,返回該默認值。
總結(jié)
模型分為 encoder 和 decoder 兩個部分,decoder 部分比較簡單,就是一層 Embedding 層加上兩層 GRU。之前處理的 batch 的格式主要是為了使用pack_padded_sequence 和 pad_packed_sequence 這兩個類對GRU輸入輸出批量處理。一定要注意各個變量的shape。
FAQs:
1.為什么要在 target sequence 前面添加 <GO>/<SOS> 呢?不能直接輸入target嗎?在末尾添加<eos>倒是可以理解。
因為預測階段你沒有 target sequence 呀。首先,train階段,因為你知道 target sequence 數(shù)據(jù),當然可以直接輸,但是 predict 的時候咋辦,你不知道第一個詞是啥,這個時候一般用<GO>啟動
train 的時候?qū)?decoder_input 預處理也要加<go>,這是因為訓練和預測需要從同樣的輸入開始(就是<go>)。比如source是common,target是 cmmnoo,當encode結(jié)束,開始decode時,給decoder的第一個輸入都是<go>,train的階段,你是希望網(wǎng)絡能學習到第一個輸出是c,這樣在predict階段,你用<go>啟動網(wǎng)絡才能輸出c。如果不加<go>,你第一個輸入就是c了,網(wǎng)絡怎么能學習到用<go>做輸入時該輸出什么呢
參考文獻
作者:濤濤江水向坡流
鏈接:http://www.lxweimin.com/p/ead1b446a124
來源:簡書
簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處。
作者:qq_18603599
來源:CSDN
原文:https://blog.csdn.net/qq_18603599/article/details/80581115
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!