NEWSIO+ 教育系列01 自己訓練一個 AI02 從照片還原 3D03 深度學習
主線 D · 模組 D2動手做 // 四個實驗台

Attention:
會查表的平均

「加權平均」為什麼能取代遞迴?Q、K、V 各是什麼?多個 head 分別在看什麼?

D1 的 RNN 一個字一個字讀,把讀過的東西塞進一個記憶向量。句子一長,那個向量就裝不下,而且必須等前一個字算完才能算下一個。這個模組換一個做法:每個字直接去「問」句子裡所有的字,誰跟自己有關就多聽誰一點,最後把大家的資訊加權平均。這個動作叫 attention。把它疊起來,再補上順序、非線性和遮罩,就是 Transformer。

SEC.01 / BOTTLENECK

整句話塞進一個向量,句子一長就裝不下

機器翻譯的第一個深度學習版本叫 seq2seq:一個 RNN(encoder)把法文句子從頭讀到尾,最後的記憶向量交給另一個 RNN(decoder),由它一個字一個字吐出英文。問題在中間那一個向量:不管句子 5 個字還是 40 個字,它的長度都一樣。講義把這叫做 information bottleneck。拉長下面的句子,看「還留得住多少」。

PROBE A / 固定長度的瓶子動手做 // 拉句長,打開 attention
句長12 個字
每個字平均分到的向量維度(d ÷ 句長)21.3
seq2seq 保留的資訊量(示意)0
加上 attention 後(示意)0
句子還短,一個向量裝得下。
這條曲線是示意,不是量測數據:我們假設 d = 256 的向量大約裝得下 20 個字,超過就開始互相擠掉。課程講義第一講的時間軸提到同樣的現象:2014 年用 LSTM 做的翻譯,句子超過 20 個字左右品質就急劇下滑。把 d 加倍試試:曲線只往後推一點,瓶頸還在。打開 attention 後,下面的圖從「一串接到一個盒子」變成「decoder 直接連到每一個字」,資訊不用再擠過同一個向量,曲線就不掉了。
瓶頸是長度固定向量的長度在訓練前就決定了,句子的長度卻每次都不同。長句子只能被壓得更扁。
遠的字要走很多步句首的字要經過每一個中間的時間步才到得了句尾,路上一直被覆寫,梯度也一路變小。
而且不能同時算第 t 步要等第 t − 1 步。GPU 擅長一次做很多件獨立的事,RNN 偏偏是一條排隊的隊伍。
細看 · 多層 RNN 翻譯、NMT 的成功、遞迴的兩個問題

多層 RNN 做翻譯:講義的例子是德文 “Die Proteste waren am Wochenende eskaliert” 翻成 “The protests escalated over the weekend”。encoder 一路讀、建立句意;decoder 每一步吃進上一個輸出的字,再生出下一個字。疊多層時,第 i 層每個時間步的 hidden state 就是第 i + 1 層的輸入(Sutskever et al. 2014;Luong et al. 2015)。

NMT 是 NLP 深度學習第一個大成功:2014 年第一篇 seq2seq 論文發表,2016 年 Google 翻譯從統計式機器翻譯(SMT)換成神經機器翻譯(NMT),到 2018 年大家都換了。講義的重點是:幾百個工程師花很多年做出來的 SMT,被一小群工程師幾個月訓練出來的 NMT 超越。

Bottleneck:講義用法文 “il a m’entarté”(他用派砸我)翻成 “he hit me with a pie”。encoder 最後那一個向量「必須裝下整句來源句的所有資訊」,這就是資訊瓶頸。

遞迴的問題一:線性互動距離。兩個相隔很遠的字要互動,需要 O(序列長度) 步。講義的例子 “The chef who … was”:chef 的資訊要穿過中間每一個字才碰得到 was。步數多,梯度問題就來了,長距離依賴很難學。而且「字的線性順序」被寫死在架構裡,可是句子的結構本來就不是一條線。

遞迴的問題二:無法平行。forward 和 backward 都有 O(序列長度) 個不能平行的運算:未來的 hidden state 必須等過去的算完。講義的圖上標著每個狀態「最少要等幾步才能算」:0、1、2、…、T。這限制了能用多大的資料集訓練。

那改用 word window(卷積)呢?每個 filter 只看一小段相鄰的字,可以平行。要看遠的字,就得疊很多層,讓上層的 filter 看下層的輸出。講義的評語:疊得很深才看得到長句子,很難處理非常遠的上下文。這是 self-attention 出場的理由。

SEC.02 / WEIGHTED AVERAGE

Attention 就是加權平均,也就是一張「軟」的查表

最陽春的做法是把每個字的向量直接平均(mean pooling),但這樣每個字的份量都一樣。查字典時,你拿一個 query 去比對每一個 key,找到完全相同的那一格,拿走它的 value。Attention 把「完全相同」放軟:query 和每個 key 都算一個相似度(點積),用 softmax 變成 0 到 1、加起來是 1 的權重,再把所有 value 照權重加起來。下面這句話有 8 個字,點一個字當 query。

PROBE B / 點一個字,看它在聽誰動手做 // 點字、調銳利度、切硬查表
query(被點的字)與它的向量它
權重最高的字貓
權重加總1.000
加權平均出來的輸出向量 [動物, 地點, 狀態, 文法]0
輸出向量最像哪個字(cosine)貓
每個字的 4 維向量是我們手工設的示意(四個維度分別代表「動物、物品或地點、動作或狀態、文法功能」)。這裡 query、key、value 都直接用字本身的向量,還沒有任何投影矩陣。點「它」:它和「貓」的點積最高,所以輸出向量大部分是貓的資訊,這就是 attention 幫「它」找到指涉對象的直覺。把銳利度拉到 4,權重幾乎全給一個字,接近硬查表;拉到 0.1,權重幾乎平均,退回 mean pooling。硬查表選的是 argmax:分數改一點點,結果要嘛完全不變、要嘛突然跳到另一個字,沒有梯度;softmax 版本每個權重都能微分,所以可以用 backprop 學。
分數 = 點積兩個向量方向越接近,點積越大。query 跟哪個 key 方向接近,就是「問題跟哪一格比較對得上」。
Softmax 把分數變成份量和 A1 一樣:先取指數,再除以總和。權重全是正的,加起來是 1。
輸出是 value 的混合拿不到某一格,而是拿到「各格依份量混在一起」的東西。權重是學出來的時候,這個動作非常強大。
細看 · seq2seq with attention 的方程式、為什麼解了瓶頸

核心想法:decoder 每一步都直接連到 encoder,挑來源句裡需要的那一段來看。講義一步一步畫:decoder 第一步(輸入 <START>)和 encoder 每個 hidden state 做點積得到 attention scores,取 softmax 得到 attention distribution(這一步大部分看在第一個字 “il”,對應 “he”),再用這個分佈把 encoder 的 hidden states 加權加總,得到 attention output,和 decoder 的 hidden state 接在一起,算出 y₁ = “he”。下一步重複,依序生出 hit、me、with、a、pie。

et = [st⊤h1, …, st⊤hN] αt = softmax(et) at = Σi αit hi → [at ; st] h 是 encoder 的 hidden states,st 是 decoder 第 t 步的 hidden state

有時候也會把上一步的 attention output 一起餵進 decoder 當輸入。

為什麼能平行、為什麼解了瓶頸:講義的說法是把每個字的表示當成 query,去一組 value 裡取資訊。不能平行的運算數量不隨序列長度增加;任兩個字的最大互動距離是 O(1),因為每一層所有字都直接看所有字。decoder 不必再從一個向量裡猜整句話,需要哪個字就直接看哪個字。

那還需要 RNN 嗎?抽象來看,attention 是「把一串 x 的資訊傳給網路的某個輸入 hₜ」的方法,而這正是 RNN 被拿來做的事。講義的問題是:如果 attention 只是更好的傳資訊方法,能不能把 RNN 整個拿掉?2017 年的答案是可以,這就是下一節的 self-attention。

SEC.03 / QUERY · KEY · VALUE

同一句話自己問自己:Q、K、V 與多個 head

Self-attention 的 query、key、value 全部來自同一個序列。每個字的向量 x 分別乘上三個學出來的矩陣:q = xWQ 是「我想找什麼」,k = xWK 是「我身上有什麼可以被找」,v = xWV 是「被選中時我交出什麼」。整句一起算只要三步矩陣運算:QKᵀ/√d、softmax、乘 V。下面的 6 個字只放了位置資訊(像 sin/cos 位置編碼),改矩陣的數字,或切換兩個 head。

PROBE C / 三個矩陣、三個步驟動手做 // 改 W、切 head、開 causal mask
目前的 headhead 1
平均注意距離 Σ aij·|i − j|(越小越看鄰近)0
第 4 個字的權重分佈(點左圖的列換字)0
被遮成 −∞ 的格數0
每一格都是真的算出來的:輸入 xi = [cos(36°·i), sin(36°·i)],是我們設的示意位置向量。head 1 的 WQ 把 query 轉了大約 37°,剛好對準前一個字的 key,所以 softmax 圖沿著對角線下方一條亮帶:它看鄰近。head 2 的數字很小,分數都接近 0,softmax 後每格差不多 1/6:它看全局,等於整句平均。同一句話、同一個 X,只因為投影矩陣不同,就看出完全不同的東西,這就是 multi-head 的意義。打開 causal mask,左圖右上三角變成 −∞,softmax 後是 0,第 1 個字只能看自己。關掉 √d 會讓分數變大、softmax 變得更尖。
Q 問、K 答、V 給同一個字在三個角色裡用不同的面貌出場,三個矩陣讓 x 的不同面向在不同角色被強調。
一個 head 只會一種看法對第 i 個字,一個 head 只在 xᵢᵀQᵀKxⱼ 高的地方看。想同時看文法上的主詞和隔壁的形容詞,就要多個 head。
多 head 不多花計算把 d 維切成 h 份,每份 d/h 維各做一次 attention,再接起來乘一個 Y。總計算量和單一大 head 差不多。
細看 · self-attention 的三個障礙、Transformer 的結構與訓練技巧

Self-attention 不能直接取代遞迴,講義列了三個障礙和解法:

  • 沒有順序:self-attention 是對「集合」的運算,把字的順序打亂,輸出只是跟著打亂。解法是給每個位置一個向量 pᵢ,在第一層加到輸入上(也可以接在後面,但大家多半用加的)。sinusoidal 位置編碼用不同週期的 sin、cos:好處是週期性暗示「絕對位置」沒那麼重要,也許能外推到更長的句子;缺點是不能學,而且外推其實不太成功。可學的絕對位置(每個 pᵢ 都是參數)比較有彈性,大部分系統用這個,但完全不能外推到訓練長度以外。也有相對位置(Shaw et al. 2018)和依語法結構的位置(Wang et al. 2019)。
  • 沒有非線性:attention 全部是加權平均,疊再多層也只是把 value 重新平均。解法是在每個輸出後面接同一個 feed-forward network(FFN)做後處理。
  • 會看到未來:翻譯和語言模型在預測時不能偷看後面的字。每一步只拿過去的字當 key 很沒效率;做法是把未來那些格的分數設成 −∞,softmax 後權重就是 0,整句仍然可以一次平行算完。

講義的結論:self-attention、位置表示、非線性、遮罩,四件事湊齊就是一個可用的 building block。但這還不是 Transformer。

Transformer encoder-decoder(Vaswani et al. 2017):輸入是 word embeddings 加上位置表示。encoder block = multi-head attention → residual + LayerNorm → feed-forward → residual + LayerNorm。decoder block 多了兩件事:第一層是 masked multi-head self-attention,中間多一層 multi-head cross-attention,query 來自 decoder、key 和 value 來自 encoder 的輸出。講義強調:唯一新的部分就是 encoder 到 decoder 的 attention。另外,transformer 輸入幾個字就輸出幾個字;decoder 也是,只是還沒產生的位置會被 mask 住。

output = softmax(XQ (XK)⊤) × XV  XQK⊤X⊤ ∈ ℝT×T 一次矩陣乘法得到所有字兩兩之間的分數

Multi-head:Qℓ、Kℓ、Vℓ ∈ ℝd×d/h,ℓ = 1…h,每個 head 獨立算 outputℓ,最後 output = Y[output1; …; outputh],Y ∈ ℝd×d。原始論文 “Attention is all you need” 用 d_model = 512、h = 8、dk = dv = 512/8 = 64。講義的圖示顯示不同 head 分別關注 global 和 local 的資訊,也有圖示不同 head 學到的語義(例如某個 head 讓 “it” 去看它指的名詞)。

訓練技巧(不增加模型能力,但讓訓練順利,兩種改進都很重要):

  • Residual connection:讓模型比較好訓練(B4 講過為什麼)。
  • LayerNorm:在每一層內把 hidden vector 正規化成平均 0、標準差 1,去掉沒有資訊的變動,訓練更快。
  • Scaled dot product:維度 d 變大時,點積的數值會跟著變大,softmax 的輸入太大會讓梯度變得很小。除以 √d 把它拉回來。Probe C 的「除以 √d」就是這件事。
  • 原始論文另外還用了 learning rate warmup(前幾千步讓 learning rate 從很小慢慢升上去)和 label smoothing(正確答案的目標機率不設成 1,留一點給其他字),都是為了讓訓練更穩。

矩陣計算:講義引用的圖解把流程寫成兩步:第一步,輸入的 embedding 乘上 WQ、WK、WV 得到每個字的 q、k、v;第二步,用 q 和每個 k 算相似度(attention score),softmax 後乘 v 加總。把所有字排成矩陣 X,整句的 self-attention 就是幾次矩陣乘法。

SEC.04 / COST

代價:n 個字要算 n² 對,但可以同時算

RNN 讀 n 個字要走 n 步,每一步都要等上一步。Self-attention 一層裡所有字兩兩都要比一次,總共 n² 對,但這 n² 件事彼此獨立,GPU 可以一起做。這是一筆交易:用更多的總計算量,換到不用排隊、而且任兩個字之間只隔一步。拉長序列,看這筆交易什麼時候開始變貴。

PROBE D / 排隊的步數 vs 配對的數量動手做 // 拉序列長度、換 GPU 的平行寬度
序列長度 n500
RNN:不能平行的步數(O(n))500
Attention:要算的配對數(O(n²),每層每個 head)250,000
Attention 照 GPU 平行寬度要跑幾輪1
相隔 k 個位置的兩個字要互動:RNN 要走 / attention 要走250 步 / 1 步
一個 head 的 n×n 注意力矩陣(float32)1 MB
兩個軸都是對數座標,所以 n 和 n² 都畫成直線,n² 的斜率是兩倍。黑線是 RNN 必須排隊的步數,橘線是 attention 的配對數,橘色虛線是「配對數 ÷ GPU 平行寬度」,也就是 attention 實際要排隊的輪數(至少 1 輪)。GPU 寬度是示意的數字,真實的硬體還要看記憶體和頻寬。n 很小時 attention 一輪就做完,RNN 卻要走 n 步;n 到幾千、上萬時,n² 的配對數和 n×n 矩陣的記憶體才變成真正的問題,這就是講義說的「二次方成本」。
步數和總工作量是兩回事RNN 總工作量少,但全部排成一列。attention 總工作量多,但可以攤開同時做。GPU 喜歡後者。
互動距離 O(1)任兩個字在每一層都直接相連,資訊和梯度不必穿過中間每一個字。長距離依賴變得好學。
代價是 n²句子長度加倍,配對數變四倍。長 context 成了新戰場,各種稀疏、局部的 attention 都在處理這件事。
細看 · 二次成本的改進、attention 與卷積、維度與非線性運算

Transformer 的缺點:講義列了兩個。一是 self-attention 的二次方計算量:算所有配對,計算量隨序列長度平方成長,遞迴模型只是線性成長。二是位置表示:簡單的絕對位置索引是我們能做到最好的嗎?相對位置(Shaw et al. 2018)、依語法結構的位置(Wang et al. 2019)都是嘗試。

改進二次成本:能不能不付 O(T²) 的全配對成本?例如 BigBird(Zaheer et al. 2021),把全配對換成幾種互動的組合:局部視窗、少數看全部的位置、隨機配對。

Self-attention vs 卷積(在視覺上):把每個像素當一個 token。卷積在小視窗裡認圖樣,很難整合不相鄰的像素,網路得疊很深才「看到全貌」。self-attention 也整合多個像素,而且相關的像素不相鄰時也有效,講義的例子是把大象的鼻子、尾巴、腳整合成一整隻大象。下一個模組 D3 就是把這套搬到影像上。

Transformer 的維度:d 也叫 embedding dimension 或 d_model,是每個 token 的 embedding 向量長度。原始論文 d_model = 512,8 個 head 每個 64 維;FFN 的中間層 d_ff 比 d_model 寬(原始論文是 2048),先放大再縮回來。

非線性運算:講義引用的 inference 全堆疊最佳化 survey(Full Stack Optimization of Transformer Inference)把 Softmax、LayerNorm、BatchNorm 放在一起比:Softmax 和 LayerNorm 依賴執行時的統計量,要對輸入掃好幾次(softmax 先掃一次算分母;LayerNorm 掃三次:平均、標準差、套用正規化);BatchNorm 用的是訓練時學好的統計量,掃一次就夠。所以在硬體上,矩陣乘法以外的這些運算也會成為瓶頸。

銜接下週:本週推出的 Q/K/V、multi-head、位置編碼是通用的序列建模工具。ViT 把影像切成 patch token 直接套用 self-attention;Transformer 少了卷積的歸納偏置,是 ViT 需要大量資料的關鍵原因;CNN 陣營也吸收了 Transformer 的訓練技巧回擊(ConvNeXt)。

SEC.05 / MISCONCEPTIONS

常見誤解

講義在本週最後整理的四句話。

attention 是 Transformer 發明的。attention 早於 Transformer(Bahdanau et al. 2014 用在 seq2seq,就是 SEC.02 的那張圖)。Transformer 的貢獻是拿掉 recurrence,只用 self-attention,不是發明 attention 本身。
Transformer 沒有任何歸納偏置,所以更強大。self-attention 本身對順序不敏感,必須額外加入位置編碼。加位置編碼本身就是一種注入歸納偏置的方式,不是「沒有偏置」。
RNN 已經被 Transformer 完全淘汰,不用學了。RNN 的閘門設計是理解 SSM、Mamba 的必要基礎;RNN 處理無限長串流輸入時不需要 KV cache,在某些場景仍有優勢。
LSTM 解決了梯度消失問題。LSTM 提供一條「加法式」的梯度捷徑(cell state),大幅緩解但沒有完全消除梯度消失,訓練極長序列仍然困難(D1)。