跳到主要内容

注意力的核心 — 打分、归一、加权混合,与 QKV 三个矩阵

这一章讲透一个机制: 注意力——大语言模型的心脏零件。 原书第 3 章是全书最难也最长的一章,我们把它拆成两章: 本章讲「注意力本身怎么算」(从无到有,加上可学习的矩阵), 下一章讲「怎么把它改造成能用于生成」(遮未来、防背题、多开几路)。 读完本章,你能用纸笔把一句六个词的句子的注意力算完。

1. 这一章讲什么

第 01 章说过,Transformer 的关键零件是注意力:让模型处理一个词时有选择地「回头看」前文的词。 这一章回答三个问题:为什么需要它(老办法卡在哪)、它具体怎么算(三步,带真实数字)、 以及 GPT 在用的版本多了哪两样东西(三个学出来的矩阵、一次除以根号 d 的缩放)。

2. 顶层全景

输入:六个词,每个词是 3 个数(第 02 章的嵌入)
"Your journey starts with one step"

对每个词(以 journey 为例)做三件事:
① 打分:journey 挨个和六个词算点积(相似度)
→ [0.95, 1.50, 1.48, 0.84, 0.71, 1.09]
② 归一:softmax 变成和为 1 的权重
→ [0.14, 0.24, 0.23, 0.12, 0.11, 0.16]
③ 混合:按权重把六个词的向量加在一起
→ journey 的新向量 = [0.4419, 0.6515, 0.5683]
(里面掺进了全句的信息,这叫「上下文向量」)

然后升级:每个词先进三个「学出来的投影」(查询 Q / 键 K / 值 V),
打分用 Q 和 K 算,混合用 V 混合——这就是 GPT 在用的版本。

图说:全程只有点积、softmax、加权求和三种运算,没有任何「智能」;
智能来自训练把那三个矩阵调对(第 06 章)。

3. 核心原理

3.1 先有病灶,后有药方:老办法卡在哪

注意力不是凭空发明的。它要解决的病,长在它的上一代——循环神经网络(行话叫 RNN)——身上。 RNN 是一种按顺序读文字的神经网络:读第一个词,更新一下内部状态;读第二个,再更新…… 内部状态像一张不断改写的便签,读完全句,便签上写的就是「全句的压缩摘要」1

拿翻译当例子(注意力当年就是为翻译发明的)。德语 "Kannst du mir helfen diesen Satz zu uebersetzen" 逐词对应英文是 "Can you me help this sentence to translate"——语法(词该怎么排队、该怎么变形的那些规矩)全错; 正确的译法 "Can you help me to translate this sentence" 需要调序, 也就是说,生成译文里的每个词时,得能回头看原文里隔了很远的某个词2

RNN 的做法是:编码器把整句压成一张便签(一个向量),解码器拿着这张便签写译文。 病灶就在这:整句话的全部信息必须挤进一个固定长度的向量,句子一长就丢上下文—— 生成第 20 个词时,第 3 个词的信息早被覆写了3

2014 年的 Bahdanau 注意力开了药方:解码器每写一词,可以回头直接访问编码器的所有中间状态, 而不是只靠最后那张便签4。三年后(2017),Transformer 论文把这个想法推到底: 既然注意力这么好用,干脆把 RNN 整个扔掉,只留注意力——这就是自注意力: 「自」的意思是,打分的双方在同一段话内部,不是两段话之间5

3.2 主走查:三步算完一个词的注意力

现在把原书第 3.3 节的完整演算走一遍。输入是六个词,每个词是 3 个数(为排版选的小尺寸; 道理与 768 维完全一样)。这些数是书里印的,可以直接复现6:

Your [0.43, 0.15, 0.89] with [0.22, 0.58, 0.33]
journey [0.55, 0.87, 0.66] one [0.77, 0.25, 0.10]
starts [0.57, 0.85, 0.64] step [0.05, 0.80, 0.55]

任务:给 journey(第 2 个词)算一个新表示,让它带上全句的上下文。

第 ① 步:打分。 拿 journey 当「发起方」,让它和六个词(包括自己)各算一次点积—— 两个向量逐位相乘再求和,得到一个数;这里它充当相似度:两个向量越同向,点积越大7。 算出来:

journey·Your=0.9544 journey·journey=1.4950 journey·starts=1.4754
journey·with=0.8434 journey·one=0.7070 journey·step=1.0865

第 ② 步:归一。 六个分数加起来不为 1,不好用。最朴素的办法是直接除以总和; 实际用的是 softmax——对每个分数取 e 的幂再归一,效果是把差距拉开(大的更大)、 保证全为正、还便于训练时把调整信号传回去8。算完得到注意力权重:

Your 0.1385 journey 0.2379 starts 0.2333 with 0.1240 one 0.1082 step 0.1581
(合计 = 1.0000)

第 ③ 步:混合。 按这组权重,把六个词的向量加权求和: 0.1385×Your的向量 + 0.2379×journey的向量 + …,结果是 [0.4419, 0.6515, 0.5683]9

这个结果就是 上下文向量(context vector):journey 的新表示,里面按「相关程度」 掺进了全句每个词的信息10。一句话总结注意力:打分、归一、加权混合,完了。

六个词都这么做一遍,就是六行计算。代码上一个双重循环就能写完, 但实际实现用两次矩阵乘法(inputs @ inputs.T 一次算完全部分数, attn_weights @ inputs 一次算完全部混合)——矩阵乘法在这里不为别的,就为快11。 「矩阵」就是排成矩形的一堆数;把两次循环折成一次矩阵乘,结果一模一样,书里逐元素核对过12

3.3 两个细节值得记住

为什么用点积当相似度? 不只是「数学上方便」:点积量的是两个向量的对齐程度, 方向越一致的向量点积越大。在注意力里,它就是「这两个词有多相关」的度量7

为什么用 softmax 而不是直接除? 书里给了三条理由:softmax 对极端值更稳、 保证权重为正(可以当相对重要度读)、训练时把调参信号往回传的性质更好。 另外书里提醒:手写的朴素 softmax 在输入很大时会数值溢出,实战中要用 PyTorch 自带的优化实现8

3.4 GPT 版:三个学出来的矩阵,与一次缩放

上面的简化版有一个硬伤:打分直接用词向量(每个词的那串嵌入数值)本身,全程没有一个可训练的参数—— 这套打分规则是写死的,学不到任何东西。GPT 在用的版本(正式名字叫 scaled dot-product attention,缩放点积注意力)补上了学习能力13

做法是插进三个权重矩阵:W_query、W_key、W_value。 每个词的向量先分别乘上这三个矩阵,投影成三个新角色:

  • 查询(query,查询向量):这个词「想找什么」——发起方;
  • 键(key,键向量):这个词「有什么可被查到的」——被打分方;
  • 值(value,值向量):这个词「真的贡献什么内容」——被混合方。

这三个名字借自信息检索——从一大堆资料里按目录找到目标、再取出来的那套做法。

query 像数据库的查询词,key 像索引(书后那张「词 → 页码」的对照表,查它不用翻全书),value 是取出来的内容14。 打分用 Q·K(查询对键求点积),混合用 V(按权重混合值向量)。 三个矩阵的元素就是参数,训练时会被调整——「注意力该看什么」是学出来的,不是人定的。

再走一遍主走查(还是那句六个词,维度从 3 投影到 2,书里的真实数字): journey 乘 W_query 得到查询向量 [0.4306, 1.4551]; 它和自己的键向量的点积是 1.8524; 对六个键的分数是 [1.2705, 1.8524, 1.8111, 1.0795, 0.5577, 1.5440]15

然后是那个名字里的「缩放」:分数要除以 √d_k(键向量维度的平方根)再进 softmax。 为什么?维数一大(GPT 里常超过 1,000),点积的值会很大,softmax 被顶进饱和区, 变得像阶跃函数——非黑即白——于是传回给参数的梯度(gradient, 每个参数「该往哪个方向挪、挪多少」的调整信号)趋近于零,训练就停滞了。 除以一个根号维数,把分数压回合理区间16。缩放后权重为 [0.1500, 0.2264, 0.2199, 0.1311, 0.0906, 0.1820], 和值向量加权混合,得到 journey 的最终上下文向量 [0.3061, 0.8210]17

这里有一个术语陷阱书里专门设框澄清:「weight」在两个地方出现,意思不同—— **权重参数(weight parameters)**是网络里学出来的系数(W_query 里的数),训练完就固定; **注意力权重(attention weights)**是每次计算时临时算出的相对重要性,随输入而变18

代码上,书里先把这三步写成一步步的循环,再收成 SelfAttention_v1 类, 再把三个手写矩阵换成 PyTorch 的 nn.Linear 层——换的原因很实际: nn.Linear 自带更讲究的初始化方案,训练更稳19

4. 作者的判断与证据

有证据的: 本章所有数字——[0.4419, 0.6515, 0.5683]、1.8524、[0.3061, 0.8210]—— 都是书里印出的真实程序输出,固定了随机种子,可逐位复现61517。 RNN 的瓶颈与 Bahdanau 2014 年的解法,是教科书级史实34

作者的判断:

  • 教学法上先讲无权重简化版再讲 QKV 版——「先抓住根本,再加参数」,这是作者的刻意安排20
  • 「为什么除以根号 d」给的是机理层面的解释(防止 softmax 饱和、梯度消失),这是 2017 年原论文的论证, 书里的转述忠实于原文16

判断(我们的,不是书里的): 这一章的主走查值得亲手在纸上演算一遍。 注意力被很多材料讲得玄妙,但拆到数之后就三件事:点积、softmax、加权求和。 「大模型会注意力」的准确含义是:它学会了往 W_query、W_key、W_value 里填什么数。 如果错,会错在: 如果读者以为「理解了三个矩阵就理解了模型为什么懂语言」——不对, 这三个矩阵学到的具体内容至今无法解读,本书也没有假装能解读。

5. 边界与局限

  • 本章的注意力是「全家可见」版:每个词能看到所有词,包括后面的。 用来生成文字时必须遮住未来——那是下一章的因果掩码。
  • 点积相似度是「裸相似度」,没有考虑两个词在句中的角色;Q/K/V 投影补上了学习能力, 但为什么学到的投影恰好有效,书里只有「训练调出来的」这一层回答。
  • 效率不在本章视野内:本章实现是教科书写法,矩阵全部物化。真实系统里注意力的瓶颈是 显存(显卡上存数据的内存)读写而不是计算量——N×N 的注意力矩阵要三次写出再读回显存。 补充(不在书里,依据我们的 frontier 书架):FlashAttention 的全部设计都从这条事实出发, 我们拆过。依据: shelf=ai-frontier-reference/flash-attention#01-io-bottleneck.md 事实=标准注意力慢在把 N×N 矩阵反复搬运,不慢在 FLOPs。

6. 可带走的

  1. 注意力要解决的病:老架构把整句压成一个固定向量,长句必丢信息;药方是「每个词都可以直接回看所有词」。
  2. 注意力三步:点积打分 → softmax 归一 → 按权重把值向量混进来。任何更玄的说法都是这三件的包装。
  3. 点积在这里当相似度用;softmax 比「直接除」稳,还保证权重为正、和为 1。
  4. 上下文向量 = 一个词掺完上下文之后的新表示;注意力的产物就是它。
  5. Q/K/V 是三个学出来的投影:查询管「找什么」,键管「被找到什么」,值管「贡献什么」。
  6. 除以根号 d 是为了防 softmax 饱和、保住梯度;这就是「scaled dot-product attention」名字的由来。
  7. 权重参数(训练完固定)和注意力权重(随输入临时算)是两种东西,别被同一个词骗了。
  8. 手写循环和矩阵乘法结果相同,矩阵乘只为快——但 PyTorch 的 nn.Linear 还多送一套好初始化。

7. 原文地图

主题原书章原文位置
RNN 瓶颈与翻译例子3 Coding attention mechanismstext/11-ch03-3-coding-attention-mechanisms.txt:76(搜「problem with modeling long sequences」) · text/11-ch03-3-coding-attention-mechanisms.txt:146(搜「can’t directly access」)
Bahdanau 2014 与「扔掉 RNN」同上text/11-ch03-3-coding-attention-mechanisms.txt:160(搜「Bahdanau」) · text/11-ch03-3-coding-attention-mechanisms.txt:190(搜「three years later」)
简化注意力三步走查同上text/11-ch03-3-coding-attention-mechanisms.txt:301(搜「0.43, 0.15, 0.89」) · text/11-ch03-3-coding-attention-mechanisms.txt:345(搜「0.9544」) · text/11-ch03-3-coding-attention-mechanisms.txt:448(搜「0.4419, 0.6515, 0.5683」)
点积当相似度同上text/11-ch03-3-coding-attention-mechanisms.txt:363(搜「measure of similarity」)
softmax 的理由与数值稳定同上text/11-ch03-3-coding-attention-mechanisms.txt:399(搜「managing extreme values」)
Q/K/V 与缩放同上text/11-ch03-3-coding-attention-mechanisms.txt:650(搜「three trainable weight matrices」) · text/11-ch03-3-coding-attention-mechanisms.txt:815(搜「scaled-dot product attention」)
两种 weight 的术语澄清同上text/11-ch03-3-coding-attention-mechanisms.txt:712(搜「Weight parameters vs. attention weights」)
SelfAttention 类与 nn.Linear同上text/11-ch03-3-coding-attention-mechanisms.txt:893(搜「SelfAttention_v1」) · text/11-ch03-3-coding-attention-mechanisms.txt:987(搜「optimized weight initialization」)

Footnotes

  1. 出处:「3 Coding attention mechanisms」第 108 段(text/11-ch03-3-coding-attention-mechanisms.txt:108,搜「recurrent neural networks」)。 原文:「An RNN is a type of neural network where outputs from previous steps are fed as inputs to the current step」。

  2. 出处:「3 Coding attention mechanisms」第 85 段(text/11-ch03-3-coding-attention-mechanisms.txt:85,搜「Kannst」)。

  3. 出处:「3 Coding attention mechanisms」第 146 段(text/11-ch03-3-coding-attention-mechanisms.txt:146,搜「can’t directly access」)。 2

  4. 出处:「3 Coding attention mechanisms」第 160 段(text/11-ch03-3-coding-attention-mechanisms.txt:160,搜「Bahdanau attention mechanism」)。 2

  5. 出处:「3 Coding attention mechanisms」第 190 段(text/11-ch03-3-coding-attention-mechanisms.txt:190,搜「three years later」)与第 233 段(text/11-ch03-3-coding-attention-mechanisms.txt:233,搜「The “self” in self-attention」)。

  6. 出处:「3 Coding attention mechanisms」第 301 段(text/11-ch03-3-coding-attention-mechanisms.txt:301,搜「0.43, 0.15, 0.89」)。 六个三维向量是原书 listing 的输入,章节里全部中间结果都由它算出。 2

  7. 出处:「3 Coding attention mechanisms」第 363 段(text/11-ch03-3-coding-attention-mechanisms.txt:363,搜「measure of similarity」)。 原文:「the dot product is a measure of similarity because it quantifies how closely two vectors are aligned」。 2

  8. 出处:「3 Coding attention mechanisms」第 399 段(text/11-ch03-3-coding-attention-mechanisms.txt:399,搜「managing extreme values」)与第 420 段(text/11-ch03-3-coding-attention-mechanisms.txt:420,搜「numerical instability」)。 2

  9. 出处:「3 Coding attention mechanisms」第 448 段(text/11-ch03-3-coding-attention-mechanisms.txt:448,搜「0.4419, 0.6515, 0.5683」)。

  10. 出处:「3 Coding attention mechanisms」第 281 段(text/11-ch03-3-coding-attention-mechanisms.txt:281,搜「interpreted as an enriched embedding」)。

  11. 出处:「3 Coding attention mechanisms」第 528 段(text/11-ch03-3-coding-attention-mechanisms.txt:528,搜「for loops are generally slow」)。

  12. 出处:「3 Coding attention mechanisms」第 516 段(text/11-ch03-3-coding-attention-mechanisms.txt:516,搜「0.9995, 0.9544」)与第 587 段(text/11-ch03-3-coding-attention-mechanisms.txt:587,搜「comparing the second row with the」)。

  13. 出处:「3 Coding attention mechanisms」第 604 段(text/11-ch03-3-coding-attention-mechanisms.txt:604,搜「scaled dot-product attention」)。

  14. 出处:「3 Coding attention mechanisms」第 870 段(text/11-ch03-3-coding-attention-mechanisms.txt:870,搜「information retrieval and databases」)。

  15. 出处:「3 Coding attention mechanisms」第 710 段(text/11-ch03-3-coding-attention-mechanisms.txt:710,搜「0.4306, 1.4551」)、第 766 段(text/11-ch03-3-coding-attention-mechanisms.txt:766,搜「1.8524」)与第 778 段(text/11-ch03-3-coding-attention-mechanisms.txt:778,搜「1.2705, 1.8524」)。 2

  16. 出处:「3 Coding attention mechanisms」第 817 段(text/11-ch03-3-coding-attention-mechanisms.txt:817,搜「avoiding small gradients」)。 原文:「As dot products increase, the softmax function behaves more like a step function, resulting in gradients nearing zero.」 2

  17. 出处:「3 Coding attention mechanisms」第 813 段(text/11-ch03-3-coding-attention-mechanisms.txt:813,搜「0.1500, 0.2264」)与第 863 段(text/11-ch03-3-coding-attention-mechanisms.txt:863,搜「0.3061, 0.8210」)。 2

  18. 出处:「3 Coding attention mechanisms」第 712 段(text/11-ch03-3-coding-attention-mechanisms.txt:712,搜「Weight parameters vs. attention weights」)。

  19. 出处:「3 Coding attention mechanisms」第 987 段(text/11-ch03-3-coding-attention-mechanisms.txt:987,搜「optimized weight initialization scheme」)。

  20. 出处:「3 Coding attention mechanisms」第 244 段(text/11-ch03-3-coding-attention-mechanisms.txt:244,搜「simplified version of it」)。