注意力的本质是可微的软寻址:用一个提问去匹配所有候选的索引标签,按匹配度加权取回内容。它最初只是机器翻译里给 RNN 打的一个补丁,三年后成了整个架构的主体。本篇讲清 QKV 三者的关系、手算一遍完整流程,并给出变体谱系的导航。

软寻址:QKV 在做什么

想象一次字典查询。你手上有个待查的词,字典里每个条目有词条和释义。传统查表是硬匹配:词条完全相等才返回释义,否则什么也没有。注意力做的是软匹配——把待查词和所有词条算一遍相似度,然后把所有释义按相似度混合起来返回。

三个角色一一对应:

角色字典类比在模型里
Query待查的词当前 token 发出的”提问”,表达它需要什么信息
Key每个条目的词条每个候选 token 的”索引标签”,宣告自己能提供什么
Value每个条目的释义每个候选 token 真正携带的内容

关键在于 Key 与 Value 的分离:用什么被检索到,和被检索到之后交出什么,是两件事。一个 token 可以用语法角色作索引,交出的却是语义内容。硬要把两者合并(令 )模型仍能训练,但表达力显著受损。

一个具体例子

句子”猫追球,它累了”。模型读到”它”时必须解决指代:它指猫还是球?

  • Query 来自”它”:编码出一个”我是代词,我需要找到一个能被形容为累的实体”的提问向量
  • Key 来自前文每个 token:“猫”的 Key 大致是”有生命的名词、主语位置”,“球”的 Key 是”无生命的物体、宾语位置”
  • Query 与”猫”的 Key 点积大,与”球”的 Key 点积小
  • softmax 把两个分数归一成权重,比如 0.74 与 0.16
  • Value 按权重混合:输出向量里绝大部分是”猫”的语义内容

这里没有任何离散的”选择”操作,全程只有点积、softmax、加权求和——所以梯度能一路回传,对齐关系是学出来的,不是规则写的。这一点是注意力相较于早期硬对齐(hard alignment)方案的根本优势,思想上与同期 Neural Turing Machine 的可微内存寻址同源。

完整公式

拆成四步:打分( 算相似度矩阵)→ 缩放(除 )→ 归一(softmax 沿 key 维)→ 混合(乘 加权求和)。,,,输出 。注意 与 可以不等——这正是 cross-attention 能连接不同长度序列的原因。

手算一遍

设 ,序列三个 token:猫、球、它。只算”它”这一个 Query。

三个 Key 与对应点积:

tokenKey除 权重
猫42.07.3890.736
球10.51.6490.164
它00.01.0000.100

指数和 ,逐项相除即得权重。取三个 one-hot 的 Value:、、,则输出恰好是权重本身:

“它”的新表示中 73.6% 的信息来自”猫”——指代消解在一次矩阵乘法里完成了。

import torch
import torch.nn.functional as F
 
q = torch.tensor([[1., 1., 0., 0.]])                  # (1, d_k)
k = torch.tensor([[2., 2., 0., 0.],                   # 猫
                  [1., 0., 1., 0.],                   # 球
                  [0., 0., 0., 2.]])                  # 它
v = torch.eye(3)                                      # Value 取 one-hot 便于读出权重
 
scores = q @ k.T / k.size(-1) ** 0.5                  # 缩放点积打分
weights = F.softmax(scores, dim=-1)
out = weights @ v
 
print(scores)   # tensor([[2.0000, 0.5000, 0.0000]])
print(weights)  # tensor([[0.7361, 0.1643, 0.0996]])
print(out)      # 与 weights 相同

为什么必要

若 各分量独立、均值 0 方差 1,则 的方差为 。维度越高分数分布越宽,softmax 被推向饱和区——输出接近 one-hot,梯度趋于零。除以 把方差拉回 1。

用上面的例子直接看差别:不缩放时原始分数 ,权重变成 ;缩放后是 。前者几乎退化成硬选择,一旦选错就没有梯度信号纠正。真实模型 常为 64 或 128, 达 8~11.3,不缩放根本训不动。

发明史

固定向量瓶颈。2014 年的 seq2seq 把整个源句压进 encoder 最后一个隐状态,decoder 全程只能读这一个向量。句子越长压缩损失越大,翻译质量随长度单调下降——20 词以上明显崩坏。问题很清楚:一个定长向量装不下变长信息。

Bahdanau attention(2014)。既然装不下,就不要压缩。让 decoder 每生成一个词时回头看 encoder 的全部隐状态,用一个小 MLP 打分:

softmax 归一后加权求和得到 context 向量。这是注意力的原型,也叫 additive attention。论文标题”Jointly Learning to Align and Translate”点明了动机:统计机器翻译时代的对齐(alignment)本是独立模块,现在把它变成可微的、与翻译联合训练的一部分。副产品是可视化的对齐热图——注意力自动学出了语序调整。

Luong attention(2015)。把打分函数从 MLP 简化为点积 ,即 multiplicative attention。少了参数、快了很多,效果不降。今天的 scaled dot-product 直接继承这一支。

self-attention(2016–2017)。此前注意力总是”decoder 查 encoder”,需要两个序列。intra-attention 的思路是让序列查自己:Q、K、V 全部来自同一份输入。这样注意力不再依附于任何外部结构,成为一个独立的序列变换算子。

Attention Is All You Need(2017)。既然 self-attention 已经能建模任意距离的依赖,RNN 的递归还有必要吗?Transformer 的回答是没有——完全删掉循环,只留注意力和 FFN 堆叠。收益不只是效果:递归被移除后,序列所有位置可以并行计算,训练吞吐提升一个量级。这才是注意力真正统治的原因。

代价是失去了 RNN 的归纳偏置,此后十年都在补:顺序信息靠位置编码,可见范围靠掩码, 复杂度靠稀疏化与算子优化。

变体总览

变体一句话代表场景
self-attentionQ、K、V 同源,序列自己看自己Transformer 编码器与解码器主体
cross-attentionQ 来自解码器,K、V 来自编码器翻译、语音识别、扩散模型注入文本条件
causal / 双向掩码约束可见范围,见 注意力掩码causal 用于自回归生成,双向用于 BERT 式理解
multi-head拆成多组子空间并行计算再拼接标准配置,演进见 从 MHA 到 MLA
multi-query / GQA多个 Q 头共享一组 K、V 头推理阶段压 KV Cache 显存
稀疏注意力只算部分 token 对,破 长上下文,见 稀疏注意力
通道 / 空间注意力对特征图的通道与空间维加权CNN 视觉骨干,见 CBAM(Convolutional Block Attention Module)注意力机制

两条正交的划分轴:Q 与 K 的来源(同源即 self,异源即 cross)和可见性约束(全可见、因果、稀疏)。任何注意力实现都能在这两个轴上定位。

两条必然的后续主线

顺序信息。 里没有任何位置项,对输入置换是等变的——“猫追球”与”球追猫”在注意力眼中完全相同。顺序必须由 位置编码 从外部注入,这条支线本身发展出了从正弦编码到 RoPE 的完整谱系。

计算效率。序列长度 下注意力矩阵是 ,显存与计算都随 平方增长。两个方向的优化:算子侧不再显式落盘中间矩阵,见 FlashAttention;推理侧缓存历史 K、V 避免重算,见 KV Cache。

体系全图见 注意力机制 MOC。

延伸阅读