Transformer 详解
Transformer 是谷歌大腦在 2017 年底發表的論文?attention is all you need?中所提出的 seq2seq 模型。現在已經取得了大范圍的應用和擴展,而 BERT 就是從 Transformer 中衍生出來的預訓練語言模型
這篇文章分為以下幾個部分
0. Transformer 直觀認識
Transformer 和 LSTM 的最大區別,就是 LSTM 的訓練是迭代的、串行的,必須要等當前字處理完,才可以處理下一個字。而 Transformer 的訓練時并行的,即所有字是同時訓練的,這樣就大大增加了計算效率。Transformer 使用了位置嵌入 (Positional Encoding) 來理解語言的順序,使用自注意力機制(Self Attention Mechanism)和全連接層進行計算,這些后面會講到
Transformer 模型主要分為兩大部分,分別是?Encoder?和?Decoder。Encoder?負責把輸入(語言序列)隱射成隱藏層(下圖中第 2 步用九宮格代表的部分),然后解碼器再把隱藏層映射為自然語言序列。例如下圖機器翻譯的例子(Decoder 輸出的時候,是通過 N 層 Decoder Layer 才輸出一個 token,并不是通過一層 Decoder Layer 就輸出一個 token)
本篇文章大部分內容在于解釋?Encoder?部分,即把自然語言序列映射為隱藏層的數學表達的過程。理解了 Encoder 的結構,再理解 Decoder 就很簡單了
上圖為 Transformer Encoder Block 結構圖,注意:下面的內容標題編號分別對應著圖中 1,2,3,4 個方框的序號
1. Positional Encoding
由于 Transformer 模型沒有循環神經網絡的迭代操作,所以我們必須提供每個字的位置信息給 Transformer,這樣它才能識別出語言中的順序關系
現在定義一個位置嵌入的概念,也就是 Positional Encoding,位置嵌入的維度為?[max_sequence_length, embedding_dimension], 位置嵌入的維度與詞向量的維度是相同的,都是?embedding_dimension。max_sequence_length?屬于超參數,指的是限定每個句子最長由多少個詞構成
注意,我們一般以字為單位訓練 Transformer 模型。首先初始化字編碼的大小為?[vocab_size, embedding_dimension],vocab_size?為字庫中所有字的數量,embedding_dimension?為字向量的維度,對應到 PyTorch 中,其實就是?nn.Embedding(vocab_size, embedding_dimension)
論文中使用了 sin 和 cos 函數的線性變換來提供給模型位置信息:
?
PE(pos,2i)=sin(pos/100002i/dmodel)PE(pos,2i+1)=cos(pos/100002i/dmodel)
上式中?pos?指的是一句話中某個字的位置,取值范圍是?[0,?max_sequence_length),i?指的是字向量的維度序號,取值范圍是?[0,?embedding_dimension/2),dmodel?指的是?embedding_dimension?的值
上面有?sin?和?cos?一組公式,也就是對應著?embedding?dimension?維度的一組奇數和偶數的序號的維度,例如?0,1?一組,2,3?一組,分別用上面的?sin?和?cos?函數做處理,從而產生不同的周期性變化,而位置嵌入在?embedding?dimension?維度上隨著維度序號增大,周期變化會越來越慢,最終產生一種包含位置信息的紋理,就像論文原文中第六頁講的,位置嵌入函數的周期從?2π?到?10000?2π?變化,而每一個位置在?embedding?dimension?維度上都會得到不同周期的?sin?和?cos?函數的取值組合,從而產生獨一的紋理位置信息,最終使得模型學到位置之間的依賴關系和自然語言的時序特性
如果不理解這里為何這么設計,可以看這篇文章?Transformer 中的 Positional Encoding
下面畫一下位置嵌入,縱向觀察,可見隨著?embedding?dimension?序號增大,位置嵌入函數的周期變化越來越平緩
-
import numpy as np
-
import matplotlib.pyplot as plt
-
import seaborn as sns
-
import math
- ?
-
def get_positional_encoding(max_seq_len, embed_dim):
-
# 初始化一個positional encoding
-
# embed_dim: 字嵌入的維度
-
# max_seq_len: 最大的序列長度
-
positional_encoding = np.array([
-
[pos / np.power(10000, 2 * i / embed_dim) for i in range(embed_dim)]
-
if pos != 0 else np.zeros(embed_dim) for pos in range(max_seq_len)])
- ?
-
positional_encoding[1:, 0::2] = np.sin(positional_encoding[1:, 0::2]) # dim 2i 偶數
-
positional_encoding[1:, 1::2] = np.cos(positional_encoding[1:, 1::2]) # dim 2i+1 奇數
-
return positional_encoding
- ?
-
positional_encoding = get_positional_encoding(max_seq_len=100, embed_dim=16)
-
plt.figure(figsize=(10,10))
-
sns.heatmap(positional_encoding)
-
plt.title("Sinusoidal Function")
-
plt.xlabel("hidden dimension")
-
plt.ylabel("sequence length")
-
plt.figure(figsize=(8, 5))
-
plt.plot(positional_encoding[1:, 1], label="dimension 1")
-
plt.plot(positional_encoding[1:, 2], label="dimension 2")
-
plt.plot(positional_encoding[1:, 3], label="dimension 3")
-
plt.legend()
-
plt.xlabel("Sequence length")
-
plt.ylabel("Period of Positional Encoding")
2. Self Attention Mechanism
對于輸入的句子?X,通過 WordEmbedding 得到該句子中每個字的字向量,同時通過 Positional Encoding 得到所有字的位置向量,將其相加(維度相同,可以直接相加),得到該字真正的向量表示。第?t?個字的向量記作?xt
接著我們定義三個矩陣?WQ,WK.WV,使用這三個矩陣分別對所有的字向量進行三次線性變換,于是所有的字向量又衍生出三個新的向量?qt,kt,vt。我們將所有的?qt?向量拼成一個大矩陣,記作查詢矩陣?Q,將所有的?kt?向量拼成一個大矩陣,記作鍵矩陣?K,將所有的?vt?向量拼成一個大矩陣,記作值矩陣?V(見下圖)
為了獲得第一個字的注意力權重,我們需要用第一個字的查詢向量?q1?乘以鍵矩陣 K(見下圖)
-
[0, 4, 2]
-
[1, 0, 2] x [1, 4, 3] = [2, 4, 4]
-
[1, 0, 1]
之后還需要將得到的值經過 softmax,使得它們的和為 1(見下圖)
-
softmax([2, 4, 4]) = [0.0, 0.5, 0.5]
有了權重之后,將權重其分別乘以對應字的值向量?vt(見下圖)
-
0.0 * [1, 2, 3] = [0.0, 0.0, 0.0]
-
0.5 * [2, 8, 0] = [1.0, 4.0, 0.0]
-
0.5 * [2, 6, 3] = [1.0, 3.0, 1.5]
最后將這些權重化后的值向量求和,得到第一個字的輸出(見下圖)
-
[0.0, 0.0, 0.0]
-
+ [1.0, 4.0, 0.0]
-
+ [1.0, 3.0, 1.5]
-
-----------------
-
= [2.0, 7.0, 1.5]
對其它的輸入向量也執行相同的操作,即可得到通過 self-attention 后的所有輸出
矩陣計算
上面介紹的方法需要一個循環遍歷所有的字?xt,我們可以把上面的向量計算變成矩陣的形式,從而一次計算出所有時刻的輸出
第一步就不是計算某個時刻的?qt,kt,vt?了,而是一次計算所有時刻的?Q,K?和?V。計算過程如下圖所示,這里的輸入是一個矩陣?X,矩陣第?t?行表示第?t?個詞的向量表示?xt
接下來將?Q?和?KT?相乘,然后除以?dk(這是論文中提到的一個 trick),經過 softmax 以后再乘以?V?得到輸出
Multi-Head Attention
這篇論文還提出了 Multi-Head Attention 的概念。其實很簡單,前面定義的一組?Q,K,V?可以讓一個詞 attend to 相關的詞,我們可以定義多組?Q,K,V,讓它們分別關注不同的上下文。計算?Q,K,V?的過程還是一樣,只不過線性變換的矩陣從一組?(WQ,WK,WV)?變成了多組?(W0Q,W0K,W0V)?,(W1Q,W1K,W1V),… 如下圖所示
對于輸入矩陣?X,每一組?Q、K?和?V?都可以得到一個輸出矩陣?Z。如下圖所示
Padding Mask
上面 Self Attention 的計算過程中,我們通常使用 mini-batch 來計算,也就是一次計算多句話,即?X?的維度是?[batch_size, sequence_length],sequence_length?是句長,而一個 mini-batch 是由多個不等長的句子組成的,我們需要按照這個 mini-batch 中最大的句長對剩余的句子進行補齊,一般用 0 進行填充,這個過程叫做 padding
但這時在進行 softmax 就會產生問題。回顧 softmax 函數?σ(zi)=ezi∑j=1Kezj,e0?是 1,是有值的,這樣的話 softmax 中被 padding 的部分就參與了運算,相當于讓無效的部分參與了運算,這可能會產生很大的隱患。因此需要做一個 mask 操作,讓這些無效的區域不參與運算,一般是給無效區域加一個很大的負數偏置,即
?
Zillegal=Zillegal+biasillegalbiasillegal→?∞
3. 殘差連接和 Layer Normalization
殘差連接
我們在上一步得到了經過 self-attention 加權之后輸出,也就是?Attention(Q,?K,?V),然后把他們加起來做殘差連接
?
Xembedding+Self?Attention(Q,?K,?V)
Layer Normalization
Layer Normalization 的作用是把神經網絡中隱藏層歸一為標準正態分布,也就是?i.i.d?獨立同分布,以起到加快訓練速度,加速收斂的作用
?
μj=1m∑i=1mxij
上式以矩陣的列?(column)?為單位求均值;
?
σj2=1m∑i=1m(xij?μj)2
上式以矩陣的列?(column)?為單位求方差
?
LayerNorm(x)=xij?μjσj2+?
然后用每一列的每一個元素減去這列的均值,再除以這列的標準差,從而得到歸一化后的數值,加???是為了防止分母為 0
下圖展示了更多細節:輸入?x1,x2?經 self-attention 層之后變成?z1,z2,然后和輸入?x1,x2?進行殘差連接,經過 LayerNorm 后輸出給全連接層。全連接層也有一個殘差連接和一個 LayerNorm,最后再輸出給下一個 Encoder(每個 Encoder Block 中的 FeedForward 層權重都是共享的)
4. Transformer Encoder 整體結構
經過上面 3 個步驟,我們已經基本了解了?Encoder?的主要構成部分,下面我們用公式把一個 Encoder block 的計算過程整理一下:
1). 字向量與位置編碼
?
X=Embedding?Lookup(X)+Positional?Encoding
2). 自注意力機制
?
Q=Linear(X)=XWQK=Linear(X)=XWKV=Linear(X)=XWVXattention=SelfAttention(Q,?K,?V)
3). self-attention 殘差連接與 Layer Normalization
?
Xattention=X+XattentionXattention=LayerNorm(Xattention)
4). 下面進行 Encoder block 結構圖中的第 4 部分,也就是 FeedForward,其實就是兩層線性映射并用激活函數激活,比如說?ReLU
?
Xhidden=Linear(ReLU(Linear(Xattention)))
5). FeedForward 殘差連接與 Layer Normalization
?
Xhidden=Xattention+XhiddenXhidden=LayerNorm(Xhidden)
其中
?
Xhidden∈Rbatch_size???seq_len.???embed_dim
5. Transformer Decoder 整體結構
我們先從 HighLevel 的角度觀察一下 Decoder 結構,從下到上依次是:
- Masked Multi-Head Self-Attention
- Multi-Head Encoder-Decoder Attention
- FeedForward Network
和 Encoder 一樣,上面三個部分的每一個部分,都有一個殘差連接,后接一個?Layer Normalization。Decoder 的中間部件并不復雜,大部分在前面 Encoder 里我們已經介紹過了,但是 Decoder 由于其特殊的功能,因此在訓練時會涉及到一些細節
Masked Self-Attention
具體來說,傳統 Seq2Seq 中 Decoder 使用的是 RNN 模型,因此在訓練過程中輸入?t?時刻的詞,模型無論如何也看不到未來時刻的詞,因為循環神經網絡是時間驅動的,只有當?t?時刻運算結束了,才能看到?t+1?時刻的詞。而 Transformer Decoder 拋棄了 RNN,改為 Self-Attention,由此就產生了一個問題,在訓練過程中,整個 ground truth 都暴露在 Decoder 中,這顯然是不對的,我們需要對 Decoder 的輸入進行一些處理,該處理被稱為 Mask
舉個例子,Decoder 的 ground truth 為 "<start> I am fine",我們將這個句子輸入到 Decoder 中,經過 WordEmbedding 和 Positional Encoding 之后,將得到的矩陣做三次線性變換(WQ,WK,WV)。然后進行 self-attention 操作,首先通過?Q×KTdk?得到 Scaled Scores,接下來非常關鍵,我們要對 Scaled Scores 進行 Mask,舉個例子,當我們輸入 "I" 時,模型目前僅知道包括 "I" 在內之前所有字的信息,即 "<start>" 和 "I" 的信息,不應該讓其知道 "I" 之后詞的信息。道理很簡單,我們做預測的時候是按照順序一個字一個字的預測,怎么能這個字都沒預測完,就已經知道后面字的信息了呢?Mask 非常簡單,首先生成一個下三角全 0,上三角全為負無窮的矩陣,然后將其與 Scaled Scores 相加即可
之后再做 softmax,就能將 - inf 變為 0,得到的這個矩陣即為每個字之間的權重
Multi-Head Self-Attention 無非就是并行的對上述步驟多做幾次,前面 Encoder 也介紹了,這里就不多贅述了
Masked Encoder-Decoder Attention
其實這一部分的計算流程和前面 Masked Self-Attention 很相似,結構也一摸一樣,唯一不同的是這里的?K,V?為 Encoder 的輸出,Q?為 Decoder 中 Masked Self-Attention 的輸出
6. 總結
到此為止,Transformer 中 95% 的內容已經介紹完了,我們用一張圖展示其完整結構。不得不說,Transformer 設計的十分巧奪天工
下面有幾個問題,是我從網上找的,感覺看完之后能對 Transformer 有一個更深的理解
Transformer 為什么需要進行 Multi-head Attention?
原論文中說到進行 Multi-head Attention 的原因是將模型分為多個頭,形成多個子空間,可以讓模型去關注不同方面的信息,最后再將各個方面的信息綜合起來。其實直觀上也可以想到,如果自己設計這樣的一個模型,必然也不會只做一次 attention,多次 attention 綜合的結果至少能夠起到增強模型的作用,也可以類比 CNN 中同時使用多個卷積核的作用,直觀上講,多頭的注意力有助于網絡捕捉到更豐富的特征 / 信息
Transformer 相比于 RNN/LSTM,有什么優勢?為什么?
為什么說 Transformer 可以代替 seq2seq?
這里用代替這個詞略顯不妥當,seq2seq 雖已老,但始終還是有其用武之地,seq2seq 最大的問題在于將 Encoder 端的所有信息壓縮到一個固定長度的向量中,并將其作為 Decoder 端首個隱藏狀態的輸入,來預測 Decoder 端第一個單詞 (token) 的隱藏狀態。在輸入序列比較長的時候,這樣做顯然會損失 Encoder 端的很多信息,而且這樣一股腦的把該固定向量送入 Decoder 端,Decoder 端不能夠關注到其想要關注的信息。Transformer 不但對 seq2seq 模型這兩點缺點有了實質性的改進 (多頭交互式 attention 模塊),而且還引入了 self-attention 模塊,讓源序列和目標序列首先 “自關聯” 起來,這樣的話,源序列和目標序列自身的 embedding 表示所蘊含的信息更加豐富,而且后續的 FFN 層也增強了模型的表達能力,并且 Transformer 并行計算的能力遠遠超過了 seq2seq 系列模型
7. 參考文章
- Transformer
- The Illustrated Transformer
- TRANSFORMERS FROM SCRATCH
- Seq2seq pay Attention to Self Attention: Part 2
總結
以上是生活随笔為你收集整理的Transformer 详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BERT却不懂Transformer?2
- 下一篇: Transformer结构详解(有图,有