最通俗易懂的Transformer教程


Transformer是谷歌大腦在2017年底發表的論文attention is all you need中所提出的seq2seq模型. 現在已經取得了大範圍的應用和擴展, 而BERT就是從transformer中衍生出來的預訓練語言模型。在NLP領域目前已經成了標配,語言模型, 命名實體識別, 機器翻譯, 可能很多人想到的LSTM等循環神經網絡, 但目前其實LSTM起碼在自然語言處理領域已經過時了, 在Stanford閱讀理解數據集**(SQuAD2.0)榜單裏, 機器的成績已經超人類表現, 這很大程度要歸功於transformer的BERT預訓練模型**。

一. transformer encoder

  1. transformertransformer模型的直覺, 建立直觀認識;
  2. positional encodingpositional \ encoding, 即位置嵌入(或位置編碼);
  3. self attention mechanismself \ attention \ mechanism, 即自注意力機制注意力矩陣可視化;
  4. Layer NormalizationLayer \ Normalization和殘差連接.
  5. transformer encodertransformer \ encoder整體結構.

0. Tansformer 整體感受

首先來說一下transformerLSTM的最大區別, 就是LSTM的訓練是迭代的, 是一個接一個字的來, 當前這個字過完LSTM單元, 纔可以進下一個字, 而transformer的訓練是並行了, 就是所有字是全部同時訓練的, 這樣就大大加快了計算效率, transformer使用了位置嵌入(positional encoding)(positional \ encoding)來理解語言的順序, 使用自注意力機制和全連接層來進行計算, 這些後面都會詳細講解.
transformer模型主要分爲兩大部分, 分別是編碼器解碼器, 編碼器負責把自然語言序列映射成爲隱藏層(下圖中第2步用九宮格比喻的部分), 含有自然語言序列的數學表達. 然後解碼器把隱藏層再映射爲自然語言序列, 從而使我們可以解決各種問題, 如情感分類, 命名實體識別, 語義關係抽取, 摘要生成, 機器翻譯等等, 下面我們簡單說一下下圖的每一步都做了什麼:

  1. 輸入自然語言序列到編碼器: Why do we work?(爲什麼要工作);
  2. 編碼器輸出的隱藏層, 再輸入到解碼器;
  3. 輸入<start><start>(起始)符號到解碼器;
  4. 得到第一個字"爲";
  5. 將得到的第一個字"爲"落下來再輸入到解碼器;
  6. 得到第二個字"什";
  7. 將得到的第二字再落下來, 直到解碼器輸出<end><end>(終止符), 即序列生成完成.

在這裏插入圖片描述

1. positional encodingpositional \ encoding, 即位置嵌入(或位置編碼);

由於transformer模型沒有循環神經網絡的迭代操作, 所以我們必須提供每個字的位置信息給transformer, 才能識別出語言中的順序關係.

現在定義一個位置嵌入的概念, 也就是positional encodingpositional \ encoding, 位置嵌入的維度爲[max sequence length, embedding dimension][max \ sequence \ length, \ embedding \ dimension], 嵌入的維度同詞向量的維度, max sequence lengthmax \ sequence \ length屬於超參數, 指的是限定的最大單個句長.

注意, 我們一般以字爲單位訓練transformer模型, 也就是說我們不用分詞了, 首先我們要初始化字向量爲[vocab size, embedding dimension][vocab \ size, \ embedding \ dimension], vocab sizevocab \ size爲總共的字庫數量, embedding dimensionembedding \ dimension爲字向量的維度, 也是每個字的數學表達.

在這裏論文中使用了sinesinecosinecosine函數的線性變換來提供給模型位置信息:
PE(pos,2i)=sin(pos/100002i/dmodel)PE(pos,2i+1)=cos(pos/100002i/dmodel)(eq.1)PE_{(pos,2i)} = sin(pos / 10000^{2i/d_{\text{model}}}) \quad PE_{(pos,2i+1)} = cos(pos / 10000^{2i/d_{\text{model}}})\tag{eq.1}
上式中pospos指的是句中字的位置, 取值範圍是[0, max sequence length)[0, \ max \ sequence \ length), ii指的是詞向量的維度, 取值範圍是[0, embedding dimension)[0, \ embedding \ dimension), 上面有sinsincoscos一組公式, 也就是對應着embedding dimensionembedding \ dimension維度的一組奇數和偶數的序號的維度, 例如0,10, 1一組, 2,32, 3一組, 分別用上面的sinsincoscos函數做處理, 從而產生不同的週期性變化, 而位置嵌入在embedding dimensionembedding \ dimension維度上隨着維度序號增大, 週期變化會越來越慢, 而產生一種包含位置信息的紋理, 就像論文原文中第六頁講的, 位置嵌入函數的週期從2π2 \pi100002π10000 * 2 \pi變化, 而每一個位置在embedding dimensionembedding \ dimension維度上都會得到不同週期的sinsincoscos函數的取值組合, 從而產生獨一的紋理位置信息, 模型從而學到位置之間的依賴關係和自然語言的時序特性.
下面畫一下位置嵌入, 可見縱向觀察, 隨着embedding dimensionembedding \ dimension增大, 位置嵌入函數呈現不同的週期變化.

在這裏插入圖片描述
在這裏插入圖片描述

對於同一個位置的字,也就是seq_len相同,在不同的hidden dimension上的明暗變化是不同的。在同一個hidden dimension維度上,沿着seq_len的維度是呈週期變化的。這樣通過sin和cos函數就把位置信息進行了編碼。

注意力矩陣是一個seq_len * seq_len的矩陣,下圖是注意力矩陣可視化後的效果:
在這裏插入圖片描述
上圖中, 我們用位置編碼矩陣乘以(矩陣乘)他本身的轉置, 也就是PE: [seq_len, embedding_dim]PE: \ [seq\_len, \ embedding\_dim ], 我們求PEPETPEPE^T, 得出的維度是[seq_len, seq_len][seq\_len, \ seq\_len ]. 我們看到上圖中, 矩陣的對角線隆起, 也就是值比較大, 是因爲一個矩陣乘以他本身的轉置之後, 形成的矩陣的對角線正是這個矩陣的每一行(row)(row)點乘這一行本身, 所以是值最大的區域(紅色部分). 對於位置編碼來說, 也就是當前位置與當前位置本身相關程度最高. 再往對角線兩邊看, 發現以對角線(紅色山峯)區域爲中心, 兩邊屬於緩慢下降趨勢, 這就說明了隨着離當前位置越遠, 其位置編碼的相關程度就越低. 由此可見, 位置編碼建立在時間維度的關聯關係.

後邊有人指出,transformer只進行了位置上的編碼,但是不能體現方向上的信息,導致transformer結構直接用於NER任務時,效果不佳,在MSRA數據集上只有86%的f1值,經過改進可以達到92.7%的f1值,比使用char-bigram-bilstm-crf的效果(92%)好一些。Adapted Transformer Encoder for Named Entity Recognition,論文鏈接:https://arxiv.org/abs/1911.04474

2. self attention mechanismself \ attention \ mechanism, 自注意力機制

在這裏插入圖片描述
在這裏插入圖片描述
Attention mask:在softmax之前,需要根據seq_len來進行mask,防止padding位置上也有信息。
在這裏插入圖片描述
注意, 在上面self attentionself \ attention的計算過程中, 我們通常使用mini batchmini \ batch來計算, 也就是一次計算多句話, 也就是XX的維度是[batch size, sequence length][batch \ size, \ sequence \ length], sequence lengthsequence \ length是句長, 而一個mini batchmini \ batch是由多個不等長的句子組成的, 我們就需要按照這個mini batchmini \ batch中最大的句長對剩餘的句子進行補齊長度, 我們一般用00來進行填充, 這個過程叫做paddingpadding.
但這時在進行softmaxsoftmax的時候就會產生問題, 回顧softmaxsoftmax函數σ(z)i=ezij=1Kezj\sigma (\mathbf {z} )_{i}={\frac {e^{z_{i}}}{\sum _{j=1}^{K}e^{z_{j}}}}, e0e^0是1, 是有值的, 這樣的話softmaxsoftmax中被paddingpadding的部分就參與了運算, 就等於是讓無效的部分參與了運算, 會產生很大隱患, 這時就需要做一個maskmask讓這些無效區域不參與運算, 我們一般給無效區域加一個很大的負數的偏置, 也就是:
zillegal=zillegal+biasillegalz_{illegal} = z_{illegal} + bias_{illegal}
biasillegalbias_{illegal} \to -\infty
ezillegal0e^{z_{illegal}} \to 0
經過上式的maskingmasking我們使無效區域經過softmaxsoftmax計算之後還幾乎爲00, 這樣就避免了無效區域參與計算.

3. 殘差連接和Layer NormalizationLayer \ Normalization

1). 殘差連接:
我們在上一步得到了經過注意力矩陣加權之後的VV, 也就是Attention(Q, K, V)Attention(Q, \ K, \ V), 我們對它進行一下轉置, 使其和XembeddingX_{embedding}的維度一致, 也就是[batch size, sequence length, embedding dimension][batch \ size, \ sequence \ length, \ embedding \ dimension], 然後把他們加起來做殘差連接, 直接進行元素相加, 因爲他們的維度一致:
Xembedding+Attention(Q, K, V)X_{embedding} + Attention(Q, \ K, \ V)
在之後的運算裏, 每經過一個模塊的運算, 都要把運算之前的值和運算之後的值相加, 從而得到殘差連接, 訓練的時候可以使梯度直接走捷徑反傳到最初始層:
X+SubLayer(X)(eq. 5)X + SubLayer(X) \tag{eq. 5}
2). LayerNormLayerNorm:
LayerNormalizationLayer Normalization的作用是把神經網絡中隱藏層歸一爲標準正態分佈, 也就是i.i.di.i.d獨立同分布, 以起到加快訓練速度, 加速收斂的作用:
μi=1mi=1mxij\mu_{i}=\frac{1}{m} \sum^{m}_{i=1}x_{ij}
上式中以矩陣的行(row)(row)爲單位求均值;
σj2=1mi=1m(xijμj)2\sigma^{2}_{j}=\frac{1}{m} \sum^{m}_{i=1} (x_{ij}-\mu_{j})^{2}
上式中以矩陣的行(row)(row)爲單位求方差;
LayerNorm(x)=αxijμiσi2+ϵ+β(eq.6)LayerNorm(x)=\alpha \odot \frac{x_{ij}-\mu_{i}} {\sqrt{\sigma^{2}_{i}+\epsilon}} + \beta \tag{eq.6}
然後用每一行每一個元素減去這行的均值, 再除以這行的標準差, 從而得到歸一化後的數值, ϵ\epsilon是爲了防止除00;
之後引入兩個可訓練參數α, β\alpha, \ \beta來彌補歸一化的過程中損失掉的信息, 注意\odot表示元素相乘而不是點積, 我們一般初始化α\alpha爲全11, 而β\beta爲全00.

4. Feed Forward

這一部分比較簡單,把上一步中的輸出,經過了一個放縮,先把維度擴大到之前的3倍,再縮小到之前的hidden_size,再經過激活函數。得到一個[seq_len, hidden_size]大小的矩陣,再經過殘差連接和Layer Normalizaion。

5. Transformer整體架構

經過上面4個步驟, 我們已經基本瞭解到來transformertransformer編碼器的主要構成部分, 我們下面用公式把一個transformer blocktransformer \ block的計算過程整理一下:
1). 字向量與位置編碼:
X=EmbeddingLookup(X)+PositionalEncoding(eq.2)X = EmbeddingLookup(X) + PositionalEncoding \tag{eq.2}
XRbatch size  seq. len.  embed. dim.X \in \mathbb{R}^{batch \ size \ * \ seq. \ len. \ * \ embed. \ dim.}
2). 自注意力機制:
Q=Linear(X)=XWQQ = Linear(X) = XW_{Q}
K=Linear(X)=XWK(eq.3)K = Linear(X) = XW_{K} \tag{eq.3}
V=Linear(X)=XWVV = Linear(X) = XW_{V}
Xattention=SelfAttention(Q, K, V)(eq.4)X_{attention} = SelfAttention(Q, \ K, \ V) \tag{eq.4}
3). 殘差連接與Layer NormalizationLayer \ Normalization
Xattention=X+Xattention(eq. 5)X_{attention} = X + X_{attention} \tag{eq. 5}
Xattention=LayerNorm(Xattention)(eq. 6)X_{attention} = LayerNorm(X_{attention}) \tag{eq. 6}
4). 下面進行transformer blocktransformer \ block結構圖中的第4部分, 也就是FeedForwardFeedForward, 其實就是兩層線性映射並用激活函數激活, 比如說ReLUReLU:
Xhidden=Activate(Linear(Linear(Xattention)))(eq. 7)X_{hidden} = Activate(Linear(Linear(X_{attention}))) \tag{eq. 7}
5). 重複3).:
Xhidden=Xattention+XhiddenX_{hidden} = X_{attention} + X_{hidden}
Xhidden=LayerNorm(Xhidden)X_{hidden} = LayerNorm(X_{hidden})
XhiddenRbatch size  seq. len.  embed. dim.X_{hidden} \in \mathbb{R}^{batch \ size \ * \ seq. \ len. \ * \ embed. \ dim.}

通過sin和cos函數對不同維度上的信息的週期是不同的,把位置信息與word embedding信息相加,再求了一個attention matrix,維度是[seq_len, seq_len]的,每一行是歸一化的,是當前字與整個句子每個字的相似度的概率分佈。因爲用了點積,本質上是自己與自己作點積得到的概率分佈。再對embedding的信息作加權,得到殘差。

multi-head是爲了提取不同的語義信息。

一個transformer層用了兩次殘差連接和兩次Layer Normalization。

transformer的本質是multi-head的self-attention。

參考:
https://github.com/aespresso/a_journey_into_math_of_ml

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