Transformer 笔记:注意力机制为什么会变成系统问题
从 QKV、掩码和 KV 缓存出发,理解 Transformer 的模型结构为什么会影响推理服务的内存与调度设计。
我以前读 Attention Is All You Need,会把注意力放在公式上:
Attention(Q, K, V) = softmax(QK^T / sqrt(d_k)) V
现在再读,我更关心它删掉了什么。
Transformer 最关键的变化不只是“用了注意力机制”,而是去掉了序列建模中按时间步传递的递归关系。RNN 的状态像一根链条,h_t 要等 h_{t-1}。Transformer 改成:同一层里,每个 token 可以直接和其它 token 建立关系。
这一步把问题从时间依赖链改成了矩阵计算,也把后来的系统问题埋下了。
flow
这篇论文真正改变的工程形状
训练阶段赢在并行矩阵计算;推理阶段则开始背 KV 状态的账。
- 1
去掉递归依赖
序列内部不再按隐藏状态串行推进
- 2
关系矩阵
token 之间的关系变成 QK^T 和 softmax
- 3
适合硬件并行
核心计算更贴近 GPU 上的大矩阵操作
- 4
状态成本
自回归推理必须保存每层历史 K/V
从一条链变成一张表
RNN 的直觉很自然:一句话从左到右读,当前状态携带过去信息。
x1 -> h1 -> h2 -> h3 -> h4
这条链的问题是,训练时序列内部不好并行,长距离依赖也要经过很多中间状态。
自注意力换了一个问法:如果第 4 个 token 想知道第 1 个 token 是否重要,为什么一定要靠 h2、h3 一步步传?为什么不能直接算它们之间的关系?
第 i 个 token
-> 为所有第 j 个 token 计算分数
-> 按权重汇总信息
这就是注意力公式背后的工程形状。它把“序列中信息怎样流动”改成了“一个 token 如何在同一层里寻址其它 token”。
QKV 像一次寻址过程
QKV 不只是三组投影。更贴近系统直觉的理解是:
Query:当前 token 发出的查询
Key: 每个历史 token 暴露的地址或标签
Value:匹配以后读回来的内容
QK^T 像是在计算查询与所有键的匹配程度,softmax 得到读取权重,最后用这组权重汇总对应的值。
这样看,Q 和 K 定义“怎么找”,V 定义“找到了拿什么回来”。这比“每个词看每个词”更准确,因为匹配空间和内容空间被拆开了。
多头注意力把这件事复制多份:不同的头有不同投影空间,可以学到不同的寻址方式。后来的 MQA/GQA 之所以有工程意义,也正是因为 K/V 头数会直接影响 KV 缓存大小。
更多 KV 头
-> 每个 token 产生更多 K/V 张量
-> KV 缓存更大
-> 推理服务承受更高的内存压力
这就是模型结构与推理系统相接的地方。
掩码和位置编码承担不同职责
自注意力给 token 之间开了直接通道,但这条通道必须受到约束。
第一个约束是掩码。
机器翻译中的解码器掩码自注意力,以及今天仅解码器大模型中的因果注意力,本质上都遵守同一条规则:生成当前位置时不能看未来。
第 t 个 token 可以关注:
位置不晚于 t 的 token
第 t 个 token 不能关注:
位置晚于 t 的 token
这条规则会一路传到 KV 缓存。缓存里的 K/V 代表“已经出现过的 token”。前缀复用、缓存命中和远端 KV 回载都不能破坏这个顺序。
第二个约束是位置信息。
去掉递归关系后,注意力机制本身不知道 token 的先后。原论文使用正弦位置编码,今天常见 RoPE、ALiBi 等变体,但它们都在补同一件事:让模型知道 token 在序列中的相对或绝对位置。
在长上下文系统里,位置信息不是小细节。复用一段 KV,不只是问 token 内容是否相同,还要问这段 K/V 在当前上下文位置下是否仍然有效。
训练阶段省掉的链,推理阶段变成了缓存
训练时,整段序列可以一起进模型,所有 token 的 Q/K/V 在每层批量算出来。
推理时不一样。自回归生成每吐一个新 token,都要看前面的历史。如果每一步都重算完整 prompt 和已生成内容,成本会不可接受。
所以系统保存历史 token 的 K/V:
预填充:
提示词 token
-> 计算每一层的 K/V
-> 保存 KV 缓存
解码:
新 token
-> 计算当前 Q/K/V
-> Q 读取缓存中的历史 K/V
-> 追加新的 K/V
KV 缓存不是一个附加优化,而是仅解码器大模型推理的基本状态。
它的大小随层数、序列长度、KV 头数、单头维度和数据类型增长:
KV 缓存大小
~= 层数
* 序列长度
* KV 头数
* 单头维度
* 2
* 每个数据元素的字节数
于是注意力机制从论文里的计算结构,变成了推理引擎中最主要的内存压力之一。
FlashAttention 和 PagedAttention 不是同一种答案
沿着注意力机制继续看,经常会遇到两个名字:FlashAttention 和 PagedAttention。它们都围绕注意力展开,但解决的问题不同。
FlashAttention 更靠近计算过程。它关心注意力内核怎样减少 HBM 读写、怎样避免完整展开注意力矩阵,以及怎样提高预填充和训练效率。
PagedAttention 更靠近推理状态管理。它关心 KV 缓存怎样分块管理、怎样避免预留连续显存,以及怎样支持共享前缀和写时复制。
FlashAttention:
优化注意力计算
减少计算分数和汇总结果时的内存传输
PagedAttention:
优化 KV 缓存的存放方式
减少推理过程中的碎片和过量预留
如果把两者都笼统地称为注意力优化,很容易错过它们的层级差异。
evidence boundary
同一种注意力机制,两个系统方向
计算路径
- QK^T / softmax / AV 的计算内核如何组织
- 是否减少 HBM 数据传输
- 预填充和训练是否更快
状态路径
- 历史 K/V 如何保存
- 不同请求如何共享或释放 KV
- 调度器是否了解缓存成本
门打开以后
Attention Is All You Need 不只是用注意力机制替换 RNN。它把序列关系变成了可并行计算的矩阵,也让自回归推理不得不长期保存 K/V 状态。
这就是为什么这篇论文会一路连到 KV 缓存、PagedAttention、FlashAttention、前缀缓存、HiCache 和远端 KV 存储。注意力成为主干以后,K/V 就不再只是中间张量,而是系统资源。