NLP硬核入門-Seq2Seq和Attention機制
- 2019 年 11 月 12 日
- 筆記
來自:數論遺珠
本文需要的前序知識儲備是:循環神經網絡RNN,詞向量WordEmbedding,門控單元VanillaRNN/GRU/LSTM。
1 seq2seq
seq2seq是sequence to sequence的縮寫。前一個sequence稱為編碼器encoder,用於接收源序列source sequence。後一個sequence稱為解碼器decoder,用於輸出預測的目標序列target sequence。
seq2seq主要用於序列生成任務,例如:機器翻譯、文本摘要、對話系統,等等。當然也可以用於文本分類等任務。

圖1.1 seq2seq
最傳統的seq2seq流程如圖1.1所示:
(1)將源序列輸入encoder網絡。
(2)encoder將源序列的信息編碼成一個定長的向量encoder vector。
(3)encoder vector被送入decoder網絡。
(4)decoder根據輸入的向量信息,輸出預測的目標序列。
seq2seq在被提出後,馬上受到了廣泛的關注和應用,也暴露出一些問題。首先被關注到的,就是人們發現把一整個文本序列通過encoder壓縮到區區一個向量里,很難通過decoder進行完美地沒有信息缺失的解碼。
此外,由於循環神經網絡RNN的特性,源序列中越遲輸入的文本,對encoder vector的影響也越大。換句話說,encoder vector里會包含更多的序列尾的文本信息,而忽略序列頭的文本信息。所以在很多早期的論文中,會將文本序列進行倒序後再輸入encoder,模型測評分數也會有一個顯着地提高。
為了讓decoder能夠更好地提取源序列的信息,Bahdanau在2014年提出了注意力機制Attention Mechanism,Luong在2015年對Bahdanau Attention進行了改進。這是兩個最經典的注意力機制模型。兩個Attention模型的本質思路是一樣的,下文均以Luong Attention模型作為範例。
2 Attention Mechanism
注意力機制的理解,可以參考CV領域的思想:我們在看到一幅畫的時候,每個時刻總會有一個關注重點,比如說某個人、某個物品、某個動作。
所以,在NLP領域,我們在通過decoder預測輸出目標序列的時候,也希望能夠有一種機制,將目標序列當前step,和源序列某幾個step的文本關聯起來。
以翻譯任務為例,將「我愛機器學習」翻譯成「I love machine learning.」在decoder輸出序列第一個step,我們希望關注輸入序列中的「我」,並將「我」翻譯成「I」;在第三個step,我們希望關注「機器」,並翻譯成「machine」。
這個例子比較簡單,我們就會產生一個初步的想法:是不是把源序列中的每個詞語單獨翻譯成英文,再依次輸出,就構成目標序列了呢?
但是,如果進一步思考下,我們就會發現兩個問題:
(1)一詞多義:源序列里的同一個詞,在輸出序列里,可能根據場景的不同,會有不同的輸出。例如「我」可能被翻譯成「I」,也有可能被翻譯成「me」。這有點類似於中文的「一詞多義」,在英文里估計是叫做「一詞多態」吧,我們姑且將這類由一個詞可以映射成多個詞的現象,廣義地統稱為「一詞多義」。解決「一詞多義」問題的一個有效途徑,就是參考源序列的語境信息,也就是上下文信息,來生成詞向量。
(2)序列順序:源序列和目標序列並不是順序依次映射的,例如「你是誰?」翻譯成「who are you?」,不同語言有不同的語法規則和順序。這就需要在decoder輸出的每一個step,確定當前step應該翻譯源序列中的哪一個詞。
這兩個問題也就是Attention機制的關注重點。

圖2.1 LuongAttention
圖2.1是一個Luong Attention的示意圖,是作者其後續的論文里呈現的一張修訂後的示意圖。
還有個理解Attention的方式,就是參考殘差網絡ResNet。因為源序列太長了,導致早期的信息無法有效地被傳遞,所以需要一個額外的通道,把早期的信息送到解碼器上。送的時候還不能只送一個詞的信息,最好把上下文信息一起給送了。
下一節會用一個最簡單的模型,介紹Attention機制的實現步驟,在此之前,先約定下參數符號:
h(output):RNN的隱藏狀態,主要用於將信息輸出到RNN模型外。
s(state):RNN的狀態,主要用於RNN模型內部,模型將信息傳遞給下一個step。
a:對齊向量
c:上下文信息向量。
x:源序列。
y:目標序列。
下標s表示源序列,下標t表示目標序列。s1表示源序列第1個step,以此類推。
括號里的output和state,是為了方便讀者將論文里的算法理論,和工業實踐里tensorflow的tf.nn.dynamic_rnn函數聯繫起來,稍微有個印象就好。dynamic_rnn函數返回兩個向量,第一個是output,也就是encoder所有step、網絡最後一層輸出的h;第二個是state,也就是encoder最後一個step、網絡層所有層輸出的s。
3 Attention五部曲
3.1 執行encoder

圖3.1 步驟一:執行encoder
步驟一的行為是將源數據依次輸入Encoder,執行Encoder。目的在於將源序列的信息,編譯成語義向量,供後續decoder使用。
在每個step,endocer會輸出一個表徵當前step語義的output(h)向量和一個state(s)向量:
(1)我們收集每個step的output(h),構成output矩陣,矩陣的維度是[step_len,dim_rnn],即源數據step長度,乘以rnn單元數量。
(2)我們收集最後一個step的state(s),作為傳入decoder的向量。
encoder對於模型的貢獻,在於提供了outputs矩陣和state向量。
注一:為了便於理解,我這裡的encoder使用了單層網絡,多層網絡的outputs和state見上一節末尾的描述。
注二:很多論文的h和s的描述並沒有一個統一的標準,經常混淆。因為早期論文的RNN單元,是用VanillaRNN或GRU實現的,這兩個門控單元在同一個step,輸出的h和s是一樣的。但是,若通過LSTM實現,h和s是不同的,這個需要引起注意。
注三:早期的論文中,encoder的state是直接傳遞給decoder,作為initial state的。但是在工程應用中,也存在直接將0序列作為initial state傳遞給decoder的情況。另外,部分論文也有將state進行一些處理,添加一些額外的信息,再傳遞給decoder的算法。總之,encoder和decoder之間傳遞state的方式比較靈活,可以根據實際情況自行選擇和改進。
注四:RNN的單元數量,即為encoder輸出向量的維度。也就是用dim_rnn維度的向量,來表徵源序列當前step文本的語義信息。對照同樣表徵語義信息的詞向量的維度dim_word_embd,我建議兩者的維度不宜相差過大,否則會造成浪費。
3.2 計算對齊係數a

圖3.2 步驟二:計算對齊係數a
步驟二解決的是第2節提出的「序列順序」的問題。在decoder的每個step,我們需要關注源序列的所有step和目標序列當前step的相關性大小,並輸出相關(對齊)係數a。
所以,在decoder輸出一個預測值前,都會針對encoder的所有step,計算一個score。這個score表示當前的decoder工作,需要從encoder的哪些step里抽取信息,以及抽取的權重大小。然後將score匯總向量化後,每個decoder step能獲得一個維度為[step_len,1]的score向量。
這個score的計算方式有很多種,圖3.2中列舉了Luong Attention提及的3種的傳統計算方式。我畫的流程圖中採用的是第1種,就是將源序列所有step的output(h)和目標序列當前step的output(h)逐個相乘,得到的值即為score。有些論文就是在score的計算方式上進行創新的。
計算出score後,很自然地按慣例使用softmax進行歸一化,得到對齊向量a,維度也是[step_len,1]。
注一:很多論文的各種參數的縮寫符號都不一樣,有一個理清模型流程順序的小技巧:就是去找softmax函數,以softmax為錨點。Attention常見的使用softmax的地方有兩個,一個是步驟二的對齊係數a,另一個在步驟五將會提到,在輸出預測詞之前,要對概率分數進行softmax歸一化處理。
注二:對齊係數a雖然只是一個過程數據,但是卻蘊含很重要的信息,可用於PointerNet和CopyNet。
3.3 計算上下文語義向量c

圖3.3 步驟三:計算上下文語義向量c
在描述這個步驟前,我們先回顧下詞向量的CBOW模型。在CBOW模型收斂後,每個詞的詞向量,等於它周圍若干個詞的詞向量的均值。這其中蘊含的意思是:表徵一個詞的,不是這個詞本身,而是這個詞的上下文(語境)。
CBOW模型是比較簡單粗暴地將上下文的詞向量求平均。實際上,如果能夠以一個加權平均的方式獲取詞向量,那麼這個詞向量一定能夠更準確地表達這個詞在當前語境里的語義。
舉個例子:「孔夫子經歷了好幾個春秋寒暑,終於修訂完成了春秋麟史。」在這裡,第一個「春秋」表示「一年」,「經歷」、「寒暑」顯然和它關係更密切,利用加權上下文構成詞向量時,應該賦予更高的權重。第二個「春秋」表示儒家六經之一,「修訂」、「麟史」關係和它更密切,同樣應該賦予高權重。
在步驟三里,我們將對齊係數a作為權重,對encoder每個step的output向量進行加權求和(對齊向量a點乘outputs矩陣),得到decoder當前step的上下文語義向量c。
注一:BERT也有用到對齊係數的思想,而且更為直觀漂亮。
3.4 更新decoder狀態

圖3.4 步驟四:更新decoder狀態
在步驟四里,需要更新decoder狀態,這個狀態可以是h,也可以是s。能夠用於更新h和s的信息數據,可以是:前step的s,現step的h,現step的上下文向量c,以及其它一些包含有效信息的數據。
BahdanauAttention和Luong Attention最大的區別就在於這個步驟,前者更新的是s,後者更新的是h。不過由於Bahdanau用的是前step的s,Luong用的是先step的h,所以後者在工程化實現上會簡單點。
具體的更新公式的細節,在這裡不作詳細描述,因為不同模型可能會採用不同的更新公式,很多論文也是圍繞更新公式進行創新點研究的。
需要注意的是,在這個環節,訓練模式和預測模式略有差別:decoder每個step都要輸入一個數據,在訓練模式,輸入的數據是目標序列當前step的真實值,而不使用前step的h;在預測模式,輸入的數據是前step的h,而不使用輸出序列的真實值。雖然在圖3.4中,我畫了兩條輸入,但是要根據模型當前處於訓練模式還是預測模式,選擇其中的一條進行輸入。
3.5 計算輸出預測詞

圖3.5 步驟五:計算輸出預測詞
這個步驟我在圖裡沒有畫全,其實很簡單,同CBOW模型/Skip-Gram模型的隱藏層到輸出層的那部分一樣,做一個語義向量到目標詞表的映射(如果attention用於分類模型,那就是做一個到各個分類的映射),然後再進行softmax就可以了。
4 其它
4.1 Local Attention和Global Attention
前文所提及的Attention都是Global Attention,還有一個Local Attention,將在這個小節作一個簡單的說明。
Global Attention就是針對源序列的所有step,求對齊係數a。而LocalAttention只針對源序列的部分step,求對齊係數a,這個部分step的長度是超參數,需要憑經驗人為配置。
Local Attention所截取的部分step的中心點的選取(對齊)方式,是另一個需要關注的點。論文中提及了兩個對齊方式:
(1)Monotonicalignment (local-m):簡單粗暴的,直接按源序列和目標序列的step絕對值對齊。
(2)Predictivealignment (local-p):通過模型,學習計算出截斷step的對齊中心。
Luong的論文里有提及,LocalAttention的效果優於Global Attention。
註:CV領域有個Soft-Attention和Hard-Attention,和這裡NLP領域的兩個Attention優點類似。
4.2 常見的可以替換改進的模塊
1.用於生成對齊向量a的分值score的計算方式。
2.h和s的更新公式。
3.基本RNN的結構,包括替換門控單元、更改RNN層數、單向改雙向等。
參考資料
[1] Bahdanau D ,Cho K , Bengio Y . Neural Machine Translation by Jointly Learning to Align andTranslate[J]. Computer Science, 2014.
[2] Luong M T ,Pham H , Manning C D . Effective Approaches to Attention-based Neural MachineTranslation[J]. Computer Science, 2015.
[3] Andrew Ng RecurrentNeural Networks