1. transformer模型
1.1 注意力機制
**注意力機制(Attention Mechanism)**在人工智能中的應用,實際上是對人類認知系統中的注意力機制的一種模擬。它主要模仿了人類在處理信息時的選擇性注意(Selective Attention),即我們不會平均分配注意力,而是會集中關注關鍵信息。
1.2 注意力機制原理
注意力機制是一種動態加權機制,用于在輸入數據中選取最相關的信息,并賦予不同的權重。它最初用于神經機器翻譯(Neural Machine Translation, NMT),后來廣泛應用于自然語言處理(NLP)、計算機視覺(CV)和異常檢測等任務。
1. 注意力計算流程
如圖所示,注意力機制主要包括 Query(查詢)、Key(鍵)、Value(值) 三個核心概念,它們的計算過程分為三步:
第一步:計算 Query 和 Key 之間的相似度
- 我們用查詢向量(Query,記作 q q q,去匹配多個鍵向量(Keys,記作 k j k_j kj?,計算它們的相似度。
- 這里采用的是點積(Dot Product來度量相似度:
e i j = q T k j e_{ij} = q^T k_j eij?=qTkj? - 其中:
- q q q 是查詢向量(query)
- k j k_j kj?是鍵向量(key)
- e i j e_{ij} eij?表示查詢 q q q 與鍵 k j k_j kj?的相似度得分
直觀理解:
- 如果 q q q和 k j k_j kj? 方向相似(即表示的信息相似),則它們的點積 e i j e_{ij} eij? 會較大。
- 反之,如果 q q q和 k j k_j kj? 方向不同,則 e i j e_{ij} eij? 會較小。
第二步:將相似度進行 Softmax 歸一化
- 由于點積計算出的相似度值 e i j e_{ij} eij? 可能較大,因此需要進行Softmax 歸一化,使其變成一個概率分布:
a i j = exp ? ( e i j ) ∑ j ′ exp ? ( e i j ′ ) a_{ij} = \frac{\exp(e_{ij})}{\sum_{j'} \exp(e_{ij'})} aij?=∑j′?exp(eij′?)exp(eij?)? - 這樣可以確保所有注意力權重 a i j a_{ij} aij? 的總和為 1,便于后續加權計算。
直觀理解:
- Softmax 歸一化后,最相關的 Key 對應的權重 a i j a_{ij} aij? 會更大,而不相關的 Key 權重會接近 0。
第三步:加權求和 Value
- 計算出的注意力權重 a i j a_{ij} aij?作用于對應的 Value(值向量),最終得到輸出:
o i = ∑ j a i j v j o_i = \sum_{j} a_{ij} v_j oi?=j∑?aij?vj? - 其中:
- v j v_j vj? 是對應的值向量(value)
- o i o_i oi? 是最終的加權求和結果(即注意力輸出)
直觀理解:
- 這個過程類似于信息檢索,即:
- 如果某個 Key 與 Query 相似度高(即權重 a i j a_{ij} aij?大),那么它的 Value( v j v_j vj?) 會被重點關注。
- 最終的輸出 o i o_i oi? 是所有 Value 的加權平均。
2. 直觀理解
可以把注意力機制類比為搜索引擎:
- Query(查詢):用戶輸入的搜索關鍵詞(如 “Transformer 論文”)。
- Keys(鍵):數據庫中的所有文檔標題(如 “Attention is All You Need”)。
- Values(值):數據庫中的所有文檔內容(如 Transformer 論文的正文)。
- 計算相似度:搜索引擎會計算用戶輸入的關鍵詞與文檔標題的相關性。
- Softmax 歸一化:搜索引擎會給每個文檔一個相關性分數,并歸一化為一個排名概率。
- 加權求和:最終,搜索引擎會按照相關性高低,返回最相關的文檔內容。
3. 在 Transformer 中的應用
在 Transformer 結構(如 BERT、GPT)中,
注意力機制被用于自注意力Self-Attention計算:
- 輸入是一個句子,每個單詞都有一個 Query、Key、Value。
- 計算 Query 與所有 Key 的相似度,確定哪些單詞對當前單詞最重要。
- 對 Value 進行加權求和,生成新的單詞表示。
示例:
- 句子:“The cat sat on the mat.”
- 計算 “cat” 的注意力:
- “cat” 可能關注 “The” 和 “sat” 的信息,而忽略 “mat”。
- Transformer 通過注意力機制動態調整單詞之間的關系,提高理解能力。
4. 計算復雜度
- 傳統 RNN 計算復雜度:( O(n) )(必須按順序計算)
- Transformer 注意力計算復雜度:( O(n^2) )(可以并行計算)
雖然 Transformer 的注意力計算復雜度較高,但由于可以并行計算,在實際應用中比 RNN 更高效。
1.3 編碼器
1. 編碼器的作用
編碼器(Encoder)是 Transformer 結構的核心組件之一,它的主要作用是將輸入數據轉換為隱藏層特征,以便后續的解碼器(Decoder)進一步處理或用于下游任務(如分類、翻譯等)。
在 Transformer 結構中,編碼器由 N 個堆疊的編碼器層(Encoder Layers) 組成,每個編碼器層都包含:
- 多頭注意力機制(Multi-Head Attention, MHA)
- 前饋神經網絡(Feed Forward Network, FFN)
- 殘差連接(Residual Connection)和層歸一化(Layer Normalization)
2. 編碼器的結構
編碼器的計算流程如下:
(1)多頭注意力(Multi-Head Attention, MHA)
- 計算輸入序列中每個詞對其他詞的注意力權重,提取全局依賴關系。
- 采用多頭注意力機制(多個注意力頭并行計算),以學習不同的特征表示。
(2)殘差連接 + 層歸一化
- 計算多頭注意力的輸出后,使用殘差連接(Residual Connection):
X l ′ = LayerNorm ( MHA ( X l ? 1 ) + X l ? 1 ) X'_l = \text{LayerNorm}(\text{MHA}(X_{l-1}) + X_{l-1}) Xl′?=LayerNorm(MHA(Xl?1?)+Xl?1?)
其中:- X l ? 1 X_{l-1} Xl?1? 是上一層的輸出
- MHA 是多頭注意力
- LayerNorm 是層歸一化,防止梯度消失或梯度爆炸
(3)前饋神經網絡(Feed Forward Network, FFN)
- 由兩個全連接層(MLP)組成:
FFN ( X ′ ) = ReLU ( X ′ W 1 + b 1 ) W 2 + b 2 \text{FFN}(X') = \text{ReLU}(X' W_1 + b_1) W_2 + b_2 FFN(X′)=ReLU(X′W1?+b1?)W2?+b2? - 作用是增加非線性變換能力,提升模型的表達能力。
(4)再次殘差連接 + 層歸一化
- 計算 FFN 輸出后,再次應用殘差連接:
X l = LayerNorm ( FFN ( X l ′ ) + X l ′ ) X_l = \text{LayerNorm}(\text{FFN}(X'_l) + X'_l) Xl?=LayerNorm(FFN(Xl′?)+Xl′?) - 這樣可以穩定梯度傳遞,提高訓練效果。
3. 公式解析
從公式來看,編碼器的計算流程可以分為兩步:
- 多頭注意力層(MHA)+ 殘差連接 + 層歸一化
X l ′ = LayerNorm ( MHA ( X l ? 1 ) + X l ? 1 ) X'_l = \text{LayerNorm}(\text{MHA}(X_{l-1}) + X_{l-1}) Xl′?=LayerNorm(MHA(Xl?1?)+Xl?1?) - 前饋神經網絡(FFN)+ 殘差連接 + 層歸一化
X l = LayerNorm ( FFN ( X l ′ ) + X l ′ ) X_l = \text{LayerNorm}(\text{FFN}(X'_l) + X'_l) Xl?=LayerNorm(FFN(Xl′?)+Xl′?)
其中:
- ( X_{l-1} ):表示編碼器第 ( l-1 ) 層的輸出
- ( X’_l ):表示經過多頭注意力后的中間結果
- ( X_l ):表示編碼器第 ( l ) 層的最終輸出
- LayerNorm:層歸一化,穩定訓練
- MHA:多頭注意力
- FFN:前饋神經網絡
4. 編碼器的特點
- 并行計算:不像 RNN 需要逐步處理序列,Transformer 編碼器可以一次性處理整個輸入序列,提高計算效率。
- 長距離依賴建模:通過自注意力機制(Self-Attention),編碼器可以捕捉遠距離的詞語關系。
- 層歸一化 + 殘差連接:防止梯度消失,提高模型穩定性。
- 多頭注意力:增強模型的表達能力,讓不同的注意力頭關注不同的信息。
5. 代碼實現
Transformer 編碼器層(Encoder Layer)的 PyTorch 實現:
import torch
import torch.nn as nnclass TransformerEncoderLayer(nn.Module):def __init__(self, d_model, num_heads, d_ff, dropout=0.1):"""Transformer 編碼器層參數:- d_model: 輸入特征維度(如 512)- num_heads: 多頭注意力的頭數- d_ff: 前饋神經網絡的隱藏層維度(一般是 4*d_model)- dropout: Dropout 概率"""super(TransformerEncoderLayer, self).__init__()# 多頭注意力層self.self_attn = nn.MultiheadAttention(embed_dim=d_model, num_heads=num_heads, dropout=dropout)# 前饋神經網絡self.ffn = nn.Sequential(nn.Linear(d_model, d_ff),nn.ReLU(),nn.Linear(d_ff, d_model))# 層歸一化self.norm1 = nn.LayerNorm(d_model)self.norm2 = nn.LayerNorm(d_model)# Dropoutself.dropout1 = nn.Dropout(dropout)self.dropout2 = nn.Dropout(dropout)def forward(self, x):"""前向傳播參數:- x: 輸入張量 (batch_size, seq_len, d_model)返回:- 編碼器層的輸出 (batch_size, seq_len, d_model)"""# 多頭注意力attn_output, _ = self.self_attn(x, x, x)x = self.norm1(x + self.dropout1(attn_output)) # 殘差連接 + 層歸一化# 前饋神經網絡ffn_output = self.ffn(x)x = self.norm2(x + self.dropout2(ffn_output)) # 殘差連接 + 層歸一化return x# 測試編碼器層
d_model = 512
num_heads = 8
d_ff = 2048
seq_len = 10
batch_size = 2encoder_layer = TransformerEncoderLayer(d_model, num_heads, d_ff)
input_tensor = torch.rand(batch_size, seq_len, d_model) # 隨機輸入
output_tensor = encoder_layer(input_tensor)print("編碼器層輸出形狀:", output_tensor.shape) # (batch_size, seq_len, d_model)
組件 | 作用 |
---|---|
多頭注意力(MHA) | 計算輸入序列中不同位置的相關性,提取全局信息 |
前饋神經網絡(FFN) | 增強特征表達能力 |
殘差連接(Residual) | 防止梯度消失,提升訓練穩定性 |
層歸一化(LayerNorm) | 歸一化激活值,穩定訓練 |
編碼器的關鍵作用是將輸入轉換為隱藏層特征,并通過層層堆疊提取更高級的語義信息。
1.4 解碼器
1. 解碼器的作用
解碼器(Decoder)是 Transformer 結構的另一核心組件,它的主要作用是將編碼器的隱藏層特征轉換為輸出序列(如翻譯任務中的目標語言句子)。
在 Transformer 結構中,解碼器由 N 個堆疊的解碼器層(Decoder Layers) 組成,每個解碼器層包含:
- 掩碼多頭注意力(Masked Multi-Head Attention, Masked MHA)
- 交叉多頭注意力(Cross Multi-Head Attention, Cross MHA)
- 前饋神經網絡(Feed Forward Network, FFN)
- 殘差連接(Residual Connection)和層歸一化(Layer Normalization)
2. 解碼器的結構
解碼器的計算流程如下:
(1)掩碼多頭注意力(Masked Multi-Head Attention, Masked MHA)
- 作用:在生成序列時,模型不能看到未來的信息,因此需要掩碼(Mask),確保解碼器在預測第 t t t 個詞時,只能利用 t t t 之前的詞,而不能看到 t + 1 t+1 t+1 及之后的詞。
- 計算方式:
Y l ′ = LayerNorm ( MaskedMHA ( Y l ? 1 ) + Y l ? 1 ) Y'_l = \text{LayerNorm}(\text{MaskedMHA}(Y_{l-1}) + Y_{l-1}) Yl′?=LayerNorm(MaskedMHA(Yl?1?)+Yl?1?)- Y l ? 1 Y_{l-1} Yl?1? 是解碼器前一層的輸出
- Masked MHA 計算當前詞與之前詞的注意力
- LayerNorm 進行層歸一化
(2)交叉多頭注意力(Cross Multi-Head Attention, Cross MHA)
- 作用:在翻譯任務中,解碼器需要關注編碼器的輸出,以獲取源語言的信息。
- 計算方式:
Y l ′ ′ = LayerNorm ( CrossMHA ( Y l ′ , X L ) + Y l ′ ) Y''_l = \text{LayerNorm}(\text{CrossMHA}(Y'_l, X_L) + Y'_l) Yl′′?=LayerNorm(CrossMHA(Yl′?,XL?)+Yl′?)- Y l ′ Y'_l Yl′? 是掩碼注意力的輸出
- X L X_L XL? 是編碼器的最終輸出
- Cross MHA 計算解碼器的隱藏狀態與編碼器輸出之間的注意力關系
(3)前饋神經網絡(Feed Forward Network, FFN)
- 作用:增強特征表達能力,提高模型的非線性變換能力。
- 計算方式:
Y l = LayerNorm ( FFN ( Y l ′ ′ ) + Y l ′ ′ ) Y_l = \text{LayerNorm}(\text{FFN}(Y''_l) + Y''_l) Yl?=LayerNorm(FFN(Yl′′?)+Yl′′?)- FFN 由兩個全連接層(MLP)組成:
FFN ( Y ′ ′ ) = ReLU ( Y ′ ′ W 1 + b 1 ) W 2 + b 2 \text{FFN}(Y'') = \text{ReLU}(Y'' W_1 + b_1) W_2 + b_2 FFN(Y′′)=ReLU(Y′′W1?+b1?)W2?+b2? - 采用 ReLU 激活函數,增強非線性表達能力
- FFN 由兩個全連接層(MLP)組成:
4. 計算解析
解碼器的計算流程可以分為三步:
- 掩碼多頭注意力(Masked MHA)+ 殘差連接 + 層歸一化
Y l ′ = LayerNorm ( MaskedMHA ( Y l ? 1 ) + Y l ? 1 ) Y'_l = \text{LayerNorm}(\text{MaskedMHA}(Y_{l-1}) + Y_{l-1}) Yl′?=LayerNorm(MaskedMHA(Yl?1?)+Yl?1?) - 交叉多頭注意力(Cross MHA)+ 殘差連接 + 層歸一化
Y l ′ ′ = LayerNorm ( CrossMHA ( Y l ′ , X L ) + Y l ′ ) Y''_l = \text{LayerNorm}(\text{CrossMHA}(Y'_l, X_L) + Y'_l) Yl′′?=LayerNorm(CrossMHA(Yl′?,XL?)+Yl′?) - 前饋神經網絡(FFN)+ 殘差連接 + 層歸一化
Y l = LayerNorm ( FFN ( Y l ′ ′ ) + Y l ′ ′ ) Y_l = \text{LayerNorm}(\text{FFN}(Y''_l) + Y''_l) Yl?=LayerNorm(FFN(Yl′′?)+Yl′′?)
其中:
- Y l ? 1 Y_{l-1} Yl?1? ):表示解碼器第 l ? 1 l-1 l?1 層的輸出
- Y l ′ Y'_l Yl′?:表示掩碼多頭注意力后的中間結果
- Y l ′ ′ Y''_l Yl′′?:表示交叉多頭注意力后的中間結果
- Y l Y_l Yl?:表示解碼器第 l l l層的最終輸出
- X L X_L XL?:編碼器的最終輸出
- LayerNorm:層歸一化,穩定訓練
- Masked MHA:掩碼多頭注意力(防止未來信息泄露)
- Cross MHA:交叉多頭注意力(關注編碼器輸出)
- FFN:前饋神經網絡
5. 編碼器 vs. 解碼器
組件 | 編碼器(Encoder) | 解碼器(Decoder) |
---|---|---|
輸入 | 輸入序列(如源語言) | 目標序列(如目標語言) |
多頭注意力 | 標準多頭注意力(Self-Attention) | 掩碼多頭注意力(Masked Self-Attention) |
交叉注意力 | ? | ?(關注編碼器輸出) |
前饋神經網絡 | ? | ? |
層歸一化 & 殘差連接 | ? | ? |
6. 代碼實現
下面是 Transformer 解碼器層(Decoder Layer)的 PyTorch 實現:
import torch
import torch.nn as nnclass TransformerDecoderLayer(nn.Module):def __init__(self, d_model, num_heads, d_ff, dropout=0.1):"""Transformer 解碼器層參數:- d_model: 輸入特征維度(如 512)- num_heads: 多頭注意力的頭數- d_ff: 前饋神經網絡的隱藏層維度(一般是 4*d_model)- dropout: Dropout 概率"""super(TransformerDecoderLayer, self).__init__()# 掩碼多頭注意力self.masked_attn = nn.MultiheadAttention(embed_dim=d_model, num_heads=num_heads, dropout=dropout)# 交叉多頭注意力(關注編碼器輸出)self.cross_attn = nn.MultiheadAttention(embed_dim=d_model, num_heads=num_heads, dropout=dropout)# 前饋神經網絡self.ffn = nn.Sequential(nn.Linear(d_model, d_ff),nn.ReLU(),nn.Linear(d_ff, d_model))# 層歸一化self.norm1 = nn.LayerNorm(d_model)self.norm2 = nn.LayerNorm(d_model)self.norm3 = nn.LayerNorm(d_model)# Dropoutself.dropout1 = nn.Dropout(dropout)self.dropout2 = nn.Dropout(dropout)self.dropout3 = nn.Dropout(dropout)def forward(self, x, encoder_output):"""前向傳播參數:- x: 解碼器輸入 (batch_size, seq_len, d_model)- encoder_output: 編碼器輸出 (batch_size, seq_len, d_model)返回:- 解碼器層的輸出 (batch_size, seq_len, d_model)"""# 掩碼多頭注意力masked_attn_output, _ = self.masked_attn(x, x, x)x = self.norm1(x + self.dropout1(masked_attn_output)) # 殘差連接 + 層歸一化# 交叉多頭注意力cross_attn_output, _ = self.cross_attn(x, encoder_output, encoder_output)x = self.norm2(x + self.dropout2(cross_attn_output)) # 殘差連接 + 層歸一化# 前饋神經網絡ffn_output = self.ffn(x)x = self.norm3(x + self.dropout3(ffn_output)) # 殘差連接 + 層歸一化return x# 測試解碼器層
decoder_layer = TransformerDecoderLayer(d_model=512, num_heads=8, d_ff=2048)
input_tensor = torch.rand(2, 10, 512) # 解碼器輸入
encoder_output = torch.rand(2, 10, 512) # 編碼器輸出
output_tensor = decoder_layer(input_tensor, encoder_output)print("解碼器層輸出形狀:", output_tensor.shape) # (batch_size, seq_len, d_model)
解碼器的核心在于:
- 掩碼多頭注意力(Masked MHA):防止未來信息泄露
- 交叉多頭注意力(Cross MHA):關注編碼器的輸出
- 前饋神經網絡(FFN):增強特征表達能力
1.5 多頭注意力層
多頭注意力(Multi-Head Attention, MHA)是 Transformer 結構的核心組件之一,它能夠讓模型關注輸入序列中不同位置的關系,并通過多個注意力頭(heads)學習不同的特征表示。
1. Scaled Dot-Product Attention(縮放點積注意力)
多頭注意力的基礎是 縮放點積注意力(Scaled Dot-Product Attention),其計算流程如下:
(1)輸入映射到 Q、K、V
給定輸入矩陣 X X X ,我們將其映射為:
- 查詢(Query, Q): Q = X W Q Q = X W^Q Q=XWQ
- 鍵(Key, K): K = X W K K = X W^K K=XWK
- 值(Value, V):$ V = X W^V$
其中:
- W Q , W K , W V W^Q, W^K, W^V WQ,WK,WV是可訓練的權重矩陣
- X X X 是輸入特征(如詞向量)
(2)計算注意力權重
注意力分數使用點積計算:
S = Q K T S = QK^T S=QKT
然后進行縮放(防止梯度爆炸):
S scaled = Q K T d k S_{\text{scaled}} = \frac{QK^T}{\sqrt{d_k}} Sscaled?=dk??QKT?
其中:
- d k d_k dk?是鍵向量的維度
- d k \sqrt{d_k} dk??作為縮放因子,防止數值過大導致梯度消失或梯度爆炸
(3)計算 Softmax 權重
α = softmax ( S scaled ) \alpha = \text{softmax}(S_{\text{scaled}}) α=softmax(Sscaled?)
Softmax 歸一化后,表示不同位置的注意力分布。
(4)加權求和
計算最終的注意力輸出:
Attention ( Q , K , V ) = α V \text{Attention}(Q, K, V) = \alpha V Attention(Q,K,V)=αV
2. 多頭注意力(Multi-Head Attention, MHA)
在單頭注意力機制中,所有信息都通過同一個注意力機制計算,可能會忽略一些重要特征。多頭注意力的核心思想是:使用多個注意力頭(heads),讓模型關注不同的特征信息。
(1)多頭注意力計算流程
-
輸入映射到多個 Q、K、V
- 對輸入 X X X進行多組不同的線性變換,得到多個查詢、鍵和值:
Q i = X W i Q , K i = X W i K , V i = X W i V , i = 1 , 2 , … , h Q_i = X W_i^Q, \quad K_i = X W_i^K, \quad V_i = X W_i^V, \quad i = 1,2,\dots,h Qi?=XWiQ?,Ki?=XWiK?,Vi?=XWiV?,i=1,2,…,h - 其中 h h h是注意力頭的數量,每個注意力頭都有自己的權重矩陣 W i Q , W i K , W i V W_i^Q, W_i^K, W_i^V WiQ?,WiK?,WiV?。
- 對輸入 X X X進行多組不同的線性變換,得到多個查詢、鍵和值:
-
每個頭獨立計算注意力
- 每個頭獨立計算縮放點積注意力:
Attention i ( Q i , K i , V i ) = softmax ( Q i K i T d k ) V i \text{Attention}_i(Q_i, K_i, V_i) = \text{softmax} \left( \frac{Q_i K_i^T}{\sqrt{d_k}} \right) V_i Attentioni?(Qi?,Ki?,Vi?)=softmax(dk??Qi?KiT??)Vi?
- 每個頭獨立計算縮放點積注意力:
-
拼接多個頭的輸出
- 將所有注意力頭的輸出拼接在一起:
MultiHead ( Q , K , V ) = Concat ( head 1 , head 2 , … , head h ) W O \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \text{head}_2, \dots, \text{head}_h) W^O MultiHead(Q,K,V)=Concat(head1?,head2?,…,headh?)WO - 其中 W O W^O WO是最終的線性變換矩陣,將拼接后的結果映射回原始維度。
- 將所有注意力頭的輸出拼接在一起:
3. 公式總結
1.單頭注意力(Scaled Dot-Product Attention)
Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax} \left( \frac{Q K^T}{\sqrt{d_k}} \right) V Attention(Q,K,V)=softmax(dk??QKT?)V
2.多頭注意力(Multi-Head Attention)
MultiHead ( Q , K , V ) = Concat ( head 1 , head 2 , … , head h ) W O \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \text{head}_2, \dots, \text{head}_h) W^O MultiHead(Q,K,V)=Concat(head1?,head2?,…,headh?)WO
其中:
head i = Attention ( Q i , K i , V i ) \text{head}_i = \text{Attention}(Q_i, K_i, V_i) headi?=Attention(Qi?,Ki?,Vi?)
4. 代碼實現
多頭注意力(Multi-Head Attention, MHA)的 PyTorch 實現:
import torch
import torch.nn as nnclass MultiHeadAttention(nn.Module):def __init__(self, d_model, num_heads, dropout=0.1):"""多頭注意力層參數:- d_model: 輸入特征維度(如 512)- num_heads: 注意力頭的數量- dropout: Dropout 概率"""super(MultiHeadAttention, self).__init__()assert d_model % num_heads == 0, "d_model 必須能被 num_heads 整除"self.d_model = d_modelself.num_heads = num_headsself.d_k = d_model // num_heads # 每個頭的維度# 線性變換矩陣self.W_q = nn.Linear(d_model, d_model)self.W_k = nn.Linear(d_model, d_model)self.W_v = nn.Linear(d_model, d_model)self.W_o = nn.Linear(d_model, d_model)self.dropout = nn.Dropout(dropout)def attention(self, Q, K, V, mask=None):"""計算縮放點積注意力"""scores = torch.matmul(Q, K.transpose(-2, -1)) / torch.sqrt(torch.tensor(self.d_k, dtype=torch.float32))if mask is not None:scores = scores.masked_fill(mask == 0, float('-inf'))attn_weights = torch.softmax(scores, dim=-1)attn_weights = self.dropout(attn_weights)return torch.matmul(attn_weights, V), attn_weightsdef forward(self, X):batch_size, seq_len, _ = X.shape# 計算 Q, K, VQ = self.W_q(X).view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)K = self.W_k(X).view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)V = self.W_v(X).view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)# 計算多頭注意力attn_output, attn_weights = self.attention(Q, K, V)# 拼接多個頭的輸出attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)# 線性變換回原始維度output = self.W_o(attn_output)return output, attn_weights# 測試多頭注意力
d_model = 512
num_heads = 8
seq_len = 10
batch_size = 2mha = MultiHeadAttention(d_model, num_heads)
input_tensor = torch.rand(batch_size, seq_len, d_model) # 隨機輸入
output_tensor, attention_weights = mha(input_tensor)print("多頭注意力輸出形狀:", output_tensor.shape) # (batch_size, seq_len, d_model)
- 多頭注意力(MHA)能夠學習不同的注意力模式,提高模型的表達能力。
- 通過多個注意力頭,模型可以關注輸入序列的不同方面。
- 縮放點積注意力(Scaled Dot-Product Attention)是 MHA 的基礎,計算 Query-Key 相關性并加權 Value。
1.6 位置編碼
在 Transformer 模型中,由于 自注意力機制(Self-Attention) 沒有內置的序列順序信息,因此需要 位置編碼(Positional Encoding) 來顯式地提供序列位置信息,使模型能夠區分不同單詞的順序。
1. 位置編碼的數學定義
位置編碼采用 正弦(sin)和余弦(cos)函數 生成,如下公式:
p t ( i ) = { sin ? ( ω k ? t ) , if? i = 2 k cos ? ( ω k ? t ) , if? i = 2 k + 1 p_t^{(i)} = \begin{cases} \sin(\omega_k \cdot t), & \text{if } i = 2k \\ \cos(\omega_k \cdot t), & \text{if } i = 2k+1 \end{cases} pt(i)?={sin(ωk??t),cos(ωk??t),?if?i=2kif?i=2k+1?
其中:
- p t ( i ) p_t^{(i)} pt(i)? 表示 位置 t t t 處,第 i i i 維的編碼值。
- ω k \omega_k ωk? 是不同維度的頻率,定義為:
ω k = 1 1000 0 2 k / d \omega_k = \frac{1}{10000^{2k/d}} ωk?=100002k/d1?
其中:- k k k表示當前維度的索引(偶數維和奇數維分別使用 sin 和 cos)。
- d d d 是隱藏層維度(例如 512)。
- 這個公式確保 不同維度的頻率不同,以捕捉不同粒度的位置信息。
2. 位置編碼的特點
- 使用不同頻率的正弦和余弦函數:
- 低維度使用較大的頻率(變化較快),編碼局部信息。
- 高維度使用較小的頻率(變化較慢),編碼全局信息。
- 編碼值是固定的,不需要訓練,不同位置的編碼是固定的數學計算結果,不依賴數據訓練。
- 可以外推到更長的序列,因為是基于數學函數計算的,而不是學習得到的。
3. 位置編碼的作用
- 提供位置信息:幫助 Transformer 區分不同單詞的順序。
- 支持長序列建模:由于編碼是基于數學函數計算的,可以推廣到比訓練時更長的序列。
- 平滑的連續變化:由于使用正弦和余弦函數,編碼值在相鄰位置之間連續變化,使得模型可以更容易學習相對位置信息。
4. 代碼實現
可以使用 Python(PyTorch)實現位置編碼:
import numpy as np
import matplotlib.pyplot as pltdef positional_encoding(seq_len, d_model):pos = np.arange(seq_len)[:, np.newaxis] # 位置 (t)i = np.arange(d_model)[np.newaxis, :] # 維度 (i)# 計算頻率omega = 1 / (10000 ** (2 * (i // 2) / d_model))# 計算位置編碼pos_encoding = np.zeros((seq_len, d_model))pos_encoding[:, 0::2] = np.sin(pos * omega[:, 0::2]) # 偶數維pos_encoding[:, 1::2] = np.cos(pos * omega[:, 1::2]) # 奇數維return pos_encoding# 生成位置編碼
seq_len, d_model = 100, 512
pos_encoding = positional_encoding(seq_len, d_model)# 可視化
plt.figure(figsize=(10, 6))
plt.imshow(pos_encoding, aspect='auto', cmap='coolwarm')
plt.xlabel("Dimension i")
plt.ylabel("Position t")
plt.title("Positional Encoding Heatmap")
plt.colorbar()
plt.show()
? 位置編碼通過 sin 和 cos 賦予 Transformer 位置信息。
? 不同維度使用不同頻率,捕捉局部和全局信息。
? 數學計算得到,不需要訓練,可推廣到長序列。
2. 問題備注
在 Transformer 結構 中,Q(Query)、K(Key)、V(Value) 的來源在 編碼器(Encoder) 和 解碼器(Decoder) 中有所不同,具體如下:
1. 編碼器(Encoder)中的 Q、K、V
在 編碼器的自注意力(Self-Attention) 機制中:
- 輸入:編碼器的輸入是一個序列 ( X )(如源語言句子)。
- Q、K、V 的來源:
Q = X W Q , K = X W K , V = X W V Q = X W^Q, \quad K = X W^K, \quad V = X W^V Q=XWQ,K=XWK,V=XWV- Query(查詢):由輸入 X X X 經過線性變換得到
- Key(鍵):由輸入 X X X 經過線性變換得到
- Value(值):由輸入 X X X 經過線性變換得到
特點:
- 自注意力(Self-Attention):Q、K、V 都來自相同的輸入 ( X )。
- 作用:讓編碼器的每個詞關注輸入序列中的其他詞,捕捉全局信息。
2. 解碼器(Decoder)中的 Q、K、V
在 解碼器的注意力機制 中,注意力分為兩種:
- Masked 自注意力(Masked Self-Attention)
- 交叉注意力(Cross-Attention)
(1)Masked 自注意力中的 Q、K、V
- 輸入:解碼器的輸入是目標序列 ( Y )(如目標語言句子)。
- Q、K、V 的來源:
Q = Y W Q , K = Y W K , V = Y W V Q = Y W^Q, \quad K = Y W^K, \quad V = Y W^V Q=YWQ,K=YWK,V=YWV- Query(查詢):由目標序列 Y Y Y 計算
- Key(鍵):由目標序列 Y Y Y 計算
- Value(值):由目標序列 Y Y Y 計算
特點:
- Masked 機制:在計算注意力時,屏蔽未來的信息,防止看到后續詞語。
- 作用:讓解碼器的每個詞只能關注當前及之前的詞,確保自回歸特性。
(2)交叉注意力(Cross-Attention)中的 Q、K、V
- 輸入:交叉注意力的輸入是 解碼器的隱藏狀態 和 編碼器的輸出。
- Q、K、V 的來源:
Q = Y W Q , K = X L W K , V = X L W V Q = Y W^Q, \quad K = X_L W^K, \quad V = X_L W^V Q=YWQ,K=XL?WK,V=XL?WV- Query(查詢):來自解碼器的隱藏狀態 Y Y Y
- Key(鍵):來自編碼器的輸出 X L X_L XL?
- Value(值):來自編碼器的輸出 X L X_L XL?
特點:
- Q 來自解碼器,K 和 V 來自編碼器,讓解碼器能夠關注編碼器的信息。
- 作用:讓解碼器在生成目標序列時,能夠參考源語言的信息。
位置 | Query(Q)來源 | Key(K)來源 | Value(V)來源 | 作用 |
---|---|---|---|---|
編碼器(Encoder) | 輸入 (X) | 輸入 (X) | 輸入 (X) | 讓每個詞關注整個輸入序列 |
解碼器(Masked 自注意力) | 目標序列 (Y) | 目標序列 (Y) | 目標序列 (Y ) | 讓解碼器的每個詞關注當前及之前的詞 |
解碼器(交叉注意力) | 目標序列 (Y) | 編碼器輸出 X L X_L XL? | 編碼器輸出 X L X_L XL? |
- 編碼器的 Q、K、V 都來自輸入 ( X ),用于捕捉全局信息。
- 解碼器的 Masked 自注意力的 Q、K、V 都來自目標序列 Y Y Y,用于保證自回歸特性。
- 解碼器的交叉注意力的 Q 來自解碼器,K 和 V 來自編碼器的輸出 X L X_L XL?**,用于關注源語言信息。
3.為什么注意力模型計算選用點積(Dot-Product)?
在 Transformer 的注意力機制(Self-Attention 和 Cross-Attention)中,點積(Dot-Product) 是計算 Query(查詢)和 Key(鍵)之間相似度的核心操作。主要原因包括以下幾點:
1. 計算效率高
點積計算的核心公式是:
S = Q K T S = QK^T S=QKT
其中:
- Q Q Q(查詢): ( b a t c h , s e q _ l e n , d k ) (batch, seq\_len, d_k) (batch,seq_len,dk?)
- K K K(鍵): ( b a t c h , s e q _ l e n , d k ) (batch, seq\_len, d_k) (batch,seq_len,dk?)
點積計算的時間復雜度為 O ( d k ) O(d_k) O(dk?),相比其他相似度計算方法(如歐幾里得距離、余弦相似度)更加高效,尤其適用于 GPU 并行計算。
對比計算復雜度:
- 點積(Dot-Product): O ( d k ) O(d_k) O(dk?)(只需乘法和加法)
- 歐幾里得距離(Euclidean Distance): O ( d k ) O(d_k) O(dk?)(需要平方、開方)
- 余弦相似度(Cosine Similarity): O ( d k ) O(d_k) O(dk?)(需要歸一化)
由于 Transformer 需要處理大規模數據(如 NLP 任務中的長文本、視頻分析中的時序數據),選擇計算效率高的點積是更優的選擇。
2. 點積能夠有效衡量相似性
(1)點積的幾何解釋
點積計算:
Q i ? K j = ∑ d = 1 d k Q i d K j d Q_i \cdot K_j = \sum_{d=1}^{d_k} Q_{id} K_{jd} Qi??Kj?=d=1∑dk??Qid?Kjd?
- 如果 Q Q Q 和 K K K 的方向相同(即兩個向量相似),點積值較大。
- 如果 Q Q Q 和 K K K 的方向正交(即無關),點積值接近 0。
- 如果 Q Q Q 和 K K K 的方向相反,點積值較小(可能為負)。
這與注意力機制的需求一致:希望讓 Query 關注與自己最相關的 Key。
(2)點積 vs. 余弦相似度
余弦相似度計算如下:
cos ? ( θ ) = Q ? K ∥ Q ∥ ∥ K ∥ \cos(\theta) = \frac{Q \cdot K}{\|Q\| \|K\|} cos(θ)=∥Q∥∥K∥Q?K?
雖然余弦相似度也能衡量向量的相似性,但它需要額外的歸一化操作(計算向量模長),這會增加計算成本。而點積本質上是未歸一化的余弦相似度,因此可以直接用于注意力計算。
3. 結合 Softmax 歸一化
點積計算的結果:
S = Q K T S = QK^T S=QKT
通常會經過 Softmax 歸一化:
α = softmax ( Q K T d k ) \alpha = \text{softmax} \left(\frac{QK^T}{\sqrt{d_k}}\right) α=softmax(dk??QKT?)
- Softmax 作用:將點積結果轉換為概率分布,使得所有注意力權重之和為 1。
- 縮放因子 d k \sqrt{d_k} dk??:防止點積值過大導致梯度消失。
這種組合方式使得點積注意力(Scaled Dot-Product Attention)既高效又穩定。
4. 經驗驗證:點積注意力效果優于加性注意力
在早期的注意力機制(如 Bahdanau Attention)中,使用的是 加性注意力(Additive Attention):
score ( Q , K ) = V T tanh ? ( W q Q + W k K ) \text{score}(Q, K) = V^T \tanh(W_q Q + W_k K) score(Q,K)=VTtanh(Wq?Q+Wk?K)
但研究發現:
- 點積注意力(Dot-Product Attention)在大規模數據上效果更好。
- 點積計算可以更好地利用矩陣運算加速(GPU 友好)。
- 加性注意力需要額外的參數 W q , W k , V W_q, W_k, V Wq?,Wk?,V,而點積注意力不需要額外參數。
因此,在 Transformer 及其變體(如 BERT、GPT、ViT)中,點積注意力成為主流選擇。
- 點積計算高效,適合 GPU 并行計算(相比加性注意力更快)。
- 點積能夠有效衡量向量相似性(與余弦相似度類似,但計算更簡單)。
- 結合 Softmax 歸一化,使注意力機制更加穩定(避免梯度消失)。
- 實驗驗證:點積注意力在大規模數據上效果優于加性注意力。
因此,Transformer 采用點積注意力(Dot-Product Attention)作為核心計算方式,并結合 Softmax 歸一化和縮放因子 d k \sqrt{d_k} dk??進行優化。
4. 為什么要除以 D \sqrt{D} D? (Scale)?
在 點積注意力(Dot-Product Attention) 計算中,我們使用 Query(Q)和 Key(K) 的點積來衡量相似性:
S = Q K T S = QK^T S=QKT
然后通過 Softmax 歸一化計算注意力權重:
α = softmax ( S ) V \alpha = \text{softmax}(S)V α=softmax(S)V
但是,如果不進行縮放,點積值可能會變得 過大,導致 Softmax 輸出極端化,影響模型訓練。
因此,我們 除以 ( \sqrt{D} )(D 為特征維度) 來進行縮放,最終計算方式如下:
S = Q K T D S = \frac{QK^T}{\sqrt{D}} S=D?QKT?
1. 避免點積值過大,防止梯度消失
在高維空間(例如 D = 512 D=512 D=512, Q Q Q 和 K K K 的元素通常服從 均值為 0,方差為 1 的正態分布(假設初始化時)。
那么,點積 S S S 的期望值和方差計算如下:
-
點積的期望值:
E [ Q K T ] = 0 \mathbb{E}[QK^T] = 0 E[QKT]=0 $$
因為 ( Q ) 和 ( K ) 服從均值為 0 的分布,所以它們的點積的均值仍然是 0。 -
點積的方差:
Var ( Q K T ) = D \text{Var}(QK^T) = D Var(QKT)=D
由于點積涉及 ( D ) 個獨立變量的乘積求和,方差會隨著 ( D ) 增大而增大。
當 D D D 很大時,例如 D = 512 或 1024 D = 512 或 1024 D=512或1024,點積值的方差也會變得很大,導致 Softmax 計算時的指數函數 e x e^x ex的值過大,使得:
- Softmax 輸出接近 one-hot(即某個值接近 1,其他值接近 0)。
- 反向傳播時梯度變得很小(梯度消失),影響模型訓練。
解決方案:
- 通過 除以 ( \sqrt{D} ),讓點積值的方差變為 1:
Var ( Q K T D ) = 1 \text{Var} \left(\frac{QK^T}{\sqrt{D}}\right) = 1 Var(D?QKT?)=1 - 這樣,Softmax 輸出更加平滑,梯度不會消失,有助于穩定訓練。
2. 直觀理解
可以用一個形象的例子來理解:
- 未縮放的點積(大值輸入 Softmax):就像一個考試成績滿分 1000 分的系統,99 分和 100 分的差距微乎其微,但 Softmax 會放大這個差距,使得 100 分的注意力遠大于 99 分。
- 縮放后的點積(小值輸入 Softmax):就像一個滿分 100 分的考試,99 分和 100 分的差距不會被過度放大,注意力分布更加合理。
? 防止 Softmax 過度偏向單個 token(避免 one-hot 現象)。
? 保持梯度穩定,防止梯度消失,使模型更容易訓練。
? 在高維空間(如 D=512)時,避免點積值過大,保證數值穩定性。
因此,Transformer 采用 Scaled Dot-Product Attention,即在計算 Q K T QK^T QKT 后除以 D \sqrt{D} D?,確保模型訓練更穩定,注意力分布更合理。
參考文獻
Datawhale大模型組隊學習地址