2017 - Transformer 注意力機制#
案件摘要#
2017 年 6 月,Google 的 Vaswani 等人發表 《Attention Is All You Need》:完全拋棄循環與卷積,只用自注意力(self-attention)處理序列。核心公式一行寫盡:
查詢 \(Q\) 對所有鍵 \(K\) 做相似度比對,加權聚合值 \(V\) ——任意兩個位置直連一步,長程依賴不再是 \(O(T)\) 步的馬拉松,而是一矩陣乘法。LSTM 被謀殺——兇手不是更強的記憶,而是直連任意距離 + 完美並行。這是本書大模型時代的總開關:BERT、GPT、ViT、CLIP 全是這一行公式的子孫。
前因 -- 為什麼會有這個案子#
- 2014-Seq2Seq與注意力.md 已埋下引線:Bahdanau、Luong 的注意力是 RNN 編碼器—解碼器之上的「補丁」——翻譯時讓解碼器回頭看編碼器的所有隱狀態。但注意力只是配角,主角仍是 LSTM。
- LSTM 的兩大死穴(見 1997-LSTM長短期記憶.md):
- 序列性:時間步 \(t\) 必須等 \(t-1\) 算完,GPU 的萬級核心閒置——訓練無法並行。
- 長程路徑:資訊要穿越 \(O(T)\) 個閘門,距離越遠越稀釋。
- 2016-WaveNet語音合成.md 證明了「捨棄 RNN、用卷積並行生成」可行,但卷積感受野仍要逐層擴張。
- Transformer 論文原文的引用列表中,PixelRNN、WaveNet、ByteNet、ConvS2S 全部在列——自注意力是所有前驅線索的合流點。
- 動機:Google 翻譯團隊要「訓練快三倍、翻譯好一截」;英國團隊(Szegedy 系)則主張卷積——最後 Vaswani、Shazeer、Parmar 的「全注意力」極端方案勝出。
線索與推理 -- 數學式、程式、理論#
第一條線索:Q、K、V——把記憶檢索變成矩陣運算#
自注意力把每個 token 向量 \(x_i\) 線性投影成三個角色:
然後整個序列的「每對位置之間的注意力」一次算完:
| 物件 | 角色 | 類比 |
|---|---|---|
| \(Q\) | 我想找什麼 | 查詢關鍵字 |
| \(K\) | 我能被搜到什麼 | 索引標籤 |
| \(V\) | 找到後取出的內容 | 資料本體 |
\(\sqrt{d_k}\) 縮放因子是穩定性細節: \(d_k\) 大時 \(QK^\top\) 方差過大,softmax 會飽和、梯度消失——除以 \(\sqrt{d_k}\) 恰好把方差歸一。
第二條線索:多頭注意力——八雙眼睛同時看#
單頭注意力只有一種「看的方式」。Transformer 把 \(d_{\text{model}}=512\) 切成 \(h=8\) 個 64 維子空間,各自做注意力再拼接:
各頭自動分工:有的頭盯語法依賴(主謂一致),有的頭做共指消解(代詞→先行詞),有的頭只看相鄰位置。這是分佈式表徵(見 1985-Boltzmann機器.md)在注意力機制上的重演:一種關係由多頭共同編碼,一個頭參與多種關係。
第三條線索:位置編碼——沒有循環,序從何來?#
自注意力對輸入是置換等變的:打亂 token 順序,輸出只是相應打亂。序列資訊必須從外面注入。Transformer 用正弦位置編碼:
每個維度是一個不同波長的振盪器——像二進位計數器的連續版本,相對位置 \(PE_{pos+k}\) 可以表示為 \(PE_{pos}\) 的線性變換。這是模型「天生知道序」的唯一線索;後續案件(BERT 用學習式位置、RoPE 旋轉編碼)都在這條線索上換工具。
第四條線索: \(O(T^2)\) 的代價——案件值與代價#
| 模型 | 每層計算量 | 序列操作 | 長程路徑 |
|---|---|---|---|
| RNN / LSTM | \(O(T \cdot d^2)\) | \(O(T)\) 步 | \(O(T)\) |
| 卷積(ByteNet) | \(O(k \cdot T \cdot d^2)\) | \(O(1)\) | \(O(T/k)\) |
| Self-Attention | \(O(T^2 \cdot d)\) | \(O(1)\) | \(O(1)\) |
自注意力用 \(O(T^2)\) 換來 \(O(1)\) 長程路徑與完美並行。 \(T\) 小時血賺; \(T\) 大時 \(T^2\) 是未來所有「長文本」案件的案值——FlashAttention、稀疏注意力、線性注意力都是後續回聲。
第五條線索:案發現場的成績——WMT14 的宣判#
WMT14 機器翻譯的 BLEU 成績(越高越好):
| 系統 | 英→德 BLEU | 英→法 BLEU | 訓練成本 |
|---|---|---|---|
| GNMT(Google,LSTM) | 24.6 | 39.9 | 96 顆 GPU × 6 天 |
| ConvS2S(Facebook,卷積) | 25.2 | 40.5 | 512 顆 GPU × 1 天 |
| Transformer(base / big) | 27.3 / 28.4 | 41.0 / 41.8 | 8 顆 GPU × 3.5 天 |
Transformer big 以十分之一的算力超越兩大陣營——BLEU 提升 3.8 分在機器翻譯史上是斷崖級跳升。消融實驗進一步宣判:拿掉多頭(改單頭)掉 0.9 BLEU、拿掉 \(\sqrt{d_k}\) 縮放導致訓練不穩、拿掉位置編碼 BLEU 崩至 8.3(模型完全不知道序)。線索至此全部合攏:QKV 注意力 + 多頭 + 位置編碼,缺一不可——2017 年 6 月的法庭判決書就此簽署。
結案 -- 後果與影響#
- WMT14 英德翻譯 BLEU 28.4、英法 41.8,大幅超越所有 RNN/卷積系統,且訓練只需 3.5 天(8 顆 GPU)。
- 架構一分為二:
- 只留編碼器 → 2018-BERT與GPT預訓練典範.md 的 BERT(雙向理解)。
- 只留解碼器 → GPT 系列的骨架(自回歸生成),見 2019-GPT-2危險模型.md、2020-GPT-3規模湧現.md。
- 注意力機制從翻譯補丁升格為通用計算原語:影像(2021-ViT視覺Transformer.md)、圖文對齊(2021-CLIP多模態對齊.md)、蛋白質(2021-AlphaFold2.md 的 Evoformer)全部注意力化。
- 序列建模的權杖正式從 LSTM 轉交 Transformer——神經網路通史的分水嶺(見「科學與歷史/神經網路/2017-瓦茲瓦尼Transformer.md」)。
- \(O(T^2)\) 伏筆:上下文長度之戰(長上下文、檢索增強、FlashAttention)成為此後十年的持續案件。
- 規模化伏筆:架構已備、並行已通,只差資料與參數——「規模即智慧」的下一案即將開庭。
關鍵人物與文獻#
- Ashish Vaswani:第一作者,主導「全注意力」架構。
- Noam Shazeer:多頭注意力與架構調校的關鍵人物,後續多個 Transformer 變體之父。
- Jakob Uszkoreit:提出捨棄 RNN 的核心主張。
- Vaswani et al., Attention Is All You Need, NIPS 2017, arXiv:1706.03762。
- Bahdanau et al., Neural Machine Translation by Jointly Learning to Align and Translate, 2014(前驅注意力)。
- Sutskever et al., Sequence to Sequence Learning with Neural Networks, 2014。
- He et al., Deep Residual Learning, 2015(殘差連接的借用)。
- 相關案件:2014-Seq2Seq與注意力.md、2016-WaveNet語音合成.md、2018-BERT與GPT預訓練典範.md、2021-ViT視覺Transformer.md、2021-AlphaFold2.md
補充 -- 程式實作(python + numpy + pytorch)#
本案三件套(縮放點積+多頭+正弦位置編碼)的最小可執行版本,見 _code/2017-Transformer.py(已實測可跑,CPU 約 1 分鐘):
# 2017 - Transformer: Attention(Q,K,V)=softmax(QKᵀ/√d_k)V
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
def sinusoid_pos(T, d):
pe = torch.zeros(T, d)
pos = torch.arange(T).float().unsqueeze(1)
div = torch.exp(torch.arange(0, d, 2).float() * -(math.log(10000.0) / d))
pe[:, 0::2] = torch.sin(pos * div)
pe[:, 1::2] = torch.cos(pos * div)
return pe
class MultiHeadSelfAttn(nn.Module):
def __init__(self, d=32, h=4):
super().__init__()
self.h, self.dk = h, d // h
self.wq = nn.Linear(d, d, bias=False)
self.wk = nn.Linear(d, d, bias=False)
self.wv = nn.Linear(d, d, bias=False)
self.wo = nn.Linear(d, d, bias=False)
def forward(self, x, mask=None):
B, T, D = x.shape
Q = self.wq(x).view(B, T, self.h, self.dk).transpose(1, 2)
K = self.wk(x).view(B, T, self.h, self.dk).transpose(1, 2)
V = self.wv(x).view(B, T, self.h, self.dk).transpose(1, 2)
s = Q @ K.transpose(-2, -1) / math.sqrt(self.dk) # √d_k 防飽和
if mask is not None:
s = s.masked_fill(mask, float("-inf"))
a = F.softmax(s, dim=-1) # 任意兩位置一步直連
return (self.wo((a @ V).transpose(1, 2).reshape(B, T, D)), a)
def main():
torch.manual_seed(0)
B, T, D = 2, 8, 32
x = torch.randn(B, T, D) + sinusoid_pos(T, D)
mha = MultiHeadSelfAttn(D, 4)
out, a = mha(x)
print(f"輸入 {tuple(x.shape)} -> 輸出 {tuple(out.shape)} "
f"(置換等變+位置編碼=序列模型; 權重形狀 {tuple(a.shape)} [B,頭,T,T])")
print("第0頭第0行注意力:", [round(float(v), 2) for v in a[0, 0, 0]])
# toy 複述: 首符號經 7 步雜訊後複述 -- 注意力一步直連, 路徑 O(1)
N = 600
X = torch.randint(2, 6, (N, T))
X[:, 0] = torch.randint(0, 2, (N,))
y = X[:, 0].long()
E = nn.Embedding(6, D)
clf = nn.Linear(D, 2)
opt = torch.optim.Adam(list(mha.parameters()) + list(E.parameters())
+ list(clf.parameters()), lr=5e-3)
for ep in range(60):
opt.zero_grad()
o, _ = mha(E(X) + sinusoid_pos(T, D))
loss = F.cross_entropy(clf(o[:, -1]), y) # 只讀末位: 須從首位取資訊
loss.backward()
opt.step()
with torch.no_grad():
o, a2 = mha(E(X) + sinusoid_pos(T, D))
acc = (clf(o[:, -1]).argmax(1) == y).float().mean().item()
focus = a2[:, :, -1, 0].mean().item()
print(f"距離7步的複述準確率={acc:.2f} (LSTM 需穿越7個閘門, 注意力一步直連)")
print(f"末位對首位平均注意力={focus:.2f} (模型學會回頭看)")
if __name__ == "__main__":
main()
執行結果(python3 _code/2017-Transformer.py,torch 2.12.0):
輸入 (2, 8, 32) -> 輸出 (2, 8, 32) (置換等變+位置編碼=序列模型; 注意力權重形狀 (2, 4, 8, 8) [B,頭,T,T])
第0頭第0行注意力 (第0個token看誰): [0.12, 0.13, 0.14, 0.12, 0.08, 0.19, 0.14, 0.08]
距離7步的複述準確率=1.00 (LSTM 需穿越7個閘門, 注意力一步直連)
末位token對首位token平均注意力=0.74 (模型學會回頭看 -- Bahdanau 的泛化)
程式解說:Q @ K.T / √d_k 即本文第一條線索的一行公式——除以 √d_k 把方差歸一,否則 softmax 飽和、梯度消失( Alder 級的細節,致命級的後果)。sinusoid_pos 是第三條線索:自注意力置換等變、本身不知序,序全靠這組不同波長的振盪器注入。toy 複述是 LSTM 兩大死穴的處刑現場:距離 7 步,LSTM 要穿越 7 個閘門(見 1997 章),注意力一步直連、準確率 1.00,且末位對首位的注意力高達 0.74——模型自己學會了「回頭看」,Bahdanau 注意力從 RNN 的補丁升格為通用計算原語。代價也在形狀裡:[B,頭,T,T] 的 T²,即第四條線索的案值。