Transformer模型結構詳解【小白必看】

Transformer模型詳解
https://terrifyzhao.github.io/2019/01/11/Transformer%E6%A8%A1%E5%9E%8B%E8%AF%A6%E8%A7%A3.html

簡介

Attention Is All You Need是一篇Google提出的將Attention思想發揮到極致的論文。這篇論文中提出一個全新的模型,叫 Transformer,拋棄了以往深度學習任務裏面使用到的 CNN 和 RNN ,目前大熱的Bert就是基於Transformer構建的,這個模型廣泛應用於NLP領域,例如機器翻譯,問答系統,文本摘要和語音識別等等方向。

Transformer總體結構

和Attention模型一樣,Transformer模型中也採用了 encoer-decoder 架構。但其結構相比於Attention更加複雜,論文中encoder層由6個encoder堆疊在一起,decoder層也一樣。

img

每一個encoder和decoder的內部簡版結構如下圖

img

對於encoder,包含兩層,一個self-attention層和一個前饋神經網絡,self-attention能幫助當前節點不僅僅只關注當前的詞,從而能獲取到上下文的語義。

decoder也包含encoder提到的兩層網絡,但是在這兩層中間還有一層attention層,幫助當前節點獲取到當前需要關注的重點內容。

現在我們知道了模型的主要組件,接下來我們看下模型的內部細節。首先,模型需要對輸入的數據進行一個embedding操作,也可以理解爲類似w2c的操作,enmbedding結束之後,輸入到encoder層,self-attention處理完數據後把數據送給前饋神經網絡,前饋神經網絡的計算可以並行,得到的輸出會輸入到下一個encoder。

img

Self-Attention

接下來我們詳細看一下self-attention,其思想和attention類似,但是self-attention是Transformer用來將其他相關單詞的“理解”轉換成我們正在處理的單詞的一種思路,我們看個例子: The animal didn’t cross the street because it was too tired 這裏的it到底代表的是animal還是street呢,對於我們來說能很簡單的判斷出來,但是對於機器來說,是很難判斷的,self-attention就能夠讓機器把it和animal聯繫起來,接下來我們看下詳細的處理過程。

1、首先,self-attention會計算出三個新的向量,在論文中,向量的維度是512維,我們把這三個向量分別稱爲Query、Key、Value,這三個向量是用embedding向量與一個矩陣相乘得到的結果,這個矩陣是隨機初始化的,維度爲(64,512)注意第二個維度需要和embedding的維度一樣,其值在BP的過程中會一直進行更新,得到的這三個向量的維度是64低於embedding維度的。

img

那麼Query、Key、Value這三個向量又是什麼呢?這三個向量對於attention來說很重要,當你理解了下文後,你將會明白這三個向量扮演者什麼的角色。

2、計算self-attention的分數值,該分數值決定了當我們在某個位置encode一個詞時,對輸入句子的其他部分的關注程度。這個分數值的計算方法是Query與Key做點成,以下圖爲例,首先我們需要針對Thinking這個詞,計算出其他詞對於該詞的一個分數值,首先是針對於自己本身即q1·k1,然後是針對於第二個詞即q1·k2

img

3、接下來,把點成的結果除以一個常數,這裏我們除以8,這個值一般是採用上文提到的矩陣的第一個維度的開方即64的開方8,當然也可以選擇其他的值,然後把得到的結果做一個softmax的計算。得到的結果即是每個詞對於當前位置的詞的相關性大小,當然,當前位置的詞相關性肯定會會很大

img

4、下一步就是把Value和softmax得到的值進行相乘,並相加,得到的結果即是self-attetion在當前節點的值。

img

在實際的應用場景,爲了提高計算速度,我們採用的是矩陣的方式,直接計算出Query, Key, Value的矩陣,然後把embedding的值與三個矩陣直接相乘,把得到的新矩陣Q與K相乘,乘以一個常數,做softmax操作,最後乘上V矩陣

img

這種通過 query 和 key 的相似性程度來確定 value 的權重分佈的方法被稱爲scaled dot-product attention。

img

Multi-Headed Attention

這篇論文更牛逼的地方是給self-attention加入了另外一個機制,被稱爲“multi-headed” attention,該機制理解起來很簡單,就是說不僅僅只初始化一組Q、K、V的矩陣,而是初始化多組,tranformer是使用了8組,所以最後得到的結果是8個矩陣。

img

img

這給我們留下了一個小的挑戰,前饋神經網絡沒法輸入8個矩陣呀,這該怎麼辦呢?所以我們需要一種方式,把8個矩陣降爲1個,首先,我們把8個矩陣連在一起,這樣會得到一個大的矩陣,再隨機初始化一個矩陣和這個組合好的矩陣相乘,最後得到一個最終的矩陣。

img

這就是multi-headed attention的全部流程了,這裏其實已經有很多矩陣了,我們把所有的矩陣放到一張圖內看一下總體的流程。

img

Positional Encoding

到目前爲止,transformer模型中還缺少一種解釋輸入序列中單詞順序的方法。爲了處理這個問題,transformer給encoder層和decoder層的輸入添加了一個額外的向量Positional Encoding,維度和embedding的維度一樣,這個向量採用了一種很獨特的方法來讓模型學習到這個值,這個向量能決定當前詞的位置,或者說在一個句子中不同的詞之間的距離。這個位置向量的具體計算方法有很多種,論文中的計算方法如下
PE(pos,2i)=sin(pos/100002i/dmodel PE(pos,2i)=sin(pos/10000^{2i}/d_{model}

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

其中pos是指當前詞在句子中的位置,i是指向量中每個值的index,可以看出,在偶數位置,使用正弦編碼,在奇數位置,使用餘弦編碼,這裏提供一下代碼。

position_encoding = np.array(
    [[pos / np.power(10000, 2.0 * (j // 2) / d_model) for j in range(d_model)] for pos in range(max_seq_len)])

position_encoding[:, 0::2] = np.sin(position_encoding[:, 0::2])
position_encoding[:, 1::2] = np.cos(position_encoding[:, 1::2])

最後把這個Positional Encoding與embedding的值相加,作爲輸入送到下一層。

img

Layer normalization

在transformer中,每一個子層(self-attetion,ffnn)之後都會接一個殘缺模塊,並且有一個Layer normalization

img

殘缺模塊相信大家都很清楚了,這裏不再講解,主要講解下Layer normalization。Normalization有很多種,但是它們都有一個共同的目的,那就是把輸入轉化成均值爲0方差爲1的數據。我們在把數據送入激活函數之前進行normalization(歸一化),因爲我們不希望輸入數據落在激活函數的飽和區。

說到 normalization,那就肯定得提到 Batch Normalization。BN的主要思想就是:在每一層的每一批數據上進行歸一化。我們可能會對輸入數據進行歸一化,但是經過該網絡層的作用後,我們的數據已經不再是歸一化的了。隨着這種情況的發展,數據的偏差越來越大,我的反向傳播需要考慮到這些大的偏差,這就迫使我們只能使用較小的學習率來防止梯度消失或者梯度爆炸。

BN的具體做法就是對每一小批數據,在批這個方向上做歸一化。如下圖所示:

img

可以看到,右半邊求均值是沿着數據 batch_size的方向進行的,其計算公式如下:
BN(xi)=α×xiμbσB2+ϵ+β BN(x_i)=α×\frac{x_i−μ_b}{\sqrt{σ^2_B+ϵ}}+β
那麼什麼是 Layer normalization 呢?它也是歸一化數據的一種方式,不過 LN 是在每一個樣本上計算均值和方差,而不是BN那種在批方向計算均值和方差!

img

下面看一下 LN 的公式:

LN(xi)=α×xiμLσL2+ϵ+β LN(xi)=α×\frac{xi−μ_L}{\sqrt{σ^2_L+ϵ}}+β

到這裏爲止就是全部encoders的內容了,如果把兩個encoders疊加在一起就是這樣的結構

img

Decode層

img

上圖是transformer的一個詳細結構,相比本文一開始結束的結構圖會更詳細些,接下來,我們會按照這個結構圖講解下decoder部分。

可以看到decoder部分其實和encoder部分大同小異,不過在最下面額外多了一個masked mutil-head attetion,這裏的mask也是transformer一個很關鍵的技術,我們一起來看一下。

Mask
mask 表示掩碼,它對某些值進行掩蓋,使其在參數更新時不產生效果。Transformer 模型裏面涉及兩種 mask,分別是 padding mask 和 sequence mask。

其中,padding mask 在所有的 scaled dot-product attention 裏面都需要用到,而 sequence mask 只有在 decoder 的 self-attention 裏面用到。

Padding Mask

什麼是 padding mask 呢?因爲每個批次輸入序列長度是不一樣的也就是說,我們要對輸入序列進行對齊。具體來說,就是給在較短的序列後面填充 0。但是如果輸入的序列太長,則是截取左邊的內容,把多餘的直接捨棄。因爲這些填充的位置,其實是沒什麼意義的,所以我們的attention機制不應該把注意力放在這些位置上,所以我們需要進行一些處理。

具體的做法是,把這些位置的值加上一個非常大的負數(負無窮),這樣的話,經過 softmax,這些位置的概率就會接近0!

而我們的 padding mask 實際上是一個張量,每個值都是一個Boolean,值爲 false 的地方就是我們要進行處理的地方。

Sequence mask

文章前面也提到,sequence mask 是爲了使得 decoder 不能看見未來的信息。也就是對於一個序列,在 time_step 爲 t 的時刻,我們的解碼輸出應該只能依賴於 t 時刻之前的輸出,而不能依賴 t 之後的輸出。因此我們需要想一個辦法,把 t 之後的信息給隱藏起來。

那麼具體怎麼做呢?也很簡單:產生一個上三角矩陣,上三角的值全爲0。把這個矩陣作用在每一個序列上,就可以達到我們的目的。

對於 decoder 的 self-attention,裏面使用到的 scaled dot-product attention,同時需要padding mask 和 sequence mask 作爲 attn_mask,具體實現就是兩個mask相加作爲attn_mask。
其他情況,attn_mask 一律等於 padding mask。
輸出層
當decoder層全部執行完畢後,怎麼把得到的向量映射爲我們需要的詞呢,很簡單,只需要在結尾再添加一個全連接層和softmax層,假如我們的詞典是1w個詞,那最終softmax會輸入1w個詞的概率,概率值最大的對應的詞就是我們最終的結果。

img

這就是本文的全部內容了,希望對你有所幫助,如果想了解更多的詳情,請參閱論文 Attention Is All You Need,下一篇博客,將會基於transformer的源碼進行代碼講解,幫助大家進一步的瞭解transformer

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章