音乐
暂未播放
GQA 完全拆解:分组查询注意力,用 1/8 的 KV 缓存保住 MHA 的质量
从”每头一套键值”说起:解码推理的内存账本#
标准的 Transformer 使用多头注意力(multi-head attention,MHA)。模型维度 dmodel 被切成 h 个注意力头,每个头有自己的查询(Query)、键(Key)、值(Value)投影矩阵,各自在维度 dk=dmodel/h 的子空间里做注意力:
headi=softmax(dkQiKi⊤)Vi其中 Qi、Ki、Vi 分别是第 i 个头投影出的查询、键、值矩阵,dk 是缩放因子。注意力的输出再拼回完整维度,经过输出投影 WO 得到这一层的输出。
预填充(prefill)阶段可以并行处理整条序列,但自回归解码(decode)阶段每次只生成一个 token。生成新 token 时,注意力需要让新 token 的查询去和历史上所有 token 的键和值做点积。如果每次现算历史 token 的 K、V,整个前缀的输入都要重新过一遍网络,代价是随序列长度平方级增长的。所以实际工程里都会把已算出的 K、V 存进显存,称为键值缓存(KV cache):解码第 t 步时,新 token 的 Qt 与缓存的全部 K1..t、V1..t 做注意力,算完把这一行新 K、V 追加进缓存。
KV cache 的内存占用因此是每层、每 token、每 KV 头都存一份,精确地说:
SKV=2×L×G×dk×BL 是层数,G 是 KV 头数量,dk 是每个头的维度,B 是每个元素占的字节数(FP16/BF16 下为 2),因子 2 来自 K 和 V 各一份。以 LLaMA-2-7B 为例:32 层、32 个头、头维度 128,BF16 下每个 token 的 KV 缓存是 2×32×32×128×2=524288 字节,即 512 KiB/token。4K 上下文就是 2 GiB,8K 就是 4 GiB——单条请求的 KV 缓存已经可以和模型权重(约 13 GB)相提并论。
为什么 KV cache 的大小这么重要?因为解码阶段是内存带宽受限(memory-bandwidth-bound)的。每一步生成,GPU 都需要:
- 把全部模型权重从 HBM 读一遍(自回归无法批量计算,权重只能复用);
- 把整条序列的 KV cache 从 HBM 读一遍,供注意力点积使用。
HBM 带宽决定了每一步能搬多少数据。Shazeer 在 2019 年的论文里给过一个干净的复杂度分析:自回归生成 n 步时,增量式 MHA 的总计算量是 Θ(bnd2)(b 为批大小),而总内存访问量是 Θ(bn2d+nd2)——第一项来自每步都要重新读一遍大小随序列长度增长的 K、V 张量,第二项来自每步都要重新读一遍投影权重。两者相除,得到内存访问与计算的比值:
R=Θ(dn+b1)当序列长度 n 接近模型维度 d(几千 token 的对话在 4096 维模型上很容易达到),或者批大小 b 接近 1 时,比值接近 1:每做一单位浮点运算就要搬一单位数据,而现代 GPU 上计算吞吐与内存带宽的比值通常在 100 以上(例如 A100 的 FP16 算力 312 TFLOPS 对 2 TB/s 带宽)。于是搬运数据成了解码的主宰,GPU 的计算单元大量空闲。
公式里的 dn 项,展开来看正是 KV cache 的带宽开销:KV 张量每步要完整读一遍,读的量 ∝ 序列长度 × KV 头数。这一项不随硬件换代而消失——HBM 带宽的增长远慢于算力的增长,所以 KV 的搬运开销在每一代 GPU 上都占解码延迟的大头。优化 KV cache 的大小,就是优化解码的延迟与吞吐。GQA 的故事从这里开始。
把上面的复杂度分析落到具体数字上,能更直观地感受”内存账本”的分量。以 A100-80GB 为例:HBM 带宽约 2 TB/s,FP16 算力 312 TFLOPS。解码一个 token 需要把全部权重从 HBM 读一遍——LLaMA-2-7B 的权重约 13.4 GB,单这一步的延迟下限就是 13.4 GB/2 TB/s≈6.7 ms;如果上下文已有 4K token,还要再读 2 GiB 的 KV cache(MHA 配置下 512 KiB/token × 4096),又加约 1 ms。也就是说,一个 token 的解码延迟里,搬运权重与搬运 KV 是两大固定成本,与计算本身几乎无关。
这里有一个 serving 场景的关键细节:权重是”每步读一次、服务整个批次”的,批大小 b 越大,权重的搬运成本被 b 个请求摊得越薄;而 KV cache 是”每步读 b 份”的,批越大 KV 搬运占比越高。所以在高并发服务中,KV cache 的带宽与容量往往比权重更早成为瓶颈——这正是 GQA 这类 KV 压缩技术价值最大的场景。
MQA:一次性把 KV 头砍到只剩一个(2019)#
2019 年,Google 的 Noam Shazeer 发表了一篇只有三页的小论文《Fast Transformer Decoding: One Write-Head is All You Need》(arXiv:1911.02150),提出多查询注意力(multi-query attention,MQA):保留多个查询头,但所有查询头共享同一个键头和同一个值头。
实现上极其简单——把 MHA 代码里 K、V 及其投影矩阵 Pk、Pv 的”头”维直接删掉即可。原来 K、V 的形状是 [b,h,n,dk],MQA 里变成 [b,n,dk]。论文用 einsum 记号给出了这个改动的全部内容:MHA 的键投影是 einsum("bd,hdk→bhk",X,Pk),MQA 把它改成 einsum("bd,dk→bk",X,Pk)——字母 h 从下标里消失,其余一字不动,查询侧的 einsum("bd,hdk→bhk",X,Pq) 保留。训练的代码改动只有几个 einsum 字符串,这是 MQA 能在 2019 年迅速被采用的重要原因。
KV cache 的规模随之缩小到原来的 1/h。Shazeer 把这种设计称为”one write-head”(单写入头):解码每步只需向缓存写入一行键和一行值,而不是 h 行——写入量缩小 h 倍,读取量(每步都要读全部 KV)同样缩小 h 倍。论文给出的带宽分析里,dn 这一项被除以了 h:
RMQA=Θ(dhn+b1)实验数据非常惊人。在 TPUv2 上、批大小 1024 的增量解码评测中,T5-XXL 基线模型的解码器每步耗时 47 ms,MQA 版本只有 3.9 ms,快了约 12 倍(编码器部分 222 ms → 195 ms,只快了 14%——编码器本来就是并行计算、不受带宽制约,印证了前一节的分析)。代价是质量的小幅下降:WMT14 英德翻译的 BLEU 从 26.7 降到 26.5,对数困惑度(ln perplexity)从 1.424 升到 1.439;论文还发现,把前馈层维度从 4096 增大到 5440 后,MQA 模型能在翻译任务上追平 MHA——用一点额外参数换带宽。质量损失在翻译任务上很小,在摘要类任务上更明显,这符合直觉:摘要需要模型从长上下文中提取并组织大量细节,对 KV 表示的表达能力更敏感。
MQA 的思路随后被 PaLM、Falcon 等模型采用(Falcon-7B 有 71 个查询头,却只配 1 个 KV 头)。但它有两个明显缺陷:
- 质量天花板低。所有查询头强制共享同一份 K、V 表示,注意力内容上的多样性被压缩到只剩查询侧。头数越多,砍掉的信息越多——而大模型恰恰头数更多(参数量以 dmodel2 增长,头数通常线性增长)。
- 训练不稳定。GQA 论文报告,MQA 在一些任务上出现质量退化甚至训练不稳定(training instability)的问题。
一句话概括:MQA 把带宽省到了极致,但砍掉了太多模型容量。能不能只砍一部分?
GQA:插值在 MHA 与 MQA 之间的分组共享(2023)#
2023 年,Google 团队的 Joshua Ainslie 等人提出分组查询注意力(grouped-query attention,GQA),论文《GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints》发表在 EMNLP 2023(arXiv:2305.13245)。核心思想一句话:把 h 个查询头分成 G 组,每组共享一个键头和值头。GQA-G 表示组数为 G 的 GQA;G=1 时退化为 MQA,G=h 时就是 MHA,GQA 是两者的插值:
G=1⟺MQAG=h⟺MHA此时 KV 头数量从 h 降到 G,每个 KV 头服务 h/G 个查询头。KV cache 的大小公式 SKV=2LGdkB 里 G 取代了原来的 h——KV cache 和每步 KV 的读取量都精确地缩小为原来的 G/h。
用符号写清楚:把查询头按下标 i=1,…,h 编号,第 g 组包含连续的第 (G(g−1)h+1) 到第 Ggh 个头。组 g 内任意查询头 i 的注意力计算为:
headi=softmax(dkQiKg⊤)Vg,i∈group gKg、Vg 是组 g 共享的键、值头。可见查询侧的投影 WQ,i 依然是每个头独立的,共享的只有键与值——这是 GQA 质量损失小的结构性原因:注意力机制里”问什么”(查询)的多样性被完整保留,“记住什么”(键值)的表达被按组压缩。对于一组内的几个查询头,它们共享同一份上下文记忆,但各自用不同的查询投影去解读这份记忆,输出仍然各不相同。
论文的架构对比图把三种注意力画在同一张图上:

从上到下看:MHA 里三组方块——查询、键、值——数量完全一致,一一对应;MQA 的键、值各自只剩一个小方块,所有查询头共用;GQA 位于中间,键、值方块的数量比查询方块少、但不止一个,形成”几根查询线汇入一根 KV 线”的分组结构。图中特意用颜色区分了分组——同一组的查询头共享同一根 K/V,组与组之间的 KV 互不干扰。
为什么中间值(GQA)比两个端点都更好? 三个原因,论文里都有直接论证:
第一,带宽收益几乎全部保留。 论文图 6 测量了 T5-XXL 上 GQA 组数 G 与推理时间的关系(输入 2048 token、输出 512 token,TPUv4):

图中曲线在 G=1 到 G=8 之间非常平缓——KV cache 从 64 头缩到 8 头已经拿到了带宽收益的大头,再往 8 头上加组,每步多读的 KV 量仍然只是总 KV 的零头;而 G 从 8 涨到 64(即回到 MHA)时,曲线陡峭上升,多出来的全是带宽负担。论文据此把 8 组选为”性价比最好的中间值”。
第二,质量接近 MHA。 主实验表(下节详述)里 GQA-8 的平均分 47.1,与 MHA-XXL 的 47.2 几乎持平,比 MQA 的 46.6 高出一截。从信息的角度看也不难理解:KV 表示在 Transformer 里承担的是”上下文记忆”的角色,一组查询头共享同一份记忆,损失的只是”对同一段记忆用不同投影去读取”的细粒度——下文会看到,均值池化后的共享表示依然覆盖了绝大多数信息,这正是消融实验中池化转换几乎不掉点的原因。
第三,容量随模型规模成比例。 论文专门指出:模型越大、头数越多,MQA 那种”砍到 1 个头”就越激进——KV 容量与模型规模不成比例。GQA 的组数则可以让 KV 容量随模型一起增长(例如头数翻倍时组数也翻倍),保持带宽收益与容量损失的比例不变。论文还提到一个张量并行(tensor parallelism,TP)场景下的细节:大模型按头切分到多张卡时,MQA 唯一的 KV 头会被复制到每个分区,造成冗余;GQA 的多个 KV 头可以分散到各分区,消除了这种浪费。
另外注意论文的应用范围:GQA/MQA 只用于解码器自注意力与交叉注意力(cross-attention),不用于编码器自注意力。原因是编码器是并行计算的,KV 只在内存里存一份、读一次,不是带宽瓶颈;而解码器(以及交叉注意力)每步都要重读全部 KV,才是带宽开销的来源。
Uptraining:把现成 MHA 检查点改造成 GQA#
GQA 论文的第二个贡献是”升级训练”(uptraining)配方——把一个已经训练好的 MHA 检查点,用很少的算力改造成 MQA/GQA 模型。这在当时是刚需:T5、LLaMA 等公开模型都是 MHA 的,推理方想用上 MQA/GQA 的速度,又不愿(也没有算力)从头训练一个模型。论文提出的流程分两步:
第一步:均值池化(mean pooling)转换。 把原来 h 个 K 头(和 V 头)的投影矩阵,按目标分组做平均,合并成 G 个。第 g 组的新键投影矩阵为:
WKg=∣g∣1i∈g∑WKiWKi 是原模型第 i 个头的键投影矩阵,∣g∣ 是该组的头数。值投影同理。下图即论文图 1 的转换流程示意:

为什么是均值池化而不是其他方法? 论文做了消融(ablation),对比三种转换方式——均值池化、“取第一个头”、随机初始化:

结果与”信息保留程度”的顺序完全一致:均值池化保留了所有头的线性组合信息,最好;取一个现成头保留了该头的信息,次之;随机初始化等于把这块知识全部丢弃,最差。这个结果直观得近乎平庸——但它说明了一个关键设计原则:转换的目标不是创造新权重,而是尽量不破坏已有权重里的信息。
第二步:用小比例算力继续预训练。 转换后的模型用原训练配方继续预训练 α 比例的训练步数。论文取 α=0.05——T5-XXL 的预训练算力约 5%,换算成硬件时间约为 600 个 TPUv3 芯片日。作为参照,T5-XXL 的完整预训练约需上万 TPUv3 芯片日,升级训练把”获得一个 MQA/GQA 模型”的成本压到了原始预训练的 5%,而且用的是现成数据管线,不需要重新采集、清洗数据。下图是 α 的扫描结果:

图中两条曲线的起点很能说明问题:GQA-8 在 α=0(只做均值池化、一步都不训练)时就有相当的性能,而 MQA 几乎必须经过升级训练才可用。原因不难理解——组内共享让 GQA 转换后的注意力分布与原来的 MHA 很接近,查询头与 KV 头的配对关系大体保留;MQA 则是把所有 KV 头的信息硬压成一个,原有配对关系被彻底打散,必须靠训练重新学习。这也再次印证:GQA 的容量损失远小于 MQA,因此对训练的依赖也更小。
论文的相关工作一节还提到,Rabe(2023)独立地开发了 GQA 及公开实现——这个方向在当时已是多方共识,只是由 Google 团队率先给出系统化的定义与实验。
质量与速度的实测权衡#
GQA 论文的主实验在 T5-Large 与 T5-XXL 上进行(T5-XXL 有 64 个注意力头、dmodel=4096),对比 MHA、MQA 与 GQA-8(升级训练比例 α=0.05),评测覆盖摘要(CNN/Daily Mail、arXiv、PubMed、MediaSum、Multi-News)、翻译(WMT14 英德)与问答(TriviaQA)。推理时间按每样本、每 TPUv4 芯片测量。完整数据如下:
| 模型 | 推理时间(s/样本) | 平均分 | CNN R1 | arXiv R1 | PubMed R1 | MediaSum R1 | MultiNews R1 | WMT BLEU | TriviaQA F1 |
|---|---|---|---|---|---|---|---|---|---|
| MHA-Large | 0.37 | 46.0 | 42.9 | 44.6 | 46.2 | 35.5 | 46.6 | 27.7 | 78.2 |
| MHA-XXL | 1.51 | 47.2 | 43.8 | 45.6 | 47.5 | 36.4 | 46.9 | 28.4 | 81.9 |
| MQA-XXL | 0.24 | 46.6 | 43.0 | 45.0 | 46.9 | 36.1 | 46.5 | 28.5 | 81.3 |
| GQA-8-XXL | 0.28 | 47.1 | 43.5 | 45.4 | 47.7 | 36.3 | 47.2 | 28.4 | 81.6 |
数据来源:arXiv:2305.13245 表 1(推理时间测量自 TPUv4,8 张 TPU、每卡最大批 32)。
逐数据集看,GQA-8-XXL 相对 MHA-XXL 的质量几乎无损,有些任务还反超:arXiv 摘要 R1 45.4 vs 45.6(-0.2)、PubMed 45.4 vs 45.6(-0.2)、MediaSum 47.7 vs 47.5(+0.2)、MultiNews 47.2 vs 46.9(+0.3)、WMT BLEU 28.4 持平、CNN/Daily Mail 43.5 vs 43.8(-0.3)、TriviaQA F1 81.6 vs 81.9(-0.3)。反超出现在长文摘要任务(MediaSum、MultiNews)上——这也间接说明 GQA-8 的 KV 表示在这些任务上保留了足够的信息,甚至因为升级训练多训了几步而小幅受益。
把时间与质量画在同一张图上(论文图 3),权衡一目了然:

四个散点的相对位置讲述了这个设计的全部逻辑:
- GQA-8-XXL 比 MHA-XXL 快 5.4 倍(1.51 s → 0.28 s),平均质量只差 0.1 分(47.1 vs 47.2),在 MediaSum、MultiNews 两个数据集上甚至反超;
- GQA-8-XXL 比 MQA-XXL 只慢 1.17 倍(0.28 s vs 0.24 s),平均质量高 0.5 分;
- 更大尺度的 MHA-Large(0.37 s,46.0 分)在速度和质量上被 GQA-8-XXL 全面压制——同样的算力预算,升级训练的 GQA-XXL 比更大的 MHA 模型更好用。
组合起来就是论文的结论:升级训练的 GQA 用接近 MQA 的速度拿到了接近 MHA 的质量。注意一个诚实的前提:GQA 省的是 KV 的内存与带宽,注意力本身的浮点运算量(FLOPs)没有减少——KV 头要广播回 h 个查询头参与点积,logits 计算量不变。所以 GQA 的加速比例不会超过带宽占比,长序列、大批量下收益最明显,短序列下则主要由其他开销主导。
由此可以推出 GQA 在推理两个阶段的差异化收益,这是部署选型时容易踩坑的地方:
- 解码阶段是主战场。每步重读 KV 是纯带宽开销,KV 缩小 h/G 倍直接等比例缩减这部分延迟;同时 KV 显存占用缩小,批容量扩大。论文表 1 测的正是解码端到端时间。
- 预填充阶段收益有限。prefill 是计算密集的(一次性并行计算整条前缀的注意力),KV 只写入一次、不需要反复读取,GQA 省下的带宽占比很小。实测中 prefill 的提速远不如 decode 显著,这也是 MQA 论文里编码器部分只快 14% 的原因——同一个道理。
- 与 FlashAttention 是互补关系。FlashAttention 优化的是”计算与访存的比值”(IO 感知的分块调度),GQA 优化的是”KV 的总量”——一个解决”每块数据怎么搬更高效”,一个解决”要搬的数据能不能少一些”,作用在不同层面,同时使用效果叠加。
主流模型里的 GQA:KV 缓存账本#
GQA 发表后迅速成为大模型的事实标准。LLaMA-2 论文(2023 年 7 月)在 34B 与 70B 上采用 GQA(70B 配置为 64 个查询头配 8 个 KV 头),明确说明动机是提升推理可扩展性;LLaMA-3 全系列、Mistral-7B、Qwen2、Gemma 等模型也都采用了 GQA,Falcon 系列则沿用更激进的 MQA。各家配置与 KV 缓存账本如下(BF16,头维度 128 除特别注明):
| 模型 | 层数 | 查询头 | KV 头 | 每 token KV | 相对 MHA |
|---|---|---|---|---|---|
| LLaMA-2-7B | 32 | 32 | 32(MHA) | 512 KiB | 1× |
| LLaMA-2-70B | 80 | 64 | 8(GQA) | 320 KiB | 1/8 |
| LLaMA-3-8B | 32 | 32 | 8(GQA) | 128 KiB | 1/4 |
| Mistral-7B | 32 | 32 | 8(GQA) | 128 KiB | 1/4 |
| Qwen2-7B | 28 | 28 | 4(GQA) | 56 KiB | 1/7 |
| Falcon-7B | 32 | 71 | 1(MQA) | 8 KiB | 1/71 |
这些数字用前面的公式 SKV=2LGdkB 直接算出。以 LLaMA-3-8B 为例:32 层 × 8 个 KV 头 × 128 维 × 2 字节 × 2(K、V)= 131072 字节 = 128 KiB/token——只有同尺寸 MHA 模型的四分之一。“GQA 用 1/8 的 KV 缓存”的说法对应 GQA-8(64 头→8 头)的模型,如 LLaMA-2-70B;组数不同,压缩比不同。
有个容易被忽略的细节:LLaMA-2 的 7B/13B 版本用的是 MHA,34B/70B 才用 GQA。这背后的权衡正是 GQA 论文的论证——KV cache 随模型维度线性增长(L 与 G 都与 dmodel 同阶),而参数量与计算量随模型维度平方增长。模型越大,KV 的带宽与容量占比越高,压缩 KV 的收益越大;小模型上 KV 占比本就低,GQA 省下的带宽有限,反而可能损失质量,所以小模型保持 MHA 更划算。到了 LLaMA-3 时代,8B 也采用了 GQA-8——长上下文支持让 KV 的绝对量急剧上升,压缩收益重新压过了质量损失。
KV 缓存账本在长上下文下尤其触目惊心。LLaMA-3.1-8B 支持 128K 上下文,若真把上下文用满,KV 缓存是 128 KiB × 131072 ≈ 16 GiB——和模型权重(约 16 GB)相当,单条请求就吃掉 A100-80GB 近四分之一的显存。
对推理服务系统来说,KV cache 的大小直接决定能并发的请求数量:权重占用是固定的,剩余显存除以单请求的 KV 占用就是批大小上限。用一个具体的容量账目说明 GQA 的价值:假设在 A100-80GB 上部署 LLaMA-3-8B(权重约 16 GB,BF16),剩余约 64 GB 留给 KV cache 与激活。4K 上下文、MHA 配置(32 个 KV 头)下单请求 KV 为 512 KiB × 4096 = 2 GiB,64 GB 只够约 30 个并发请求;换成 GQA-8 后单请求 KV 降到 512 MiB,并发上限变成约 120 个——同样的显存,4 倍的批容量。而批大小又是解码吞吐的关键:批量越大,每步读一次的权重被越多请求分摊,GPU 越接近”算得过来”的状态。这也是为什么连续批处理(continuous batching)与 KV 管理技术(如 vLLM 的 PagedAttention,本站已有完全拆解)能与之叠加:GQA 在”结构”上压缩 KV,分页在”分配”上消除 KV 的碎片浪费,两者正交。
工程实现:权重布局、repeat 与 Kernel 融合#
从代码层面看,GQA 的实现改动集中在两个地方:投影矩阵的输出维度和注意力计算前的头对齐。
权重布局。 MHA 的 WK、WV 形状是 [h,dk,dmodel];GQA 直接把它们改成 [G,dk,dmodel],总参数量除以 h/G。Hugging Face Transformers 里对应 num_key_value_heads 配置项——k_proj 与 v_proj 的输出维度从 h×dk 变成 G×dk,查询侧不变。推理框架通常更进一步,把 K、V 的投影合并成单块权重 WKV:[G,2dk,dmodel],一次 GEMM 同时产出 K、V,减少矩阵乘次数与权重搬运(这正是 vLLM 与 llama.cpp 的常见做法)。
头对齐。 注意力计算时,查询有 h 个(batch 维上分头),键/值只有 G 个,两者的 batch 维不匹配,无法直接做 batched GEMM。Transformers 的做法是把 KV 头沿头维”重复”(repeat)h/G 次,展开成 h 个后照常计算,对应代码即建模文件里的 repeat_kv:
1def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:2 batch, num_key_value_heads, slen, head_dim = hidden_states.shape3 if n_rep == 1:4 return hidden_states5 hidden_states = hidden_states[:, :, None, :, :].expand(6 batch, num_key_value_heads, n_rep, slen, head_dim7 )8 return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)调用处:
1key_states = repeat_kv(key_states, self.num_key_value_groups)2value_states = repeat_kv(value_states, self.num_key_value_groups)num_key_value_groups = num_heads // num_key_value_heads 即每组查询头数 h/G。expand 只复制元数据不复制数据,reshape 后同一组 KV 头的副本共享底层内存——注意这只是”视图”层面的重复,数据本身没有复制。
显式 repeat 的代价在哪里? 展开后的 K、V 张量虽然不占额外 HBM,但在 CUDA kernel 里,头副本最终要进入寄存器/共享内存参与计算,等效于把 KV 数据的读取量放大了 h/G 倍——正好抵消 GQA 省下的带宽。所以生产级实现不会真的”展开”,而是让 kernel 直接感知分组:FlashAttention-2 论文明确声明其实现推广到了 MQA 与 GQA(arXiv:2307.08691),做法是把”头”拆成”组下标 + 组内下标”,共享的 KV 头在共享内存中只加载一次,多个查询头复用同一份数据;vLLM 的 PagedAttention kernel(arXiv:2309.06180)同样原生支持 GQA 的头组共享——它在分页块(block)的布局里按 KV 头组织数据,kernel 内部做组内广播,GQA 的模型权重在 vLLM 的配置里就是 num_kv_heads 字段,与 num_attention_heads 一起驱动显存分配与 kernel 选型。这就是”结构压缩”与”kernel 感知”缺一不可的原因:GQA 在模型结构上省下的带宽,只有底层 kernel 不重新把数据复制回来,才算真正落地。
训练侧的实现则绕开 repeat:PyTorch 的 scaled_dot_product_attention(SDPA)直接支持查询头数与 KV 头数不等的输入——传入形状为 [b,h,n,dk] 的 Q 和 [b,G,n,dk] 的 K、V,CUDA 后端自动走 FlashAttention 的高效广播路径,训练与推理共用同一套 kernel。框架层面,Hugging Face 的模型配置里只需声明 num_key_value_heads,attention_mask、位置编码、RoPE 等其余逻辑全部复用——这也是 GQA 在 2023 年后能迅速铺开的原因之一:从模型架构到推理框架,改动都被收敛到了一个配置字段和几个 kernel 特化上。
还有一个容易被忽略的工程点是缓存的读取模式。GQA 的 KV cache 在显存里按”层 × KV 头 × 序列位置”组织,解码时每个 KV 头被同一组的多个查询头反复读取。如果 kernel 把每个查询头都单独发起一次 KV 加载,带宽收益照样会打折扣;正确的做法是让同一组的查询头在同一个线程块(thread block)里处理,KV 块加载一次、组内所有查询头复用。FlashAttention 与 vLLM 的实现都遵循这个”组内共驻”的调度原则,CPU 推理框架(如 llama.cpp)的 QK 融合路径也按同样的思路处理。
更进一步的 KV 压缩#
GQA 打开了”压缩 KV 头”的思路。把这条线上几类方案放在一起对比,可以看清各自的压缩对象与代价:
| 方案 | 压缩对象 | KV 压缩比 | 质量影响 | 代表 |
|---|---|---|---|---|
| MHA | 无 | 1× | 基线 | 早期 GPT、LLaMA-2-7B/13B |
| MQA | KV 头数压到 1 | h× | 明显下降 | PaLM、Falcon-7B |
| GQA | KV 头数压到 G | h/G× | 轻微 | LLaMA-2/3、Mistral、Qwen2 |
| MLA | 低秩潜在向量压缩 | 数倍以上 | 轻微 | DeepSeek-V2/V3 |
| KV 量化 | 每元素位数 | 2–8× | 轻微(需校准) | QEvict 等 |
“压缩比”一栏以 KV cache 内存为口径:MQA/GQA 压缩的是头数维度,MLA 压缩的是表示本身的维度,量化压缩的是数值精度——三者作用在不同维度,可以任意组合。后续工作在这条线上继续推进:
- MLA(Multi-head Latent Attention):DeepSeek-V2 提出的低秩(low-rank)KV 压缩——把 KV 压缩进一个低维潜在向量,比 GQA 的分组共享更激进、压缩比更高,本质上可以看作 GQA 向”连续分组”方向的推广。本站已有MLA 完全拆解,其与 GQA 的对比(1/8 vs 1/16 乃至更高的压缩比)在那篇文章里有详细讨论。
- KV 缓存量化:把缓存的 K、V 从 FP16 压到 INT8/INT4(例如本站拆解过的 QEvict 的 INT2 档位),与 GQA 完全正交——GQA 减少”头数”,量化减少”每元素字节数”,两者相乘。
- 注意力复用与淘汰:MAC-Attention 复用相似查询的注意力结果、QEvict 淘汰低价值 KV,处理的是”哪些 KV 值得存”的问题,与”KV 头怎么排布”互补。
边界也要说清楚:GQA 改变的是 KV cache 的系数,不改变它随序列长度线性增长的事实。在 128K、1M 级长上下文下,即使 KV 缩小 8 倍,总容量仍然可观——这也是长上下文推理里 KV 卸载(如 DualPath)、分层存储等技术依然必要的原因。GQA 解决的是”单位 token 的 KV 成本”,而不是”上下文长度”问题。
小结#
GQA 是注意力机制演进中一个简洁而关键的分水岭。它把 MHA 的”每头一套 KV”与 MQA 的”全部共享一套 KV”统一进一个可调参数 G:查询头保留完整的多头多样性,键值头按组共享,KV cache 与解码带宽精确缩小 G/h 倍。配合均值池化 + 5% 算力的升级训练配方,已有的 MHA 检查点可以低成本地改造为 GQA 模型——论文实测 T5-XXL 上获得 5.4 倍推理加速,平均质量仅下降 0.1 分。
三个值得记住的要点:
- 解码推理是内存带宽受限的,KV cache 的大小直接决定延迟、批容量与吞吐;GQA 压缩的是 KV 头数量(结构压缩),与量化(数值压缩)、分页(分配优化)正交叠加。
- GQA 的收益与代价都来自”组内共享”:省带宽、保容量,但注意力 FLOPs 不减;组数 G 是一个可调旋钮,G=8 是论文实测的性价比甜点,主流模型(LLaMA-2/3、Mistral、Qwen2)普遍采用 4–8 组。
- 工程落地要求 kernel 感知分组:显式 repeat 会把省下的带宽重新读回去,FlashAttention 与 PagedAttention 的原生 GQA 支持才是收益真正到手的环节。
如果要在”省 KV 内存”这条路上继续深入,下一站是 MLA 的低秩压缩与 KV 量化;如果想把 GQA 放在整个推理服务里看,连续批处理与 PagedAttention 的配合是完整的图景——本站均已单独拆解,可对照阅读。
参考资料#
- GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints(arXiv:2305.13245)
- GQA 论文 EMNLP 2023 正式版(ACL Anthology)
- Fast Transformer Decoding: One Write-Head is All You Need(arXiv:1911.02150)
- LLaMA 2: Open Foundation and Fine-Tuned Chat Models(arXiv:2307.09288)
- The Llama 3 Herd of Models(arXiv:2407.21783)
- Mistral 7B(arXiv:2310.06825)
- Qwen2 Technical Report(arXiv:2407.10671)
- FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning(arXiv:2307.08691)
- Efficient Memory Management for Large Language Model Serving with PagedAttention(arXiv:2309.06180)
- DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model(arXiv:2405.04434)
- Hugging Face Transformers LLaMA 建模实现(repeat_kv 源码位置)
- 深度解析 Group Query Attention (GQA) 为什么能给 LLM decoder 带来极大推理加速(知乎)
- 深度解析新型注意力机制 Group Query Attention (GQA)(CSDN)
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分内容可能已过时
评论区
分享你的想法,与大家交流讨论
音乐
暂未播放



