上一篇我们立稳了直觉:注意力 = 加权平均,权重由 query 决定。这一篇回到 2014 年蒙特利尔大学 Yoshua Bengio 实验室,看 Bahdanau, Cho, Bengio 三人如何把这个直觉第一次落成可训练的工程实现。
这篇论文标题里就埋了关键词「Jointly Learning to Align and Translate」——把 SMT 时代的对齐变量当作隐变量学出来,端到端,可微。它是 attention 走进主流 NLP 的起点,也是 Q/K/V 这个抽象的雏形。
读完这一篇你会带走什么:
- 知道 Sutskever 2014 的 Sequence-to-Sequence 模型为什么撞上了「固定长度 context vector」这堵墙;
- 理解 Bahdanau 2014 怎么把这堵墙拆掉:让 decoder 在每一步动态加权 encoder 的所有 hidden state;
- 把
e_{t,i} = vᵀ tanh(W₁ s_{t-1} + W₂ h_i)这一行公式每一项的语义、张量形状、参数维度都拆清楚; - 看清这是 additive attention,明白它和 Luong 2015 提出的 multiplicative / dot / general 三种打分函数各自的取舍;
- 理解为什么 Bahdanau 用了双向 RNN(biRNN)做编码——这件事和 attention 本身有什么关系;
- 看清 Bahdanau 论文里那张著名的 Fig 3 对齐矩阵到底说明了什么;
- 知道这个机制虽然历史上被 Transformer 取代,但 Q/K/V 抽象就是从这里抽出来的。
一、2014 年的现场
1.1 那一年 NMT 的几个关键节点
把 Bahdanau 这篇论文放在历史时间线上理解,会让公式更亲切。
2013 年底,Mikolov 的 word2vec 让词向量在大规模语料上的训练成为可能;KyungHyun Cho 还在蒙特利尔做博士后,研究方向是「能不能把神经网络直接用在机器翻译上」。
2014 年 6 月,Cho、van Merriënboer、Bahdanau 等人挂出「Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation」(EMNLP 2014),第一次把 encoder-decoder RNN 用作 SMT 的特征提取器。
这篇论文还带来了一个副产品:GRU(Gated Recurrent Unit)。
2014 年 9 月,Sutskever、Vinyals、Le 在 Google 挂出「Sequence to Sequence Learning with Neural Networks」(NeurIPS 2014),证明只用 LSTM encoder + LSTM decoder(不带 attention)就能在 WMT’14 英法翻译任务上接近统计模型。
但 Sutskever 自己也在论文里承认了一件事:长句翻译质量明显下降。
他们的工程 trick 是把源句倒过来输入——这样源句的开头距离 decoder 的开头更近,效果竟然显著提升。
这件事本身已经在暗示「定长 context vector 的瓶颈是真的」。
不仅如此,Sutskever 团队后来还观察到:模型对长句的 BLEU 衰减曲线,几乎不取决于具体超参,更像是「定长向量容量本身的天花板」——这个观察后来被 Bahdanau 论文引为直接动机之一。
换句话说,「向 c 里塞太多东西」是一个结构问题,不是一个调参问题——再调也调不过去。
这是 attention 出场的精确时机:一个结构性瓶颈,需要结构性的解。
2014 年 9 月,几乎同时,Bahdanau、Cho、Bengio 在蒙特利尔挂出 arXiv 论文 1409.0473:「Neural Machine Translation by Jointly Learning to Align and Translate」。
这篇论文给出了一个截然不同的解法:不要把源句压成一个固定向量,让 decoder 在每一步动态选择源端要看的部分。
这就是注意力机制在 NMT 上的首次系统应用。
值得停下来感叹一下:从 Cho 2014 EMNLP(提出 encoder-decoder + GRU)到 Bahdanau 2014 arXiv(提出 attention),中间只隔了三个月,作者还有大量重叠。
这说明 attention 不是某次顿悟,而是同一批人在「定长 context vector 不够用」这件事上反复打磨之后必然出现的下一步。
也说明深度学习这个领域那几年迭代有多快——一个想法从「问题被识别」到「解决方案被发表」可能只有几周。
放到 2026 年回看,这种节奏在 LLM 时代变得习以为常,但在 2014 年是非常震撼的——你能看到一个领域在你眼前实时地长出新器官。
1.2 论文为什么取这个名字
注意论文标题:「Jointly Learning to Align and Translate」。
「align」是 SMT 时代一个非常具体的术语——IBM Models 几代人做的就是 align,把它当作隐变量学出来,再用对齐配上翻译概率。
Bahdanau 的标题在向 SMT 同行致敬:你们花二十年学的对齐,我们用一个 softmax 一起把它和翻译模型一起端到端学出来。
「jointly」是关键——在他们之前,对齐是离散的隐变量、翻译模型是另一个组件,二者用 EM 算法或 pipeline 串起来,互相独立训练。
Bahdanau 把对齐变成连续的、可微的、由神经网络生成的权重分布,然后让对齐和翻译共享同一套梯度,一起被优化。
这不仅是工程改进,更是哲学转向:离散的、独立训练的子模块 → 连续的、端到端联合训练的整体。
这条「让原本离散的中间态变得连续可微,并入主网络一起训」的思路,后来在 attention、Gumbel-softmax、可微编程里被反复使用,成了现代深度学习设计模式的母体之一。
Bahdanau 的工作把对齐变成连续的 softmax,可以和翻译模型用同一个反向传播一起学,这是「joint」最严格的意义。
1.3 Bengio 实验室那时的氛围
蒙特利尔那个时候在做什么是个有意思的旁白。
Bengio 实验室 2013–2015 年是深度学习 NLP 的几个中心之一,Cho 当时几乎一年发三四篇关键论文,Bahdanau 还是博士生。
Cho 后来到了 NYU,Bengio 拿了图灵奖,Bahdanau 去了 Element AI(后被 ServiceNow 收购)继续做 RL。
这条线的人脉影响了后续整个 NLP 与语音生态——后来著名的 Attention 是「Bahdanau 的孩子们」最熟悉的工具,并不是 Vaswani 一组人独立造出来的。
历史是连续的,重要论文之间的距离比你想的更近。
二、为什么固定 context vector 撞墙
2.1 Sutskever 2014 的范式
把 Bahdanau 之前的 NMT 套路再走一遍,能让你立刻理解他在解决什么。
Sutskever 的 Seq2Seq 模型简化下来是这样:
Encoder LSTM:
h₁ = LSTM(x₁, h₀)
h₂ = LSTM(x₂, h₁)
...
h_T = LSTM(x_T, h_{T-1})
固定 context vector:
c = h_T (只取最后一个 hidden state)
Decoder LSTM:
s₁ = LSTM(<bos>, c)
s₂ = LSTM(y₁, s₁)
...
y_t = softmax(W · s_t)
整个 source 句的所有信息被压缩在 c = h_T
这一个固定维度的向量里。
decoder 在生成所有 target 词的过程中,看到的「source 信息」就只有这个 c。
2.2 这堵墙具体是什么
这种设计有三道墙。
第一道,长程信息丢失。
LSTM 的 hidden state 是定长的(比如 256 或 1000 维)。
你想象一下,一个 100 词的英语长句,所有的语义都要被压进 256 维向量里——这个压缩比远超 LSTM 的容量。
实际表现是:生成 target 句的最后几个词时,模型已经「忘记」了 source 句开头说了什么。
第二道,信息瓶颈不可分配。
c 是固定的,不管你 decoder 在生成哪个 target 词,它看到的 source 表示都一样。
但翻译时不同 target 词依赖的 source 信息显然不一样——生成法语动词时主要看英语动词,生成名词时主要看英语名词。
固定 c 强迫模型用同一个表示应付所有需求,本质上违反了任务结构。
第三道,梯度路径过长。
decoder 的最后一个时间步要影响 encoder 的第一个时间步参数,梯度要走完 encoder 所有时间步 + decoder 所有时间步——典型的 RNN 长程梯度问题。
LSTM 有 gating 部分缓解,但 100 步以上的依赖仍然会衰减。
2.3 三个症状
这三道墙一起作用,导致几个具体症状。
第一个症状:长句翻译的 BLEU 显著低于短句。
Sutskever 论文里给过分长度段的 BLEU,30 词以下还行,超过 50 词就明显掉。
第二个症状:长句容易丢句尾。
decoder 生成到中后段就开始重复或者跑题,因为关于 source 末尾的信息已经在 c 里被前面的信息「覆盖」掉了。
第三个症状:训练 loss 会不规则地停滞或上升,特别是 batch 里夹了几个长句时。
这些都是 Bahdanau 试图解决的问题。
三、Bahdanau 的核心改动:动态 context
3.1 一句话总结这篇论文做的事
让 decoder 在每个时间步重新计算一个 context vector,而不是只用一个固定的 c。
这一句话就是论文 60% 的贡献。
剩下 40% 是「这个动态 context 怎么算」「编码器要换成双向 RNN」「整套机制怎么放进端到端训练」这些工程细节。
3.2 把 c 换成 c_t
具体地,Bahdanau 把 decoder 的更新公式从:
s_t = LSTM(y_{t-1}, s_{t-1}, c) 固定 c
y_t = softmax(W · s_t)
改成:
c_t = Σ_i α_{t,i} · h_i 动态 c_t(每步重新算)
s_t = LSTM(y_{t-1}, s_{t-1}, c_t)
y_t = softmax(W · s_t)
注意 c 现在带下标 t——它对每个 decoder 时间步都不同。
每生成一个新 target 词,模型重新决定要看 source 端的哪一部分。
3.3 双向 RNN 做编码
Bahdanau 还把 encoder 从单向 RNN 换成了双向 RNN(bidirectional RNN, biRNN)。
为什么?
因为单向 RNN 的 h_i 只编码了「位置 i 之前的内容」,而我们希望 h_i 是位置 i 处的双向上下文表示——既知道前面说了什么,也知道后面说了什么。
具体地:
forward: →h_i = RNN_fwd(x_i, →h_{i-1})
backward: ←h_i = RNN_bwd(x_i, ←h_{i+1})
组合: h_i = [→h_i; ←h_i] (拼接)
每个位置的 h_i 是 forward + backward hidden state 的拼接,所以维度是 2 × hidden_size。
这种 biRNN 编码 + attention 的组合是后续很多工作的标配。
3.4 整体架构图
把上面的部件拼起来:encoder biRNN 给出 h₁..h_T;decoder RNN 在每个时间步用上一时刻 hidden state s_{t-1} 当 query,跟 h_i 们打分得到 α_{t,i},加权得到 c_t,把 c_t 喂给 decoder RNN 算出 s_t,再用 s_t 算 y_t 的概率。
这是注意力第一次完整地嵌入一个端到端模型。
四、Additive Attention:那一行公式
现在我们把 Bahdanau 的打分函数完整拆开。
4.1 公式
打分函数(论文叫 alignment model):
e_{t,i} = a(s_{t-1}, h_i) = vᵀ · tanh(W₁ · s_{t-1} + W₂ · h_i)
其中:
s_{t-1}:decoder 上一时刻 hidden state,维度 d_sh_i:encoder 第 i 个位置的(双向)hidden state,维度 d_h(biRNN 时是 2 × hidden_size)W₁ ∈ ℝ^{d × d_s}:把 s 投影到 d 维W₂ ∈ ℝ^{d × d_h}:把 h 投影到 d 维tanh:把投影后的和过一个非线性v ∈ ℝ^{d}:把 d 维向量打成一个标量分数e_{t,i} ∈ ℝ:标量,表示 s_{t-1} 与 h_i 的对齐强度
4.2 为什么是 additive
这种打分形式被称为 additive attention,因为关键操作是 W₁ s + W₂ h 这个加法(而不是 s 和 h 的乘法)。
把它拆成步骤:
- 把 query s 投影到 d 维空间;
- 把 key h 投影到同样 d 维空间;
- 两个投影向量相加,过 tanh 引入非线性;
- 用一个学得的方向向量 v 把它打成标量分数。
整个过程相当于一个隐藏层维度为 d、输入是 [s; h] 拼接、输出 1 维的小 MLP——只不过实现上把拼接换成了「分别投影后相加」,二者等价。
具体来说,设 W = [W₁, W₂](按列拼接),则 W · [s; h] = W₁ s + W₂ h,所以「分别投影相加」和「拼接后用一个矩阵投影」数学上是同一件事。
4.3 softmax 归一化
得到所有 i 的 e_{t,i} 后,做一次 softmax:
α_{t,i} = exp(e_{t,i}) / Σ_j exp(e_{t,j})
α_{t,i} 现在是一个概率分布,i 取遍 1..T。
它表示「decoder 在时间步 t,应该把多少注意力分给 source 端第 i 个位置」。
4.4 加权求和
c_t = Σ_i α_{t,i} · h_i
c_t 是 source 端 hidden state 的加权平均,按 α_{t,i} 加权。
这就是动态 context vector,喂给 decoder RNN:
s_t = LSTM(y_{t-1}, s_{t-1}, c_t)
4.5 张量形状走一遍
为了把维度搞清楚,我们用具体数字走一遍。
假设:source 长度 T=20,hidden_size=256,biRNN 所以 d_h=512;decoder hidden 维度 d_s=256;attention 隐层 d=128。
参数维度:
- W₁: (128, 256)
- W₂: (128, 512)
- v: (128,)
每个时间步:
- s_{t-1}: (256,),W₁ s_{t-1}: (128,)
- 对每个 i:W₂ h_i: (128,),相加: (128,),tanh: (128,),vᵀ · tanh(…): 标量
- 共 20 个 i,所以 e_{t,1..20}: (20,)
- softmax: α_{t,1..20}: (20,),和为 1
- c_t = Σ α_{t,i} h_i: (512,)
这个流程每个 decoder 时间步要算一次。
batch 维度可以并行,但「跨 t 时间步」是顺序的——这件事我们后面会回来讨论。
4.6 把 additive 写成矩阵形式
虽然 additive attention 通常逐对调用,但我们也可以把它批处理。
设 H ∈ ℝ^{T×d_h} 是 encoder 所有 hidden state 拼起来的矩阵(一共 T 行),s ∈ ℝ^{d_s} 是 query。
定义:
- A = H · W₂ᵀ ∈ ℝ^{T×d} 所有 key 的投影
- b = s · W₁ᵀ ∈ ℝ^{d} query 的投影
- E = (A + b) · vᵀ broadcast 加 + 投影成标量
E ∈ ℝ^T,softmax(E) 给出 α,再 αᵀ · H 得到 context。
这样写出来就能在 GPU 上批处理一个时间步,但 t 之间仍然是顺序的——这是 Bahdanau 不可解的并行瓶颈。
4.7 几何直觉
最后给一个几何直觉。
W₁ s + W₂ h 这个加法发生在「投影后的 d 维空间」里。
W₁ 把 query 投到这个空间的某个点 p_q,W₂ 把每个 key 投到这个空间的若干点 p_{k,i}。
p_q + p_{k,i} 是 query 点和 key 点的「向量和」(在投影空间里)。
tanh 把和压进 [-1, 1]^d 的盒子里——一个非线性激活。
vᵀ 在这个盒子里指一个方向,把每个 (p_q + p_{k,i}) 投到这个方向上得到标量分数。
模型要学的事情就是:W₁、W₂、v 这三个参数,让 (p_q + p_{k,i}) 沿 v 方向的投影长度,准确反映「query 和 key_i 应该多匹配」。
这是一个非常通用、也非常不显然的相似度函数——比简单的内积更灵活,但代价是参数和计算量都更大。
五、Luong 2015:另一种打分函数
Bahdanau 之后一年,Luong, Pham, Manning 在 EMNLP 2015 发了一篇「Effective Approaches to Attention-based Neural Machine Translation」,对 Bahdanau 的若干设计做了简化和改进。
5.1 三种打分函数
Luong 提出了三种 score 形式:
dot: score(s, h) = sᵀ · h
general: score(s, h) = sᵀ · W · h
concat: score(s, h) = vᵀ · tanh(W · [s; h]) (等价 Bahdanau additive)
dot 形式无任何参数,最简单;general 加一个 W 矩阵让 s 和 h 维度可以不同;concat 就是 additive 的另一种写法。
5.2 multiplicative 的优势
dot 和 general 被合称为 multiplicative attention,因为关键运算是矩阵乘 sᵀh 或 sᵀWh。
multiplicative 比 additive 有几个工程优势:
第一,所有 query 和所有 key 的打分可以一次矩阵乘算完——QKᵀ 一次得到一整张 score 矩阵,不用逐对调用 MLP。
第二,参数更少(dot 形式甚至没参数)。
第三,GPU 矩阵乘原语高度优化,速度快。
第四,没有 tanh 这种非线性,反向传播更简单。
5.3 additive 的优势
additive 也有它的好处。
第一,它带 tanh 非线性,理论表达能力更强。
第二,s 和 h 维度不同时不需要额外处理(因为有 W₁、W₂ 各自投影)。
第三,在 d_k 较大时数值更稳定——multiplicative 在 d_k 大时点积方差爆涨,需要做 √d_k 缩放(这就是第十五篇要讲的事)。
实证上,在 Bahdanau 那一代的中等模型尺寸(hidden size 几百)下,additive 略好;模型变大后 multiplicative + scaling 反而更强,最终成为 Transformer 的选择。
5.4 对比表
| 维度 | Additive (Bahdanau) | Multiplicative (Luong) |
|---|---|---|
| 公式 | vᵀ tanh(W₁s + W₂h) |
sᵀh 或 sᵀWh |
| 参数量 | W₁、W₂、v,三组 | 0(dot)或 1 组(general) |
| 非线性 | tanh,有 | 无 |
| GPU 友好度 | 中(需逐对 MLP,可矩阵化但不漂亮) | 高(纯矩阵乘) |
| 高维表现 | 稳定 | 需 √d_k 缩放 |
| 历史归宿 | 早期 NMT 主流 | 被 Transformer 选中 |
| 何时仍有用 | small d、需要表达力 | 大模型 |
5.5 Luong 还提了 global vs local
Luong 那篇论文还有另一个改进:local attention。
global attention 让 query 看 source 端所有位置;local attention 用一个学得的「对齐位置」p_t 把注意力限制在 [p_t - D, p_t + D] 这一窗口内,外面的权重设 0(或用高斯权重衰减)。
local 的动机是省计算:source 长度 T 很大时,softmax over T 既慢又会分散注意力。
但 local 在 NMT 上效果增益不稳定,最终没成为主流。
到了 Transformer 时代,long-context 的研究又回到了 local/sparse attention 这条线,Longformer、BigBird、Sparse Transformer 等等可以看作 Luong local attention 的远房后代。
六、那张著名的对齐矩阵
Bahdanau 论文里有几张可视化图,最有名的是 Fig 3——英法翻译里学到的对齐矩阵。
6.1 这张图说明了什么
每个像素的灰度对应 α_{t,i} 的大小:横轴是英语(source),纵轴是法语(target),格子越亮意味着这对位置之间注意力越强。
主对角线说明大多数位置一对一对齐——这是英法翻译里很常见的局部一致性。
但论文重点在那些反对角的交叉:
- 英语「European Economic Area」 ↔︎ 法语「zone économique européenne」
法语的形容词后置 + 名词反序,让对齐线在矩阵里出现了清晰的「交叉」。
如果用 SMT 时代的硬对齐 + 单调假设,这种交叉非常难学。
软对齐 + 端到端训练让它自然涌现。
6.2 这是「学到」还是「巧合」
要警惕一件事:可视化对齐矩阵看起来很合理,并不意味着模型「真的」按这个对齐做翻译。
attention 的权重和模型决策之间不一定是因果关系——这一点我们在第十一篇已经强调过,会在第五十二篇详细展开。
但在 Bahdanau 这一代相对简单的模型上,可视化的可解释性比后来的 Transformer 强不少,因为:
- 模型层数浅(一层 attention),权重没有被多层叠加打混;
- 单 head,没有 multi-head 的解耦干扰;
- 任务结构(翻译)天然有强对齐先验。
所以 Fig 3 那种漂亮的对齐图,在 Transformer 时代不再容易得到——多层多头之后注意力权重往往看起来很乱。
6.3 对齐矩阵的工程价值
虽然「对齐 ≠ 因果」,对齐矩阵在工程上仍然非常有用。
它可以用来:
- 诊断模型是否学到了合理的对齐结构(如果完全均匀,模型可能有问题);
- 检测训练异常(比如 padding mask 漏了,导致 pad 位置拿到非零权重);
- 在低资源语言对上做 attention transfer 之类的迁移学习。
写 NMT 模型时,把 attention 权重保存下来做可视化是一个很标准的 sanity check 步骤。
七、为什么这是 Q/K/V 的雏形
现在我们站到 Transformer 的视角回看 Bahdanau,看一眼 Q/K/V 是怎么从这一组公式里抽出来的。
7.1 把 Bahdanau 改写成 Q/K/V
Bahdanau 原始公式:
e_{t,i} = vᵀ tanh(W₁ s_{t-1} + W₂ h_i)
α_{t,i} = softmax(e_{t,i})
c_t = Σ α_{t,i} h_i
把 s_{t-1} 改名叫 query q,把 h_i 改名叫 key k_i 与 value v_i(注意这里 K 和 V 取自同一个 h):
e_{t,i} = score(q, k_i) 打分函数
α_{t,i} = softmax(e_{t,i}) 归一
output = Σ α_{t,i} · v_i 加权求和
抽象出来就是 attention 的标准三步流水线:score → softmax → weighted sum。
7.2 Bahdanau 的「Q/K/V 退化版」
Bahdanau 实际上隐含了两个简化:
第一,没有显式的 W_Q、W_K、W_V 投影。
s 和 h 被直接当 query 和 key/value 用,只有 W₁、W₂ 在 score 函数内部做投影。
第二,K = V。
attention 用 h_i 算分数,又用 h_i 加权——key 和 value 是同一个东西。
这两个简化在小模型上没什么问题,但限制了模型的表达力:
- 没有 W_Q/K:query 和 key 必须用同一种「角度」表示,无法分别学最适合检索的两种投影;
- K = V:检索 key 和取出 value 必须共享表示,无法学出「按一个角度索引、按另一个角度取信息」。
Vaswani 2017 在 Transformer 里把这两个简化都拆掉了——这就是第十三篇要讲的事。
7.3 一行话总结
Bahdanau 是 attention 的「最朴素可用版」,Vaswani 是「显式投影、矩阵化、可堆叠」版。
机制相同,工程框架不同。
八、Bahdanau 实现的工程细节
讲完概念,来看几个 Bahdanau 实现里容易踩的具体细节。
8.1 query 用 s_{t-1} 还是 s_t
注意 Bahdanau 公式里是
e_{t,i} = a(s_{t-1}, h_i)——用的是 decoder
上一时刻 hidden state s_{t-1} 当 query。
为什么不是 s_t?
因为 s_t 还没算出来。
decoder 的递归是
s_t = LSTM(y_{t-1}, s_{t-1}, c_t),要算 s_t
必须先有 c_t;要有 c_t 必须先有 α_{t,i};要有 α 必须先有
query;query 只能用 s_{t-1}。
这是一个循环依赖问题,Bahdanau 用「先用 s_{t-1} 当 query」解决。
Luong 的另一个改进是用 s_t 当 query——他先用 s_{t-1} + y_{t-1} 算一个临时 s_t,再用这个 s_t 做 attention,最后再算正式的输出。
二者实证差异不大,但实现复杂度有差。
8.2 attention 算在 LSTM 之前还是之后
Bahdanau:先算 attention 得 c_t,把 c_t 喂进 LSTM 算 s_t,s_t 直接产 y_t。
Luong:先算 LSTM 得 s_t,再算 attention 得 c_t,把 c_t 和 s_t 拼接过一个 MLP 产 y_t。
两种风格都被后来用过,没有谁压倒谁。
8.3 attention 的并行性受限
Bahdanau 的 attention 在 decoder 时间步上仍然是顺序的:
for t in 1..T_target:
α_t = softmax(score(s_{t-1}, h_*))
c_t = Σ α_t * h
s_t = LSTM(y_{t-1}, s_{t-1}, c_t)
虽然每个 t 内部的 score、softmax、加权求和都可以并行,但 t 之间是顺序的(s_t 依赖 s_{t-1})。
这是 Bahdanau 没能解决的根本瓶颈——decoder 仍然是 RNN,仍然没办法并行。
Transformer 的另一个关键贡献是把 decoder 也变成 attention(带 causal mask 的 self-attention),训练时整段 target 序列可以并行 forward——这是第十七、二十二、二十三篇要讲的事。
8.4 数值稳定与 softmax mask
工业实现里要注意:
- softmax 必须用 max-subtract 稳定形式;
- 如果 source 端有 padding,mask 必须加在 score 上(而不是 α 上);
- score 的 dtype 通常用 fp32,即使前后是 fp16;
- 大 batch 训练时记得把 attention 矩阵的 shape 算清楚——T_target × T_source × batch 的内存随长度平方涨。
8.5 一个最小的 PyTorch 实现
import torch
import torch.nn as nn
class BahdanauAttention(nn.Module):
def __init__(self, d_s, d_h, d_a=128):
super().__init__()
self.W1 = nn.Linear(d_s, d_a, bias=False)
self.W2 = nn.Linear(d_h, d_a, bias=False)
self.v = nn.Linear(d_a, 1, bias=False)
def forward(self, s_prev, h, mask=None):
# s_prev: (B, d_s)
# h: (B, T, d_h)
# mask: (B, T) 1=valid, 0=pad
s_proj = self.W1(s_prev).unsqueeze(1) # (B, 1, d_a)
h_proj = self.W2(h) # (B, T, d_a)
e = self.v(torch.tanh(s_proj + h_proj)).squeeze(-1) # (B, T)
if mask is not None:
e = e.masked_fill(mask == 0, -1e9)
alpha = torch.softmax(e, dim=-1) # (B, T)
c = torch.einsum("bt,btd->bd", alpha, h) # (B, d_h)
return c, alpha这就是 Bahdanau attention 的全部代码。
把 RNN encoder + 这一段 attention + RNN decoder 接起来,就能跑出 2014 年那个 SOTA NMT 模型。
8.6 实现里几个常见 bug
第一个常见 bug:忘了 unsqueeze(1)。
s_prev 是 (B, d_s),h_proj 是 (B, T, d_a),要相加必须把 s_proj 扩出 T 维度——unsqueeze(1) 后 broadcast 出 (B, T, d_a),加法才合法。
漏了 unsqueeze 直接加会报维度不匹配,但有时候用错了 unsqueeze(0) 反而能跑通——broadcast 出错误的形状,模型完全学不到东西。
第二个常见 bug:mask 的极小值。
-1e9 在 fp32 下没问题,但 fp16
下会溢出(fp16 最小是 -65504)。
混合精度训练时要把 mask 值改成 fp16 安全的 -1e4 或者 -float(‘inf’)。
第三个常见 bug:attention pooling 的方向搞错。
einsum("bt,btd->bd") 的语义是「按 t
维度做加权求和,每个 b 输出一个 d 维向量」。
如果不小心写成 einsum("bt,btd->btd")
就变成逐位置乘,没有 sum——结果维度不对,但 PyTorch
不会报错,只会让模型学不出来。
第四个常见 bug:把 alpha 当 probability log 了。
alpha 已经是 softmax 后的概率了,不需要再 log 或 exp。
但有些教材把 attention 的 entropy 也作为正则项,那时候要 -Σ α log α,注意不要重复指数化。
8.7 训练时的 attention 监控
训练 NMT 模型时,attention 权重的几个指标值得监控:
attention entropy:H(α) = -Σ α log α,反映分布锐度。训练初期通常很高(接近均匀),慢慢下降到中等值。如果一直很高说明模型没学到对齐;如果突然变 0 说明 collapse 到单点了,通常意味着 mask 错了。attention coverage:每个 source 位置在整个 target 序列上累积获得的注意力总和。理想状态下大致均匀(每个 source 词都被「翻译过」),如果某个 source 位置 coverage 接近 0,可能是这个词在 target 里被漏译。argmax alignment quality:取 α 的 argmax 作为硬对齐,和 fast_align(一个 SMT 时代的对齐工具)的对齐做比较,看 F1。这是 NMT vs SMT 对齐质量的经典对照。
这些监控指标在第十一篇讨论 attention ≠ explanation 时已经埋下伏笔——它们能帮你诊断模型,但不能直接解释模型决策。
九、Bahdanau 之后的几个变种
讲完 Bahdanau 本体,我们简单提一下 2014–2017 年间在 attention 上做的几个变种,这些变种很多被 Transformer 吸收或绕开。
9.1 Coverage Mechanism
Tu 等 2016 提出 Coverage:在 score 函数里加入「这个 source 位置之前已经被关注过多少」的累积量,惩罚重复关注、鼓励覆盖未被关注的位置。
数学上是给 score 加一项:
e_{t,i} = a(s_{t-1}, h_i, coverage_i)
coverage_i = Σ_{t'<t} α_{t',i}
这个 trick 在 NMT 翻译漏译/重译的问题上有显著效果。
但它是一个领域特定的 inductive bias——Transformer 时代被通过 multi-head + 位置编码 + 大数据自然解决,不再需要显式 coverage。
9.2 Local / Global / Hybrid
Luong 提的 local attention 把权重限制在固定窗口内。
后来 Yang 等的 hierarchical attention(用于文档分类)把 attention 分层:词级 attention 聚合成句子表示,句子级 attention 聚合成文档表示。
这些层级 attention 的思路在 Transformer 时代被「堆深」替代——多层 self-attention 自然形成层级表示。
9.3 Position-aware Attention
纯 content-based 的 attention 在某些任务(语音对齐、单调翻译)上不够稳。
Tacotron 系列使用 location-sensitive attention:除了内容打分外,还加入「上一时刻 attention 分布的卷积特征」,鼓励 attention 单调向前移动。
这种位置感知 attention 在 Transformer 时代被位置编码替代,但在 TTS 任务上至今仍在用。
9.4 Attention over Attention
Cui 等 2016 的 AoA Reader(Attention over Attention)在阅读理解上把 attention 矩阵自身作为新的输入,再过一层 attention。
这种「双层 attention」思路被 Transformer 的 multi-head + 多层堆叠自然包含。
十、实验结果与历史地位
10.1 论文的实验
Bahdanau 论文在 WMT’14 英→法翻译任务上做了实验。
baseline 是不带 attention 的 RNN encoder-decoder(30 词词表的 phrase-based)。
attention 模型称作「RNNsearch」,baseline 称作「RNNenc」。
主要结论:
- RNNsearch-30(30k 词表)BLEU 比 RNNenc-30 高约 4 分;
- RNNsearch-50(50k 词表)BLEU 比 RNNenc-50 在长句上提升尤其明显;
- 长句翻译的 BLEU 不再随长度衰减——这是 attention 最直接的胜利。
具体数字论文里写得很清楚,这里不复述(也不编造)。
我特别提一句:他们对比了 30 词长度以上的句子,RNNenc 的 BLEU 跌得非常快,而 RNNsearch 几乎保持平稳。
这条曲线后来被反复引用,是 attention 替代 fixed-length context 的最直接证据。
10.2 从 Bahdanau 到 Transformer 的三年
2014 年 Bahdanau;2015 年 Luong;2016 年 Google 的 GNMT 在生产环境上线 attention-based NMT;2017 年 Transformer 出现,把 RNN 完全去掉。
这三年时间线说明几件事:
第一,attention 一旦被发明,工业界采纳速度极快——Google 不到两年就把它用到生产翻译系统。
第二,attention 和 RNN 的结合一直被视为 NMT 的标配,直到 2017 年才被「纯 attention」替代。
第三,「能不能去掉 RNN」是这三年里反复被问的问题——Bahdanau 的方法把 RNN 作为骨架、attention 作为补充,Transformer 反过来:attention 作为骨架,RNN 完全消失。
10.3 历史地位
Bahdanau 这篇论文在 NLP 历史上的地位类似 word2vec 在词向量历史上的地位——不是第一个想到这件事的人,但是第一个把它做出来、做对、做成主流的人。
到 2026 年这个时间点,这篇论文的引用量已经过万。
更重要的是:现在所有 NLP 学生第一次接触 attention,几乎都是从「Bahdanau 公式」开始学的。它既是历史遗物,又是教学起点。
十一、现在还能用 Bahdanau 吗
到 2026 年,几乎所有大模型都用 Transformer-style scaled dot-product attention,Bahdanau 这种 additive attention 是不是已经被淘汰?
不完全是。
11.1 哪些地方仍然在用
第一,TTS 和语音对齐里还经常出现 Bahdanau-like attention。
Tacotron(Google 2017)用的是位置敏感版的 additive attention,因为语音对齐需要单调对齐先验,纯 dot-product 不够稳。
第二,pointer networks(Vinyals 2015)用类似 Bahdanau 的形式,因为输出 vocabulary 直接就是输入位置——score 越高表示越倾向「指向」该位置。
第三,graph neural network 的 GAT(Graph Attention Network, Veličković 2017)用的是单层 additive attention 在邻居上算权重。
11.2 在小模型 + 短序列场景下,additive 仍然有竞争力
在中等规模 + 中等序列长度(比如 hidden size 几百、序列长度几十到几百)的任务上,additive attention 由于带 tanh 非线性,表达力略强,且不需要 √d_k 缩放,训起来更稳。
只有当模型规模上来、d_k 上 64/128/256,且需要堆十几层 attention 时,scaled dot-product 的 GPU 友好度和数值稳定性才显出绝对优势。
11.3 教学价值
最重要的:Bahdanau 是理解 Q/K/V、scaled dot-product、multi-head 的最佳起点。
跳过它直接学 Transformer,会失去很多直觉锚点——尤其是「为什么是这三个矩阵」「为什么要 softmax」「为什么 K 和 V 可以分开」这些问题,没经过 Bahdanau 这一层就很难自然回答。
11.4 一份「Bahdanau → Transformer」的对照表
为了把这一篇收尾时的视角立住,我把两篇论文的关键设计放在一起对照:
| 维度 | Bahdanau 2014 | Vaswani 2017 |
|---|---|---|
| Encoder | biLSTM/biGRU | self-attention 堆栈 |
| Decoder | LSTM/GRU 加 attention | masked self-attention + cross-attention 堆栈 |
| Query | s_{t-1} | 投影后的 X · W_Q |
| Key | encoder hidden h_i | 投影后的 X · W_K |
| Value | encoder hidden h_i(与 K 相同) | 投影后的 X · W_V(与 K 不同) |
| 打分函数 | additive: vᵀ tanh(W₁q + W₂k) | scaled dot-product: qᵀk / √d_k |
| 多头 | 单头 | multi-head(h 个并行注意力子空间) |
| 训练并行 | decoder 顺序 | encoder/decoder 整段并行(causal mask) |
| 位置信息 | RNN 递归隐式编码 | 显式位置编码 |
| 历史归宿 | 2014–2017 NMT 主流 | 2017+ 几乎所有大模型 |
这张表看下来你会发现:Bahdanau → Transformer 不是一步飞跃,而是几个独立改动的叠加——投影、矩阵化、多头、纯注意力骨架、位置编码——每一项都独立可解。
第十三到十六篇会把这几项分别讲透。
十二、关键概念回顾
回顾这一篇,最核心的几句话是:
Bahdanau 把 Sutskever 那个固定的 context vector c 拆成动态的 c_t——decoder 在每个时间步重新计算一次 context,不再一刀切地用一个向量应付所有 target 词。
这个动态 context 由打分
e_{t,i} = vᵀ tanh(W₁ s + W₂ h) 算出,softmax
归一成 α,再加权求和 h 得到 c_t。
打分这一行是 additive attention 的标准形式:query 和 key 分别投影后相加、过 tanh、用一个学得的方向向量打成标量。
加性 vs 乘性的区别我们用了一节专门讲清楚:additive 表达力略强、对 d 不敏感;multiplicative 矩阵乘友好、参数少;高维下乘性需要 √d_k 缩放——这个我们留到第十五篇。
我们也指出了 Bahdanau 的两个简化:没有显式 W_Q/W_K/W_V 投影、K=V。这两点正是 Transformer 要拆掉的,第十三篇会专门展开。
最后回到历史:Bahdanau 是 attention 在主流 NMT 上的首次大规模成功,也是 Q/K/V 抽象的种子。从它到 Transformer 之间只有三年,但抽象的飞跃已经基本完成。
十三、常见误解
12.1 Bahdanau 用了 LSTM 还是 GRU
Bahdanau 论文用的是 GRU(Cho 在前一篇 EMNLP 论文里刚提出的门控单元),不是 LSTM。
但教学上常常含糊地说「Bahdanau 用 LSTM」——不影响理解,但写论文时要注意原文细节。
12.2 Bahdanau attention = Bahdanau RNNsearch 模型
attention 是机制,RNNsearch 是用了这个机制的具体模型。
很多人把二者混着说,导致讨论时不知道是在讲注意力本身还是整套模型架构。
12.3 Bahdanau 是第一个把 attention 用在 NLP 的
不准确。
Graves 2013 的语音生成、若干早期对齐模型已经在用「学一个权重分布加权」的思路。
Bahdanau 的独特贡献是在主流 NMT 任务上端到端可训地证明 attention 能赢——这是工程影响力的胜利,不是机制发明的胜利。
12.4 attention 之后 RNN 就没用了
Bahdanau 之后的三年里,attention + RNN 是 NMT 的标配。
直到 Transformer 把 RNN 砍掉,「attention 替代 RNN」才成现实。
但 RNN 在某些小模型、某些时序任务(特别是 streaming 推理)里到 2026 年仍然有应用。
12.5 additive attention = soft attention,multiplicative = hard attention
完全不对。
soft / hard 是按「是否离散选择」分的,softmax 是软的、argmax + 采样是硬的。
additive / multiplicative 是按「打分函数形式」分的,前者用 MLP,后者用矩阵乘。
二者是两个独立的维度,不要混。
12.6 attention 必须配 RNN
也不对。
Bahdanau 配的是 RNN,是历史选择。
Transformer 把 attention 配上 FFN + 残差 + LayerNorm,没有 RNN,照样工作。
attention 本身只规定了「query 加权候选」,不规定 query/key/value 要从什么模型生成。
12.7 注意力权重一定单调
很多人看到 Bahdanau Fig 3 的对角线,就以为 attention 学的对齐总是单调的。
不是。
英法翻译里有大量交叉对齐,日文-英文翻译几乎完全反序对齐。
attention 没有任何「单调」先验——这是它的优势,也是为什么它能学到 SMT 硬对齐学不到的复杂模式。
但代价也是明显的:它没有先验,就得靠数据把对齐学出来——数据不够时,它会学出各种奇怪的、看起来像噪声的对齐。
这件事到 Tacotron 这种 TTS 任务上尤其明显:纯 Bahdanau attention 在前几千步几乎学不出像样的对齐,需要加位置敏感先验或 forward attention 才稳定。
也就是说,「有没有先验」不是好坏问题,而是「数据 + 先验」的总信息量够不够把对齐学出来。
12.8 attention 越多层越好
也不准确。
Bahdanau 是单层 attention,Transformer 是多层 attention 堆叠。
但「层数」与「性能」之间不是单调关系——浅层模型在小数据上可能优于深层模型,过深还会带来梯度问题。
「多 head」和「多层」也是两件事,初学者容易混。
12.9 看到漂亮对齐图就说 attention「理解」了语言
这是最常见也最深的误解。
Bahdanau Fig 3 那种漂亮的对角线,让人本能地认为「模型学到了对齐 = 学到了语义」。
但 Jain & Wallace 2019、Wiegreffe & Pinter 2019 的工作告诉我们:attention weights ≠ 模型解释。
我们后面专门有一篇(第 52 篇预告)讨论这件事,这里只先埋一个伏笔——漂亮的 attention 图很容易让你过度解读。
十四、Bahdanau 注意力的可视化与调试
最后再补一节,讲一些实操中观察、调试 Bahdanau attention 的经验。
14.1 怎么画对齐图
收集每个 (t, i) 的 α_{t,i},得到一个 T_tgt × T_src 的矩阵。
用热力图画出来——颜色越亮表示权重越高。
这就是 Bahdanau 论文 Fig 3 的画法。
横轴是 source 词、纵轴是 target 词,对角线代表「位置对应」。
14.2 一些常见模式
训练初期:α 接近均匀,因为 W₁/W₂/v 没学好,所有 e_{t,i} 接近相等。
训练中期:开始出现微弱对角线,模型先学会「邻近位置更相关」。
训练后期:清晰对角线 + 局部偏移(语序差异处的交叉对齐)。
如果训了很久 α 仍接近均匀——大概率是学习率、梯度裁剪或 mask 处理出了问题。
如果 α 总是塌缩到第一个或最后一个 token——通常是 EOS / pad 处理错误,或者 score 数值溢出导致 softmax 退化。
14.3 看 entropy 比看图更可靠
人眼看图容易自我欺骗(看到自己想看到的对齐)。
更客观的做法是看每行 α 的熵:
H(α_t) = -Σ α_{t,i} log α_{t,i}
熵接近 log(T) 表示几乎均匀(没学到对齐);熵接近 0 表示几乎 one-hot(强对齐)。
训练过程中 H(α_t) 应该从 log(T) 单调下降到一个稳定值(通常在 1 ~ 3 之间,取决于源句长度和任务难度)。
如果熵不下降,attention 没在学;如果熵下降到接近 0,说明 attention 极度尖锐——这有时不是好事,可能是过拟合了某个 spurious shortcut。
14.4 Coverage vector:检测 over-translation 与 under-translation
Tu et al. 2016 引入的 coverage vector 给每个 source token 累计「累计被 attend 的总权重」:
C_i = Σ_t α_{t,i}
如果某个 i 的 C_i ≈ 0,说明 source 第 i 个词在整段翻译里没被 attend——很可能漏译。
如果某个 i 的 C_i 远大于 1,说明被反复 attend——可能重复译。
这是一个简单但非常实用的诊断工具,到 2026 年的 NMT 系统里仍有人用。
14.5 用「梯度归因」交叉验证 attention 是否可解释
光看 α 不够,还要用 input gradient(如 saliency、Integrated Gradients)做交叉验证。
如果 attention 高但梯度归因低——说明这个 token 虽然被 attend,但对最终预测影响有限(attention 在「装样子」)。
如果 attention 低但梯度归因高——说明模型其实是通过别的路径(残差、上一层)拿到信息的。
两条信号相互印证才比较可靠。
这件事在 BERT/Transformer 时代成了一门小学科,但思路在 Bahdanau 时代就埋下了。
十五、下一步
下一篇 13|Q/K/V 三件套 会把 Bahdanau 的写法完全抽象成 Q/K/V 三个矩阵的组合——这是整个系列最核心的一篇。
我们会回答:为什么是这三个矩阵不是别的?为什么 K 和 V 要分开?W_Q/W_K/W_V 各自学到了什么?scaled dot-product 的内核为什么是 softmax(QKᵀ/√d_k) V?
读完那一篇,你应该能对着 Transformer 的 attention 公式,把每一项的语义、几何意义、工程动机立刻说清楚。
再往后:14|Self-Attention 会展示当 Q/K/V 都来自同一序列时,cross-attention 退化成 self-attention 的过程;15|Scaled Dot-Product 会推导那个看似奇怪的 √d_k 是怎么来的。
如果时间充裕,强烈推荐把 Bahdanau 原文(arXiv:1409.0473)通读一遍。
它只有十几页,公式不复杂,但叙述的节奏感非常值得学习——尤其是怎么用一段话讲清楚「为什么固定 context vector 不行」这件事,比任何二手讲解都更紧致。
读到 §3.2 的对齐模型与 Fig 3 的对齐图时,建议停下来,自己用纸笔画一画 αᵀh 的求和过程,你会对「加权平均」这件事有更具体的体感。
如果想动手,hugginface transformers 仓库里仍保留 BahdanauAttention / LuongAttention 的实现(在 OpenNMT、AllenNLP 这些框架里也有)。
把它们和后面我们要写的 nn.MultiheadAttention 对照阅读,能直观看到「投影 → 打分 → softmax → 加权」这条线在两种范式里几乎同构,只是「打分」一步用了不同的函数。
十六、参考文献
下面列出本篇直接引用与延伸阅读,按相关度排序。每条后面附一句话提示其在本篇中的角色,方便后续按需跳读。
- Bahdanau, D., Cho, K., Bengio, Y. “Neural Machine Translation by Jointly Learning to Align and Translate.” ICLR 2015 (arXiv:1409.0473, 2014). 本篇主角,additive attention 的源头论文,Fig 3 对齐图必看。
- Sutskever, I., Vinyals, O., Le, Q. V. “Sequence to Sequence Learning with Neural Networks.” NeurIPS 2014. 固定 context vector 的代表作,是 Bahdanau 想要打破的对象。
- Cho, K. et al. “Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation.” EMNLP 2014. GRU 的提出与首版 encoder-decoder。
- Luong, M.-T., Pham, H., Manning, C. D. “Effective Approaches to Attention-based Neural Machine Translation.” EMNLP 2015. multiplicative attention、global/local attention 的源头。
- Vaswani, A. et al. “Attention Is All You Need.” NeurIPS 2017. Transformer 论文,本系列后续核心引用。
- Vinyals, O., Fortunato, M., Jaitly, N. “Pointer Networks.” NeurIPS 2015. 把 attention 直接当成「指针」的早期变体。
- Wang, Y. et al. “Tacotron: Towards End-to-End Speech Synthesis.” Interspeech 2017. 位置敏感 additive attention 的代表用法。
- Veličković, P. et al. “Graph Attention Networks.” ICLR 2018. additive attention 在图神经网络里的迁移。
- Wu, Y. et al. “Google’s Neural Machine Translation System: Bridging the Gap between Human and Machine Translation.” arXiv:1609.08144, 2016.(GNMT,attention NMT 的工业落地里程碑)
- Brown, P. F. et al. “The Mathematics of Statistical Machine Translation: Parameter Estimation.” Computational Linguistics, 1993. 经典 IBM Models,软对齐思路的统计机器翻译先声。
- Tu, Z. et al. “Modeling Coverage for Neural Machine Translation.” ACL 2016. coverage vector 与漏译/重复译诊断方法。
- Xu, K. et al. “Show, Attend and Tell.” ICML 2015. soft vs hard attention 的视觉版对照。
- Jain, S., Wallace, B. C. “Attention is not Explanation.” NAACL 2019.
- Wiegreffe, S., Pinter, Y. “Attention is not not Explanation.” EMNLP 2019. 与 13 配套读,关于 α 是否能当作模型解释的争论。
← 上一篇:11|「注意力」的直觉 | 下一篇:13|Q/K/V 三件套 →
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【Transformer 与注意力机制】19|《Attention Is All You Need》论文背景
回到 2017 年 6 月那篇论文:八位作者、Google Brain/Translate 的内部背景、LSTM 时代的工程困境、为什么这篇在当年是「机器翻译的论文」、为什么七年后却被读成了「大模型时代的圣经」。
【Transformer 与注意力机制】03 矩阵乘法的两种视角
把矩阵乘法掰开成两种等价但风格不同的视角——『行 × 列』的点积视角和『列的线性组合』视角,最终落到 QK^T 的形状分析。
【Transformer 与注意力机制】01|为什么要从这里开始
这是【Transformer 与注意力机制】系列的第一篇,承担两件事:一是把这套五十多篇文章为谁写、解决什么问题、彼此之间是什么关系交代清楚;二是为完全没基础的读者画出一条从向量、点积、矩阵乘法走到自注意力、再走到大语言模型的爬升路径,让你在投入时间之前先知道终点在哪、路上要经过哪些坎、读完之后你会、还不会做什么事。
【Transformer 与注意力机制】系列总览
从《Attention Is All You Need》出发,把注意力机制、Transformer 架构、训练范式、模型变体、推理工程、可解释性与未来架构串成一条 58 篇的深度博客线。