图 1:本文封面,三种注意力机制的头数结构对比。
先说明属性:这是一篇经典题解析,题目出自本地历史面试资料,与任何公司的当前招聘无关。MHA、MQA、GQA 的对比是注意力机制方向的标准题,考点不在三个缩写各代表什么,而在于:它们其实是同一条权衡轴上的三个点——KV 头数越少,KV Cache 越小、解码越快,但质量损失越大。
先看要省的东西:KV Cache 为什么是大头。
自回归解码时,每生成一个新 token 都要对之前所有 token 的键(K)和值(V)做注意力。为了不重复计算,历史 K、V 全部缓存在显存里,这就是 KV Cache。它的体积 = 2(K 和 V 两份)× 层数 × KV 头数 × 头维度 × 序列长度 × 每元素字节数。序列越长、批量越大,KV Cache 越膨胀,长上下文推理往往先被它卡住显存。而解码阶段每步都要把这份缓存从显存完整读一遍,所以解码速度本质上是显存带宽受限——KV Cache 小一倍,读取就快一倍。
MHA:质量天花板,KV 也最大。
Multi-Head Attention 是 2017 年 Transformer 原论文的标准结构:每个查询头(Q)都有自己独立的一套 K、V 头。8 个 Q 头就配 8 套 KV。每个头可以关注不同的位置与模式,表达能力最强、质量最好,但 KV Cache 按满配头数算,一分不少。
共享在计算上是怎么落地的也要说清楚:Q 头数不变,只是同一组内的 Q 头在注意力计算时复用同一份 K、V——相当于把那一套 KV 头「广播」给组内所有 Q 头用。注意力的数学形式一行没改,改的只是 K、V 矩阵的来源,所以训练成本几乎不受影响,省的全在推理侧。
MQA:所有 Q 头共享一份 KV。
2019 年的 MQA 论文(arXiv:1911.02150)做了个大刀阔斧的改动:所有注意力头共享同一套 K、V。8 个 Q 头只配 1 套 KV,KV Cache 直接缩到 1/8,解码时读取量同比例下降,论文验证了解码显著加速、只伴随轻微质量损失。代价也直观:所有头被迫从同一个角度看历史信息,头的多样性没了,质量下降是可感知的,尤其在大模型上。
GQA:折中点,分组共享。
2023 年的 GQA 论文(arXiv:2305.13245)把两个极端连成一条轴:把 Q 头分成若干组,每组共享一套 KV。8 个 Q 头分 4 组就配 4 套 KV——KV Cache 是 MHA 的 1/4,是 MQA 的 4 倍。论文的实验结论是这个折中非常划算:GQA 的质量几乎贴近 MHA,速度则接近 MQA。论文还顺手给了个实用配方:已训练好的 MHA 模型可以用约 5% 的原始预训练算力「升级训练」成 GQA,不必从头训。这正是主流开源大模型广泛采用 GQA 的原因——它是质量与显存的甜点。
用代码把三档 KV Cache 算出来。
下面这段 Node.js 22.15.0 程序(node kvcache.js)以一个 7B 量级配置为例:32 层、32 个注意力头、头维度 128、FP16,算三种机制在 4096 与 32k 上下文下的 KV Cache 体积。
const layers = 32, qHeads = 32, headDim = 128, seq = 4096, bytes = 2;
const kvBytes = kvHeads => 2 * layers * kvHeads * headDim * seq * bytes; // K、V 两份
const fmt = b => (b / 1024 / 1024).toFixed(0) + ' MB';
console.log('MHA(32 个 KV 头):', fmt(kvBytes(32)));
console.log('GQA(8 个 KV 头) :', fmt(kvBytes(8)), '—— 约为 MHA 的 1/4');
console.log('MQA(1 个 KV 头) :', fmt(kvBytes(1)), '—— 约为 MHA 的 1/32');
console.log('序列加长到 32k,MHA:', fmt(2 * layers * 32 * headDim * 32768 * bytes));
console.log('序列加长到 32k,GQA:', fmt(2 * layers * 8 * headDim * 32768 * bytes));
实测输出:
MHA(32 个 KV 头): 2048 MB
GQA(8 个 KV 头) : 512 MB —— 约为 MHA 的 1/4
MQA(1 个 KV 头) : 64 MB —— 约为 MHA 的 1/32
序列加长到 32k,MHA: 16384 MB
序列加长到 32k,GQA: 4096 MB
单条 4096 长度请求,MHA 就要 2GB KV Cache;拉到 32k 上下文直接 16GB——这还没算批量。GQA 把它压到 1/4,MQA 压到 1/32。数字摆出来,「为什么 decode 阶段省 KV 就是省钱」就不需要解释了。
图 2:三种机制的 KV Cache 体积与质量权衡。
面试现场的答题结构。
先立起权衡轴:KV 头数是旋钮,往小调省显存提速,往大调保质量;再按 MHA(满配、质量天花板)、MQA(共享一份、极致省但有损)、GQA(分组共享、甜点)三点展开,每个点给出 KV Cache 的变化倍数;然后补两个加分细节——解码是显存带宽受限所以 KV 小就快、已训模型可低成本升级成 GQA;最后可以提一句它与其他推理优化手段(量化、分页管理)是正交的,可以叠加。你部署的模型用的是哪种注意力?有没有实测过 KV Cache 对吞吐的影响?欢迎聊聊。