天天看點

Attention is All You Need -- 淺析

       由于最近bert比較火熱,并且bert的底層網絡依舊使用的是transformer,是以再學習bert之前,有必要認真了解一下Transformer的基本原理以及self-attention的過程,本文參考Jay Alammar的一篇博文,翻譯+學習

原文連結:https://jalammar.github.io/illustrated-transformer/

       Transformer模型利用注意力機制來提高模型訓練速度練。Transformer在特定任務中的性能優于谷歌的基于神經網絡的翻譯模型。然而,Transformer最大的好處是可以并行計算。

       整個模型如果用黑盒的視角來看的話,将一句話輸入到Transformer模型中,它的輸出将是該句的翻譯。

Attention is All You Need -- 淺析

       将這個黑盒打開,我們可以看到一組編碼器和一組解碼器: 

Attention is All You Need -- 淺析

       整個Encoders是由6個相同的encoder組成,數字6沒有什麼特殊的含義,并且能夠嘗試一下其他的組合。同樣,Decoders元件是一堆相同數量的decoder組成。 

Attention is All You Need -- 淺析

       編碼器在結構上都是相同的(但它們不共享權重)。每一個被分解成兩個子層: 

Attention is All You Need -- 淺析

        一個self-attention層和一個feed forwoard network,Encoder的輸入首先通過一個self-attention層,這個層的作用是在處理一個特定單詞的時候,編碼器也能照顧到句子中的其他單詞(作用類似與RNN的記憶傳播功能)。我們将在稍後的文章中更深入地研究self-attention。self-attention的輸出被輸入到前饋神經網絡。每個位置的前饋網絡完全相同,但是互相是獨立的

解碼器也有這兩層,但在這兩層之間是一個Encoder-Decoder-attention層,它幫助解碼器将注意力集中在輸入句子的相關部分(類似于注意在seq2seq模型中所做的事情)。

Attention is All You Need -- 淺析

 Bringing The Tensors Into The Picture

        現在我們已經了解了模型的主要元件,讓我們開始研究各種向量/張量,以及它們如何在這些元件之間流動,進而将訓練好的模型的輸入變成輸出。

        與NLP應用程式中的一般情況一樣,我們首先使用Embedding算法将每個輸入單詞轉換為一個向量。

Attention is All You Need -- 淺析

       Embedding隻作用在最底層的編碼器中。所有編碼器都會接收到一個embedding vector,每個vector的大小都是512,對于Encoder的最底層,其輸入是embedding矩陣,但是對于其他層編碼器,其輸入是下面的編碼器的輸出。這個embedding vector的超參數是自定義的,基本上就是我們的訓練資料集中最長句子的長度。

       在輸入序列進行word embedding之後,每個單詞都流經編碼器的兩層。

Attention is All You Need -- 淺析

       在這裡,我們看到Transformer的一個關鍵特性,即每個位置上的字在編碼器中通過它自己的路徑。在self-attention層中,這些路徑之間存在依賴性。但是,前饋層沒有這些依賴關系,是以在流經前饋層時可以并行執行各種路徑。

       接下來,我們将把示例轉換為一個更短的句子,并檢視在編碼器的每個子層中發生了什麼。

Now We’re Encoding!

       正如我們已經提到的,編碼器接收一個向量作為輸入。它通過将這些向量傳遞到一個“self-attention”層,然後進入前饋神經網絡來處理這個向量,然後将輸出向上發送到下一個編碼器。

Attention is All You Need -- 淺析

 Self-Attention at a High Level

        不要被我用“self-attention”這個詞愚弄,好像每個人都應該熟悉這個概念。我個人從來沒有遇到過這個概念,直到讀了Attention is All You Need。讓我們提煉一下它是如何工作的。

         假設以下句子是我們要翻譯的輸入句子:

        “這隻動物沒有過馬路,因為它太累了。”

        這句話中的“it”是什麼意思?它指的是街道還是動物?這對人來說是一個簡單的問題,但對算法來說就不那麼簡單了。

        當模型處理單詞“it”時,自我注意允許它将“it”與“animal”聯系起來。

        當模型處理每個單詞(輸入序列中的每個位置)時,self attention允許它檢視輸入序列中的其他位置,以尋找有助于對該單詞進行更好編碼的線索。

        如果您熟悉RNN,請考慮如何維護一個隐藏狀态,以允許RNN将它已處理的前一個單詞/向量的表示與它正在處理的目前單詞/向量結合起來。self-attention是Transformer用來将其他相關單詞的“了解”轉化為我們正在處理的單詞的方法。

Attention is All You Need -- 淺析

 Self-Attention in Detail

       我們先來看看如何用向量來計算自我關注,然後再來看看它是如何實作的——使用矩陣。

       計算自我注意的第一步是從編碼器的每個輸入向量中建立三個向量(在本例中,是每個單詞的嵌入)。是以,對于每個單詞,我們建立一個Query向量、一個Key向量和一個Value向量。這些向量是通過将embedding乘以我們在訓練過程中訓練的三個矩陣得到的。

       注意,這些新向量在維數上比嵌入向量小。它們的維數為64,而嵌入和編碼器的輸入/輸出向量的維數為512。它們不必更小,這是一種架構選擇,可以使多頭注意力(大部分)的計算保持不變。

Attention is All You Need -- 淺析

什麼是Query向量、一個Key向量和一個Value向量?

       這些抽象概念對于計算和思考注意力很有用。當你開始閱讀下面是如何計算注意力的,你就會知道你需要知道的關于這些向量所扮演的角色的幾乎所有東西。

        self-attention計算的第二步依舊是計算一個分數。假設我們計算本例中第一個單詞“Thinking”的self-attention score。我們需要對輸入句子中的每個單詞進行打分。分數決定了我們在某個位置編碼一個單詞時,對輸入句子的其他部分的關注程度。

        分數是通過Query向量與對應單詞的Key向量的點積計算出來的。是以如果我們處理位置1的單詞的self-attention,第一個分數就是q1和k1的點積。第二個分數是q1和k2的點積。

Attention is All You Need -- 淺析

 第三步和第四步是将分數除以8(論文中使用的關鍵向量的維數的平方根- 64)。這可以使得梯度更加穩定。這裡可能有其他可能的值,但這是預設值),然後通過softmax操作傳遞結果。Softmax将分數标準化,使它們都是正數,加起來等于1。

Attention is All You Need -- 淺析

        這個softmax得分決定了每個單詞在這個位置的表達量。很明顯,這個位置的單詞會有最高的softmax得分,但是有時候更加關注與這個詞相關的詞也是有幫助的。

        第五步是将每個Value向量乘以softmax分數(為求和做準備)。這裡的直覺是保持我們想要關注的單詞的值不變,以及淹沒無關單詞的值(例如,将它們乘以0.001這樣的小數字)。

        第六步是權重值向量的求和。這會在這個位置(對于第一個單詞)産生self-attention層的輸出。

Attention is All You Need -- 淺析

         這就是self-attention計算的結論。得到的向量是一個我們可以發送到前饋神經網絡的向量。但是在實際的實作中,為了更快的處理,這種計算是以矩陣形式進行的。現在我們來看看這個我們已經在單詞層面上看到了計算的直覺。

Matrix Calculation of Self-Attention

Attention is All You Need -- 淺析

矩陣中的每一行對應輸入句子中的一個單詞。我們再次看到嵌入向量(512,或圖中4個方框)和q/k/v向量(64,或圖中3個方框)的大小差異

最後,由于我們處理的是矩陣,我們可以将步驟2到步驟6濃縮到一個公式中來計算self-attention層的輸出。

Attention is All You Need -- 淺析

The Beast With Many Heads 

通過添加“多頭”注意機制,進一步細化了self-attention層。這從兩個方面提高了注意力層的性能:

  • 它擴充了模型關注不同位置的能力。是的,在上面的例子中,z1包含了一些其他編碼,但是它可以被實際單詞本身所控制。如果我們翻譯像“動物沒過馬路是因為它太累了”這樣的句子,它會很有用,我們想知道“它”是指哪個單詞。
  • 它給了注意層多重“表示子空間”。正如我們接下來将看到的,對于多頭注意力,我們不僅有一個,而且有多個查詢/鍵/值權重矩陣(轉換器使用8個注意頭,是以對于每個編碼器/解碼器,我們最終得到8個集合)。這些集合中的每一個都是随機初始化的。然後,經過訓練後,使用每個集合将輸入嵌入(或來自較低編碼器/解碼器的向量)投影到不同的表示子空間中。
Attention is All You Need -- 淺析

如果我們做同樣的self-attention計算,就像我們上面概述的,用不同的權重矩陣做8次不同的計算,我們最終得到8個不同的Z矩陣。

Attention is All You Need -- 淺析

這給我們留下了一點挑戰。前饋層不需要八個矩陣——它隻需要一個矩陣(每個單詞對應一個向量)。是以我們需要一種方法把這8個壓縮成一個矩陣。

我們怎麼做呢?我們把這些矩陣連起來然後用一個額外的權重矩陣把它們乘起來。

Attention is All You Need -- 淺析

這就是多頭自我關注的全部内容。讓我試着把它們都放在一個圖像中這樣我們就能在一個地方看到它們 

Attention is All You Need -- 淺析

既然我們已經提到了注意力頭,讓我們回顧一下之前的例子,看看當我們在例句中編碼單詞“it”時,不同的注意力頭部集中在哪裡: 

Attention is All You Need -- 淺析

然而,如果我們把所有注意力都集中到畫面上,事情就很難解釋了: 

Attention is All You Need -- 淺析

Representing The Order of The Sequence Using Positional Encoding 

正如我們目前所描述的,模型中缺少的一件事是一種解釋輸入序列中單詞順序的方法。

為了解決這個問題,Transformer向每個embedding的輸入添加一個向量。這些向量具有固定的模式,這有助于它确定每個單詞的位置,或序列中不同單詞之間的距離。直覺的感覺是,将這些值添加到embedding vector中可以提供嵌入向量之間有意義的距離,這些向量一旦被投影到Q/K/V向量中,并且在點積注意期間。

Attention is All You Need -- 淺析

如果我們假設embdding的維數為4,那麼實際的位置編碼應該是這樣的: 

Attention is All You Need -- 淺析

這個模式是什麼樣的?

在下面的圖中,每一行對應一個向量的位置編碼。是以第一行就是我們要在輸入序列中嵌入第一個單詞的向量。每行包含512個值—每個值在1到-1之間。我們用顔色标記了它們,是以圖案是可見的。

Attention is All You Need -- 淺析

 位置編碼的公式在論文(3.5節)中有描述。您可以在get_timing_signal_1d()中看到生成位置編碼的代碼。這不是唯一可能的位置編碼方法。然而,它的優勢在于能夠縮放到看不見的序列長度(例如,如果我們訓練過的模型被要求翻譯比我們訓練集中的任何一個句子都長的句子)。

The Residuals

在繼續之前,我們需要提到編碼器架構中的一個細節,即每個編碼器中的每個子層(self-attention, ffnn)在其周圍都有一個剩餘連接配接,然後是一個分層規範化步驟。

Attention is All You Need -- 淺析

如果我們将向量和與自我關注相關的層範數運算形象化,它會是這樣的: 

Attention is All You Need -- 淺析

這也适用于解碼器的子層。如果我們考慮一個由兩個堆疊編碼器和解碼器組成的變壓器,它看起來是這樣的:

Attention is All You Need -- 淺析

The Decoder Side

既然我們已經介紹了編碼器方面的大部分概念,我們基本上也知道了解碼器的元件是如何工作的。但是讓我們來看看它們是如何一起工作的。

        編碼器從處理輸入序列開始。将頂層編碼器的輸出轉換為一組注意向量K和v,每一個譯碼器在其“編碼器-譯碼器注意”層中使用,使譯碼器在輸入序列中适當的位置集中注意力:

Attention is All You Need -- 淺析

下面的步驟重複這個過程,直到到達一個特殊的符号,表明變壓器解碼器已經完成了它的輸出。每一步的輸出都會在下一次的時候回報給底層解碼器,解碼器就會像編碼器一樣,将解碼結果放大。就像我們對編碼器輸入所做的那樣,我們将位置編碼嵌入并添加到這些譯碼器輸入中以訓示每個單詞的位置。 

Attention is All You Need -- 淺析

解碼器中的自注意層與編碼器中的自注意層的工作方式略有不同:

在解碼器中,自注意層隻允許注意到輸出序列中較早的位置。這是通過在自我注意計算的softmax步驟之前屏蔽未來位置(将它們設定為-inf)來實作的。

編碼器-解碼器注意層的工作原理與多頭自注意層類似,隻是它從下面的層建立查詢矩陣,并從編碼器堆棧的輸出中擷取鍵和值矩陣。

 The Final Linear and Softmax Layer

解碼器堆棧輸出一個浮點向量。我們怎麼把它變成一個詞?這是最後一個線性層的工作,然後是一個Softmax層。

線性層是一個簡單的完全連接配接的神經網絡,它将解碼器堆棧産生的向量投影到一個更大的稱為邏輯向量的向量上。

讓我們假設我們的模型知道10,000個從它的訓練資料集中學習的惟一英語單詞(我們的模型的“輸出詞彙”)。這将使logits向量寬10000個單元格——每個單元格對應一個唯一單詞的分數。這就是我們解釋線性層之後的模型輸出的方式。

softmax層然後将這些分數轉換為機率(都是正數,加起來等于1.0)。選擇機率最大的單元格,并生成與之關聯的單詞作為此時間步驟的輸出。

Attention is All You Need -- 淺析

既然我們已經介紹了通過一個經過訓練的轉換器的整個前向傳遞過程,那麼了解一下訓練模型的直覺感受将是非常有用的。

在訓練過程中,未經訓練的模型會經曆完全相同的傳遞過程。但是由于我們是在一個标記的訓練資料集上訓練它,我們可以将它的輸出與實際正确的輸出進行比較。

為了了解這一點,我們假設輸出詞彙表隻包含6個單詞(“a”、“am”、“i”、“thanks”、“student”和“”(“句子結束”的縮寫))。

Attention is All You Need -- 淺析

一旦定義了輸出詞彙表,就可以使用相同寬度的向量來表示詞彙表中的每個單詞。這也稱為一熱編碼。例如,我們可以用以下向量表示am:

Attention is All You Need -- 淺析

The Loss Function 

假設我們正在訓練我們的模型。假設這是我們教育訓練階段的第一步,我們正在用一個簡單的例子來教育訓練它——把“謝謝”翻譯成“謝謝”。

這意味着,我們希望輸出是一個表示“謝謝”的機率分布。但是由于這個模型還沒有經過訓練,是以現在還不太可能實作。

Attention is All You Need -- 淺析

如何比較兩個機率分布?我們隻要從另一個中減去一個。要了解更多細節,請檢視交叉熵和庫爾巴克-萊布爾散度。

但請注意,這是一個過于簡化的示例。更實際一點,我們将使用一個句子,而不是一個單詞。例如,輸入:“je suis etudiant”,預期輸出:“我是一名學生”。這真正的意思是,我們想要我們的模型連續輸出機率分布,其中:

每個機率分布都由一個寬度為vocab_size的向量表示(在我們的玩具示例中是6,但更實際的情況是一個數字,比如3,000或10,000)

第一個機率分布在與“i”相關聯的單元格上具有最高的機率

第二個機率分布在與am相關的單元格中具有最高的機率

以此類推,直到第五次輸出分布表示“語句>的結尾”符号,該符号也有一個從10,000個元素詞彙表中關聯的單元格。

Attention is All You Need -- 淺析

在對模型進行足夠長時間的大資料集訓練之後,我們希望得到的機率分布是這樣的: 

Attention is All You Need -- 淺析

現在,因為這個模型每次産生一個輸出,我們可以假設這個模型從機率分布中選擇機率最大的單詞,然後扔掉其餘的。這是一種方法(稱為貪婪解碼)。另一個方法是堅持,說,前兩個單詞(說,比如“我”和“a”),然後在下一步中,運作模型兩次:一次假設第一個輸出位置是“我”這個詞,而另一個假設第一個輸出位置是‘我’這個詞,和哪個版本産生更少的錯誤考慮# 1和# 2儲存位置。我們對2号和3号位置重複這個。這種方法稱為“beam search”,在我們的例子中,beam_size是2(因為我們在計算位置#1和#2的beam之後比較了結果),top_beam也是2(因為我們保留了兩個單詞)。這兩個超參數都可以進行實驗。

繼續閱讀