跳到主要内容

「那只猫没过马路,因为它太累了」— 「它」这一步的分数是怎么算出来的

这一章讲三件事: 注意力这个机制到底在算什么(比名声简单得多); 它凭什么一次解决循环网络的两个死结;以及让它能干活的几块补丁。

它在全书链条里的位置: 这是全书最承重的一章。 第 07 章末尾留下的独木桥问题,在这里被拆掉; 下一章把它打包成标准积木,第 14 章以后每一章都站在这两章上面。

1. 那只猫没过马路,因为它太累了

先读一句话:那只猫没过马路,因为它太累了。

「它」指谁?你一眼就答得出:猫。一个会「累」的东西,不会是马路。 但你回头想想自己刚才是怎么答的——你其实做了一件很了不起的事: 读到「它」时,你的目光跳回了大半句话之前,把「猫」那份意思调了过来。

这个「跳回去」的能力,正是第 07 章那份边走边改写的笔记做不到的。 笔记从「那」一路改写到「它」,中间过了七八次手, 「猫」的那份意思早就衰减得所剩无几。书里用的例子是这句话的英文原版, 并点明:这类指代跨越几十甚至上百个词时,循环网络就极其脆弱1

这一章要讲的机制,就是让「它」不必等笔记传过来,而是直接回头问每一个词: 「你跟我有多大关系?」 关系大的多掺一点,关系小的少掺一点—— 掺完之后,「它」这一步就带着「猫」的意思了。

这个机制叫注意力。名字听着玄,做的事就是上面这句话。

2. 从字典查询说起

理解注意力最好的入口,是一本你从小就用的东西:字典。

一本字典的每一条有两半:标签(词条名)和内容(词条解释)。 查字典就是拿一个词去对标签,对上了,取走内容2

但普通字典只做精确匹配:你查的词必须和某个标签一字不差。 现实里你想要的常常是柔性的——查「机器学习」,字典里未必有这一条, 但有「人工智能」「神经网络」「深度学习」,这几条相关的都应该各取一部分3

柔性查询的做法:拿你查的词和每个标签比一下「像不像」, 越像的标签,它那条内容占的份额越大,最后按份额把所有内容混成一份综合答案。

注意力就是一次柔性的查字典。 接下来三节,把这句话拆成零件。

3. 三个角色:查询、键、值

柔性查询里有三个角色,名字直接沿用字典的那套4:

角色在字典里在那句话里
查询你正在查的那个词当前这个位置:「它」
每条的标签其他每个位置挂出的「我是谁」的牌子
每条的内容其他每个位置真正携带的那份信息

这张表看的是分工:查询负责提问,键负责被比较,值负责被取走。

注意一个设计:同一个位置要挂三块牌子,不是一块。 「猫」这个词,它挂出的键是「我是一只动物、名词、句首」, 它携带的值是它那份完整的意思。键负责「被匹配」,值负责「被取走」—— 匹配用的外貌和真正交付的内容,是分开的两份。

具体做法:每个位置的输入,被三个不同的矩阵各自乘一遍, 变出它的查询、键、值三份5同一份输入,三种身份。

4. 走查:打分、归一、加权求和

这一章的主走查:把「它」这一步完整算一遍。

读到「它」这个位置,三件事依次发生。

第一步:打分。 拿「它」的查询,去和前面每个位置的键算一个「像不像」的分数。 最常用的算法是点积(把两串数对应位置相乘再相加; 两串数指向越像,这个积越大)6下面这组分数是为演示编的,不是真实模型的输出:

「它」对前面各位置的原始打分:
那 0.4 只 0.3 猫 4.2 没 0.2 过 0.3
马路 1.1 因为 0.6 太 0.5 累 0.7 了 0.4

第二步:归一。 原始打分有大有小、没有总量约束, 把它们过一道 Softmax(把一串分数变成一组加起来正好等于 1 的权重; 分数之间的差距会被拉开,高者更高)7。过完变成:

位置权重
0.55
0.10
马路0.08
因为0.06
太 / 了各 0.05
那 / 只 / 没 / 过各 0.02–0.04

这张表看的是归一之后的样子:全部权重加起来正好是 1,而「猫」独占一半以上。

第三步:加权求和。 按这个权重,把每个位置的值各取一份,混成「它」的新内容: 「猫」那份取 0.55,「马路」那份只取 0.08,其余按份掺入8

三步走完,「它」这个位置的新表示里,大半是「猫」的意思—— 所以它后面接的词,会像在说猫,而不是在说马路。

查询(它) × 每个键 → 原始打分 → Softmax 归一 → 按权重取每个值 → 混成新表示

这张图看的是全部三步。注意力的全部计算就是这三步,没有第四步。

补充(不在书里,依据我们的 frontier 书架): 这三步在真实代码里就是三行—— 点积、把不许看的位置置成负无穷、Softmax,然后拿权重去乘值。 一个教学级实现的手写版本,核心就是这三行9

5. 为什么要除以维度的平方根

第 4 节第一步里藏着一个细节,原论文的公式名字里就带着它:缩放

点积有个脾气:参与计算的数串越长,积就越容易大。 几百维的向量做点积,动不动就冒出很大的数。 而 Softmax 的脾气是:输入的分数差距一大,它就把几乎全部权重压给最大的那个, 其余位置拿到的权重趋近于零——往回传的信号也跟着趋近于零, 这就是第 05 章那个老毛病的又一次出现10

解法朴素:打分先除以维度的平方根,把数值按回温和的范围,再归一。 这个完整的版本叫缩放点积注意力,是后面所有变体的出发点11

这个「除一下」看着像凑数,其实是第 08 章那条主线的又一次应验: 信号一旦数值失控,学习就停摆;稳住数值尺度,一切才转得动。

6. 柔性带来的好处:可微

第 2 节说过,注意力是柔性的:每个位置都分到一点权重,只是多少有别。 你可能会问:为什么不干脆「只取最像的那一个」?不是更直接吗?

因为那样就没法用梯度下降训了。 第 03 章说过,往回拧参数的前提是 每一步计算都是连续可变的——旋钮拧一丁点,结果变一丁点。 「只取一个」是硬切换:打分的第二名再怎么努力,结果也纹丝不动, 它收不到任何「该往哪改进」的信号12

柔性的加权求和则处处连续:任何一份打分变一点,混合结果就变一点, 每个位置的权重都能收到自己的改进信号。 这就是为什么要 Softmax:它既实现了「有选择地看」这个语义, 又保住了可微这条命脉。

Softmax 还顺手带来一个好性质:打分差距大时,权重自动集中(相当于精确匹配); 打分接近时,权重自动分散(相当于综合考虑)。 模型可以自己决定这次是聚焦还是兼顾13

7. 自注意力:查询、键、值都来自同一句

第 3 节说过三个角色,但没说它们从哪来。最早用在翻译里时, 查询来自正在写的那半边,键和值来自已经读完的原句——两个来源。

自注意力的想法很大胆:让查询、键、值都来自同一个序列14

同一句话里,每个位置都变出自己的三份; 每个位置再拿自己的查询,去和所有位置(包括自己)的键比一遍。 于是每个位置的新内容,都综合了整句话的信息, 而且不同位置综合的方式各不相同——「猫」那一步多掺了「马路」 (它过的就是马路),「它」那一步多掺了「猫」。

这一章的主走查用的就是自注意力。全章只有这一处变化: 三个角色同源,其余计算和前面一模一样。

8. 两个痛点,一次解决

现在对照第 07 章的两个死结,看自注意力各出了什么招15:

死结循环网络自注意力
长距离依赖弱「猫」到「它」要经过七八次改写,一路衰减任意两个位置一次打分直接相连,距离不再是障碍
无法并行第 100 个词必须等第 99 个算完每个位置的输出只依赖输入、不依赖其他位置的输出,全句一口气算完

这张表看的是同一个改变的两个侧面:把「一步步传」换成「两两直连」, 距离和串行两个问题一起消失。

第二行的意义怎么强调都不过分。显卡最擅长的就是同时做几万件互不等待的小计算—— 自注意力让「训练一个很大的模型」第一次和显卡的胃口对上了。 书里的话:这两个突破让训练大规模语言模型变得可行16

代价也存在,而且就在第 4 节那张打分表里:每个位置都要和所有位置比一遍, N 个位置就是 N × N 个格子。句长翻倍,这张表变四倍—— 这是第 11 章那一整章的账单,先记住它的形状。

9. 多头:同一句话,同时看几遍

一次自注意力,用的是同一套「像不像」的标准。 但一句话里的关系不止一种:语法(谁是主语谓语)关系、语义(哪些词意思相近)关系、 指代关系(「它」指谁)——一种标准看不全17

多头的做法:同时开好几套,每套有自己独立的三块牌子(查询、键、值), 各看各的,最后把几套的结果拼起来。 一个头可能盯着语法,一个头盯着语义,第三个头正好盯着指代—— 书里把这个比作不同专家从各自角度分析同一段文字,最后综合意见18

原始论文用了 8 个头,后来的大模型常用 12、16、32 个; 每个头的维度相应变小,总参数量(参数的总个数)不随头数线性涨19

10. 因果掩码:不许偷看后文

自注意力让每个位置看到所有位置——左右两边都看得见。 但有一类任务不许这样:生成

模型写下一个词时,只能依据已经写出来的部分; 如果训练时让它看到答案的后半截,它什么都学不到,只会照抄。 可是第 8 节刚说过,并行是命根子——要并行,就得把整句话一起送进去; 要不偷看,就得在打分表里动手脚。

解法是因果掩码:在打分之后、归一之前, 把所有「向后看」的格子的分数置成负无穷。 负无穷过了 Softmax 就变成 0——那些位置一份值也取不走20。 这样训练时仍然全句并行,但每个位置只看得到自己和前文。

打分表(N×N),「它」那一行:

没掩码: 那 只 猫 没 过 马路 因为 它 太 累 了
0.4 0.3 4.2 0.2 0.3 1.1 0.6 0.5 0.7 0.4 0.4

加掩码: 那 只 猫 没 过 马路 因为 它 |太 累 了|
0.4 0.3 4.2 0.2 0.3 1.1 0.6 0.5 −∞ −∞ −∞
└── 不许看 ──┘

这张图看的是掩码做的事:把打分表的右上半部分整片关掉。 第 4 节那个手写实现里,这一步就是三行中间的那一行9

这块掩码在第 10 章还要用一次——它是「同一块积木搭出三种模型」的关键差别之一。

11. 交叉注意力:让一段去查另一段

最后一种变体,其实就是回到第 7 节说的两个来源。

交叉注意力:查询来自一段(比如正在生成的译文), 键和值来自另一段(比如已经读完的原句)21。 计算本身一步不变——打分、归一、加权求和,还是那三步。

它用在「输入一段、输出另一段」的任务里:翻译时让译文这边 随时回头查原文那边;第 21 章你会再见它一次—— 画图时让图像的每个位置回头查文字那段。 名字有三个(注意力、自注意力、交叉注意力),机制只有一个。

12. 作者的判断与证据

有证据的部分。 缩放点积注意力的形式是 2017 年论文的内容; 「并行 + 长距离」两个优势的机制分析是数学事实; 2014 年注意力在翻译上的首次成功有论文记录; 多头、掩码是标准实现的组成部分。

要分开看的三处:

  1. 「一个头看语法、一个头看指代」。 这是可视化研究里观察到的倾向, 不是每个头都有干净的分工。真实模型里,很多头的行为至今难以命名。
  2. 走查里那组分数是我们编的。 真实模型的打分来自训练出来的矩阵, 但「打分→归一→加权求和」这三步的形状,和演示完全一样。
  3. 「没有注意力就没有大模型」。 书里的措辞是「很难以这样的规模和形态出现」。 近年也有别的架构在逼近同等效果(第 07 章末尾提过), 「唯一可行」这个说法今天已经不能照单全收22

判断(我们的,不是书里的):注意力真正的发明不是「看哪个词」,是第 4 节那张 N×N 的表。 在它之前,位置之间的关系是靠「传」的;在它之后,关系变成了一张可以一次性算出来、 摊开在显卡上的表。表能摊开算,是这一切的工程本质;表随长度平方涨,是这一切的代价。 后面第 11 章的全部省法,都是在和这张表讨价还价。 如果错,会错在: 这个读法把「可并行性」抬到了机制本身之上。 也有人认为注意力真正的优势是表达力——它能在数据里学出任意位置间的依赖, 而并行只是顺手的红利。两种读法目前都解释得通证据。

13. 边界与局限

  • 「像不像」的标准从哪来,这一章没讲。 那三个矩阵是训练出来的, 训练之前,打分毫无意义;这一章只讲了机制,没讲它怎么被学会。
  • 点积为什么能表达「关系」,只给了直觉。 两串数指向越像积越大, 但「语法关系」「指代关系」为什么能被编码进指向里,书里没展开。
  • 多头之后怎么拼,只给了一句话。 拼接后再过一次线性变换,细节在下一章。
  • N² 那张表在这一章只是提了个醒。 它具体怎么变成钱和显存,第 11 章算。
  • 注意力「理解」了什么,书里另有一节诚实地说:离真正理解还很远23

14. 可带走的

  1. 注意力 = 一次柔性的查字典:查询提问、键被比较、值被取走。
  2. 全部计算只有三步:打分、归一、加权求和。 「它」的分数你亲手能算。
  3. 同一个位置挂三块牌子:被匹配的外貌和真正交付的内容是分开的。
  4. 除以维度的平方根,是为了让信号不因为数值过大而停摆。
  5. 必须柔性,因为硬切换收不到改进信号。 Softmax 既给选择性,又保可微。
  6. 自注意力只是三个角色同源。 任意两位置一步直连,距离和串行一起消失。
  7. 并行的红利:它第一次让「训大模型」对上显卡的胃口。
  8. 代价是一张 N×N 的表:句长翻倍,表变四倍。 这是第 11 章的账单。
  9. 多头 = 同一句话同时用几套标准看。 因果掩码 = 把右上半张表整片关掉。
  10. 三个名字,一个机制。 交叉注意力只是查询和键值来自两个地方。

15. 原文地图

主题原书章原文位置
猫与「它」的例子第5章 注意力:引爆大模型的引擎text/06-ch05.txt:33(搜「didn’t cross the street」)
RNN 两个困境第5章 注意力:引爆大模型的引擎text/06-ch05.txt:37(搜「无法并行」)
字典查询比喻第5章 注意力:引爆大模型的引擎text/06-ch05.txt:58(搜「字典查询」)
柔性查询第5章 注意力:引爆大模型的引擎text/06-ch05.txt:65(搜「柔性查询」)
三个矩阵变出三种身份第5章 注意力:引爆大模型的引擎text/06-ch05.txt:145(搜「三个可学习的矩阵」)
点积与缩放第5章 注意力:引爆大模型的引擎text/06-ch05.txt:83(搜「最常用的是点积」) · :84(搜「为了防止数值过大」)
缩放点积与 N² 表第5章 注意力:引爆大模型的引擎text/06-ch05.txt:92(搜「缩放点积注意力」) · :95(搜「谁该看谁」)
柔性与可微第5章 注意力:引爆大模型的引擎text/06-ch05.txt:122(搜「可微分」)
翻译中的首次成功第5章 注意力:引爆大模型的引擎text/06-ch05.txt:128(搜「2014 年 Bahdanau」)
自注意力第5章 注意力:引爆大模型的引擎text/06-ch05.txt:144(搜「都来自同一个序列」)
两个痛点一次解决第5章 注意力:引爆大模型的引擎text/06-ch05.txt:159(搜「先看长距离依赖」) · :164(搜「一口气算完」)
多头第5章 注意力:引爆大模型的引擎text/06-ch05.txt:182(搜「多头自注意力」) · :185(搜「不同专家」) · :199(搜「用 8 个头」)
因果掩码第5章 注意力:引爆大模型的引擎text/06-ch05.txt:296(搜「因果掩码」)
交叉注意力第5章 注意力:引爆大模型的引擎text/06-ch05.txt:307(搜「交叉注意力」)
Softmax 的温度敏感第5章 注意力:引爆大模型的引擎text/06-ch05.txt:498(搜「温度敏感」)
离真正理解还很远第5章 注意力:引爆大模型的引擎text/06-ch05.txt:466(搜「还有很长的路要走」)

Footnotes

  1. 出处:「第5章 注意力:引爆大模型的引擎」第 33 段(text/06-ch05.txt:33,搜「didn’t cross the street」)。 原文的例子是英文:「The animal didn't cross the street because it was too tired」, 人类一眼看出 it 指 animal;本章的走查把它换成了中文句,打分逻辑不变。

  2. 出处:「第5章 注意力:引爆大模型的引擎」第 58 段(text/06-ch05.txt:58,搜「字典查询」)。

  3. 出处:「第5章 注意力:引爆大模型的引擎」第 65 段(text/06-ch05.txt:65,搜「柔性查询」)。

  4. 出处:「第5章 注意力:引爆大模型的引擎」第 60 段(text/06-ch05.txt:60,搜「键」)。 原文:字典的每一条包含键(词条标签)和值(词条内容),查询是正在找的词。

  5. 出处:「第5章 注意力:引爆大模型的引擎」第 145 段(text/06-ch05.txt:145,搜「三个可学习的矩阵」)。

  6. 出处:「第5章 注意力:引爆大模型的引擎」第 83 段(text/06-ch05.txt:83,搜「最常用的是点积」)。 原文:「两个向量方向越接近,它们的点积越大」。

  7. 出处:「第5章 注意力:引爆大模型的引擎」第 76 段(text/06-ch05.txt:76,搜「加起来为 1」)。 这张权重表是我们按 Softmax 的性子编的演示数,不是真实模型的输出。

  8. 出处:「第5章 注意力:引爆大模型的引擎」第 100 段(text/06-ch05.txt:100,搜「加权聚合」)。

  9. 补充(不在书里,依据我们的 frontier 书架):nanoGPT 的手写注意力实现, 点积一行、把掩码为零的位置置成负无穷一行、Softmax 一行,然后拿权重乘值。 依据: shelf=ai-frontier-reference/nanogpt@src:model.py:67 @3adf61e154c3fe3fca428ad6bc3818b27a3b8291 事实=第 67 到 69 行依次是点积、masked_fill 置负无穷、softmax,正是正文那三步。 2

  10. 出处:「第5章 注意力:引爆大模型的引擎」第 84 段(text/06-ch05.txt:84,搜「为了防止数值过大」)。 原文:「当向量维度 d 很大时点积会很大,导致 Softmax 梯度消失」。

  11. 出处:「第5章 注意力:引爆大模型的引擎」第 92 段(text/06-ch05.txt:92,搜「缩放点积注意力」)。

  12. 出处:「第5章 注意力:引爆大模型的引擎」第 122 段(text/06-ch05.txt:122,搜「可微分」)。 原文:「如果用『硬』查询(只选一个最相关的 key),操作就不可微了,没法用主流优化方法训」。

  13. 出处:「第5章 注意力:引爆大模型的引擎」第 498 段(text/06-ch05.txt:498,搜「温度敏感」)。

  14. 出处:「第5章 注意力:引爆大模型的引擎」第 144 段(text/06-ch05.txt:144,搜「都来自同一个序列」)。

  15. 出处:「第5章 注意力:引爆大模型的引擎」第 159 段(text/06-ch05.txt:159,搜「先看长距离依赖」) 与第 162 段(text/06-ch05.txt:162,搜「一步连接」)。

  16. 出处:「第5章 注意力:引爆大模型的引擎」第 165 段(text/06-ch05.txt:165,搜「训练大规模语言模型变得可行」)。

  17. 出处:「第5章 注意力:引爆大模型的引擎」第 180 段(text/06-ch05.txt:180,搜「只能从一个」)。

  18. 出处:「第5章 注意力:引爆大模型的引擎」第 185 段(text/06-ch05.txt:185,搜「不同专家」)。

  19. 出处:「第5章 注意力:引爆大模型的引擎」第 199 段(text/06-ch05.txt:199,搜「用 8 个头」)。

  20. 出处:「第5章 注意力:引爆大模型的引擎」第 296 段(text/06-ch05.txt:296,搜「因果掩码」)。 原文:「所有『向后看』位置的打分都会被置为负无穷 (经过 Softmax 后,对应的注意力权重就变成 0)」。

  21. 出处:「第5章 注意力:引爆大模型的引擎」第 307 段(text/06-ch05.txt:307,搜「交叉注意力」)。

  22. 出处:「第5章 注意力:引爆大模型的引擎」第 582 段(text/06-ch05.txt:582,搜「不是唯一可行」)。 原文:「它们证明了一点,『注意力』不是唯一可行的序列建模方式。架构的竞争远未尘埃落定」。

  23. 出处:「第5章 注意力:引爆大模型的引擎」第 466 段(text/06-ch05.txt:466,搜「还有很长的路要走」)。