【翻譯】圖解自注意力機制

語言: CN / TW / HK

theme: condensed-night-purple

持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第12天,點選檢視活動詳情


這是我翻譯這位大佬的第二篇文章了。這篇文章是受到大佬認證的了。他的原文中有翻譯連結,直接指向我。

image.png

作者部落格:@Jay Alammar

原文連結:The Illustrated GPT-2 (Visualizing Transformer Language Models) – Jay Alammar – Visualizing machine learning one concept at a time. (jalammar.github.io)

重大宣告

  1. 這個文章是《圖解GPT-2 | The Illustrated GPT-2 (Visualizing Transformer Language Models)》的一部分,因為篇幅太長我就單獨拿出來了。

    當然如果你只想瞭解自注意力機制也可以看看本文章的前半部分,這篇文章屬算是入門科普讀物了,不需要太多知識鋪墊。 後半部分主要是講masked self-attention在GPT-2中的應用,不瞭解GPT-2的可以忽略這部分內容。

  2. 我補充的內容格式如下:

    這是我補充的內容。


正文

看一下下圖,下圖表示的是注意力處理輸入序列的it單詞的時候。

在這裡插入圖片描述

接下來我們詳細介紹一下這一過程是如何實現的。

注意,接下來的圖解過程會用到很多 “向量” 來圖解演算法機制,而實際實現中是使用 矩陣 進行計算的。這個分析過程是想讓讀者瞭解在處理過程中每個單詞發生了什麼,因此本文的重點是單詞級(word-level)處理邏輯進行解析。

自注意力

我們從原始的自注意開始,它是在一個encoder元件中計算的。我們先來看看這個簡單的Transformer元件,它一次只能處理四個tokens。

僅需三步即可實現自注意力: 1. 為每個單詞路徑建立Query、Key、Value。 2. 對於每個輸入token,使用其Query向量對其他所有的token的Key向量進行評分。 3. 將Value向量乘以上一步計算的分數後加起來。

在這裡插入圖片描述

1. 建立Query、Key、Value

現在我們只關注第一個路徑,我們需要用它的Query和所有的Key比較,這一步驟會為每個路徑都生成一個注意力分數。

先不管什麼是多頭注意力,先看一個head 的情況。自注意力計算的第一步就是要計算出每個路徑的Query、Key、Value三個向量。

  1. 看一下下圖是一次處理四個tokens,每個token都有它單獨的路徑,第一路徑指的是$X_1$這個token。
  2. 對於每個詞來說對應的QKV是一個向量,而在實際計算中是使用整個輸入序列的矩陣。
  3. 獲得Query、Key、Value三個向量的方法是$每個單詞的表示向量×對應的權重矩陣(W^Q、W^K、W^V)$。

在這裡插入圖片描述

2. 計算注意力分數

現在我們已經有了那三個向量,在第二步我們只需要用到query和key向量。因為我們關注的是第一個token,所以我們將其第一個token的query乘以其他token的key向量,這樣計算會得到每一個token的注意力分數。

這裡是兩個向量做點乘積,不是按位乘。

在這裡插入圖片描述

3. 求和

我們現在可以將上一步得到的注意力分數乘以Value向量。將相乘之後的結果加起來,那些注意力分數大的佔比會更大。

看下圖,注意力分數乘以每個Value向量,原作者用不同深淺的藍色的框框表示計算之後的結果。可以看到$V_3$比較顯眼,$V_2$幾乎看不到了,然後將計算結果加起來得到$Z_1$。這個$Z_1$就是$X_1$新的表示向量,這個向量除了單詞本身,還涵蓋了上下文其他token的資訊。 這一過程可以認為注意力分數就是求不同單詞重要性權重的過程,這一步的計算就是求所有token的加權和。

在這裡插入圖片描述

對於Value向量,注意力分數越低,顏色越透明。這是為了說明乘以一個小數如何稀釋不同token的Value向量。

如果我們對每個路徑進行相同的操作,最終會得到每個token新的表示向量,其中包含該token的上下文資訊。之後會將這些資料傳給encoder元件的下一個子層(前饋神經網路):

在這裡插入圖片描述

圖解帶Mask的自注意力

現在我們已經瞭解了Transformer中普通的自注意機制,讓我們繼續看看帶mask的自注意。

帶mask的自注意和普通的自注意是一樣的,除了第二步計算注意力分數的時候。

假設模型只有兩個token作為輸入,我們當前正在處理第二個token。在下圖的例子中,最後兩個token會被mask掉。這樣模型就能干擾計算注意力分數這一步驟,它會讓未輸入的token的注意力得分為0,這樣未輸入的token就不會影響當前的計算,當前詞彙的注意力只會關注到在它之前輸入的序列。

在這裡插入圖片描述

這種遮蔽通常以矩陣的形式實現,稱為注意力遮蔽(attention mask)。

還是假設輸入序列由四個單片語成,例如robot must obey orders。在語言建模場景中,這個序列包含四個處理步驟,每個單詞一個步驟(假設現在每個單詞都是一個token)。由於模型是按照批量(batch)進行處理的,我們可以假設這個模型的批量大小為4(batch_size = 4),然後模型將把整個序列作為一個batch處理進行四步處理。

假設現在每個單詞都是一個token,這個假設是因為單詞word ≠ token,在不同的分詞方式中同一個單詞可能會劃分為不同的token。

在這裡插入圖片描述

在矩陣形式中,我們通過將Query矩陣乘以Key矩陣來計算注意力分數。讓我們像下面這樣進行視覺化,注意,單詞無法直接進行矩陣運算,所以要把他們的Query和Key丟到矩陣中。

在這裡插入圖片描述

完成乘法運算後,我們要加上上三角形式的mask矩陣。它會將我們想要遮蔽的單元格設定為$-∞$或一個非常大的負數(GPT-2中的為負一億):

在這裡插入圖片描述

然後,對每一行進行softmax就會轉化成我們需要的注意力分數:

在這裡插入圖片描述

這個分數表的含義如下: - 當模型處理資料集中的第一個單詞時,也就是第一行,其中只包含一個單詞robot,它的注意力將100%集中在這個單詞上。

  • 當模型處理資料集中的第二個單詞時(第二行),其中包含單詞robot must,當它處理單詞“must”時,48%的注意力會放在robot上,52%的注意力會放在must上。
  • 以此類推……

GPT-2的 Masked Self-attention

讓我們更詳細地瞭解一下GPT-2的masked注意力。

現在假設模型做預測任務,每次處理一個 token。

我們用模型進行預測的時候,模型在每次迭代後都會增加一個新詞,對於已經處理過的token來說,沿著之前的路徑重新計算效率很低。

作者意思是比如a robot must obey the rule,如果第一次迭代時候只有a,僅需要計算它的QKV,第二次迭代時候是a robot,如果直接算二者的QKV,那就是重複計算了a的,這樣會造成巨大的計算開銷。 GPT-2的高效處理方法如下:

假設我們處理輸入序列的第一個tokena時(暫時忽略<s>)。

在這裡插入圖片描述

GPT-2會保留atoken的Key和Value向量。每個自注意力層都有各自的Key和Value向量,不同的decoder元件中Key和Value向量不共享:

在這裡插入圖片描述

在下一次迭代中,當模型處理單詞robot時,它不需要為a重新生成Query、Key、Value,而是直接用第一次迭代中儲存的那些:

在這裡插入圖片描述

1. 建立queries, keys和values

讓我們假設這個模型正在處理單詞it。如果我們討論的是最底層的decoder元件,那麼它接收的token的輸入是token的嵌入+ 第九個位置的位置編碼:

在這裡插入圖片描述

Transformer中的每個元件之權重不共享,都有自己的權重。我們首先要和權重矩陣進行計算,我們使用權重矩陣建立Query、Key、Value。

在這裡插入圖片描述

自注意力子層會將輸入乘以權值矩陣(還會加上bias,圖中沒表示出來),乘法會產生一個向量,這個向量是單詞it的Query、Key、Value的拼接向量。

在這裡插入圖片描述

將輸入向量乘以注意力權重向量(然後新增一個偏差向量),就會得到這個token的Query、Key、Value向量。

1.5 劃分注意力頭

在前面的例子中,我們只專注於自注意力,忽略了“多頭”(muti-head)的部分。現在說一下什麼是“多頭”。 就是將原來一個長的Query、Key、Value向量按照不同位置擷取並拆分成短的向量。

在這裡插入圖片描述

前邊的例子中我們已經瞭解了一個注意力頭怎麼計算,現在我們考慮一下多頭注意力,如下圖考慮有三個head。

在這裡插入圖片描述

2. 注意力分數

現在我們可以開始打分了,你們應該知道,我們這隻畫出來一個注意力頭(head #1),其他的頭也是這麼計算的:

在這裡插入圖片描述

現在,該token可以針對其他token的所有Value進行評分:

在這裡插入圖片描述

3. 加和

和前邊講的一樣,我們現在將每個Value與它的注意力分數相乘,然後將它們相加,產生head #1的自我注意結果$Z$:

在這裡插入圖片描述

3.5 合併注意力頭

不同的注意力頭會得到不同的$Z$,我們處理不同注意力頭的方法是把這個$Z$連線成一個向量:

在這裡插入圖片描述 但是這個拼接結果向量還不能傳給下一個子層。

我們首先需要把這個拼接向量轉換成對齊表示。

作者原文寫的“We need to first turn this Frankenstein’s-monster of hidden states into a homogenous representation.” 直譯是“我們需要首先把這個隱藏狀態的弗蘭肯斯坦怪物變成對齊的表示。” 弗蘭肯斯坦是一個人造人,是個怪物,作者是瑪麗·雪萊,這本書可以看作是科幻小說開山之作。感興趣的可以看一下。

對映/投影

我們將讓模型學習如何將自注意力的拼接結果 更好地對映成前饋神經網路可以處理的向量。下面是我們的第二個大權重矩陣,它將注意力的結果投射到自注意力子層的輸出向量中:

在這裡插入圖片描述

之後我們就產生了可以傳送到下一層的向量:

在這裡插入圖片描述

GPT-2 全連線神經網路第一層

全連線神經網路的輸入是自注意力層的輸出,用於處理自注意力子層得到的token的新的表示,這個表示包含了原始token及其上下文的資訊。

它由兩層組成。第一層是模型大小的4倍(因為GPT-2 small是768,所以GPT-2中的全連線神經網路第一層會將其投影到768*4 = 3072個單位的向量中)。為什麼是四倍?因為原始Transformer的也是四倍,這裡就沒改。

在這裡插入圖片描述

上圖沒畫出bias。

GPT-2 全連線神經網路第二層:投影到模型維度

第二層將第一層的結果再投射回模型的維度(GPT-2 small為768)。這個計算結果就是整個decoder元件對token的處理結果。

在這裡插入圖片描述

上圖沒畫出bias。

You’ve Made It!

總結一下輸入向量都會遇到哪些權重矩陣:

在這裡插入圖片描述

每個Transformer元件都有自己的權重。另外,該模型只有一個token的嵌入矩陣和一個位置編碼矩陣:

在這裡插入圖片描述

如果你想看到模型的所有引數,我在這裡對它們進行了統計:

在這裡插入圖片描述

由於某種原因,它們加起來有124M的引數,而不是117M。我不知道為什麼,但這就是在釋出的程式碼中它們的數量(如果我錯了歡迎指正)。

上圖中部落格作者對GPT-2 small的引數進行了統計,計算結果和OpenAI開源的GPT-2模型的引數量不一樣。 作者算的是124M,實際程式碼中只有117M, 原因如下: OpenAI團隊說:“我們論文裡引數計算方法寫錯了。所以你現在可以看到GPT-2 small模型引數只有117M……” 在這裡插入圖片描述 截圖來源http://github.com/openai/gpt-2