土法炼钢兴趣小组的算法知识备份

【Transformer 与注意力机制】11|「注意力」的直觉

文章导航

分类入口
transformer
标签入口
#attention#soft-alignment#softmax#intuition#transformer

目录

本系列前十篇打底了向量、矩阵、softmax、嵌入与 RNN 的局限,本篇正式进入第二部分「注意力机制原理」。

这一篇刻意不写一行严谨推导,只做一件事:把「注意力」这个词从人类的视觉行为和翻译任务里拆下来,让你在第一次看到 softmax(QK^T/√d) V 时,脑子里先有一个真切的画面,再有一组数学符号。

读完这一篇你会带走什么:

读完之后,下一篇我们会回到 2014 年的 Bahdanau,看他怎么把这个直觉写成第一份具体的实现。


一、先承认一件事:人类的注意力不是个隐喻

工程师拿到一个名词,第一反应往往是把它当成隐喻:「哦,注意力就是模型学会看哪里嘛,好懂。」

这种说法不算错,但太轻。

轻到让你在看 softmax(QK^T/√d_k) V 时,仍然无法回答「为什么是这三个矩阵」「为什么是 softmax 而不是 argmax」「为什么要除一个 √d_k」。本系列把第二部分的第一篇花在直觉上,就是想避免这种轻。

1.1 William James 的那句被反复引用的话

注意力(attention)作为一个词,在认知科学里有一百多年的研究史。1890 年 William James 在《心理学原理》(The Principles of Psychology)里写过一句几乎所有 attention 综述都会引用的话:

“Everyone knows what attention is.”

这句话之所以被反复引用,恰恰是因为它几乎完全错了。每个人都觉得自己知道注意力是什么,可一旦被要求定义,就会发现自己说不清。

James 自己接下来给的定义是:mind 在多个同时可得的对象或思想中,以清晰、生动的方式占据其中之一。

这句定义在今天看仍然很准。注意力的核心不是「看到什么」,也不是「选了什么」,而是「在多个候选里挑选一个或几个,并以不同强度去处理它们」。

把 James 这句翻译到机器学习的语言:注意力是「在 n 个候选上构造一个非均匀分布」。这个分布告诉系统每个候选要被处理多少。一旦你接受这个抽象,整个机制后面的数学几乎是必然的。

1.2 视觉系统:为什么必须有「下一步看哪里」

把这件事再往物理一层放,就成了视觉系统的工作机制。

人类视网膜中央有一小块叫中央凹(fovea)的区域,视锥细胞密度极高,能看清物体的细节。

离中央凹越远,分辨率就越低,到边缘几乎只能感知到形状和运动。

换句话说,眼睛不是一台均匀清晰的摄像机,而是一台「中间一小块清楚、四周模糊」的镜头。

要看清整张报纸,你不可能一下子全看清,必须一次又一次把中央凹移到不同位置——这个动作叫扫视(saccade)。

两次扫视之间的短暂稳定阶段叫注视(fixation),通常持续约 200 到 300 毫秒。

一次正常的阅读,每秒大概发生 3 到 4 次扫视。

眼动示意

这给我们的第一个启发是:注意力不是因为「我们想专注」才出现,而是因为「带宽不够」才出现

如果视网膜每个点都和中央凹一样清晰,如果大脑可以同时处理整个视野的信息,那就不需要选择。

正是因为信息处理通道有限,必须挑选——挑哪个、挑多大权重、何时切换——才有了注意力这件事。

1.3 模型也是一台「带宽不够」的机器

模型也一样。

当我们让一个 decoder 在每一步都能「同时充分」看完编码器给出的所有 hidden state 时,问题就从「能不能看到」变成了「以多大的强度看哪一部分」。

这正是注意力的位置。

具体一点说,假设 encoder 给出 50 个位置的 hidden state,每个 256 维,decoder 要在每个生成步从这 50 × 256 个数字里挑出对当前最有用的那一部分。

如果让 decoder 一视同仁地看完所有 50 个位置——比如直接取平均——那它会被无关信息淹没。

如果让 decoder 只看一个位置——硬选一个——那它丢掉了所有可能的辅助信号。

中间这条路:按 query 内容分配一个 50 维的概率分布,按这个分布加权 50 个位置——就是注意力。

1.4 阅读心理学的更精细图景

人在阅读时并不是按字逐字读,而是有所谓「中心 - 外围」的并行:注视点上的字最清楚,但视野中相邻几个字也能被弱感知,构成感知跨度(perceptual span)。

读到一个不熟悉的术语,注视会停得久;读到「the」「of」这样的功能词,常常会被跳过。

读到歧义结构(比如 garden-path 句),眼睛甚至会回跳到前面再读一遍——这种回跳叫 regression。

这种「按内容动态分配注视时间和注视位置」的机制,对应到模型里就是「按 query 内容动态分配权重」。

换句话说,注意力本来就不是均匀的,注意力是「可调节的非均匀」

1.5 软:人类的注意力本来就不是 0/1

第三个启发也许最关键:人类的注意力是「软」的。

当你阅读「The cat sat on the mat」时,你的视觉系统并不会真的把「cat」之外的所有词清零。

它只是把更多处理资源分给「cat」,同时保留对「the」「sat」的弱响应。

这种「主要看 A、但也看一点 B、几乎不看 C」的分布,正是软对齐(soft alignment)这个名词的物理来源。

注意力机制不是把一个 token 选出来,而是给每个 token 分一个 0 到 1 之间的权重,总和为 1。

把这三件事合在一起:注意力来自带宽限制、注意力是按内容动态分配的、注意力是软的(连续的)。

这三句话决定了我们后面看到的所有具体设计——softmax 的非负与归一性、权重的可微性、动态依赖 query 的机制——都不是任意选择,而是把上面这三句话写成数学。


二、机器翻译里那个老问题:对齐

把注意力从人类拉到机器,最自然的入口是机器翻译。

机器翻译这件事在统计 NLP 时代有一个困扰所有人的概念:对齐(alignment)

2.1 一对例子:英→法→日

给定一个英语句子和它的法语译文,要把 source 端的词对应到 target 端的词,对应关系既不是一对一,也不是顺序保持的。

看一个最朴素的例子:

英语:The cat sat on the mat
法语:Le  chat est assis sur le tapis

把这两句对一对:「The → Le」「cat → chat」「mat → tapis」「on → sur」这四对很顺。

但「sat → est assis」就有问题——一个英语动词要对应两个法语词。

同样的词组,西班牙语会写成「El gato se sentó en la alfombra」,又出现了反身代词「se」这种英语里完全没有对应的成分。

再换成日语「猫がマットの上に座った」,词序整体翻转,连冠词都没了。

到德语会出现复杂的格变化,到阿拉伯语方向甚至要从右往左排,到中文「猫坐在垫子上」连「the」都没法对应。

2.2 SMT 时代的硬对齐

统计机器翻译(Statistical Machine Translation, SMT)几十年的核心研究,绝大部分都在和「怎么把不对称的对齐学出来」斗争。

经典的 IBM Models(Brown 等 1993)把对齐当作隐变量,学一个对齐分布 a:要做的是「给定 source word,target word 来自哪一个 source 位置」。

Model 1 假设对齐均匀,Model 2 加位置先验,Model 3 引入 fertility(一个源词能生成几个目标词),Model 4 加 distortion(位移),Model 5 修补 deficiency。

这一条线技术非常深,但它有一个根本约束:对齐被建模成离散的、硬的

每个 target 词必须对应到一个具体的 source 位置,或者一个具体的 source 子集。

这种硬对齐在训练时要用 EM 算法,要用复杂的剪枝,工程实现极其难调,并且很难端到端嵌进一个深度网络。

2.3 NMT 早期的固定 context vector 瓶颈

时间到 2013–2014 年,神经机器翻译(Neural Machine Translation, NMT)开始登场。

Cho 等的 RNN Encoder-Decoder(2014)和 Sutskever 等的 Sequence to Sequence(2014)几乎同时提出:用一个 RNN 把 source 句压成一个固定长度的向量 c,再用另一个 RNN 从 c 出发生成 target 句。

这个范式优雅、可端到端、漂亮,但有一个所有人都立刻看到的问题。

当 source 句很长时,把所有信息压进一个固定维度的向量 c 里,就像把一本书装进一个手提箱——总会丢东西。

Sutskever 自己在论文里也观察到长句翻译质量明显下降。

他甚至用了「把 source 句反过来输入」这种工程 trick 来缓解最早一段信息容易被遗忘的问题。

这种 trick 不是优雅的解,是工程上的应急。

2.4 把固定 c 换成动态 c

换个角度看这件事,它其实和人类阅读的带宽问题一模一样。

RNN 的 hidden state 维度就是带宽,一个 256 维的 hidden state 就那么多容量。

你不能指望它在一个时间步里同时编码「主语 + 谓语 + 宾语 + 修饰语 + 时态 + 语气」。

SMT 时代的应对是显式的硬对齐;NMT 时代的最初应对是「用更大的 hidden state、用更深的 RNN、用 LSTM/GRU」。

这些都是治标。

真正的解药是承认一件事:decoder 在生成第 t 个 target 词时,不应该只看一个固定的 c,而应该有权动态选择 source 端的某一部分作为这一刻的 context

这就把硬对齐换成了软对齐,把固定上下文换成了动态上下文,把 SMT 时代的隐变量换成了一组可微的权重。

软对齐矩阵

2.5 软对齐解决了什么

但有个细节常被忽略:从硬对齐到软对齐的转换不仅仅是「把离散变成连续」这么简单。

它解决了三件事。

第一,softmax 输出非负归一的权重,可以解读为概率,便于和概率框架接上。

第二,权重连续可微,意味着可以用反向传播一路把误差传回去优化对齐策略,不再需要 EM。

第三,权重依赖 query(也就是当前 decoder 状态),而不是固定的全局对齐表,意味着同一对源句和目标句在不同位置可以有不同对齐。

这三件事合起来,让翻译质量在长句上立刻翻盘。

后面我们会看到,这一组性质几乎被原样搬到了 Transformer 里。

2.6 为什么我把对齐讲这么长

我特意把「对齐」这个老问题讲长一点,是因为很多教材直接从 Bahdanau 的公式开始,跳过了它要解决的问题。

结果学生看完公式只记住了 score = v_a^T tanh(W_a [s; h]) 这一行,但说不清这个 score 究竟在算什么。

它在算的就是「decoder 当前状态 s 和 encoder 第 i 个位置的 h_i 之间的对齐强度」。

这个强度就是软化版的对齐变量 α。

一旦你把对齐这个直觉装回脑子里,后面的所有公式都只是这个直觉的不同写法而已。


三、加权平均:注意力的内核就这么一句话

把直觉从认知科学和翻译里拉回机器学习,注意力的内核可以浓缩成一句话:

给定一个 query,从 n 个候选里按相似度算权重,取一个加权平均。

这一句话已经包含了我们要讨论的所有数学。

我们一项一项拆。

3.1 query 是「现在我想知道什么」

「query」是什么?

query 是「现在我想知道什么」的表示。

在 Bahdanau 的翻译模型里,query 是 decoder 当前 hidden state s_t——它编码了「我现在准备生成的是 target 句的第几个词、我刚刚说了什么、我打算说什么」。

在自注意力里,query 是序列中某一个 token 的当前表示——它编码了「这个 token 现在需要哪种信息来更新自己」。

在图像描述里,query 可能是 caption decoder 的状态;在 image classification 的某些注意力变种里,query 可能是一个学得的全局向量。

把 query 想成「问题」就行:每一次注意力调用,都是一次「问候选库一个问题」的过程。

3.2 候选:key 和 value 的雏形

「候选」是什么?

候选是被查的对象集合。

它们最终会以「value」的身份贡献信息,但在计算权重时通常用一个「key」来和 query 对相似度。

在第十三篇我们会专门拆 Q/K/V 三件套,这里只需要建立一个粗粒度直觉:候选库里的每条记录都有「拿来比对的部分(key)」和「拿来取出的部分(value)」。

在最简单的设定下,key 就等于 value——比如 Bahdanau 把 encoder 的 hidden state 同时当作 key 和 value 用。

到了 Transformer,K 和 V 被显式分开成两份不同的投影,这是后话。

3.3 相似度函数:打分的几种经典选择

「相似度」是什么?

相似度可以有很多种——点积、cos 距离、欧式距离反过来、可学的 MLP。

它们的共同目的是:给每个候选打一个分,分数越高说明 query 和这个候选越「契合」。

点积是最常见的选择,因为高维空间里点积和向量内积的几何意义清楚(投影长度),而且矩阵化之后非常适合 GPU 并行。

Bahdanau 用的是 additive attention(一个小 MLP),Luong 用的是 multiplicative attention(点积变种),Transformer 选了 scaled dot-product。

这三种我们后面都会讲。

但关键是它们都先打了一个分。

3.4 加权平均与凸组合

「加权平均」是什么?

把上面的分变成权重 α,要求 α_i ≥ 0 且 Σ α_i = 1,然后输出 = Σ α_i · v_i。

这就是注意力的输出——一个新向量,它是候选 value 的凸组合(convex combination)。

凸组合的几何意义清楚:输出一定落在所有候选 value 的凸包内部,不会跑到外面去。

这件事在数值稳定上是天然的好性质:模型不会因为「权重把某个 value 放大十倍」而爆掉,所有输出都被候选自己 bound 住了。

加权平均

3.5 为什么权重必须非负且和为一

为什么权重必须非负且加和为一?

我们一条一条想。

如果权重可以是负数,那「加权平均」就不再是凸组合,输出可以走到候选凸包外,而且训练时正负权重容易彼此抵消,梯度方向不稳定。

如果权重可以大于 1,输出量级会随权重直接爆涨,没有自然 bound。

如果不归一化(不强制和为 1),那权重的绝对大小没有「意义」,只能比较相对大小,跨样本、跨层的权重分布会失去可比性。

把这两条写在一起:权重应该是 n 个候选上的概率分布

一旦同意这件事,softmax 就是把任意 n 个分数变成概率分布的最自然映射——它把分数指数化保持单调、归一化保证和为一、可微保证可训练、连续保证小扰动不会让权重跳变。

3.6 softmax 不是审美选择

我们暂时还不展开 softmax 的更细节(第七篇已经讲过基础,第十五篇还会回来讨论 √d_k 的缩放问题),这里只把它当作「分数 → 概率」的标准件用。

但要强调一句:softmax 在这里不是审美选择,是约束推出来的工程结果

任何把实数映射到概率单纯形(probability simplex)上的连续可微算子,都满足上面那三条要求。

softmax 是这一类映射里最朴素、最便宜、最稳定的一个。

后来人们试过 sparsemax(Martins & Astudillo 2016)、entmax、Talking-Heads 之类的变种,它们也都满足非负归一与可微,只是在「稀疏性」「头之间共享」这些次级目标上做了取舍。

但内核没变。

3.7 三步流水线:打分 → 归一 → 加权求和

把这一节合起来:注意力 = 给候选打分 → softmax 归一化成概率 → 用概率加权候选 value → 输出新向量。

三步,仅此而已。

后面所有花样——multi-head、scaled、causal、cross、self——都是在「query 是谁」「候选是谁」「打分函数是什么」「mask 怎么加」这四件事上做手脚。

直觉一旦立住,剩下的就是工程组合。


四、为什么不是 argmax:可微优先

写到这里你可能已经想到一个问题:既然要「挑」,为什么不直接挑分数最高的那个?

干脆 argmax,把权重设成 one-hot,然后直接取那个候选的 value。

这样不更干净吗?

4.1 argmax 的致命缺陷:不可微

这个问题不傻。

事实上 SMT 时代的硬对齐就是这种思路,IBM Models 训练时甚至要用 EM 反复推断这个离散选择。

但放到神经网络里,argmax 有一个致命缺陷:它不可微

argmax 的输出是一个离散索引,输入做小扰动时输出要么不变要么直接跳到另一个候选,没有梯度。

没有梯度意味着反向传播传不下去。

意味着上层的 loss 没法用来更新「打分函数」自身的参数。

整个端到端训练就断了。

4.2 为什么 Gumbel-softmax 等替代方案不够

有人会说,那不能用 Gumbel-softmax 这种 trick 让 argmax 可微吗?

可以。

Gumbel-softmax(Jang et al. 2017)的思路是:在分数上加一个 Gumbel 噪声,然后做温度可控的 softmax,温度趋于 0 时近似一个 one-hot 采样。

它有几个代价。

第一,前向引入了随机性,所以同一输入两次前向输出不同,对 batch normalization、对推理一致性都不友好。

第二,反向用了 straight-through 或重参数近似,方差大、训练不稳。

第三,引入了一个温度超参,需要 anneal schedule。

工程复杂度大增,并且方差通常不友好。

Bahdanau 那一年的工业界还没有这些工具,即使有,也仍然要回答一个更根本的问题:

离散选择和连续平均,哪个更适合表达「decoder 在第 t 步要的信息」?

4.3 软选择更符合任务本质

答案在大多数任务上都是连续平均更合理。

看翻译的例子,当 decoder 生成法语「assis」时,最相关的源词显然是英语「sat」。

但「on」也提供了一些位置和介词搭配的信息,「the cat」提供了主语一致性。

如果硬选一个最相关,剩下的辅助信息就被全部丢掉。

软选择允许 decoder 同时摄取主信息和侧信息,权重的分布本身就携带了「这个时刻信息怎么混合」的策略。

这个混合策略是任务相关的、是数据相关的、是上下文相关的——把它学出来比手工硬选要强。

4.4 softmax 能逼近 argmax,但反过来不行

换个角度想,softmax 还有一个常被低估的好处:它可以「逼近」argmax。

当分数差距很大时,softmax 的输出会非常接近 one-hot;当分数接近时,softmax 给出更平滑的分布。

这意味着模型在训练初期可以保持「软」以获得稳定梯度,训练后期当某些选择确实变得清晰时,可以自动收敛到接近硬选择的形态。

这种「能软能硬」的弹性是 argmax 给不了的。

反过来,scaled dot-product 的 √d_k 缩放就是为了控制 softmax 不要过早进入「过硬」的饱和区——这件事我们会在第十五篇详细讨论。

4.5 可微是硬约束,不是锦上添花

我特别想强调一句:在深度学习里,「可微」不是一种锦上添花的性质,它是设计选择的硬约束

无数巧妙的算法之所以最后没有进入主流神经网络,就是因为它们不可微或者只能近似可微而代价太高。

注意力之所以能成为基础组件,正是因为它在保持「选择」语义的同时,把整个机制塞进了可微框架里。

这件事说起来轻巧,做起来花了 NLP 整个领域将近 30 年。


五、一个更广义的视角:注意力是寻址

到这里我们已经有了三层直觉:人类视觉的带宽问题、机器翻译的对齐问题、加权平均的可微选择。

把它们抽象一层,注意力其实在做一件经典计算机科学问题:寻址(addressing)

5.1 硬寻址 vs 内容寻址

一台传统计算机用「索引」做寻址:你给一个地址 i,从内存数组里取出 array[i]。

这是硬寻址,和 argmax 完全一致——输入是离散索引,输出是该位置的精确值,没有梯度。

一台神经网络要做的是「内容寻址(content-based addressing)」:你给一个 query 向量,从「内存」里取出和 query 最匹配的内容。

匹配是用相似度衡量的,输出是「按匹配度加权的所有内容」。

这是软寻址。

5.2 神经图灵机的伏笔

神经图灵机(Neural Turing Machine, Graves 2014)和可微神经计算机(Differentiable Neural Computer, Graves 2016)就是这个思路的早期完整尝试。

他们直接借用了「memory」这个名字。

在那一线工作里,注意力被明确叫作 read head 的 addressing 机制。

NTM 还引入了 location-based addressing(按位置寻址)和 content-based addressing(按内容寻址)的二分。

Transformer 后来基本只保留了 content-based 这条线,把 location 信息显式编码到位置编码里——这是另一种取舍。

5.3 cross / self 都是寻址的特例

把注意力理解成寻址,有一个非常实际的好处:你立刻就能解释为什么有 cross-attention 和 self-attention 两种用法。

cross-attention 是「我用 query 去访问另一个序列的内容」——典型的查询-数据库分离场景,比如 decoder 查 encoder。

self-attention 是「我用 query 去访问自己所在序列的内容」——序列内部的交叉寻址,每个 token 既当 query、又当 key+value,互相检索。

这两种用法在数学上完全相同,区别只在于 query 和候选库的来源。

5.4 KV Cache:把寻址工程化

寻址视角还能解释 KV Cache 这种工程优化。

在自回归推理中,每生成一个新 token,就要把它插入到「可被未来 token 查询的内存」里——这就是 KV cache 的物理意义。

它本质上是一个「不断增长的 key-value 数据库」,新 token 把自己的 K、V 写入数据库,未来 token 用自己的 Q 来读这个数据库。

这件事在第二十二、四十八、五十篇会深入展开。

5.5 内存本身也是学出来的

但有个细节常被忽略:注意力作为寻址机制,和真正的内存系统有一个本质差别——注意力的「内存」本身不是固定的,它是和模型一起学出来的。

Q、K、V 都是从输入投影出来的,投影矩阵 W_Q、W_K、W_V 在训练中被优化。

换句话说,模型不只是学怎么查,还在学「内存里应该按什么角度组织信息以便被查」

这一点在 Bahdanau 那一代还不明显,因为 K=V=encoder hidden state,没有显式的投影。

到了 Vaswani 2017 引入 W_Q/W_K/W_V 之后,「投影成检索友好的表示」就成了模型容量的一部分。

这件事要等到第十三篇我们才会展开。


六、把直觉写成代码:一个最小例子

直觉讲了五节,我们把它落成最小的代码。

先不引入 Q/K/V 三件套,只用最朴素的版本:query 一个向量,候选若干个向量(key=value),用点积打分,softmax 归一,加权求和。

6.1 二十行实现

import numpy as np

def naive_attention(query, candidates):
    """
    query:      shape (d,)
    candidates: shape (n, d),每行是一个候选向量
    返回 (output, weights)
    """
    scores = candidates @ query              # (n,) 点积打分
    weights = np.exp(scores - scores.max())  # 数值稳定的 softmax
    weights = weights / weights.sum()        # 归一化为概率分布
    output = weights @ candidates            # (d,) 加权平均
    return output, weights

这段代码只有四行核心逻辑,但已经是注意力机制的全部内核。

后面的 Q/K/V 投影、scaled、multi-head、causal mask、kv cache,都是围绕这四行做的工程化扩展。

6.2 一个玩具例子

query = np.array([1.0, 0.0])
candidates = np.array([
    [1.0,  0.1],   # 与 query 几乎共线,分数最高
    [0.5,  0.5],   # 中等
    [0.0,  1.0],   # 与 query 正交,分数最低
    [-0.8, 0.2],   # 与 query 反向
])
out, w = naive_attention(query, candidates)
print("权重:", w.round(3))
print("输出:", out.round(3))

跑一下,权重大约是 [0.451, 0.273, 0.165, 0.111],输出是这四个候选的凸组合。

第一项权重最大,因为它和 query 最贴近。

第四项权重不为零,因为 softmax 不会把任何候选权重压到精确的 0——这是软选择和硬选择最直观的区别。

6.3 实验一:放大某个候选会发生什么

我们再做一个小实验:把第一个候选缩放放大十倍,看看权重会变成什么样:

candidates_scaled = candidates.copy()
candidates_scaled[0] *= 10
out2, w2 = naive_attention(query, candidates_scaled)
print("权重:", w2.round(4))

你会发现权重几乎全部集中在第一项(接近 one-hot)——因为它的点积分数比其他项大了一个数量级。

softmax 此时进入了饱和区,分布从「软」变成了「硬」。

这个现象在维度变高时也会自然出现:即使各候选量级正常,d 越大,点积期望方差也越大,softmax 也越容易进入饱和。

这就是 √d_k 缩放要解决的问题——后面那一篇我们再回来。

6.4 实验二:用 argmax 代替 softmax

最后一个小实验:如果我们用 argmax 代替 softmax,看看会发生什么:

def argmax_attention(query, candidates):
    scores = candidates @ query
    idx = np.argmax(scores)
    return candidates[idx], idx

这个版本只能返回一个候选向量,输出是离散的。

如果稍微扰动 query,输出要么完全不变,要么直接换到另一个候选——梯度信息全部丢失。

这就是为什么深度学习里所有「选择」机制几乎一律用 softmax 的原因。

6.5 实验三:小批量、可训练版本

把上面这段写成可训练形式,再加一个 W_q 的投影:

import torch
import torch.nn as nn

class TinyAttention(nn.Module):
    def __init__(self, d):
        super().__init__()
        self.W_q = nn.Linear(d, d, bias=False)

    def forward(self, query, candidates):
        # query:      (B, d)
        # candidates: (B, n, d)
        q = self.W_q(query)                       # (B, d)
        scores = torch.einsum("bd,bnd->bn", q, candidates)
        weights = torch.softmax(scores, dim=-1)   # (B, n)
        output = torch.einsum("bn,bnd->bd", weights, candidates)
        return output, weights

这段代码已经具备了「学一个 query 投影」的能力——这是从「直觉版 attention」走向「真正可训练 attention」的第一步。

如果再加一个 W_k 让 key 和 value 走不同投影、再加一个 W_v 让 value 也独立投影,就到了第十三篇要讲的 Q/K/V 三件套。

6.6 这段代码教给我们什么

这段代码至少教给我们三件事。

第一,attention 的内核非常薄。四行 Python 就够。

第二,attention 的计算图天然 batch 友好——所有运算都是矩阵乘和 softmax,没有任何递归依赖。这就是它能比 RNN 快几十倍的根因。

第三,attention 的所有「能力」最终都体现在 W_q、W_k、W_v 这些可学投影上。机制本身只是一个固定算子,模型的容量来自投影。

后面 Transformer 的所有设计——multi-head、深度堆叠、残差、LayerNorm——都可以理解成「给这套机制加更多投影 + 让它能堆深」。


七、几对常被混的概念

直觉建立到这里基本就够了,但有几对概念在中文资料里经常被混着用,值得在进入 Bahdanau 之前先说清楚。

7.1 一张澄清表

概念 含义 容易和谁混 真正区别
注意力(attention) 一个机制:从候选里加权取信息 自注意力 self-attention 是 attention 的特例,区别在 query 和候选是否来自同一序列
软对齐 学到的连续权重分布 硬对齐 硬对齐是离散索引,软对齐是 n 个 softmax 概率
score 未归一化的相似度分数 权重 score 是任意实数,权重是 0 到 1 的概率
加权平均 输出 = Σ αᵢ vᵢ 加权和 加权平均特指权重和为 1 的加权和;attention 的输出严格是加权平均
注意力权重 softmax 后的 α 注意力分数 不少教材把 score 和 weight 写成一个符号,导致混乱
query / key / value Bahdanau 之后被固化的术语 「输入」 同一份输入被投影成三种角色,不能混称

7.2 score 和 weight:写代码时务必区分

我把这张表放在这里,是因为后面所有篇章我们都会按这套术语严格区分。

中文里「权重」「分数」「打分」「评分」很容易混着用。

写代码的时候 scoresweights 是两个不同的张量,前者是点积结果,后者是 softmax 结果,量级和性质都不一样。

这个区分一旦混淆,公式就读不准。

读论文也是同理:有些论文里 α 是 scores,有些里 α 是 weights,如果不区分会让推导步骤完全错位。

7.3 注意力 ≠ 解释

还有一个常见误解需要现在拆掉:注意力不是「看哪里」的解释

这是非常顽固的一个误解,连不少综述都在用。

注意力权重高的位置在生成当前输出时贡献了更多 value,这只能说明它在数学上贡献了更多信号;但它不一定就是模型「真正依赖的因果证据」。

Jain 和 Wallace 2019 那篇有名的「Attention is not Explanation」做过一组实验:把 RNN 加注意力的模型注意力权重整个换成另一个随机但等效的分布,模型预测几乎不变。

Wiegreffe & Pinter 2019 反驳了一部分论点,但也承认权重和决策之间的关系比直觉更弱。

这件事我们会在第五十二篇专门展开,这里只是先打个预防针:注意力是一个加权机制,不是一个解释机制

它本质上和卷积里的「特征图响应强度」是同一档的东西——可以辅助分析,不能直接当作模型给出的因果说明。


八、注意力不是 Transformer 发明的

很多人误以为「注意力 = Transformer」,事实并非如此。

注意力机制在 Transformer 之前已经在多个领域走了很长的路,把这条路看清楚,对理解后面的设计选择非常关键。

8.1 NMT 这条主线

最早把「学一个权重分布去加权候选」这件事系统化做出来的,是 NMT 领域 2014 年 Bahdanau 等人的论文「Neural Machine Translation by Jointly Learning to Align and Translate」。

这篇论文的标题里就埋了关键词「Jointly Learning to Align」——把对齐这个 SMT 时代的隐变量端到端学出来。

它是 attention 在主流 NLP 任务上的首次大规模应用,也是这个机制开始被叫作 attention 的起点。

差不多同时,Luong 等 2015 又给出了一些改进:global vs local attention、dot vs general vs concat 三种打分函数。

这两篇我们会在下一篇详细讲。

8.2 视觉这条线

差不多同时,图像描述(image captioning)领域 Xu 等 2015 的「Show, Attend and Tell」用 attention 让 caption 生成模型能在生成每个单词时聚焦图像不同区域。

它分了 soft attention 和 hard attention 两种实现:soft 用 softmax 加权,hard 用 REINFORCE 训练 categorical 采样。

soft 比 hard 训得更稳,最后基本所有后续工作都默认用 soft 的版本。

视觉问答(VQA)后续也大量用 attention,包括 Yang 等的 Stacked Attention Networks(2016)。

8.3 神经图灵机这条线

再早一点,Graves 2013 的「Generating Sequences With Recurrent Neural Networks」其实已经在用一种基于位置的高斯混合形式做语音合成对齐——那是 attention 的另一种形式。

神经图灵机 NTM(Graves 2014)把 content-based 和 location-based addressing 显式拆开做存储读写,是更通用的形式。

DNC(Differentiable Neural Computer, 2016)在 NTM 基础上加了链表式的时间寻址。

这一条线影响了后来一些显式记忆网络的工作(Memory Networks, End-to-End Memory Networks),但在 NLP 主流里没成为主干件——主干件最终被 Transformer 占了。

8.4 更早的伏笔:MoE 与 saliency

在更早的年代,注意力的雏形甚至可以追溯到对齐模型与混合专家(Mixture of Experts, MoE)这两条线。

MoE(Jacobs 1991)让一个 gating network 输出一组概率分布去选专家,这本质上就是 attention——只不过 candidates 换成了「专家网络」。

一些计算机视觉里的 saliency map 工作(早至 90 年代)也是在算「图像哪里更重要」。

把这些线索串起来你会发现:注意力不是一个新发明,它是一种在多个子领域不约而同被重新发现的通用机制

8.5 Transformer 的真正贡献

Vaswani 等 2017 的贡献不是发明注意力,而是证明了「只用注意力就够了」,把一个广泛存在的机制从 RNN/CNN 的辅助件升级成主干件。

「Attention Is All You Need」这句标题本身就是一种宣言:把 RNN 全部去掉,把卷积也去掉,只留注意力 + FFN + 残差 + LayerNorm,照样能做 SOTA 翻译。

后面的事情大家都知道了。

理解这一点对你看后续工作的眼光很重要。

当我们看 FlashAttention、Sparse Attention、Linear Attention 这些名字时,它们绝大多数都不在改注意力的「加权求和」内核,而是在改「以什么样的方式存候选、以什么样的复杂度查候选、以什么样的精度算 softmax」——也就是寻址的工程实现。

注意力本身的核心定义已经稳了十年。


九、几个常被忽略的工程踩坑

讲完直觉和历史,最后说几个在工程里反复看到的小坑,提前知道能省掉很多调试时间。

9.1 softmax 数值稳定

第一个坑是 softmax 数值稳定性。

朴素写法 exp(x) / sum(exp(x)) 在 x 较大时会溢出,较小时会下溢。

工业标准做法是先减去最大值:exp(x - max(x)) / sum(exp(x - max(x)))

减去最大值不改变 softmax 输出(因为分子分母同时除以 exp(max)),但保证最大的指数是 0,避免溢出。

PyTorch 的 F.softmax 内部自动做这个稳定化,所以业务代码一般不用关心。

但当你自己实现 attention(比如学习目的、或者写自定义 kernel)时,必须自己减去最大值,否则随便一个长序列就会数值爆掉。

FlashAttention 这种工程优化甚至要在分块累积时维护 running max 和 running sum,难度更大——第四十二篇会展开。

9.2 attention mask 与 padding

第二个坑是 padding mask。

实际训练里 batch 里每个样本长度不同,要 pad 到一样长。

pad 出来的位置不应该参与 attention——它们是无意义的零向量,让它们参与会污染 softmax 分布。

标准做法是构造一个 mask,把 pad 位置的 score 设成 -∞(实际是 -1e9 或 -1e4,具体看 fp16/fp32),然后再做 softmax。

exp(-∞) = 0,所以这些位置 softmax 后权重为 0,不参与加权。

这个 trick 在第十七篇 causal mask 时还会再用一次,是 attention 实现里的标准件。

9.3 对齐 != 因果

第三个坑是混淆「对齐」和「因果」。

attention 学到的对齐反映的是「在数据集里这两个位置统计上相关」,不是「这两个位置在语义上必须配对」。

短语翻译里这二者大多重合;但在长文本生成里,模型可能学到一些奇怪的对齐——比如 token 大量看自己(self-loop),或者集中看某个标点(“attention sink”,Xiao et al. 2023)。

这些奇怪现象不是 bug,是模型在用 attention 的自由度做工程上意想不到的事。

9.4 注意力的「平均偏置」

第四个坑是 attention 输出的「平均偏置」。

注意力的输出是凸组合,所以它天然有「向均值收缩」的倾向。

如果候选 value 量级接近、又没有特别突出的 query 匹配,输出就接近所有 value 的平均,看起来什么也没学到。

这种情况在训练初期非常常见。

工程上对应的诊断是:看 attention 权重的熵。

如果熵很高(接近 log n),说明模型还没把权重分化出来;训练正常进行后熵会逐渐下降,但不应该下降到 0(那意味着完全 one-hot,又过分集中)。

健康的训练曲线下,attention entropy 通常会稳定在某个中间值。

9.5 不要把 batch dim 和 sequence dim 搞混

第五个坑是张量维度。

attention 的张量通常是 (batch, heads, seq, dim)(batch, seq, dim),softmax 应该沿 seq 维做,加权求和也应该沿 seq 维做。

新手最常见的 bug 是 softmax 沿了错误的维度——结果权重并没有归一到「序列上的概率分布」,模型表现莫名其妙地差但不报错。

写 attention 时建议每一步都打一遍 shape 注释,确保每个算子作用在正确的维度上。

9.6 一个真实的调试故事

我自己在做一个翻译模型时踩过一次很典型的坑。

batch 里短句被 pad 到长句长度,但我忘了给 pad 位置加 mask。

训练 loss 看起来在下降,但翻译输出出现大量「the the the」「,,,」这种重复,质量明显异常。

调了三天才发现原因:pad 位置参与了 softmax 归一,pad 的零向量在某些层会拿到非零权重,导致这些位置的 value 被错误地混入输出。

加上 mask 之后,模型一夜之间从「重复 token 怪兽」变成正常翻译。

这种坑写一遍永远记得。

attention 的设计干净,但「干净」不等于「不容易出错」——任何一个维度、一个 mask、一个 softmax 轴搞错,模型行为都会以一种「不报错但结果离谱」的方式失败。

这也是为什么我们花了一整篇先讲直觉:直觉立稳,调试时才有锚点。

9.7 attention 不一定要全连接

最后一个常被忽略的事实:attention 在原始定义里要求「每个 query 看所有候选」,但工程上完全可以裁剪。

local attention 只让 query 看附近 k 个候选;sparse attention 给定一个稀疏模式(比如 strided、block-sparse);linear attention 用 kernel trick 把 softmax 换成可分解的相似度函数从而避免 n×n 矩阵。

这些都不是把 attention 推翻,而是在「打分函数」「候选范围」「softmax 形式」这三个维度上做不同取舍。

理解了直觉版本,再看这些变种就只是组合学问题。


十、把直觉串到下一篇:硬对齐到软对齐的瞬间

让我们把这一篇的几条线收拢,把视角准备到下一篇 Bahdanau Attention 上。

10.1 三条线的合流

第一条线:人类阅读时的眼动告诉我们,注意力是带宽限制下的「按内容动态分配」机制。

这条直觉到了机器,就是「decoder 在第 t 步动态选择要看 encoder 的哪些 hidden state」。

第二条线:机器翻译里几十年的硬对齐研究,意味着我们已经知道「source 和 target 之间应该有一种位置 - 内容映射」。

这条线到了 NMT,就是把硬对齐换成 softmax 上的概率分布——同样的语义,但变成连续可微版。

第三条线:加权平均的内核——非负、和为一、按 query 算权重、可微——决定了我们必然会用 softmax 而不是 argmax,决定了 attention 必然是凸组合,也决定了它必然能用反向传播一路训练下去。

10.2 机制的必然性

把这三条放到一起,你会发现一个非常清晰的预测:

只要有人把「decoder 当前状态」当作 query、把「encoder 各位置的 hidden state」当作候选库、把「点积或一个小 MLP」当作打分函数、把「softmax + 加权求和」当作输出,这个机制就一定会出现

Bahdanau 那一篇的工程贡献不是想到了什么前所未有的东西,而是把这件「该来的事」第一次以可训练的形式做出来。

它的价值在于把整套机制塞进了一个端到端可训的 NMT 框架,而不是发明了一个新概念。

10.3 下一篇预告

下一篇我们就把 Bahdanau 的具体公式逐项拆开,看一看 score(s, h) = v_a^T tanh(W_a [s; h]) 每一个符号在做什么、为什么这么写、和后来 Luong 2015 的 multiplicative attention 有什么不同。

再下一篇,我们会进一步把 Bahdanau 的写法抽象成 Q/K/V 三件套——这一抽象彻底打开了通向 Transformer 的大门。

到第十五篇我们会详细讲 √d_k 那个看似奇怪的常数;到第十六篇讲 multi-head 为什么要分头;到第十七篇讲 causal mask 怎么让模型只看过去。

这一段六篇连起来,是整个系列最核心的「机制原理」段落。


十一、关键概念回顾

回到开头那句话:本篇不写公式推导,只把直觉立稳。

回看一遍,注意力的内核就是「query 决定权重、加权平均候选 value」

我们用人类视觉的中央凹和扫视讲了为什么注意力天然存在——带宽不够,必须挑——也用机器翻译里的硬对齐告诉你,这件事在 NLP 里有几十年的伏笔,所谓 NMT 的 attention 不过是把硬对齐软化成了一个 softmax 概率分布。

我们花了一节专门讨论「为什么不是 argmax」,结论是可微优先:神经网络的所有设计都必须是端到端可训的,离散选择因为没有梯度而不能直接用,而 softmax 既能保留软选择语义、又是一个连续可微算子,自然成了第一选择。

我们也用最小代码把整个机制写了出来:点积打分、softmax 归一、加权求和。十几行代码已经包括了 attention 的全部数学内核,后面所有花样都是这十几行的扩展和优化。

我们顺手指出了几个常被混的概念:score 和 weight 不是一回事;硬对齐和软对齐量纲完全不同;attention 不是 Transformer 的专利,它在 NMT、image captioning、神经图灵机里都早已存在;attention 不等同于解释,权重高的地方不一定是因果证据。

这些细节在后续读论文、读源码、调模型时都会反复出现,提前看清能少踩很多坑。

最重要的是:注意力是寻址

把它放到这个抽象层级上,cross-attention、self-attention、kv cache、sparse attention、linear attention 这些后来的名字才有一个统一的解释——它们都是「query 一个内存库」的不同变种,区别只在内存库怎么构造、怎么压缩、怎么查询。

这个视角会在后面 47 篇里反复用到。


十二、常见误解

12.1 注意力 ≠ 替代 RNN 的全部

第一个误解:注意力可以替代 RNN,所以注意力比 RNN 强

这种说法跳过了关键。

注意力本身只是「按权重取信息」的机制,它没有规定信息怎么编码、怎么累积、怎么前进。

Transformer 之所以能替代 RNN,是因为它把注意力 + 残差 + LayerNorm + FFN + 位置编码组合成了一个完整架构,每一件都不可缺。

光有注意力,连「序列顺序」这件事都搞不定——self-attention 是排列等变的,必须靠位置编码补上。

这件事在第十四、第二十一篇会专门讲。

12.2 注意力 ≠ 权重

第二个误解:softmax 之后的权重就是注意力

不准确。

注意力是整个机制:query 给候选打分、softmax 归一、加权求和、输出新向量。

softmax 之后的权重 α 只是这个流水线的一个中间产物。

把 α 当作 attention 的全部,会让你忽略 V 的存在——而 V 的设计(包括 W_V 投影矩阵)才是模型从候选里取出什么的关键。

12.3 注意力越集中越好?不一定

第三个误解:注意力权重越集中越好

也不一定。

集中的权重意味着模型「确认」了从某个候选取信息,但同时也意味着它放弃了其他候选的辅助信号。

在很多翻译、问答、长文本任务上,权重适度分散反而能保留更多上下文。

研究上对「注意力的熵」和模型性能之间的关系也有大量讨论,结论是没有简单的「越集中越好」或「越分散越好」。

这件事的细节会在第五十二篇讨论可解释性时再回来。

12.4 注意力 ≠ Transformer 发明的

第四个误解:注意力是 Transformer 发明的

前面已经讲过了,Bahdanau 2014、Xu 2015、Graves 的 NTM 2014 都比 Transformer 早。

Transformer 的真正贡献是「Attention Is All You Need」这句话——只用注意力,不要 RNN,照样能行。

这是一次架构选择上的极简化,不是机制本身的发明。

12.5 注意力 ≠ 解释

第五个误解:注意力机制天然能解释模型行为

这条最顽固也最危险。

Jain & Wallace 2019、Serrano & Smith 2019、Wiegreffe & Pinter 2019 一系列论文已经反复证明:attention 权重和模型决策之间没有可靠的因果关系。

看注意力图可以做诊断、可以做 ablation 的提示,但不能直接当作「模型为什么这么决定」的解释。

第五十二篇会详细展开。

12.6 自注意力 ≠ 自反注意力

第六个误解:「self-attention」里的「self」很多人理解成「自己看自己」,认为每个 token 主要看自己那一行。

事实上 self-attention 里 query/key/value 都来自同一个序列的不同投影,但每个 token 在计算 attention 时主要看的是其他 token——尤其是和它语义相关的 token。

「self」指的是「同一序列内部」,不是「自反」。

很多论文里 self-attention 权重图会显示明显的对角线,这只是因为某个 token 自己投影出的 q 和 k 内积常常较高,但绝不是设计目标。

健康的 self-attention 应该有跨位置的强连接,不然就退化回 RNN 没有信息流的问题。

12.7 注意力的复杂度不可避免吗

第七个误解:「attention 的 O(n²) 是无法避免的代价」。

并非如此。

Linear Attention(Katharopoulos 2020)、Performer(Choromanski 2020)、Mamba(Gu 2023)等线性时间复杂度的替代方案已经存在多年。

但它们各有取舍——通常在长程依赖、训练稳定性、表达能力上有所妥协。

第十八篇会专门讨论复杂度,第五十五到五十八篇会讨论替代架构。

至少当下,O(n²) 仍然是质量最高的实现。


十三、下一步

下一篇 12|Bahdanau Attention 会回到 2014 年,把 Bahdanau, Cho, Bengio 那篇「Neural Machine Translation by Jointly Learning to Align and Translate」逐段拆开。

我们会看:固定长度 context vector 的瓶颈是什么、怎么把 attention 嵌入 encoder-decoder、v_a^T tanh(W_a [s; h]) 每一项的语义、为什么这是 additive attention、和 Luong 2015 的 multiplicative attention 比起来各自的取舍是什么。

这一篇是从直觉到工程的桥梁。

再往后,13|Q/K/V 三件套 会把 Bahdanau 的写法抽象成 query/key/value 三个矩阵,这一步抽象是后面所有 Transformer 工作的起点。

读完这两篇,你应该能对着 softmax(QK^T/√d_k) V 这一行公式,把每一项的语义、几何意义、工程动机都立刻说清楚。


十四、参考文献

  1. James, W. The Principles of Psychology. Henry Holt and Company, 1890.(注意力的早期定义来源)
  2. Rayner, K. “Eye movements in reading and information processing: 20 years of research.” Psychological Bulletin, 124(3): 372–422, 1998.
  3. Bahdanau, D., Cho, K., Bengio, Y. “Neural Machine Translation by Jointly Learning to Align and Translate.” ICLR 2015 (arXiv:1409.0473, 2014).
  4. Sutskever, I., Vinyals, O., Le, Q. V. “Sequence to Sequence Learning with Neural Networks.” NeurIPS 2014.
  5. Cho, K. et al. “Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation.” EMNLP 2014.
  6. Brown, P. F. et al. “The Mathematics of Statistical Machine Translation: Parameter Estimation.” Computational Linguistics, 19(2): 263–311, 1993.
  7. Xu, K. et al. “Show, Attend and Tell: Neural Image Caption Generation with Visual Attention.” ICML 2015.
  8. Graves, A. “Generating Sequences With Recurrent Neural Networks.” arXiv:1308.0850, 2013.
  9. Graves, A., Wayne, G., Danihelka, I. “Neural Turing Machines.” arXiv:1410.5401, 2014.
  10. Luong, M.-T., Pham, H., Manning, C. D. “Effective Approaches to Attention-based Neural Machine Translation.” EMNLP 2015.
  11. Vaswani, A. et al. “Attention Is All You Need.” NeurIPS 2017.
  12. Jain, S., Wallace, B. “Attention is not Explanation.” NAACL 2019.
  13. Wiegreffe, S., Pinter, Y. “Attention is not not Explanation.” EMNLP 2019.
  14. Jang, E., Gu, S., Poole, B. “Categorical Reparameterization with Gumbel-Softmax.” ICLR 2017.
  15. Martins, A., Astudillo, R. “From Softmax to Sparsemax: A Sparse Model of Attention and Multi-Label Classification.” ICML 2016.
  16. Yang, Z. et al. “Stacked Attention Networks for Image Question Answering.” CVPR 2016.
  17. Serrano, S., Smith, N. A. “Is Attention Interpretable?” ACL 2019.
  18. Xiao, G. et al. “Efficient Streaming Language Models with Attention Sinks.” arXiv:2309.17453, 2023.
  19. Jacobs, R. A. et al. “Adaptive Mixtures of Local Experts.” Neural Computation, 3(1): 79–87, 1991.
  20. Graves, A., Wayne, G., et al. “Hybrid computing using a neural network with dynamic external memory.” Nature, 538(7626): 471–476, 2016.

← 上一篇:10|RNN 的根本局限 | 下一篇:12|Bahdanau Attention

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-15 · transformer

【Transformer 与注意力机制】01|为什么要从这里开始

这是【Transformer 与注意力机制】系列的第一篇,承担两件事:一是把这套五十多篇文章为谁写、解决什么问题、彼此之间是什么关系交代清楚;二是为完全没基础的读者画出一条从向量、点积、矩阵乘法走到自注意力、再走到大语言模型的爬升路径,让你在投入时间之前先知道终点在哪、路上要经过哪些坎、读完之后你会、还不会做什么事。


By .