注意力机制的诞生:从 Seq2Seq Attention 到注意力的本质
导学
在 Transformer 诞生之前,序列建模任务长期依赖 RNN、LSTM、GRU 这类循环结构。循环模型依靠时序逐次迭代完成文本编码,天然存在时序依赖,无法实现并行计算,长距离信息传递过程中还会出现梯度消失问题,当文本序列长度增加,模型很难捕捉相距较远词语之间的关联关系。注意力机制(Attention Mechanism)的提出,正是为了解决循环网络的信息遗忘瓶颈。本章将完整回溯注意力机制的发展脉络,从早期 Seq2Seq 架构的固有缺陷切入,讲解加法注意力、乘法注意力的数学推导,剖析注意力的底层本质,对比不同注意力变体的优劣,结合代码演示基础注意力的实现,同时打通注意力机制与后续 Transformer 架构之间的技术演进链路,为后续自注意力、完整 Transformer 章节做好理论铺垫。对于零基础学习者,本章会规避过度晦涩的数学陷阱,把公式和业务场景、文本实例绑定,建立 "注意力本质就是模型动态权重分配策略" 这一核心认知。
27.1 Seq2Seq 架构的固有缺陷
Seq2Seq,又称为编码器‑解码器(Encoder‑Decoder)框架,2014 年被正式提出,最早应用于机器翻译任务,也是大模型出现之前主流的序列生成框架。整套架构分为两大部分:编码器负责读取输入完整序列,把全部输入信息压缩为一个固定维度的上下文向量 Context Vector,也常被叫做语义向量;解码器读取这个唯一的上下文向量,逐 token 生成输出序列。
以中英机器翻译场景举例,输入中文句子 "人工智能正在改变世界",编码器循环遍历每一个汉字,一步步更新内部隐状态,当把全部 7 个汉字处理完毕之后,输出一个固定大小的上下文向量,这个向量理论上存储整句话全部语义。之后解码器以这个向量作为初始输入,循环输出英文单词 "Artificial intelligence is changing the world"。
这里最致命的矛盾点就来自固定长度上下文向量。无论输入句子是 5 个字符,还是数百字符长文本,最终都要被挤压到同一个固定维度向量内部。向量的存储容量存在上限,句子较短的时候,还可以勉强容纳关键语义;当序列变长,大量语义信息会被丢失。句子越冗长,句子开头位置的词语信息,经过多步循环传递之后,在隐状态中被不断稀释。解码器拿到的上下文向量已经发生信息损耗,无法获取输入序列每个位置细粒度信息。
在没有注意力的原始 Seq2Seq 中,解码器每一步生成单词,只能依赖这唯一的全局向量,无法选择性聚焦输入句子当中某几个关键词语。比如翻译长句时,生成后半段英文单词,模型没有办法回溯、重点参考中文句子开头的词汇,这就造成长文本翻译效果急剧下滑。
RNN 系列时序网络另外一个硬伤是计算模式。循环网络必须严格按照时间顺序一步一步运算,第 t 时刻隐状态依赖 t‑1 时刻输出,整个编码过程不能并行。硬件 GPU 擅长大规模并行矩阵运算,但是 RNN 的时序特性无法充分利用算力,处理长序列时训练速度会变得十分缓慢。在 2014‑2016 年的工业实践当中,研究人员发现,只要输入句子长度超过 30 个 token,原始 Seq2Seq 模型翻译 BLEU 评分就会出现断崖式下跌,这也倒逼学术界寻找新的解决方案,注意力机制就在这样的背景下应运而生。
27.2 软注意力:Seq2Seq 框架引入注意力模块
2015 年 Bahdanau 注意力论文《Neural Machine Translation by Jointly Learning to Align and Translate》,第一次将注意力机制嵌入编码器‑解码器架构,彻底改变序列模型信息交互方式。核心思想不再把全部输入信息压缩成单一向量,而是保留编码器全部时刻的隐状态,解码器每生成一个 token,都会动态计算一组权重,依据权重对编码器全部隐状态做加权求和,得到该解码步专属的上下文向量。
通俗来讲,做机器翻译的时候,解码器输出每一个英文单词,模型会自动判断,应该多参考中文句子里面哪几个汉字,给对应位置分配更高权重,无关词语分配更低权重。这个动态对齐的过程,就是注意力对齐。
我们把输入源序列记作\(x=\{x_1,x_2...x_n\}\),编码器 RNN 处理每一个输入 token,输出全部时刻隐状态集合\(H=\{h_1,h_2...h_n\}\),不再丢弃中间隐状态。解码器第t时刻隐状态记为\(s_{t-1}\)。 计算流程分为四步: 第一步,计算对齐分数\(e_{t,i}\),代表解码器 t 时刻和编码器第 i 位置隐状态之间的匹配程度,Bahdanau 使用加法注意力(Additive Attention):
\(e_{t,i}=v^Ttanh(W_1s_{t-1}+W_2h_i)\) 式中\(W_1、W_2、v\)都是可训练参数矩阵。把解码器隐状态、编码器隐状态分别经过线性变换,叠加之后送入 tanh 激活,再用向量 v 投影得到一个标量分数。加法注意力适合 Q、K 维度不一致的场景。
第二步,对全部对齐分数执行 softmax 归一化,得到注意力权重\(\alpha_{t,i}\),所有权重相加等于 1:
\(\alpha_{t,i}= \frac{exp(e_{t,i})}{\sum_{k=1}^n exp(e_{t,k})}\) \(\alpha_{t,i}\)取值区间 0~1,数值越大,代表当前解码步骤,模型对输入序列第 i 个位置关注度越高。
第三步,使用归一化之后的注意力权重,对编码器全部隐状态加权求和,生成该解码步专属上下文向量\(c_t\):
\(c_t=\sum_{i=1}^n\alpha_{t,i}h_i\)
第四步,将上下文向量\(c_t\)和解码器当前隐状态\(s_{t-1}\)拼接融合,送入解码器,完成当前时刻预测输出。
对比原始 Seq2Seq,最大变化:每一步解码,都拥有独立上下文向量\(c_t\),而不是全局唯一向量。当解码器生成某个词语,模型可以自主选择重点关注源序列部分 token,长距离信息不再需要经过时序循环一步步传递,直接通过权重读取对应位置隐状态。
这种实现属于软注意力(Soft Attention)。所谓 "软",指权重是 0‑1 之间连续浮点数,所有输入位置都会参与加权求和,只是权重大小不同,整个流程完全可微,可以端到端反向传播训练。与之对应的硬注意力 Hard Attention,会直接筛选少数位置,其余全部置 0,属于离散采样,不可直接求导,训练难度大,工业场景极少使用。
27.3 乘法注意力(Luong Attention)与变体对比
在 Bahdanau 加法注意力提出之后,Luong 在 2015 论文进一步提出乘法注意力(Multiplicative Attention),简化打分计算逻辑,降低计算开销,后续成为自注意力打分函数的基础。
加法注意力内部存在多层线性变换,计算开销偏大;乘法注意力直接利用向量点积完成相似度打分。
\(e_{t,i}=s_{t-1}^\top W h_i\) W为可训练权重矩阵。特殊情况,如果 Q、K 维度完全一致,还可以直接去掉权重矩阵,直接\(e_{t,i}=s_{t-1}^\top h_i\),也就是点积注意力。
我们对比两种主流注意力打分函数的适用场景:
- 加法注意力(Bahdanau):当 Query 与 Key 向量维度不一样时表现更好,参数量更大,计算速度慢,早期 Seq2Seq 解码器广泛使用。
- 乘法注意力(Luong 点积):向量维度相同时效果优异,矩阵点积可以充分利用 GPU 矩阵加速,计算效率高,后续 Transformer 自注意力就是基于缩放点积注意力演化而来。
这里需要厘清一个容易混淆知识点:Seq2Seq 当中的注意力属于交叉注意力(Cross‑Attention)。Query 来自解码器隐状态,Key、Value 全部来自编码器输出。也就是查询来自一个序列,键值来自另外一个序列,实现两个不同序列之间信息对齐。这和后面章节的自注意力有本质区分,自注意力 QKV 全部来自同一套输入序列。
我们用翻译实例直观理解交叉注意力的工作过程:源句 "我喜欢人工智能",翻译输出 "I like artificial intelligence"。当解码器生成单词 "intelligence",此时注意力权重会在中文词语 "人工智能" 位置出现很高的数值,其余词语权重被压低。我们可以可视化注意力权重矩阵,横向源序列,纵向目标序列,可以直观看到词语之间的对齐关系,这也是早期机器翻译可解释性的重要手段。
但是即便引入交叉注意力,整套模型依然依附 RNN 循环单元。编码器、解码器内部依旧是时序循环,编码阶段依旧不能并行,注意力只是缓解长距离依赖,没有从根源消除循环网络的时序约束。这也是为什么后续 Transformer 要彻底抛弃 RNN 循环结构。
27.4 注意力的底层本质解读
很多学习者只记忆注意力计算公式,却忽略底层逻辑,我们在这里提炼注意力的三层本质。
第一层本质:注意力是一种动态、可学习的信息筛选机制。模型不需要把全部信息全部压进固定向量,每一步计算时,根据查询 Query,动态评估所有 Key 的匹配度,依据匹配度分配权重,从 Value 集合当中抽取需要的信息。人类阅读文本的行为与之高度相似:阅读长段落,读到后半句,需要回溯前文某个概念,大脑不会把整篇文章全部同等记忆,而是重点调取相关片段,忽略无关内容。注意力机制就是把这种人脑的聚焦行为数学化。
第二层本质:注意力 = 相似度计算 + 权重归一化 + 加权聚合。 不管后续衍生出多少变种注意力,基本流程不会跳出这三步: ①相似度计算:Query 和每一个 Key 做匹配打分,衡量二者关联强弱; ②归一化:通过 softmax,把分数转为总和为 1 的概率分布,得到注意力权重; ③加权聚合:使用权重对 Value 加权求和,输出聚合之后的特征向量。
不同变体改动的只有第一步打分函数,可以是加法、点积、余弦相似度,甚至神经网络,后两步范式基本不变。
第三层本质:注意力是一种软寻址。可以类比数据库查询:Query 相当于查询语句,Key 相当于数据库每条数据的索引,Value 是存储的数据。模型输入查询,计算索引匹配度,读取对应数据。区别在于传统数据库是硬匹配,匹配到就取出;注意力是软寻址,所有数据都会按相似度比例参与输出,没有绝对的命中或者不命中。
同时也要客观看待注意力的能力边界。注意力不是万能的,权重分配是依赖数据学习出来的,如果训练数据集当中没有对应的对齐模式,模型无法自动获得对齐能力。注意力只是提供信息流通通道,语义理解依旧依靠网络当中的非线性层完成。
27.5 基础注意力 PyTorch 极简代码实现
下面给出交叉注意力简易实现,帮助理解计算流程,适配 Seq2Seq 场景。
import torch
import torch.nn as nn
import torch.nn.functional as F
class BahdanauAttention(nn.Module):
def __init__(self, query_dim, key_dim, hidden_dim):
super().__init__()
self.W1 = nn.Linear(query_dim, hidden_dim)
self.W2 = nn.Linear(key_dim, hidden_dim)
self.v = nn.Linear(hidden_dim,1)
def forward(self, query, keys, values):
# query:[batch,query_dim] 解码器隐状态
# keys,values:[batch,seq_len,key_dim]编码器全部隐状态
query_expand = query.unsqueeze(1) # [B,1,Q_dim]
score = self.v(torch.tanh(self.W1(query_expand)+self.W2(keys))) # [B,seq_len,1]
score = score.squeeze(-1) # [B,seq_len]
attn_weight = F.softmax(score,dim=-1) #注意力权重
context = torch.bmm(attn_weight.unsqueeze(1),values) # [B,1,key_dim]
return context.squeeze(1), attn_weight
我们解读代码逻辑:输入 query 来自解码器,keys、values 是编码器全部时刻隐状态。先通过 W1、W2 做线性变换相加,tanh 激活,v 投影得到每个位置分数,softmax 归一化得到权重,使用 bmm 批量矩阵乘法完成加权求和,输出上下文向量与注意力权重。运行之后,我们可以打印 attn_weight,观察不同 token 对应的权重分布。
27.6 本章小结与实践任务
本章完整复盘注意力诞生的技术背景,原始 Seq2Seq 受限于固定维度上下文向量,长序列信息丢失严重。Bahdanau 将软注意力引入编码器解码器架构,通过动态计算注意力权重,每一步解码生成专属上下文向量,解决长距离信息读取难题。我们拆解加法注意力、乘法注意力数学公式,区分交叉注意力概念,提炼注意力三层底层本质,完成简易代码实现。
需要明确:本章讲解的属于 Seq2Seq 框架下的交叉注意力,Q 来自解码器,K、V 来自编码器,它依旧搭建在 RNN 循环网络之上,编码器和解码器内部循环结构没有消除,时序并行问题依旧存在。想要彻底摆脱循环单元,就需要自注意力机制,也就是下一章的核心内容。
实践任务: 1. 手动推演一遍加法注意力完整计算流程,理解每一步张量维度变化; 2. 修改示例代码,实现乘法点积注意力版本; 3. 思考软注意力和硬注意力之间的优缺点,分析为什么工业大模型几乎全部使用软注意力。