輕鬆理解 Transformers(2):Attention部分
編者按:隨著人工智慧技術的不斷髮展,Transformers 模型架構已成為自然語言處理領域的重要基石。然而,許多人對其內部工作機制仍然感到困惑。本文透過淺顯易懂的語言和生活中的例子,幫助讀者逐步理解 Transformers 中最核心的 Attention 機制。
本文是Transformers系列的第二篇。作者的核心觀點是:Attention 機制是 Transformers 模型區分關鍵資訊的關鍵所在。本文透過直觀的類比和數學公式,讓讀者對 Attention 的計算過程有更深入的理解。文章詳細介紹了Attention 機制如何辨別不同單詞的重要性;Query、Key、Value 矩陣及其在 Attention 計算過程中的作用;Masking 如何遮蔽無關內容;Dropout、Skip Connection 等機制如何提升模型穩定性;Add & Norm 層的工作原理,以及歸一化對模型學習的重要性。
雖然 Transformers 中各個元件之間相互關聯,難以一口氣理解全貌,但本文透過耐心講解 Attention 這一核心機制,確實讓讀者對整體架構有了更紮實的把握。我們衷心希望這類通俗易懂的文章能幫助更多讀者瞭解 Transformers 技術的執行原理。
以下是譯文,enjoy!
作者 | Chen Margalit
編譯 | 嶽揚
???歡迎小夥伴們加入 AI技術軟體及技術交流群 ,追蹤前沿熱點,共探技術難題~
Transformers 對人工智慧領域,乃至對整個世界都產生了深遠的影響。這種模型架構由多個元件構成,但正如提出該架構那篇論文的題目——Attention is All You Need,顯然注意力機制(Attention)具有特別重要的意義。本系列的第二部分將主要關注注意力(Attention)及其相關功能,這些功能確保了 Transformer 各元件的良好配合。
圖片來自 Vaswani, A. 等人的論文[1]
01 注意力機制(Attention)
在 Transformers 中, attention 指的是一種機制,它能讓模型在處理過程中專注於輸入(input)的相關部分。 可以將其想象成一把手電筒,照亮句子的特定部分,並根據語境(context)判斷其在句子中的重要程度。 我認為舉幾個例子比直接將定義擺出來更有效,透過提供具體的例子,可以激發大腦的思考和理解能力,使大腦有機會自己去理解概念,而不僅僅依賴於定義。
當看到句子“The man took the chair and disappeared”時,我們自然而然地會對句子的不同部分賦予不同程度的重要性(即注意力(attention))。但令我驚訝的是,如果我們去掉一些特定的詞,句子的意思基本保持不變:“man took chair disappeared”。雖然這個版本的英語句子有些不通順,但與原句相比,我們仍然可以理解這句話要表達資訊的本質。有趣的是,三個單詞("The"、"the "和 "and")佔據了句子中詞數的43%,但對句子的整體意思沒有太大影響。在柏林生活期間,我的德語說得非常棒,每一個接觸過我的柏林人可能都這麼認為(要麼學好德語,要麼快樂,這是我必須做出的決定),但對於機器學習模型來說,這一點就不那麼明顯了。
過去,像迴圈神經網路(RNNs)等早期架構面臨著一個重大挑戰: 它們難以“記住”輸入序列(長度通常超過20個單詞)中較遠位置的單詞。眾所周知,這些模型主要依靠數學運算來處理資料。不幸的是,這些早期模型架構中使用的數學運算不夠高效,無法將單詞表徵傳遞到序列的較遠位置。
這種長程依賴(long-term dependency)的限制阻礙了迴圈神經網路(RNN)長時間保持上下文資訊的能力,從而影響了語言翻譯或情感分析等任務,因為在這些任務中,理解整個輸入序列至關重要。然而, Transformer 透過其注意力機制和自注意力機制更有效地解決了這個問題。 它們可以高效地捕捉輸入中的長距離依賴關係,使模型能夠保留語境和關聯資訊,即使是序列中更早出現的單詞也不例外。因此,Transformer 已成為克服以往架構限制的開創性解決方案,並明顯提高了各種自然語言處理任務的效能。
要創造出像我們今天所遇到的聊天機器人這樣的優秀產品,就必須讓模型具備區分高價值和低價值單詞的能力,並在輸入的中長距離保留上下文資訊。Transformer架構為應對這些挑戰而引入的機制被稱為注意力機制。
02 點積 Dot Product
神經網路模型如何在理論上辨別不同單詞的重要性呢? 在分析句子時,我們的目標是找出相互之間關係更緊密的詞語。 由於這些單詞已經被表示為向量(由數字組成),因此我們需要一種測量數字之間相似性的方法。測量向量之間相似性的數學術語是“點積”(Dot Product)。它涉及將兩個向量的元素相乘,併產生一個標量值(例如2、16、-4.43),該值能夠表示這兩個向量的相似性。機器學習建立在各種數學運算的基礎上,其中點積尤為重要。因此,我將花時間詳細解釋這一概念。
個人感覺和直觀理解 Intuition
假設我們將下面這5個單詞轉換為嵌入表徵:“florida”, “california”, “texas”, “politics”和“truth”。由於嵌入(embeddings)只是由數字組成的陣列,我們可以將其繪製在圖表上。但是,由於它們的維度(用於表示單詞的數字數量)很高,可以輕鬆達到 100 到 1000,因此我們無法將它們原封不動地繪製出來。我們無法在2維的計算機/手機螢幕上繪製一個100維的向量。此外,人腦很難理解超過3維的東西。4維向量是什麼樣子的?我不知道。
為了解決這個問題,我們採用了主成分分析(PrincipalComponentAnalysis,PCA[2])這一降維技術。透過應用PCA,我們可以將嵌入投影到二維空間(x、y座標)。 維度的減少有助於我們在圖表上更直觀地展現資料。雖然降維後會丟失一些資訊,但希望這些降維後的向量仍然能夠保持足夠的相似性,從而使我們能夠深入瞭解和理解詞語之間的關係。
這些資料基於 GloVe 嵌入[3]。
florida = [-2.40062016, 0.00478901] california = [-2.54245794, -0.37579669] texas = [-2.24764634, -0.12963368] politics = [3.02004564, 2.88826688] truth = [4.17067881, -2.38762552]
也許你會發現這些數字有一些規律可循,但為了方便起見,現在來繪製這些數字。
圖中有5個二維向量
在這張圖片中,我們能夠看到五個二維向量(用x,y座標表示),分別代表五個不同的單詞。正如我們所見,該圖示表明了有些單詞與其他單詞的關聯度更高。
數學 math
透過一個簡單的方程,我們可以將向量的數學表示與這張圖片對應起來。如果您對數學不是特別感興趣,並且記得《Attention is All You Need》的論文作者將 Transformer 架構描述為“簡單的網路架構”,有時候機器學習領域的概念和技術可能會讓人感到複雜或難以理解,這可能是真的,但在本文中不會是這樣,本文所討論的內容都是相對簡單的。我來解釋一下:
點積計算公式
符號 ||a|| 表示向量 "a" 的大小,它表示從原點(點 0,0)到向量頂端的距離。計算向量大小的公式如下:
向量大小計算公式
計算結果會是一個數字,如 4 或 12.4。
Theta(θ)指的是向量之間的角度(可以看一下上方的圖片)。角度的餘弦值,表示為cos(θ),是將餘弦函式應用於該角度值的簡單結果。
程式碼 Code
史丹佛大學的研究人員使用 GloVe 演算法[3]為這些單詞生成嵌入,這些我們在前文討論過。雖然他們有建立這些嵌入的自有技術,但其基本概念與我們在本系列前一部分中談到的相同。舉個例子,選取 4 個單詞,將它們的維度降為 2,然後將它們的向量直接繪製成 x 座標和 y 座標。
為了使這個過程順利執行,下載GloVe[3]是必要的先決條件。
*下面是部分程式碼,第一個框中的程式碼,靈感來自於我之前見過的一些程式碼,但我似乎已經找不到原始碼了。
import pandas as pd path_to_glove_embds = 'glove.6B.100d.txt' glove = pd.read_csv(path_to_glove_embds, sep=" ", header=None, index_col=0) glove_embedding = {key: val.values for key, val in glove.T.items()}
words = ['florida', 'california', 'texas', 'politics', 'truth'] word_embeddings = [glove_embedding[word] for word in words] print(word_embeddings[0]).shape # 100 numbers to represent each word. --------------------- output: (100,)
pca = PCA(n_components=2) # reduce dimensionality from 100 to 2. word_embeddings_pca = pca.fit_transform(word_embeddings)
for i in range(5): print(word_embeddings_pca[i]) --------------------- output: [-2.40062016 0.00478901] # florida [-2.54245794 -0.37579669] # california [-2.24764634 -0.12963368] # texas [3.02004564 2.88826688] # politics [ 4.17067881 -2.38762552] # truth
現在我們擁有了這5個單詞的真實表徵,下一步是進行點積計算。
計算向量大小:
import numpy as np florida_vector = [-2.40062016, 0.00478901] florida_vector_magnitude = np.linalg.norm(florida_vector) print(florida_vector_magnitude) --------------------- output: 2.4006249368060817 # The magnitude of the vector "florida" is 2.4.
計算兩個相似向量之間的點積:
import numpy as np florida_vector = [-2.40062016, 0.00478901] texas_vector = [-2.24764634 -0.12963368] print(np.dot(florida_vector, texas_vector)) --------------------- output: 5.395124299364358
計算兩個不同向量之間的點積:
import numpy as np florida_vector = [-2.40062016, 0.00478901] truth_vector = [4.17067881, -2.38762552] print(np.dot(florida_vector, truth_vector)) --------------------- output: -10.023649994662344
從點積計算中可以明顯看出,它似乎捕捉並反映了不同概念之間的相似性。
03 縮放點積注意力 Scaled Dot-Product attention
個人感覺和直觀理解 Intuition
既然我們已經基本掌握了點積的計算方法,那麼就可以開始深入研究注意力機制(attention)了,特別是自注意力機制(self-attention mechanism)。 使用自注意力機制使模型能夠確定每個單詞的重要性,而不管它與其他單詞的“物理”距離是多少。這使得模型能夠根據每個單詞的上下文相關性(contextual relevance)做出比較明智的決策,從而更好地理解單詞。
為了實現這一宏偉目標,我們建立了由可學習引數(learnable parameters)組成的3個矩陣,稱為查詢矩陣(Query)、鍵矩陣(Key)和值矩陣(Value)(Q、K、V)。查詢矩陣(query matrix)可以看作是一個包含使用者查詢或詢問的矩陣(例如,當您詢問ChatGPT:“上帝今天下午5點有空嗎?(god is available today at 5 p.m.?)”時,這就是查詢矩陣)。鍵矩陣(key matrix)包含序列中的所有其他單詞。透過計算這些矩陣之間的點積,我們可以得到每個單詞與我們當前正在研究的單詞之間的相關程度(例如,翻譯或回答使用者的詢問)。
值矩陣(value matrix)為序列中的每個單詞都提供了一種“乾淨”的表示(“clean” representation)。其他兩個矩陣都是以類似的方式形成的,為什麼我只說值矩陣“乾淨(clean)”?因為值矩陣保持在其原始形式,我們不會在與另一個矩陣相乘後使用它,也不會用某個值對它進行歸一化處理。這種區別使得值矩陣與眾不同,確保它保留了原始的嵌入,無需額外的計算或變換。
這三個矩陣的大小都是
word_embedding (512)
。不過,它們都被分成了“heads”。在論文中,作者使用了8個“heads”(譯者注:heads指的是將注意力機制分解為多個子機制的一種方法。模型可以同時關注輸入的不同方面,並從中獲取更豐富的資訊。這種分解可以增加模型的表達能力和效能。),因此每個矩陣的大小為 sequence_length 乘以64。你可能會問,為什麼同樣的操作要對 1/8 的資料執行 8 次,而不是對所有資料執行一次。這種方法的理論基礎是,透過使用8組不同的權重(如前所述,這些權重是可學習的)進行相同的操作8次,我們可以利用資料中固有的多樣性。每個“heads”可以專注於輸入中的一個特定方面,總的來說,這可以帶來更好的效能。
*在大多數實施方案中,我們實際上並沒有將主矩陣分成8份。透過索引,可以實現分割,從而為每個部分實現並行處理。然而,這些只是實現細節。不過,這些只是實現細節。從理論上講,我們可以用 8 個矩陣完成幾乎相同的操作。
我們將查詢矩陣Q和鍵矩陣K進行點積運算,然後將結果除以維度的平方根進行歸一化。然後將歸一化後的結果透過Softmax函式[4]進行處理,然後將結果與值矩陣V相乘。之所以要對結果進行歸一化處理,是因為查詢矩陣Q和鍵矩陣K是以某種隨機方式生成的矩陣。它們的維度可能完全不相關(獨立),而獨立矩陣之間的乘法可能會產生非常大的數值,這可能會對學習效果產生不利影響,我將在本系列的後續部分中解釋。
然後,我們使用一種名為Softmax[4]的非線性變換(non-linear transformation),使所有數字的範圍介於 0 到 1 之間,並且總和為1。結果類似於機率分佈(因為有從0到1的多個結果數字相加得到1)。這些數字體現了序列中每個單詞與其他單詞的相關性。
最後,我們將結果與矩陣V相乘,就得到了自注意力分數(self-attention score)。
*實際上,編碼器由N(在論文中,N=6)個相同的層構成,每個層從上一層獲取輸入並執行相同的操作。最後一層將資料傳遞給解碼器(我們將在本系列的後續部分中討論)和編碼器的上層。
這是自注意力機制的視覺化影像。它就像教室裡的一群朋友,有些人與某些人的聯絡更緊密,有些人與任何人的聯絡都不太緊密。
圖片來自 Vaswani, A. 等人的論文[1]
數學 math
查詢矩陣Q、鍵矩陣K和值矩陣V是透過對嵌入矩陣進行線性變換得到的。線性變換在機器學習中非常重要,如果你對成為一名機器學習從業者感興趣,我建議你進一步探索這個知識點。本文不會進行深入探討,但我要說的是,線性變換是一種將向量(或矩陣)從一個空間移動到另一個空間的數學操作。聽起來似乎比實際要複雜得多。想象一個箭頭指向一個方向,然後移動到右邊30度的位置。這就是線性變換的示例。線性變換需要滿足一些條件才能被認為是線性的,但現在並不重要。關鍵是它保留了許多原始向量的特性。
自注意力層的整個計算是透過應用以下公式來完成的:
縮放點積注意力——圖片來自 Vaswani, A. 等人的論文[1]
計算過程如下所示:
-
用Q乘以K的轉置(翻轉)。
-
將結果除以矩陣 K 維度數的平方根。
-
然後有了描述每個單詞與其他單詞相似程度的“注意力矩陣分數(attention matrix scores)”。將每一行進行 Softmax[4](非線性)變換。Softmax[4] 有三個有趣的相關功能:
-
a. 縮放所有數字,使其介於 0 和 1 之間。
-
b. 使所有數字的和為1。
-
c. 突出差距,使得稍微更重要的部分變得更加重要。因此,我們現在可以輕鬆區分模型對單詞x1與x2、x3、x4等之間聯絡的不同感知程度。
-
將計算出來的分數與矩陣V相乘。這是自注意力機制操作的最終結果。
04 Masking
在本系列的前一章中,我已經解釋過我們使用虛擬標記來處理句子中出現的特殊情況,如句子中的第一個單詞、最後一個單詞等。其中一個標記,表示為<PADDING>,表示沒有實際資料,但我們需要在整個過程中保持一致的矩陣大小。為了確保模型理解這些是虛擬標記,並且在自注意力的計算過程中對其不予考慮,我們將這些標記表示為負無窮大(例如一個非常大的負數,例如-153513871339)。掩碼值被新增到Q乘以K的乘積結果中。然後,Softmax將這些數字轉換為0。 這使我們能夠在注意力機制(attention mechanism)中有效地忽略虛擬標記,同時保持計算的完整性。
05 Dropout
在自注意力層之後,應用了一種被稱為 Dropout 的操作。Dropout是機器學習中廣泛使用的一種正則化技術。正則化的目的是在訓練過程中對模型施加約束,使其難以過度依賴特定的輸入細節。這樣,模型就能夠更加穩健地學習,並提高其泛化能力。具體實現包括隨機選擇一些啟用值(activations)(來自不同層的輸出數字),並將它們置零。在同一層的每次傳遞中,都有不同的啟用值將被置零,從而防止模型根據所獲資料找到特定的解決方案。 從本質上講,Dropout有助於增強模型處理不同輸入內容的能力,並使模型難以針對資料中的特定模式進行調整。
06 Skip Connection
Transformer架構中的另一個重要操作稱為Skip Connection。
圖片來自 Vaswani, A. 等人的論文[1]
Skip Connection是一種在不經過任何轉換的情況下傳遞輸入(input)的方法。舉例來說,假設我向我的經理報告,而我的經理又向他的經理報告。即使是出於讓報告更有用的純粹目的,輸入內容在經過另一個人(或 ML 層)處理時,也會進行一些修改。在這個類比中,Skip Connection就像是我直接向我的經理的經理彙報。因此,上級經理既透過我的經理(經過處理的資料)接收輸入,也直接從我這裡(未經處理的資料)接收輸入。然後,上級經理可以更好地做出決策。 採用Skip Connection是為了解決梯度消失等潛在問題,我將在下一節中解釋。
07 Add & Norm 層
"Add & Norm"層主要執行加法和歸一化操作。我先說加法,因為它比較簡單。基本上,我們是將自注意力層的輸出與原始輸入(透過Skip Connection接收)相加。這個加法是逐元素進行的(每個數字與其相同位置的數字相加)。然後對結果進行歸一化。
我們之所以進行歸一化處理,原因是每個層都要進行大量計算。數字多次相乘可能會導致意想不到的情況。例如,如果我取一個小數,比如0.3,然後將其與另一個小數,比如0.9相乘,得到的結果是0.27,會比起開始時更小。如果我多次這樣做,最終可能會得到非常接近於0的結果。這可能會導致深度學習中的一個問題,即梯度消失。
我現在不想說得太深,以免這篇文章讀起來費時費力,但我想說的是,如果數字變得非常接近 0,模型將無法學習。 現代機器學習的基礎是計算梯度,並使用這些梯度(以及其他一些因素)調整權重。如果這些梯度接近於0,模型將很難有效地學習。
相反,當非小數與非小數相乘時,可能會發生相反的現象,即 梯度爆炸(exploding gradients) ,會導致值變得過大。因此, 由於權重和啟用值的巨大變化,模型的學習會面臨困難,從而導致訓練過程中的不穩定性和發散性。
機器學習模型有點像小孩子,它們需要保護。保護這些模型免受數字過大或過小的影響的一種方法是歸一化。
數學 math
層歸一化操作雖然看起來很可怕(一如既往),但實際上相對簡單。
圖片由 Pytorch 提供,摘自此處[5]
在層歸一化操作中,我們需要對每個輸入執行以下簡單步驟:
-
從輸入中減去其均值。
-
除以方差的平方根並加上一個epsilon(ε)(一個非常小的數字),用於避免除以零。
-
將得到的得分乘以一個可學習的引數,稱為gamma(γ)。
-
新增另一個可學習的引數,稱為beta(β)。
-
這些步驟可確保均值接近於0,標準差接近於1。歸一化過程增強了訓練的穩定性、速度和整體效能。
程式碼 Code
# x being the input. (x - mean(x)) / sqrt(variance(x) + epsilon) * gamma + beta
08 總結
至此,我們對編碼器的主要內部工作原理有了紮實的瞭解。此外,我們還探索了Skip Connection,這是機器學習中一種純粹的技術(也是一種重要的技術),可以提高模型的學習能力。
儘管本節內容有點複雜,但讓我們已經對整個 Transformer 架構有了實質性的瞭解。隨著我們在本系列中的進一步深入,理解本部分的內容將有助於你理解本系列中其他部分的內容。
請記住,這是一個前沿領域中的較新技術。學起來並不簡單。即使您現在仍然沒有完全理解所有內容,您也已經取得了很大的進步!
下一部分將介紹機器學習中的另一個基礎(且更簡單)的概念,即前饋神經網路。
圖片來自 Vaswani, A. 等人的論文[1]
END
???歡迎小夥伴們加入 AI技術軟體及技術交流群 ,追蹤前沿熱點,共探技術難題~
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70018536/viewspace-2991905/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 輕鬆理解 Transformers (3): Feed-Forward Layer 部分ORMForward
- 輕鬆理解 Transformers (4) :Decoder 和 Output 部分ORM
- 輕鬆理解 Spring AOPSpring
- 看動畫輕鬆理解「 堆 」動畫
- 輕鬆理解分庫分表
- 輕鬆理解HTTP快取策略HTTP快取
- 輕鬆學習 JavaScript——第 2 部分:函式中的 Rest 引數JavaScript函式REST
- 看圖輕鬆理解計數排序排序
- 【動畫】看動畫輕鬆理解「Trie樹」動畫
- 看圖輕鬆理解最小(大)堆
- 輕鬆理解JVM的分代模型JVM模型
- 幾張圖輕鬆理解String.intern()
- RxJava2輕鬆入門RxJava
- 機器閱讀理解Attention-over-Attention模型模型
- 兩個案例輕鬆理解MyBatis中的TypeHandler!MyBatis
- 輕鬆理解建構函式和原型物件函式原型物件
- 輕鬆理解 Java 靜態代理/動態代理Java
- 輕鬆學習 JavaScript——第 8 部分:JavaScript 中的類JavaScript
- 如何輕鬆歸檔檔案?2種方法輕鬆建立歸檔檔案!
- 輕鬆學習 JavaScript——第 1 部分:瞭解 let 語句JavaScript
- 輕鬆學習 JavaScript——第 6 部分:JavaScript 箭頭函式JavaScript函式
- 輕鬆學習 JavaScript——第 5 部分:簡化函式提升JavaScript函式
- Java 輕鬆理解深拷貝與淺拷貝Java
- 看動畫輕鬆理解時間複雜度(二)動畫時間複雜度
- 看動畫輕鬆理解時間複雜度(一)動畫時間複雜度
- 4個角度輕鬆理解 Flink中的Watermark
- 輕鬆使用“Explain Shell”指令碼來理解 Shell 命令AI指令碼
- Yii2 - Active Record 輕鬆學習
- 輕鬆學習 JavaScript——第 7 部分:物件屬性描述符JavaScript物件
- 輕鬆學習 JavaScript——第 4 部分:函式中的 arguments 物件JavaScript函式物件
- 看動畫輕鬆理解「遞迴」與「動態規劃」動畫遞迴動態規劃
- Kafka 架構圖-輕鬆理解 kafka 生產消費Kafka架構
- 5分鐘搞懂Kubernetes:輕鬆理解所有元件元件
- 看圖輕鬆理解資料結構與演算法系列(2-3樹)資料結構演算法
- 辦公室革命,教你輕鬆搞定輕鬆玩轉ExcelExcel
- 輕鬆理解JS函式節流和函式防抖JS函式
- 理解BERT Transformer:Attention is not all you need!ORM
- substrate輕鬆學系列2:區塊鏈與substrate區塊鏈