MHA、MQA、GQA、MLA 有什么区别?为什么大模型要改注意力的 KV?
简化版
它们都是多头注意力的变体,核心区别在于多个 Query 头共享多少个 Key/Value 头,目的都是压缩推理时的 KV Cache、加快解码:
- MHA(标准多头):每个 Query 头配独立 K/V 头,KV Cache 最大;质量是比较基线而非保证最好。
- MQA(多查询):所有 Query 头共享一套 K/V,理论缓存缩小到 MHA 的
1/h;端到端速度和质量变化依模型与 kernel。 - GQA(分组查询):折中——把 Query 头分成 g 组,每组共享一套 K/V,g 介于 1(=MQA)和 h(=MHA)之间,兼顾质量与显存。Llama 2/3 用的就是它。
- MLA(多头潜注意力):缓存低维潜表示,并通过可吸收进投影的低秩结构参与注意力;另有解耦的 RoPE 分量。它与 GQA 的缓存量及质量必须按具体维度和实现比较。
详细版
为什么要动 KV
自回归解码时,为避免重复计算,历史 token 的 Key/Value 会被缓存下来(KV Cache)。它的大小 = 层数 × KV头数 × head_dim × 序列长度 × 2(K和V) × batch。长上下文 + 大 batch 时,KV Cache 会占用巨量显存,成为推理瓶颈(甚至超过模型权重)。减少 KV 头数,就能成倍压缩缓存、提高吞吐。
四者对比
| 变体 | Query 头 | KV 头 | KV Cache 大小 | 质量 | 代表模型 |
|---|---|---|---|---|---|
| MHA | h | h | 基准(最大) | 独立 KV,作为质量基线 | 原始 Transformer |
| MQA | h | 1 | 理论缩小 h 倍 | 共享最强,需评测回归 | 多种生成模型 |
| GQA | h | g(1<g<h) | 理论缩小 h/g 倍 | 共享程度可调 | 多种现代 LLM |
| MLA | h | 低维潜表示+位置分量 | 由潜维等配置决定 | 低秩约束,需同预算比较 | DeepSeek 系列 |
GQA 为什么成主流
GQA 提供可调共享程度:例如 64 个 Query 头配 8 个 KV 头,理论 KV 元素数是 MHA 的 1/8。质量是否“几乎无损”、吞吐提升多少要在同训练与部署条件下测;它常被采用是因为缓存—质量折中可控,不是固定甜点。
完整版教学
一、先复习多头注意力,锁定”KV 头”这个变量
标准多头注意力(MHA)把注意力拆成 h 个头,每个头有独立的投影矩阵,分别产生自己的 Q、K、V:
head_i = Attention(Q·W_Q^i, K·W_K^i, V·W_V^i)
MHA = Concat(head_1, ..., head_h) · W_O
关键点:MHA 里 Q、K、V 的头数都是 h,各头互相独立。推理时要缓存的是每个 K/V 头的历史,所以 KV Cache 正比于 KV 头的数量。MQA/GQA/MLA 动的全是这个「KV 头」维度——Query 头一般保持 h 不变(保留多头的表达多样性),只削减 KV 头。
二、KV Cache 为什么是推理的命门
自回归生成第 t 个 token 时,注意力需要用到前面所有 token 的 K 和 V。如果每步都重算历史的 K/V,复杂度是 O(t²) 且大量重复。KV Cache 的做法是把历史 K/V 存起来,每步只算新 token 的 K/V 并追加,把每步降到 O(t)。
代价是显存。KV Cache 大小:
KVCache = 2 × 层数 × KV头数 × head_dim × 序列长度 × batch × 精度字节
举例:一个 80 层、64 头、head_dim=128 的模型,处理 32K 上下文、batch=16,用 MHA 的 KV Cache 能轻松到几十上百 GB,远超模型权重本身。长上下文和高并发场景下,KV Cache 直接决定了能塞多大 batch、多长上下文——这就是为什么削减 KV 头如此值得。
记住这条因果链:削减 KV 头数 → KV Cache 变小 → 能放更大 batch / 更长上下文 → 吞吐和成本大幅改善。这是 MQA/GQA/MLA 的共同动机。
三、MQA:一套 KV 走天下
MQA 把所有 Query 头共享同一套 K、V(KV 头数=1):
head_i = Attention(Q·W_Q^i, K·W_K, V·W_V) # W_K, W_V 全体共享
- 好处:KV 元素数理论缩小 h 倍,并减少解码读取量。
- 代价:共享约束更强,可能造成质量回归;幅度不应按模型大小或上下文先验断言。
- 是否适合取决于质量门禁、带宽占比和引擎支持。
四、GQA:分组是关键的折中艺术
GQA 把 h 个 Query 头分成 g 组,每组内的 Query 头共享一套 K/V,共 g 套 KV:
- g=1:所有头共享 → 退化为 MQA。
- g=h:每头独立 → 退化为 MHA。
- 1<g<h:折中,KV Cache 是 MHA 的 g/h。
典型配置中,64 个 Query 头配 8 个 KV 头,缓存主项降到 1/8。分组比单 KV 保留更多独立 K/V 子空间,因此提供更灵活的容量折中;这只是结构直觉,能否守住质量仍需训练结果证明。
已有 MHA 权重可以通过合并 KV 头初始化 GQA 并继续训练,这类 uptraining 能降低改造成本,但不是零训练等价转换,也不是所有模型都采用同一路径。
五、MLA:换个思路,压缩而非共享
DeepSeek 的 MLA(Multi-head Latent Attention)不走「减少头数」的路,而是低秩压缩:
- 把每个 token 的 K/V 通过一个下投影压成一个低维潜向量(latent),只缓存这个潜向量,而不是完整的多头 K/V。
- 计算上可把上投影与后续 Q/K 或输出投影合并,未必在运行时显式“解压出完整 KV”;具体取决于实现。
- 为兼容 RoPE(旋转位置编码不能简单低秩分解),MLA 采用解耦 RoPE:一部分维度专门承载位置信息、单独处理。
MLA 的缓存量由潜维、解耦位置分量和精度决定,不能脱离某个 GQA 配置说必然更小。低秩约束对质量和 kernel 的影响也需实测。理解 MLA 抓一句:缓存压缩潜表示与必要的位置分量,而非每头完整 K/V。
六、按 KV 头数计算缓存收益
设 query 有 32 个头、每头 128 维。MHA 使用 32 个 KV 头,MQA 只用 1 个,GQA 若分成 8 组则用 8 个。KV Cache 与 KV 头数线性相关,因此相同层数、精度和上下文下,MQA 理论上只有 MHA 的 1/32,8-KV-head 的 GQA 是 MHA 的 1/4。
| 结构 | Q 头数 | KV 头数 | 相对 KV Cache | 典型权衡 |
|---|---|---|---|---|
| MHA | 32 | 32 | 1 | 表达充分、缓存最大 |
| GQA | 32 | 8 | 1/4 | 质量与带宽折中 |
| MQA | 32 | 1 | 1/32 | 缓存最省、共享最强 |
这个比例不等于端到端吞吐会按相同比例提升,因为权重读取、调度、量化和其他层仍有成本。真正验收应同时观察 decode tokens/s、最大并发、长上下文质量以及目标引擎是否有对应优化 kernel。
七、常见误区与追问
- 误区:MQA 会减少 Query 头数。 它保留多个 Q 头,只让这些 Q 头共享一组 K/V;减少的是 KV 头。
- 追问:GQA 为什么常是更实用的折中? 它按组共享 K/V,缓存和带宽明显下降,同时保留多组 KV 表达,通常比 MQA 更易守住质量。
- 误区:KV Cache 缩小 4 倍,吞吐必然提升 4 倍。 模型权重读取、调度、其他层和 kernel 效率仍占时间,缓存比例不能直接等同吞吐比例。
- 追问:从 MHA 改造到 GQA 能直接零训练转换吗? 可通过合并或平均 KV 头初始化,但分布改变通常需要继续训练或微调并做质量回归。
- 追问:如何验证选了多少 KV 头? 同时画质量、TPOT、最大并发和显存的 Pareto 曲线,而不是只追求最少 KV 头。
八、加强记忆
MHA/MQA/GQA 主要改变 Q 头与 KV 头的共享关系:h 个 KV、1 个 KV、g 个 KV;缓存主项分别按 KV 头数缩放。MLA 改用低维潜表示和解耦位置分量,不能只按“头数”比较。缓存更小通常释放并发空间、减少带宽,但不保证吞吐同比上升,也不预设质量排序。正确回答要给出缓存公式,再说明用长上下文质量、TPOT、并发和实际引擎做 Pareto 选择。