Attention isAllYouNeed詳細解讀
國家數字化學習工程技術研究中心 鮑一鳴
論文原址:https://arxiv.org/abs/1706.03762
本人博客地址:https://www.cnblogs.com/baobaotql/p/11662720.html
論文復現:https://github.com/baobaotql/CCNU_Algorithm/tree/master/Transformer
引言
自從Attention機制在提出之后,加入Attention的Seq2Seq模型在各個任務上都有了提升,所以現在的Seq2Seq模型指的都是結合RNN和Attention的模型。傳統的基於RNN的Seq2Seq模型難以處理長序列的句子,無法實現並行,並且面臨對齊的問題。
所以之后這類模型的發展大多數從三個方面入手:
(1)Input的方向性從單向到多向;
(2)深度從單層到多層;
(3)類型從RNN到LSTM GRU。
但是依舊收到一些潛在問題的制約,神經網絡需要能夠將源語句的所有必要信息壓縮成固定長度的向量。這可能使得神經網絡難以應付長時間的句子,特別是那些比訓練語料庫中的句子更長的句子;每個時間步的輸出需要依賴於前面時間步的輸出,這使得模型沒有辦法並行,效率低;仍然面臨對齊問題。
再然后CNN由計算機視覺也被引入到Deep NLP中,CNN不能直接用於處理變長的序列樣本但可以實現並行計算。完全基於CNN的Seq2Seq模型雖然可以並行實現,但非常占內存,很多的trick,大數據量上參數調整並不容易。
本篇文章創新點在於拋棄了之前傳統的Encoder-Decoder模型必須結合CNN或者RNN的固有模式,只用Attention機制。文章的主要目的在於減少計算量和提高並行效率的同時不損害最終的實驗結果。以下為個人對文章詳細解讀,希望有助於正在學習本篇論文的各位讀者。
1.整體框架
整體框架很容易理解,但是圖1.1又很復雜,可以簡化如圖1.2。

圖1.1 Transformer模型架構圖
圖1.2 Transformer簡化圖
我們可以看到,這其實就是一個SeqSeq模型,左邊一個Encoder將輸入讀進去,右邊一個Decoder的單獨輸出。當我們第一眼看到論文中的模型結構圖時,我們會產生疑問。左邊的Encoder是如何與右邊的Decoder結合的呢?其實因為Decoder里面是有N層的,我們再通過一張圖可以直觀的看到。

圖1.3 Encoder-Decoder結合方式圖
也就是說,Encoder的輸出會和每一層的Decoder進行結合,我們取其中的一層進行詳細的展示。

圖1.4Encoder-Decoder內部結構圖
2.Attention結構
Google 的一般化 Attention思路也是一個編碼序列的方案,因此我們也可以認為它跟 RNN、CNN 一樣,都是一個序列編碼的層。
2.1Attention定義
Attention用於計算"相關程度",例如在翻譯過程中,不同的英文對中文的依賴程度不同,Attention通常可以進行如下描述,表示為將query(Q)和key-value pairs映射到輸出上,其中query、每個key、每個value都是向量,輸出是V中所有values的加權,其中權重是由Query和每個key計算出來的,計算方法分為三步:
(1) 計算比較Q和K的相似度,用f來表示:

(2) 將得到的相似度進行Softmax操作,進行歸一化:

(3) 針對計算出來的權重,對V中所有的values進行加權求和計算,得到Attention向量:

在第一步中的計算方法包括以下四種:
(1)點乘Dot Product:

(2)權重General:

(3)拼接權重Concat:

計算自注意力的第一步就是從每個編碼器的輸入向量(每個單詞的詞向量)中生成三個向量。也就是說對於每個單詞,我們創造一個查詢向量、一個鍵向量和一個值向量。這三個向量是通過詞嵌入與三個權重矩陣后相乘創建的。可以發現這些新向量在維度上比詞嵌入向量更低。他們的維度是64,而詞嵌入和編碼器的輸入/輸出向量的維度是512. 但實際上不強求維度更小,這只是一種基於架構上的選擇,它可以使多頭注意力(multiheaded Attention)的大部分計算保持不變。X1與WQ權重矩陣相乘得到q1, 就是與這個單詞相關的查詢向量。最終使得輸入序列的每個單詞的創建一個查詢向量、一個鍵向量和一個值向量。
2.2 ScaledDot-ProductAttention(縮放點積注意力)
像大部分NLP應用一樣,我們首先將每個輸入單詞通過詞嵌入算法轉換為詞向量。每個單詞都被嵌入為512維的向量,我們用一些簡單的方框來表示這些向量。詞嵌入過程只發生在最底層的編碼器中。所有的編碼器都有一個相同的特點,即它們接收一個向量列表,列表中的每個向量大小為512維。在底層(最開始)編碼器中它就是詞向量,但是在其他編碼器中,它就是下一層編碼器的輸出(也是一個向量列表)。向量列表大小是我們可以設置的超參數——一般是我們訓練集中最長句子的長度。將輸入序列進行詞嵌入之后,每個單詞都會流經編碼器中的兩個子層。
接下來我們看看Transformer的一個核心特性,在這里輸入序列中每個位置的單詞都有自己獨 特的路徑流入編碼器。在自注意力層中,這些路徑之間存在依賴關系。而前饋(feed-forward)層沒有這些依賴關系。因此在前饋(feed-forward)層時可以並行執行各種路徑。
首先從輸入開始理解,Scaled Dot-Product Attention里的Q, K, V從哪里來:按照我的理解就是給我一個輸入X, 通過3個線性轉換把X轉換為Q,K,V。兩個單詞,Thinking, Machines. 通過嵌入變換會X1,X2兩個向量[1 x 4]。分別與Wq,Wk,Wv三個矩陣[4x3]想做點乘得到,{q1,q2},{k1,k2},{v1,v2} 6個向量[1x3]。
圖2.2.1 QKV線性轉換
向量{q1,k1}做點乘得到得分(Score) 112, {q1,k2}做點乘得到得分96。
圖2.2.2 QK點乘結果
對該得分進行規范,除以8。這個在論文中的解釋是為了使得梯度更穩定。之后對得分[14,12]做softmax得到比例 [0.88,0.12]。
圖2.2.3 softmax處理
用得分比例[0.88,0.12] 乘以[v1,v2]值(Values)得到一個加權后的值。將這些值加起來得到z1。這就是這一層的輸出。仔細感受一下,用Q,K去計算一個thinking對thinking, machine的權重,用權重乘以thinking,machine的V得到加權后的thinking,machine的V,最后求和得到針對各單詞的輸出Z。(譯注:自注意力的另一種解釋就是在編碼某個單詞時,就是將所有單詞的表示(值向量)進行加權求和,而權重是通過該詞的表示(鍵向量)與被編碼詞表示(查詢向量)的點積並通過softmax得到。),然后即得到自注意力層在該位置的輸出(在我們的例子中是對於第一個單詞)。
上述例子是單個向量的運算例子。下圖展示的是矩陣運算的例子。輸入是一個[2x4]的矩陣(單詞嵌入),每個運算是[4x3]的矩陣,求得Q,K,V。Q對K轉制做點乘,除以dk的平方根。做一個softmax得到合為1的比例,對V做點乘得到輸出Z。那么這個Z就是一個考慮過thinking周圍單詞(machine)的輸出。

圖2.2.4 矩陣表示

圖2.2.5 矩陣運算表示
QKT其實就會組成一個word2word的Attentionmap!(加了softmax之后就是一個合為1的權重了)。比如說你的輸入是一句話 "i have a dream" 總共4個單詞,這里就會形成一張4x4的注意力機制的圖:

圖2.2.6 4*4注意力機制圖
這樣一來,每一個單詞就對應每一個單詞有一個權重。主要Encoder里做Self-AttentionDecoder里面叫做masked Self-Attention在這里的masked就是要在做language modelling(或者翻譯)的時候,不給模型看到未來的信息。masked就是不給模型看到未來的信息,mask就是沿對角線把灰色的區域用0覆蓋掉。詳細的來說,i作為第一個單詞,只能有和i自己的Attention。have作為第二個單詞,有和i, have 兩個Attention。a 作為第三個單詞,有和i,have,a 前面三個單詞的Attention。到了最后一個單詞dream的時候,才有對整個句子4個單詞的Attention。做完softmax后就像這樣,橫軸和為1。

圖2.2.7 softmax矩陣表示
2.3 Multi-HeadAttention(多頭注意力機制)
Multi-Head Attention就是把Scaled Dot-Product Attention的過程做H次,然后把輸出Z合起來。論文中,它的結構圖如下:

圖2.3.1 多頭注意力機制結構圖
論文中還增加一種稱為Multi-Headed注意力機制,可以提升注意力層的性能。我們還是以上面矩陣的形式來解釋:

圖2.3.2多頭注意力矩陣表示
它使得模型可以關注不同位置。雖然在上面的例子中,z1 包含了一點其他位置的編碼,但當前位置的單詞還是占主要作用, 當我們想知道“The animal didn’t cross the street because it was too tired” 中 it 的含義時,這時就需要關注到其他位置。這個機制為注意層提供了多個“表示子空間”(representation Subspaces)。下面我們將具體介紹:
(1)經過 Multi-Headed,我們會得到和 heads 數目一樣多的 Query / Key / Value 權重矩陣組.論文中用了8個,那么每個Encoder/Decoder我們都會得到 8 個集合。這些集合都是隨機初始化的,經過訓練之后,每個集合會將input Embeddings (或者來自較低編碼器/解碼器的向量)投影到不同的表示子空間中。
(2)我們重讀記憶八次相似的操作,得到八個Zi矩陣。簡單來說,就是定義8組權重矩陣,每個單詞會做8次上面的Self-Attention的計算這樣每個單詞就會得到8個不同的加權求和Z。
圖2.3.3 單詞加權求和
(3)feed-forward處只能接受一個矩陣,所以需要將這八個矩陣壓縮成一個矩陣。方法就是先將八個矩陣連接起來,然后乘以一個額外的權重矩陣W0。為了使得輸出與輸入結構對標 乘以一個線性W0 得到最終的Z
3.Transformer結構
絕大部分的序列處理模型都采用Encoder-Decoder結構,其中Encoder將輸入序列(x1,x2,…,xn),然后Decoder生成一個輸出序列(y1,y2,…,yn),每個時刻輸出一個結果。從框架中我們可以知道Transformer模型延續了這個模型。
3.1 PositionEmbedding(位置嵌入)
因為模型不包括Recurrence/Convolution,因此是無法捕捉到序列順序信息的,例如將K、V按行進行打亂,那么Attention之后的結果是一樣的。但是序列信息非常重要,代表着全局的結構,因此必須將序列的分詞相對或者絕對Position信息利用起來。這里每個分詞的Position Embeddingg向量維度也是dmodel, 然后將原本的input Embedding和Position Embedding加起來組成最終的Embedding作為Encoder/Decoder的輸入。其中Position Embedding計算公式如下:

其中 pos 表示位置index, i 表示dimension index。Position Embedding本身是一個絕對位置的信息,但在語言中,相對位置也很重要,Google選擇前述的位置向量公式的一個重要原因是,由於我們根據如下三角函數:

這表明位置p+k的向量可以表示成位置p的向量的線性變換,這提供了表達相對位置信息的可能性。結合位置向量和詞向量有幾個可選方案,可以把它們拼接起來作為一個新向量,也可以把位置向量定義為跟詞向量一樣大小,然后兩者加起來。在其他NLP論文中,大家也都看過Position Embedding,通常是一個訓練的向量,但是Position Embedding只是extra features,有該信息會更好,但是沒有性能也不會產生極大下降,因為RNN、CNN本身就能夠捕捉到位置信息,但是在Transformer模型中,Position Embedding是位置信息的唯一來源,因此是該模型的核心成分,並非是輔助性質的特征。

圖3.1.1 Position Embedding表示
3.2 Position-WiseFeed-forwardNetworks(位置前饋網絡)
在進行了Attention操作之后,Encoder和Decoder中的每一層都包含了一個全連接前向網絡,對每個Position的向量分別進行相同的操作,包括兩個線性變換和一個ReLU激活輸出:

即在每個Sub-Layer,針對Self-Attention層的輸出,先使用一個線性變換,再針對該線性變換的輸出使用RELU函數,最后再針對RELU函數的輸出使用一個線性變化。那么,做這么繁瑣的變換有什么意義呢?
我們將FNN與CNN做對比,其實可以發現,其效果與加上一層卷積核大小為1*1的CNN是一樣的。那么這就好理解了,這層所謂的FNN其實也是做特征提取的。至於它為什么不直接取名為1*1CNN Layer,這就要去問Tranformer的發明者了。
3.3 Encoder(編碼器)
Encoder有N=6層,每層包括兩個Sub-Layers:第一個Sub-Layer是Multi-Headed Self-Attentionmechanism,用來計算輸入的Self-Attention;第二個Sub-Layer是簡單的全連接網絡。在每個Sub-Layer我們都模擬了殘差網絡,每個Sub-Layer的輸出都是:

其中SubLayer(x) 表示Sub-Layer對輸入 x 做的映射,為了確保連接,所有的Sub-Layers和Embedding Layer輸出的維數都相同dmodel。

圖3.3.1 Sub-Layer結構示意圖
3.4 Decoder(解碼器)
Decoder也是N=6層,每層包括3個Sub-Layers:
(1) 第一個是Masked multi-head Self-Attention,也是計算輸入的Self-Attention,但是因為是生成過程,因此在時刻 i 的時候,大於 i 的時刻都沒有結果,只有小於 i 的時刻有結果,因此需要做Mask。
(2) 第二個Sub-Layer是全連接網絡,與Encoder相同。
(3) 第三個Sub-Layer是對Encoder的輸入進行Attention計算。同時Decoder中的Self-Attention層需要進行修改,因為只能獲取到當前時刻之前的輸入,因此只對時刻 t 之前的時刻輸入進行Attention計算,這也稱為Mask操作。

圖3.4.1 整體結構圖
3.5 最后的Linear和Softmax層
解碼器最后會輸出一個實數向量。我們如何把浮點數變成一個單詞?這便是線性變換層要做的工作,它之后就是Softmax層。線性變換層是一個簡單的全連接神經網絡,它可以把解碼組件產生的向量投射到一個比它大得多的、被稱作對數幾率(logits)的向量里。不妨假設我們的模型從訓練集中學習一萬個不同的英語單詞(我們模型的“輸出詞表”)。因此對數幾率向量為一萬個單元格長度的向量——每個單元格對應某一個單詞的分數。接下來的Softmax 層便會把那些分數變成概率(都為正數、上限1.0)。概率最高的單元格被選中,並且它對應的單詞被作為這個時間步的輸出,同時將Decoder的堆棧輸出作為輸入,從底部開始,最終進行單詞預測。
圖 3.5.1 Decoder層預測
4.實驗
最后我們通過實驗可以看出,Transformer用了最少的資源得到了state-of-art的輸出回報。
表4.1英語-法語、英語-德語機翻結果對比表
參考:https://mp.weixin.qq.com/s/RLxWevVWHXgX-UcoxDS70w
https://github.com/tensorflow/tensor2tensor
https://baijiahao.baidu.com/s?id=1622064575970777188&wfr=spider&for=pc
