
時隔八年,再次提筆續(xù)寫這個系列。毫無疑問,智能時代已經(jīng)到來,AGI(通用人工智能)似乎也不再是遙不可及的夢想。大模型,作為目前最接近實現(xiàn)AGI的技術(shù),甚至已經(jīng)可以寫代碼了!這對于程序員真是數(shù)十年未有之大變局,因此學(xué)好、用好大模型,就將一步跨過JavaScript、Java、C++直接走到鄙視鏈的頂端,用自然語言編程,成為超級程序員。希望這個系列幫助你快速從零基礎(chǔ)達到真入門級水平,理解大模型才能用好大模型。零基礎(chǔ)意味著你不需要太多的數(shù)學(xué)知識,只要會寫程序就行了。雖然文中會有很多公式你也許看不懂,但同時也會有更多的代碼,程序員的你一定能看懂的(我周圍是一群狂熱的Clean Code程序員,所以我寫的代碼也不會很差)。
文章列表
零基礎(chǔ)入門深度學(xué)習(xí)(1) - 感知器
零基礎(chǔ)入門深度學(xué)習(xí)(2) - 線性單元和梯度下降
零基礎(chǔ)入門深度學(xué)習(xí)(3) - 神經(jīng)網(wǎng)絡(luò)和反向傳播算法
零基礎(chǔ)入門深度學(xué)習(xí)(4) - 卷積神經(jīng)網(wǎng)絡(luò)
零基礎(chǔ)入門深度學(xué)習(xí)(5) - 循環(huán)神經(jīng)網(wǎng)絡(luò)
零基礎(chǔ)入門深度學(xué)習(xí)(6) - 長短時記憶網(wǎng)絡(luò)(LSTM)
零基礎(chǔ)入門深度學(xué)習(xí)(7) - 遞歸神經(jīng)網(wǎng)絡(luò)
零基礎(chǔ)入門深度學(xué)習(xí)(8) - Transformer(1/3)
零基礎(chǔ)入門深度學(xué)習(xí)(9) - Transformer(2/3)
零基礎(chǔ)入門深度學(xué)習(xí)(10) - Transformer(3/3)
往期回顧
在前面的文章中,我們介紹了全連接神經(jīng)網(wǎng)絡(luò)、卷積神經(jīng)網(wǎng)絡(luò)、循環(huán)神經(jīng)網(wǎng)絡(luò)等,可以它們看做是不同結(jié)構(gòu)的神經(jīng)網(wǎng)絡(luò)。本文將繼續(xù)從結(jié)構(gòu)這個角度出發(fā),介紹一種新的、極其重要的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu):Transformer。從時間來看,它也不是那么新,畢竟是2017年出現(xiàn)的,到現(xiàn)在(2025年)也將近十年了。然而,沒有一個更新的網(wǎng)絡(luò)結(jié)構(gòu)比它還成功,也說明其結(jié)構(gòu)設(shè)計是極為優(yōu)秀的。Transformer出現(xiàn)后,用了5年左右的時間,逐漸取代卷積神經(jīng)網(wǎng)絡(luò)成為當(dāng)紅一哥,又用另外5年開辟了一個全新的時代:大模型時代??梢哉f,目前所有的大模型,都是以Transformer結(jié)構(gòu)為基礎(chǔ)的各種變體。如果您剛剛開始學(xué)習(xí)AI,可以嘗試快速跳過Transformer之前的部分,把主要的精力放在Transformer的學(xué)習(xí)上。
由于Transformer內(nèi)容較多,為了保持一個良好的節(jié)奏,我將整個內(nèi)容分成三個部分。第一部分比較簡單,是一些基礎(chǔ)知識的介紹;第二部分重點講述transformer的注意力機制,這也是它的核心部分;第三部分講述模型的整體結(jié)構(gòu),以及訓(xùn)練和推理。
自注意力機制
集中注意力是人類智能中一個非常重要的能力。人類可以從海量的輸入信息中,抓住重點的少量信息,忽略掉無用的大量冗余信息,高效完成信息處理,從而達到很高的智能。那么,模型是否可以建立和人類類似的注意力機制,并且從中獲益呢?答案是肯定的。
注意力機制的核心,就是提升關(guān)鍵信息的權(quán)重,降低非關(guān)鍵信息的權(quán)重,通過給每個信息賦予不同的注意力權(quán)重,達到聚焦關(guān)鍵信息,忽略非關(guān)鍵信息的效果。下圖展示了在視覺處理中,注意力機制的原理:

在圖像分類的任務(wù)中,重點是對動物進行分類的準確性,與之最相關(guān)的圖像部分被注意力機制賦予了更高的權(quán)重,對分類結(jié)果影響舉足輕重;圖像的背景部分則被注意力機制賦予很低的權(quán)重,以至于對最終的分類結(jié)果影響極小。這種注意力機制有效的提升了模型分類準確率。
自然語言理解任務(wù)則更為復(fù)雜一些,因為同樣的詞在不同的上下文中,其含義是不一樣的,例如下面兩句話:
The animal didn't cross the street because it was too tired.
The animal didn't cross the street because it was too wide.
顯然,兩句話中僅僅最后一個單詞不同,但導(dǎo)致了代詞it 指代的含義不同。在第一句話中,it 指代 animal,而第二句話中,it 指代 street。從注意力機制角度看,顯然,第一句話中的it應(yīng)該更加注意road,而第二句話中的it應(yīng)該更注意cat。我們希望模型的注意力機制能夠達到下面的效果:

那么,我們應(yīng)該設(shè)計什么樣的模型結(jié)構(gòu),使得每個token可以根據(jù)上下文的不同,為每個Token分配不同的注意力權(quán)重?這就是Transformer結(jié)構(gòu)核心:自注意力機制(Self-Attention)。其核心是一個序列中的任意兩個Token之間都要計算注意力權(quán)重,也就是每個Token都要注意自身以及序列中的其它Token,因而叫做自注意力機制。
具體該如何計算呢?為了不失普遍性,我們任意選取序列中的兩個Token: 和
,詳細介紹他們之間如何計算注意力權(quán)重。只要把這個說清楚了,整個序列的注意力計算也就容易理解了。
第一步,需要為每個Token 線性投影出三個向量,分別命名為
,
,
。這三個向量的維度可以和詞向量的維度不同,
的維度可以和
,
不同。例如,可以設(shè)置詞向量
維度是
,
,
的維度是
,
的維度是
。當(dāng)然,
,
,
,
都設(shè)置成同樣的維度也沒問題,很多模型也都是這樣做的。計算
,
,
很簡單,只要經(jīng)過一次線性映射:
其中,,
,
分別是三個權(quán)重矩陣,是模型可學(xué)習(xí)參數(shù)的一部分,在模型訓(xùn)練過程中尋找這些參數(shù)的最優(yōu)值。顯然,
,
。注意,這里我們按照慣例,用
表示列向量,用
表示行向量。
q, k, v分別是Query、Key、Value的縮寫,它們是對注意力機制計算過程的抽象表達,有助于我們理解和實現(xiàn)。
第二步,計算兩個Token之間的注意力權(quán)重。我們以 和
為例,這里需要注意的是
對
的注意力和
對
注意力是不同的。其實這很容易理解,人與人之間的注意力不也是如此么,黎叔說的好:我本將心向明月,奈何明月照溝渠,大概就是這么一回事。計算方法很簡單:
當(dāng)qk兩個向量的維度較大時,點積的數(shù)值幅度會顯著增大,會導(dǎo)致訓(xùn)練時梯度消失。為緩解這一效應(yīng),需要對點積的結(jié)果進行縮放。Transformer采用的縮放因子是 ,其中
是qk向量的維度。
假設(shè)整個序列長度為 ,那么,所有Token之間兩兩計算注意力權(quán)重后,我們必然會得到一個注意力矩陣
。即:
其中, 是序列中所有Token的
向量組成的矩陣,
是序列中所有Token的
向量組成的矩陣。
,
,
。
第三步,利用Softmax對注意力權(quán)重歸一化。前面計算出來的注意力權(quán)重a是未歸一化的,而我們希望歸一化,即所有注意力權(quán)重都應(yīng)該是非負數(shù),且加在一起應(yīng)該等于1,這樣注意力權(quán)重就等同于概率。例如,對于長度為 序列,任意Token
會計算得
個注意力權(quán)重,即它對序列中
個Token(包含對其自身)的注意力。我們希望這
個注意力權(quán)重都是非負值,且加在一起的和正好是1。做法也很簡單,只要對權(quán)重矩陣
按行計算Softmax即可:
其中, 表示權(quán)重矩陣A的第i行,
表示從矩陣
中取出第
行的分塊。
第四步
對序列中的每個Token,按照第三步計算的注意力權(quán)重,對序列中所有的v向量計算加權(quán)和。例如,對于Token ,在上一步計算出
個注意力權(quán)重(保存在A矩陣的第
行),用它們來計算所有
個
向量的加權(quán)和。
其中是所有
向量組成的矩陣。
我們可以把整個四個步驟用矩陣計算來表示:
以上就是自注意力機制,雖然看著復(fù)雜,其實也不簡單,但相信大家都能理解。如果還不理解,可以再參考下面的示意圖。在示意圖中,我們設(shè)置序列長度 ,詞向量維度
,
向量維度
,
向量維度
。

下面自注意力機制的示例代碼:
import numpy as np
import math
S=3 # 序列長度
Dq=2 # q向量維度
Dk=2 # k向量維度
Dv=3 # v向量維度
Dx=4 # 詞向量維度
def softmax(x, axis=None):
"""數(shù)值穩(wěn)定的 Softmax 實現(xiàn)"""
e_x = np.exp(x - np.max(x, axis=axis, keepdims=True)) # 減去最大值避免溢出
return e_x / e_x.sum(axis=axis, keepdims=True)
# 初始化輸入詞向量矩陣,以及Wq、Wk、Wv三個權(quán)重矩陣
X=np.array([[0.312, 0.395, -1.343, 2.102],
[1.232, 2.567, -0.123, 0.838],
[2.134, -3.123, 0.123, -0.271]])
Wq=np.random.rand(Dx, Dq)
Wk=np.random.rand(Dx, Dk)
Wv=np.random.rand(Dx, Dv)
def self_attention(X):
# 計算序列X的self_attention
# 第一步,計算Q、K、V
Q=np.dot(X,Wq)
K=np.dot(X,Wk)
V=np.dot(X,Wv)
# 第二步,計算注意力權(quán)重
A=np.dot(Q,K.T)/math.sqrt(Dq)
# 第三步,歸一化注意力權(quán)重
AA=softmax(A, axis=1)
# 第四步,計算v向量加權(quán)和
O=np.dot(AA, V)
return O
多頭注意力
研究發(fā)現(xiàn),如果僅僅為一個Token線性投影出一份 的
、
、
向量,那么模型很難關(guān)注來自不同語義子空間的信息,因為多個語義子空間的信息被平均化了。解決方法就是多頭注意力機制,即為每個Token投影出
份不同
、
、
向量,每份
、
、
向量都執(zhí)行上一章講述的自注意力函數(shù),并將最終的
份輸出拼接在一起,再次進行投影,從而得到最終的值,如下圖所示。

為了不增加計算量和模型參數(shù)量,在多頭注意力機制中,通常設(shè)置每個注意力頭的維度是原先的 ,即
,
。
計算公式為:
其中 ,
,
,
。注意,這里設(shè)置最終的輸出維度為
,和詞向量的維度一致。
因果注意力
文本處理任務(wù)通??梢苑殖?strong>文本理解和文本生成兩大類,為了便于解釋,讀者可以簡單將其類比為人類的讀和寫,它們對注意力機制的要求是不同的。對于文本理解任務(wù),可以從前到后、從后到前兩個方向?qū)ξ谋具M行全面理解,相當(dāng)于我們在讀一篇文章時,可以翻來覆去的正著讀、倒著讀,這樣可以加深對文章的理解(可能您閱讀本文時就需要這樣)。對于文本生成任務(wù),文本是一個詞接著一個詞按順序生成,在生成當(dāng)前這個詞的時候,只能注意到它前面已經(jīng)生成的詞,因為后面的字還沒有被生成。相當(dāng)于我們在寫一篇文章時,當(dāng)前落筆更多的是參考之前寫下的內(nèi)容,因為后面容還沒有被寫出來,對當(dāng)前要寫的具體內(nèi)容影響極?。赡苤挥幸粋€大概的思路)。這樣,我們就需要兩種不同的注意力機制來對應(yīng)上述兩個不同的過程。對于文本理解,注意力機制設(shè)計為雙向的,即一個Token既可以注意序列中之前的Token,也可以注意后續(xù)的Token。對于文本生成,注意力機制設(shè)計為單向的,即一個Token既只能注意序列中之前的Token,不能注意到后續(xù)的Token。這種單向的注意力機制,也叫做因果注意力機制。取這個名字,可能是提醒我們不能倒果為因吧。
上一節(jié)我們實現(xiàn)的是雙向注意力,這一節(jié)我們介紹單向注意力的實現(xiàn)方法。單向注意力機制可以用過掩碼實現(xiàn),設(shè)輸入序列長度為 ,掩碼矩陣
的元素定義為:
其中:
-
表示
向量的位置。
-
表示
向量的位置。
掩碼矩陣 實際上是一個上三角矩陣,其具體形式為:
在計算注意力權(quán)重時,掩碼通過加法融入注意力矩陣:
經(jīng)過掩碼后,Softmax僅對有效位置( )歸一化。因果注意力計算公式為:
以下是實例代碼:
import numpy as np
def causal_attention(Q, K, V):
"""
實現(xiàn)Transformer Decoder的因果注意力機制
參數(shù):
query (np.ndarray): 查詢矩陣,形狀為 [S, dq]
key (np.ndarray): 鍵矩陣,形狀為 [S, dk]
value (np.ndarray): 值矩陣,形狀為 [S, dv]
返回:
np.ndarray: 注意力輸出,形狀為 [S, dv]
"""
# 獲取維度信息
d_k = query.shape[1]
seq_len = query.shape[0]
# 生成因果掩碼矩陣 M
mask = np.triu(np.ones((seq_len, seq_len)), k=1)
mask = np.where(mask == 0, 0.0, -1e9) # 無效位置設(shè)為極大負數(shù)
# 計算原始注意力矩陣
attn = np.dot(query, key.T) / np.sqrt(d_k)
# 應(yīng)用掩碼
masked_attn = attn + mask
# Softmax歸一化
attention_weights = np.exp(masked_attn) / np.sum(np.exp(masked_attn), axis=1, keepdims=True)
# 計算加權(quán)和
output = np.dot(attention_weights, value)
return output
按位置前饋網(wǎng)絡(luò)
自注意力機制可以看做Token與Token之間的信息混合,那么,Token內(nèi)部各個維度(通道)也可以做信息混合,類似卷積神經(jīng)網(wǎng)絡(luò)中卷積核的大小為1時起到的效果。
該部分由兩個全連接層組成,中間采用ReLU激活函數(shù)鏈接,如下圖所示:
![][5]
計算公式為:
其中,、
、
、
都是模型的可學(xué)習(xí)參數(shù)。令
表示中間維度,則
,
,
,
。中間維度通常設(shè)置為輸入維度的4倍,即
。
以下是示例代碼:
import numpy as np
# 維度定義
S = 3 # 序列長度
Dx = 4 # 輸入維度
Di = 4 * Dx # 中間維度
# 初始化權(quán)重參數(shù)
W1 = np.random.randn(Dx, Di) * 0.01 # 第一隱藏層權(quán)重
b1 = np.zeros(Di) # 第一隱藏層偏置
W2 = np.random.randn(Di, Dx) * 0.01 # 輸出層權(quán)重
b2 = np.zeros(Dx) # 輸出層偏置
def FFN(x):
"""
前向傳播實現(xiàn)位置前饋網(wǎng)絡(luò)
輸入形狀:(S, Dx)
輸出形狀:(S, Dx)
"""
# 將二維輸入轉(zhuǎn)換為一維 (S, Dx)
x_flat = x.reshape(-1, x.shape[-1])
# 計算第一個線性變換 + ReLU激活
h = np.maximum(0, np.dot(x_flat, W1) + b1)
# 計算第二個線性變換
output_flat = np.dot(h, W2) + b2
# 恢復(fù)原始三維形狀
output = output_flat.reshape(S, -1)
return output
小節(jié)
本文介紹了Transformer的核心:注意力機制。Transformer通過注意力機制實現(xiàn)了Token間信息混合,又通過按位置前饋網(wǎng)絡(luò)實現(xiàn)了Token內(nèi)不同通道信息的融合,從而為模型提供了豐富的表達能力。至此,Transformer模型結(jié)構(gòu)的關(guān)鍵元素都已經(jīng)介紹完了。下一篇文章,我們將介紹Transformer模型及其訓(xùn)練和推理方法,看看是如何將這些要素整合為一個強大的模型。

參考資料
相關(guān)文章
零基礎(chǔ)入門深度學(xué)習(xí)(8) - Transformer (1/3)
零基礎(chǔ)入門深度學(xué)習(xí)(9) - Transformer (2/3)
零基礎(chǔ)入門深度學(xué)習(xí)(10) - Transformer (3/3)