寫在前面

很早就看了

Attention is all you need

這篇paper,當時就準備寫下閱讀筆記,但是忙於畢設的緣故,就被擱置了。現在倒是有時間,可以將自己的理解寫下來。最為主要的是加深自己的理解,分享自己淺陋的見解,希望更多人去看看原文paper。

我們不一樣,

每個人對文章的側重點不一樣,所以看法都不盡相同。本文是萌新的閱讀筆記,簡單粗暴,只寫自己的閱讀理解。

Paper的亮點

大量使用attenton機制,完全摒棄cnn和rnn。

高度並行化處理,節省計算開支並且不損害計算結果

提出並標準和規範化 self-attention

提出mutli-head attention方法,相當於不同子空間聯合學習(ensemble)

模型結構介紹

萬物皆Attention的鼻祖之作 《Attention is all you need》閱讀筆記

圖1是原文的模型結構圖。模型分為左右兩部分:左邊編碼器,右邊解碼器。其實seq2seq模型還是很經典的encoder-decoder。

Encoder Stack

編碼器由6個獨立的層組成。每一層裡面又有兩個子層,Mutli-head self attention(多抽頭的自注意力)和Position-wise feed forward network(逐位置的前饋網路)。每一子層輸出之後跟了一個殘差連線(residual connection)和層規範(LayerNorm,LN)。

Encoder 過程

輸入的是sentence embedding和按位置加上position embedding的x。

encoder第一個子層是多抽頭自注意力(下文介紹)輸出表示為sublayer(x),經過add&norm(殘差連線和層規範,下文介紹)為輸出

output_1=LN(x+sublayer(x))

encoder第二層是逐項前饋網路FFN層。其實第二個子層就是一個MLP網路,由兩個線性變換組成。

FFN(output_1)=max(0,output_1*w_1+b_1)*w_2+b_2

線性變換之後再經過add&norm。我感覺加入FFN層是為了提高模型特徵抽取的能力。

Decoder Stack

解碼器也是有6個獨立層構成的。但是它每一層包含三個子層:Mutli-head self attention(來自編碼器)、Position-wise feed forward network以及帶掩碼的 Masked mutli-head self attention(下文介紹)。每一子層輸出之後也跟了一個殘差連線和層規範。

Decoder 過程

輸入是已經翻譯好K個詞的embedding和按位置加上position embedding的詞向量。

decoder第一個子層是 Masked mutli-head self attention,它比encoder部分多了一個掩碼,就是為了保證後面的資訊不參與計算(簡單粗暴把後面的值減去一個無窮大的值),然後經過add&norm輸出。

decoder第二個子層是mutli-head self attention,其中encoder部分的輸出與decoder第一個子層的輸出作為輸入,本質還是做多抽頭的注意力,然後還要經過add&norm輸出。

decoder第三個子層是Position-wise feed forward network,這一子層和encoder部分的一樣,之後再經過add&norm輸出。

比較重要的就是encoder和decoder這兩部分,這個是谷歌團隊提出的核心部分。另外對解碼器來說,第三個子層輸出的結果透過線性變換以及softmax函式轉化為一個預測下一個詞的機率。本文下一部分將介紹模型的關鍵元件。

模型關鍵元件

殘差連線

我們都知道網路的深度對複雜模型的重要程度,它可以提取到更為豐富、抽象和具有語義資訊的特徵。深度增加不能簡單地透過增加層數,這樣不僅會導致梯度出現彌散或者爆炸,更為嚴重的是會導致模型退化(也就是在訓練集上效能飽和甚至下降,與過擬合不同)。深度殘差網路就是為了解決退化的問題。其實引入殘差連線,也是為了儘可能保留原始輸入x的資訊。

層規範

為什麼要用Layer Normalization(LN)得從batch normalization(BN)說起,那篇幅就賊長了,這裡就省略batch normalization的門門道道。簡單地說LN就是為了針對BN的問題所提出來的。字面意思BN是對一個batch資料進行歸一化,LN是對本次輸入模型的一個樣本做歸一化(歸一化因子是本層的神經元的個數)。相較於BN來說LN訓練速度更快。

Scaled Dot-Product Attention

萬物皆Attention的鼻祖之作 《Attention is all you need》閱讀筆記

圖2 放縮點積注意力

圖2是paper中的Scaled Dot-Product Attention,這種Q、K、V式的求Attention值方法,我以前寫過這類文章(知無我:淺談Attention機制的理解)感興趣的可以去瞧一瞧。本文用的計算Attention值公式為:

Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt{d_k}})V

其實像這種Attention的求解方式不是創新點,但是由於谷歌全球的影響力,我倒是覺得它將Attention來了一個行業規定,從此就出現了一個Attention層了。要真說新鮮感的話應該是添加了縮放因子

\sqrt{d_k}

。為什麼多了這個縮放因子,先從原文中講的兩種求注意力值的演算法開始。

加法式注意力

萬物皆Attention的鼻祖之作 《Attention is all you need》閱讀筆記

圖3 加法式注意力

萬物皆Attention的鼻祖之作 《Attention is all you need》閱讀筆記

加法式注意力是非常經典的注意力機制。它其實就是一個全連線網路來計算注意力分配機制,原理很簡單。求得得分函式

e_{ij}

後使用softmax歸一化就成了要分配的注意力係數。

乘積式注意力

萬物皆Attention的鼻祖之作 《Attention is all you need》閱讀筆記

乘積式注意力計算也是一種很便捷的方法,它不需要全連線來實現,因此不需要額外的空間來儲存隱藏層的變數,保證了空間複雜度;另外也可以用最佳化矩陣的演算法來計算,時間複雜度也會降低。

這時來談談放縮因子

\sqrt{d_k}

原文中比較了兩種注意力計算方式,

d_k

指的是輸入的維度。當

d_k

維度比較小的時候,兩者就變得很相似;當

d_k

較大時,空間和時間複雜度就會增加,使用乘積式注意力比加法式要好,但是因為維度比較大,點積後幅度會變得很大,經過softmax就會趨向1或者0,這樣就很尬了,因此需要使用縮放因子

\sqrt{d_k}

。為什麼用根號是因為原文假設資料符合標準正態分佈,為了使點乘結果的方差為1,所以使用了根號。

Multi-head Attention

萬物皆Attention的鼻祖之作 《Attention is all you need》閱讀筆記

圖4 多抽頭注意力

多抽頭的注意力就是本文最大的創新點了。說Mutli-head self attention之前得先要說一下self-attention。

self-attention

我之前文章寫過,attention的求解可以看做一個軟定址的過程。原文中也講了attention值就是查詢(Query)到一系列鍵值對(Key、Value)的對映。在encoder-decoder結構中Query來自之前上一層Decoder,而Key和Value則是上一層Encoder的輸出。這種機制使得句子中的每一個部分都可以參與Encoder-Decoder的過程。簡單地用數學表示式可以這樣統一描述注意力值

y_t=f(q_t,K,V)

其中

q_t

是原序列第t個維度向量,K和V是另一個序列(矩陣)。當Q=K=V時,這時的求解注意力函式就變成了self-attention。在self attention中,Query,Key和Value都來自相同的上一步的輸出,也就是說它是在序列內部將每個詞與其他詞進行相似度計算來得到整個序列的注意力值。self-attention可以一步到位的獲取了全域性資訊,無視距離直接兩兩詞拿來比較計算依存關係。

Mutli-head self attention

多抽頭自注意力就是在縮放點積式自注意力的基礎上,重複做h次,然後把結果拼接。每一次的結果都成為一個head。圖4中linear就是一個線性對映,同次變換的權重相同。這樣會不會使得模型變得很複雜那?其實上它在做線性對映的時候是將輸入維度降低了,這裡線性對映就是一個帶隱藏層的前饋網路,最後使得它與原來不做線性對映只做一次self-attention的維度一樣。原文解釋這樣做可以讓序列可以在不同的表示子空間中學習,相當於使用了一個聯合學習方法(ensemble)。

Position Embedding

像上文寫的self-attention,它充其量是一個巧妙的“詞袋”模型,沒有將語序資訊加入到裡面。原文為了新增語序資訊引入了Position Embedding。講道理我是沒有太理解它引入這個位置編碼為什麼是相加式的而不是拼接式的。位置編碼公式如下:

PE(pos,2i)=sin(pos/10000^{2i/d_{model}})

PE(pos,2i+1)=cos(pos/10000^{2i/d_{model}})

這個位置編碼為什麼是這樣,原文中沒有詳細解釋,只是說了他們比較過直接訓練出來的位置向量和上述公式計算出來的位置向量,效果是接近的。出於對正餘弦函式的敏感,往正餘弦和差公式上想的話

sin(a+b)=sinacosb+cosasinb

直觀上看是不是一個線性變換?而且這個位置資訊本身就是一個絕對資訊,但是引入正餘弦的話從另一個角度是不是可以看作是加入了相對順序?(位置c=a+b可以看成a的線性組合)另外從直覺上說,將位置編碼拼接給輸入向量效果會好,但實驗結果確實相加效果最好,難道相加不會覆蓋資訊嗎?我是比較疑惑的。

最後來幾句

這篇paper確實寫的很工程,不是純理論的內容,對實踐有很大的幫助。我曾經也借鑑網上大佬關於這篇paper寫的程式碼,跑了一些程式,我覺得計算開銷還是真的不小。原文也是在8塊GPU上訓練了三天多才跑出來。原文中很是強調只用self-attention完全不用CNN和RNN,總感覺是在跟FaceBook以前寫的 Convolutional Sequence to Sequence Learning 較勁哩。不管怎麼說,谷歌這篇paper的創新確實很亮,至於題名,顯得就很霸氣了,我覺得注意力機制在seq2seq上確實有很大的突破,但是其他方面還是得落實到具體的應用上。行吧,萬物皆Attention的鼻祖之作就分享到此吧。