分组查询注意力 GQA:效率与质量的平衡方案

FreeGuideOnline 最新 2026-06-22

分组查询注意力 GQA:效率与质量的平衡方案

在现代大语言模型中,注意力机制的计算开销随序列长度呈二次方增长,内存带宽更成为推理速度的核心瓶颈。多查询注意力(MQA) 大幅压缩了 KV 缓存,却可能损伤模型质量;而多头注意力(MHA) 质量最优,但缓存占用过高。分组查询注意力(Grouped-Query Attention, GQA) 在两者之间找到一个优雅的平衡点 —— 通过将查询头分组共享键值头,在几乎不损失效果的前提下,显著降低内存占用并提升推理吞吐。

为什么需要 GQA:从 MHA 到 MQA 的演进

多头注意力 MHA 的原理与瓶颈

标准的多头注意力将输入投影到 $h$ 个不同的查询($Q$)、键($K$)、值($V$)子空间,每个头独立计算注意力:

  • 每个头拥有独立的 $Q_i, K_i, V_i$
  • 输出为所有头拼接后线性投影

自回归生成时,每个 token 都要保存所有层的 $K$ 和 $V$,形成 KV 缓存。对于 $h$ 个头、$d_k$ 维度的键,每个 token 的缓存大小为 $2 \times h \times d_k$。在长序列与大批次推理中,这成为显存带宽的绝对瓶颈。读取 KV 缓存的耗时往往远大于实际计算,模型只能等待数据搬运,GPU 利用率极低。

多查询注意力 MQA 的激进压缩

MQA 提出所有查询头共享同一组键值头

  • 仍然有 $h$ 个 $Q$ 头
  • 但只有 1 组 $K, V$(即单个键值头)

KV 缓存缩减为原来的 $1/h$,内存占用和带宽需求骤降,推理速度大幅提升。然而,生成质量常常出现退化,尤其在需要丰富表达的任务上,因为单一的键值头被迫为所有查询头提供相同的上下文,限制了注意力模式的多样性。

分组查询注意力 GQA 的折衷方案

GQA 将查询头分成 $g$ 个组($1 < g < h$),每个组内共享同一组键值头。即:

  • 查询头数量仍为 $h$
  • 键值头数量缩小为 $g$
  • 每个查询头选用所属组的 $K_g, V_g$

当 $g=1$ 时退化为 MQA,当 $g=h$ 时退化为 MHA。GQA 用数量介于中间的键值头,在效率与质量之间取得了近乎最优的帕累托前沿。典型配置如 Llama 2 70B 使用 $g=8$,而 MHA 头数为 64,实现了接近 MHA 的质量,同时 KV 缓存缩小为原来的 1/8。

GQA 核心原理与计算流程

层内结构:查询分组与键值共享

假设模型有 $h$ 个注意力头,键值头数 $kv\_heads = g$,且 $h$ 是 $g$ 的整数倍。GQA 层内部:

  1. 将输入线性投影得到 $Q$ 矩阵,并按 $h$ 头切分。
  2. 将输入线性投影得到 $K$ 和 $V$ 矩阵,但仅切分为 $g$ 个头,每个 K/V 头的维度与 Q 头相同。
  3. 建立映射:第 $i$ 个查询头使用第 $\lfloor i / (h/g) \rfloor$ 组键值头。
  4. 每个查询头与对应组的 $K$ 计算注意力分数,并与该组的 $V$ 加权求和。
  5. 将 $h$ 个头的输出拼接后做最终线性投影。

这样,每个 token 的 KV 缓存仅需存储 $g$ 组键值向量,空间为 MHA 的 $g/h$ 倍。

自回归推理中的内存带宽优势

在逐个 token 生成的 decode 阶段,每次前向推理都需要从显存读取整个 KV 缓存。显存带宽消耗大致正比于缓存大小。GQA 将缓存大小降为 MHA 的 $1/(h/g)$,极大缓解了带宽压力。当组数远小于头数时,推理速度可成倍提升,同时允许更大的批处理尺寸,进一步摊薄计算开销。

训练阶段几乎无额外开销

训练期间,GQA 仍需对每个查询头独立计算注意力,只是 K 和 V 的投影矩阵变小(输出通道从 $h \times d_k$ 降为 $g \times d_k$)。这微幅减少了参数量和计算量,但整体训练吞吐变化不大。GQA 的主要收益体现在推理端,属于“训练一次,处处加速”的低迁移成本技术。

GQA 质量与效率的量化平衡

与 MHA、MQA 的性能对比

多篇研究(如 Llama 2 论文)在相似参数量下对比了三种架构的 perplexity 与下游任务。典型结论:

  • MHA:质量最优,但 KV 缓存最大,推理最慢。
  • MQA:推理最快,但在一些需要复杂推理的任务上 drop 明显。
  • GQA (如 8 组):质量非常接近 MHA,甚至在某些任务上持平,同时推理速度接近 MQA。

以 Llama 2 70B 为例,采用 8 组 GQA 后,相比 MHA,键值缓存缩减为原来的 1/8,推理成本大幅下降,而 MMLU、常识推理等基准分数几乎没有下降。

分组数量如何选择

$g$ 的选取取决于目标任务对质量的要求和硬件的内存约束。经验法则:

  • 若内存紧张且优先吞吐量,可使用较小 $g$(如 2 或 4)。
  • 若希望无损迁移 MHA 模型,$g$ 通常设为 8 或 16 即可接近质量上限。
  • 对于超大型模型,由于头数本身就多,适度共享(如 $h=64, g=8$)几乎觉察不到质量差异。

调优时可将 $g$ 视为超参数,在验证集上快速扫描。

工程实现要点

张量重塑与组映射

在 PyTorch 等框架中实现 GQA,关键步骤是调整 $K$ 和 $V$ 维度后使用 repeat_interleaveexpand 来匹配查询头数。示例代码片段:

# h: 总头数, g: 键值头数, d: 每个头的维度
q = q.view(batch, seq_len, h, d).transpose(1, 2)  # (B, h, L, d)
k = k.view(batch, seq_len, g, d).transpose(1, 2)  # (B, g, L, d)
v = v.view(batch, seq_len, g, d).transpose(1, 2)

# 扩展 k, v 使其头数与 q 对齐
repeat_factor = h // g
k = k.repeat_interleave(repeat_factor, dim=1)  # (B, h, L, d)
v = v.repeat_interleave(repeat_factor, dim=1)

# 后续按标准缩放点积注意力计算
attn_out = scaled_dot_product_attention(q, k, v)

实际部署时,很多高效注意力内核(如 FlashAttention、xFormers)已原生支持 GQA 的广播机制,无需显式扩展,直接传入不同头数的 Q、K、V 即可节省内存。

权重加载与转换

当从预训练 MHA checkpoint 转换为 GQA 时,常采用均值池化:将相邻的若干 K/V 权重矩阵在 head 维度上分组合并平均。例如将原来的 64 个 K 头平均池化为 8 个组头。这种转换可在少量微调后快速恢复质量,甚至可直接用于评估而下降有限。

推理优化:连续批处理与分页注意力

GQA 天然适合结合连续批处理PagedAttention 等技术。由于 KV 缓存只按 $g$ 组存储,内存碎片更少,分页管理效率更高。vLLM、TensorRT-LLM 等推理框架均深度优化了 GQA 变体,可在一个批次内混合多个不同长度的序列,显著提高硬件利用率。

GQA 的变体与研究前沿

  • 交错分组 GQA:不同层使用不同的组数,浅层采用更多组保留细节,深层使用更少组压缩全局信息,进一步消减缓存。
  • 动态 GQA:在推理时根据输入长度动态调整组数,长序列自动降低 $g$ 以维持吞吐,短序列恢复全多头保证精度。
  • GQA + 稀疏注意力:将 GQA 与滑动窗口、全局令牌等模式结合,使长文本处理更高效。

开始使用 GQA

对于新手,最简单的实践路径是:

  1. 使用支持 GQA 的预训练模型:如 Llama 2、Llama 3、Mistral 部分版本、Gemma 等,直接调用 HuggingFace Transformers 加载,底层已自动处理分组逻辑。
  2. 调整推理参数:在 model.generate() 时配置 use_cache=True,通过 past_key_values 感受加速效果。
  3. 自定义模型训练:修改注意力类,将 K/V 投影矩阵的输出维度从 h * d 改为 g * d,并插入前述 repeat 逻辑。推荐参考 Llama 模型的实现源码。

GQA 已成为当前大模型架构的标准组件之一。理解其动机与实现,能帮助你在有限硬件上更高效地部署高质量语言模型,亦是深入掌握现代 Transformer 推理优化的关键一步。