想象一下,你是某大型演唱会的主办方。从前,你的场馆里每位明星(query)都配了一个专属速记员(key/value),一字不落地记录着全程影像。速记员人数与明星相同,成本高昂到难以承受。于是,你尝试砍掉所有速记员,只留一人负责全部记录——省钱了,可这位速记员要同时服务上百位明星,忙得焦头烂额,记录质量急剧下降。怎么办?聪明的你灵机一动:把明星分成几个小组,每组配一位速记员。组内共享记录,组间各司其职。既省了人力,又保留了服务质量。这个巧妙的折中方案,正是我们今天的主角——GQA(分组查询注意力)。它让 Llama 2 等大模型的推理成本大幅降低,而性能几乎无损。
GQA 是解码阶段释放内存带宽的钥匙。掌握它,你就能在模型架构设计、推理性能优化岗位上,用最少的 KV Cache 撬动最大的吞吐量提升。
可视化演示
展示MHA/GQA/MQA三种注意力头结构的KV共享机制,以及KV Cache与推理效率的对比。
本节配有交互动效(进入视口后自动加载,约 3KB)。
MHA/GQA/MQA头结构对比 MHA:每个Q头独立KV头,质量最高开销最大 GQA:分组共享KV头,质量与效率平衡 MQA:所有Q头共享一个KV头,效率最高 KV heads: MHA=8, GQA=4, MQA=1 GQA在质量与效率间取得最优平衡
MHA内存瓶颈
先回到我们的演唱会现场。多头注意力(MHA)是这个场馆的“全明星速记员制”:每位明星(query 头)都配备一名专属速记员(对应的 key/value 头)。这种“一人一记”的豪华配置确实让记录质量无懈可击,可问题也随之而来——当明星越来越多,速记员团队的规模就变得极其庞大,场馆的后台(显存)根本堆不下这么多人手。
这就是 MHA 在推理阶段遭遇的尴尬。Transformer 解码时,需要把历史上所有 token 的键值对(Key 和 Value)缓存下来——这就是 KV Cache。在 MHA 中,每个查询头都有一份独立的键值缓存,假设模型有 32 个注意力头,每层就要存 32 份键值对。乘以 32 层、乘以序列长度、乘以批大小,显存瞬间就被塞满了。
更致命的是内存带宽瓶颈。解码阶段每一步只生成一个 token,却要把缓存中的所有键值从头到尾读取一遍。哪怕计算本身只需要几毫秒,读数据的时间却要几十毫秒——就像演唱会观众只有一位,却要把整个速记员团队叫醒来翻阅所有档案。这种“算的少、读的多”的模式,让内存带宽成了真正的短板。MHA 对显存和带宽的挥霍,正是我们要解决的第一道难题。
回到演唱会:MHA 就像给每位明星配一个速记员,显存就是后台的休息室。明星越多,休息室越拥挤,最终走廊上都站满了人,进出都困难。
- 缓存数量每层每个注意力头的 KV 都要缓存一份,所以先乘上“层数 $\times$ 头数”。
- 单头大小每个头的 K 和 V 向量各有一段长度,由维度决定。
- 总占用最后乘上批大小(同时处理几条样本)和字节数(每维度占用内存)。因子 2 是因为 K 和 V 都要存。
MQA激进简化
既然“一人一记”这么贵,为什么不试试“全场只用一个速记员”呢?这就是多查询注意力(MQA)的思路——所有查询头共享同一组键值头。相当于演唱会雇了一位金牌速记员,同时为台上所有的明星服务。
这个方案的效果立竿见影。KV Cache 的占用直接从“头数份”缩减到“1 份”。如果原本有 32 个查询头,那么 KV 缓存的内存直接降为原来的 1/32!内存带宽压力也同步大减,解码速度飙升。理论上,这是效率的极致。
然而,代价同样明显。想象这位速记员要同时记录 32 位明星的每句话、每个动作,他能记住谁说了什么?注意力头本应从不同角度(语法、语义、指代)捕捉信息,现在所有头共用一份键值对,信息多样性大打折扣。实验表明,MQA 与 MHA 相比,在质量上确实有所下滑,尤其是在长文本任务上。
更麻烦的是,MQA 的模型训练收敛速度偏慢,且很难通过事后微调从 MHA 直接转换。它像一个“一步到位”的激进改革:省了成本,但伤了元气。MQA 把“省”做到了极致,却也把“好”牺牲了太多,这不是我们想要的终点。
MQA 就像把速记员团队裁到只剩一人,省钱是省到了极致,可这位速记员记不过来。我们需要一个既省钱又不降质的方案。
- 共享头数所有查询头复用 1 组 KV,所以头数位置直接为 1。
- 体积锐减与 MHA 对比,此处少了“$\times$ 头数”这一项,显存与带宽压力大降。
GQA折中智慧
回到演唱会后台,主办方想通了一个道理:我们不需要给每位明星配速记员,也不需要全场合用一个,而是可以把明星分成 4 组,每组配一名速记员。这就是分组查询注意力(GQA)的核心思想。
GQA 的做法非常优雅:假设模型有 32 个查询头,我们设定分组数为 8,那么每组包含 4 个查询头,共享一组键值对。这样一来,KV Cache 的占用就是 MHA 的 8/32 = 1/4,也就是减少了 75% 的显存。而由于 4 个查询头共享一组 KV,它们捕捉信息的多样性虽然比 MHA 略逊,但远比 MQA 丰富。
为什么 GQA 能保持较好的质量?因为注意力头的多样性需求主要集中在 Query 侧——查询角度可以丰富多彩,而 Key/Value 侧记录的事实信息没必要重复太多。比如一段话里的“苹果”,既可以指水果,也可以指公司,但“苹果”这个名词本身对应的键值信息是确定的,不同头只是从不同角度查询它而已。因此,共享 KV 并不会造成严重的信息丢失。
GQA 还具备一个极佳的工程特性:分组数是一个可调节的超参数。设为 1 时退化为 MQA,设为与头数相同时退化为 MHA。它像一个可伸缩的旋钮,让我们在质量和效率之间自由滑动。Llama 2 之所以选择 GQA,正是看中了这种灵活性。
GQA 的分组共享,就像是给演唱会配了 8 位速记员,每位负责 4 位明星。成本降了,记录质量还基本保留——两全其美。
- 分组数 $G$KV 头数从 $H$ 降为 $G$,$G$ 通常在 4 到 16 之间,如 Llama 2 用 8。
- 压缩比例显存降至 MHA 的 $G/H$,假设 $H=32$、$G=8$,则只需原来的四分之一。
- 内存公式整体公式结构与 MHA 相同,差异只在于头数位置由 $G$ 取代了 $H$。
KV Cache机制
前面反复提到 KV Cache,它是理解 GQA 价值的基石。你可以把 KV Cache 想象成演唱会的“舞台提词器”:当明星(模型)需要回忆之前说过什么、观众反应如何时,不用重新翻看整场录影,而是直接看提词器上的摘要。
在 Transformer 解码过程中,每生成一个新 token,都需要与之前所有 token 计算注意力。如果不缓存,就得把之前的 token 全部重新计算一遍键值——这相当于每次说一句话都要重新读完整个剧本,计算量是灾难性的。KV Cache 的聪明之处在于:把历史 token 的 Key 和 Value 矩阵保存下来,新 token 只需计算自己的 Query 与缓存的 Key/Value 做注意力运算。
然而,KV Cache 也是有代价的——它占显存。我们以 Llama 2-7B 为例:该模型有 32 层,每层有 32 个注意力头,每头维度 128。当序列长度为 2048 时,如果采用 MHA,KV Cache 的占用约为 32 × 32 × 128 × 2048 × 2 字节 ≈ 512MB。这还只是一个请求,如果批处理 8 个请求,就是 4GB!显存瞬间被缓存吞没。
而 KV Cache 的减半机制,正是通过减少 KV 头数实现的。在 MHA 中,32 个查询头对应 32 组 KV;在 GQA 中,这 32 个查询头只对应 8 组 KV,缓存体积直接缩到原来的 1/4。缓存小了,内存带宽压力也同步下降,解码速度自然提升。
这个机制就像演唱会后台不断增加的道具箱。MV Cache 就是为了避免每次演出都重搬道具,但道具箱本身也占地方。GQA 的妙处在于:减少道具箱的数量,但不影响演出效果。
- 层数与头数每个 Transformer 层都有独立的 KV 缓存,所以乘上 $n_{\text{layers}} \times n_{\text{KV heads}}$。
- 向量维度每个 KV 头包含一个维度为 $d_{\text{head}}$ 的向量。
- 序列与批处理$L$ 是当前序列长度(已生成的 token 数),$b$ 是批大小,最后乘 2 字节(半精度)。
分组比例设计
既然 GQA 的分组数 $G$ 可以自由调节,那到底选多少合适?是做事的另一大关键。如果 $G$ 选太小(比如 1),就成了 MQA,质量受损;如果 $G$ 选太大(比如 32),就等于 MHA,显存压力又回来了。
实践中,最常见的配置是 $G=8$。Llama 2 全系列模型都采用“32 个查询头 + 8 个 KV 头”的配置。为什么是 8?这背后有硬件适配的考量:现代 GPU 的并行计算单元通常以 8 或 16 为粒度,选择 8 组 KV 头可以让每组查询头数量(32/8=4)在硬件上实现高效的内存访问模式。
不同分组比例对性能的影响也呈现鲜明的权衡曲线。把 $G$ 从 32 降到 8,KV Cache 内存下降 75%,而质量损失几乎可以忽略(约 0.1% 的困惑度增加)。继续降到 4,内存再降一半,但质量损失开始显性化。到 2 或 1 时,质量下降已经能让用户感知了。这就是所谓的“边际收益递减”——前期的压缩几乎无痛,后面的压缩则开始伤害效果。
此外,分组比例的选择还要考虑任务性质。对于短文本的对话场景(序列长度几百 token),KV Cache 的绝对占用不大,$G$ 大一点没关系;但对于长文本生成(如论文摘要、代码生成),序列长度动辄几万 token,KV Cache 每压缩一点都能省出大量显存,此时选择较小的 $G$ 更划算。
分组比例的设计就像演唱会速记员人数的取舍:速记员太多,后台拥挤(显存高);太少,记录质量堪忧(效果差)。8 人组是一个经过实践检验的“黄金人数”。
- 压缩比公式与原始 MHA 相比,GQA 将 KV 头数从 $H$ 压缩到 $G$,节省的显存比例。
- 数值实践$H=32, G=8$ 时压缩 75%,质量几乎无损;$G=2$ 时压缩 93.75%,但质量明显下降。
解码阶段提速
解码阶段,才是 GQA 真正发光的舞台。理解这一点,要先搞懂 Transformer 生成文本的方式:它是逐 token 生成的,就像作家一个字一个字地写文章。每写一个字,都要回顾之前已经写下的所有内容。
在每一步解码中,模型都要执行这样的操作:将当前 token 的 Query 与 KV Cache 中所有的 Key 计算注意力分数,再与对应的 Value 加权求和。关键在于——KV Cache 中存储的数据量通常远大于计算量。一个 7B 模型,每一步解码的浮点运算量只有几百万次,但要读取的 KV 数据却有几十 MB。GPU 的算力再强,也追不上内存搬运的速度,这就是“内存带宽瓶颈”。
GQA 怎么解决这个问题?减少 KV 头数 = 减少需要搬运的数据量。假设原本每步要读取 32 组 KV(MHA),现在只需读取 8 组(GQA),数据搬运量直接减少 75%。每一步省下 75% 的带宽,累积到几千步的解码过程,生成速度的提升是肉眼可见的。
这就是为什么 GQA 能显著提升推理吞吐量的原因。它不仅省显存(能塞下更大的批大小),还省带宽(每步解码更快)。两者叠加,推理吞吐量自然水涨船高。这也是 Llama 2 选择 GQA 的底层逻辑——它不是为了让模型训练得更快,而是为了让部署后的推理成本更低、响应更快。
解码的提速原理,相当于让速记员只把“小组摘要”递给明星,而不是把整场演唱会的所有记录都抱过来。小组摘要够用,还更省时间。
- 每步读取量每生成一个 token,需要把 KV Cache 中所有数据读一遍。
- 关键变量$n_{\text{KV heads}}$ 从 32(MHA)降到 8(GQA),读取量同步减少 75%。
Llama工程实践
理论说得再好,最终要看工程实践。Meta 的 Llama 2 系列模型,是 GQA 大规模落地的最佳注脚。Llama 2-7B 和 13B 模型采用 MHA,而到了 34B 和 70B 的大规模版本,则切换到了 GQA。
为什么大模型更需要 GQA?因为 KV Cache 的开销与模型规模成正比——层数越多、头数越多、维度越大,KV Cache 的膨胀就越恐怖。70B 模型如果用 MHA,KV Cache 能撑爆几乎所有的显存,根本没法做长文本推理。Llama 2-70B 采用 GQA 后,KV Cache 显存直接除以 4,让 70B 模型可以在有限的 GPU 上流畅运行。
实测数据显示,GQA 带来的提升非常可观。在相同的硬件环境下,Llama 2-70B(GQA)的推理吞吐量比同规模的 MHA 模型提升了大约 2-3 倍。同时,长文本生成的中间态显存峰值显著下降,这使得 Llama 2 能够支持更长的上下文窗口。在工程部署中,这种优势意味着更低的成本、更高的并发。
Llama 之外,Falcon 系列、Mistral 系列也纷纷采用 GQA 或变体。这说明 GQA 已经从学术研究走向工业标准,成为大模型推理优化的事实标配。对于 AI 从业者来说,GQA 不是可学可不学的知识,而是打开高性能推理大门的一把钥匙。
Llama 2 的实践表明:在真正的工业场景中,GQA 不是纸上谈兵的优化技巧,而是撑起大规模模型高效推理的基石工程。
- 带宽需求比解码阶段的吞吐量受内存带宽制约,带宽需求与 KV 头数 $H$ 成正比。
- 实际效果MHA 的 $H=32$ 变为 GQA 的 $G=8$,理论提升 4 倍,实测约 2-3 倍。
AI 应用场景
大语言模型推理服务部署
阶段:模型推理优化
解决的问题:在 GPU 上部署 70B 以上的大模型时,KV Cache 会占据大量显存,导致批大小受限、并发能力差。传统 MHA 架构下,一个请求就要占 1-2GB 显存,根本没法支撑高并发服务。GQA 将 KV 头数压缩 4 倍,显存占用同步降低,批大小提升 2-3 倍,让推理服务成本大幅降低。
具体案例:某 AI 公司基于 Llama 2-70B 搭建企业内部知识库问答服务。部署时采用 GQA 架构(32 个查询头、8 个 KV 头),在相同 A100 显卡配置下,单卡同时处理的请求数从 4 个提升到 16 个,生成速度提升了约 2 倍。用户的服务响应时间从 6 秒降到 2 秒。
长文本生成场景优化
阶段:序列生成加速
解决的问题:撰写论文、生成代码、分析长文档时,序列长度可达数万 token。MHA 架构下 KV Cache 随序列长度线性增长,很容易 OOM(显存溢出)。GQA 将缓存占用缩小 4 倍,让长文本生成成为可能。
具体案例:用于论文写作的 AI 助手需要一次生成 5000 token 以上的草稿。使用 GQA 优化后的模型,KV Cache 从 3.2GB 降到 0.8GB,多轮编辑对话的响应速度提升了近 3 倍,且生成质量与 MHA 版本基本一致。
边缘设备部署
阶段:端侧推理压缩
解决的问题:手机、嵌入式设备上的内存资源极其有限,无法承载大模型的 KV Cache 开销。GQA 通过减少 KV 头数,让模型的显存需求适配边缘设备的紧缺资源。
具体案例:某智能硬件公司尝试在手机端部署 7B 对话模型,采用 GQA 配置后,KV Cache 占用从 1.4GB 降至 350MB,首次实现了手机端的流畅对话体验。生成速度提升了约 1.8 倍。
Python 演示
import mathimport torchimport torch.nn as nnimport torch.nn.functional as F class MHA(nn.Module): """多头注意力:每个查询头配一组独立的KV头""" def __init__(self, d_model, n_heads): super().__init__() self.n_heads = n_heads self.head_dim = d_model // n_heads self.wq = nn.Linear(d_model, d_model) self.wk = nn.Linear(d_model, d_model) self.wv = nn.Linear(d_model, d_model) self.wo = nn.Linear(d_model, d_model) def forward(self, x): B, L, _ = x.shape q = self.wq(x).view(B, L, self.n_heads, self.head_dim).transpose(1, 2) k = self.wk(x).view(B, L, self.n_heads, self.head_dim).transpose(1, 2) v = self.wv(x).view(B, L, self.n_heads, self.head_dim).transpose(1, 2) attn = F.scaled_dot_product_attention(q, k, v) return self.wo(attn.transpose(1, 2).reshape(B, L, -1)) class GQA(nn.Module): """分组查询注意力:n_heads个查询头共享n_kv_heads组KV头""" def __init__(self, d_model, n_heads, n_kv_heads): super().__init__() self.n_heads = n_heads self.n_kv_heads = n_kv_heads self.head_dim = d_model // n_heads self.wq = nn.Linear(d_model, d_model) self.wk = nn.Linear(d_model, n_kv_heads * self.head_dim) self.wv = nn.Linear(d_model, n_kv_heads * self.head_dim) self.wo = nn.Linear(d_model, d_model) def forward(self, x): B, L, _ = x.shape q = self.wq(x).view(B, L, self.n_heads, self.head_dim).transpose(1, 2) k = self.wk(x).view(B, L, self.n_kv_heads, self.head_dim).transpose(1, 2) v = self.wv(x).view(B, L, self.n_kv_heads, self.head_dim).transpose(1, 2) # 分组扩展:将KV头复制到对应的查询组 reps = self.n_heads // self.n_kv_heads k = k.repeat_interleave(reps, dim=1) v = v.repeat_interleave(reps, dim=1) attn = F.scaled_dot_product_attention(q, k, v) return self.wo(attn.transpose(1, 2).reshape(B, L, -1)) def count_kv_cache(seq_len, batch_size, d_model, n_heads, n_kv_heads): """计算KV Cache占用(半精度字节数)""" head_dim = d_model // n_heads mha_cache = 2 * seq_len * batch_size * n_heads * head_dim * 2 gqa_cache = 2 * seq_len * batch_size * n_kv_heads * head_dim * 2 return mha_cache, gqa_cache if __name__ == "__main__": torch.manual_seed(42) d_model, seq_len, batch_size = 512, 128, 4 n_heads, n_kv_heads = 32, 8 model_mha = MHA(d_model, n_heads) model_gqa = GQA(d_model, n_heads, n_kv_heads) x = torch.randn(batch_size, seq_len, d_model) out_mha = model_mha(x) out_gqa = model_gqa(x) mha_cache, gqa_cache = count_kv_cache(seq_len, batch_size, d_model, n_heads, n_kv_heads) print(f"MHA 输出形状: {out_mha.shape}") print(f"MHA KV Cache 占用: {mha_cache / 1024:.2f} KB") print(f"GQA 输出形状: {out_gqa.shape}") print(f"GQA KV Cache 占用: {gqa_cache / 1024:.2f} KB") print(f"显存节省比例: {(1 - gqa_cache / mha_cache) * 100:.1f}%")
self.wq = nn.Linear(d_model, d_model)查询头保持全量,每个头都有独立的查询变换self.wk = nn.Linear(d_model, n_kv_heads * head_dim)键投影压缩到 n_kv_heads 组,这是 GQA 的核心self.wv = nn.Linear(d_model, n_kv_heads * head_dim)值投影同样压缩到 n_kv_heads 组k.repeat_interleave(reps, dim=1)将 KV 头复制到对应的查询组,完成分组共享F.scaled_dot_product_attention(q, k, v)PyTorch 内置的高效缩放点积注意力count_kv_cache(...)验证 KV Cache 的显存节省比例,MHA 与 GQA 对比一目了然
常见误区
误区KV 头数不是越少越好吗?减少到 1 个不就是最省显存、最快速度?那为什么还要用 GQA 而不是直接上 MQA?
正解KV 头数减少到 1 确实显存最优,但模型质量会明显下降。MQA 在最省资源的同时,也牺牲了注意力头捕捉多样信息的能力。GQA 的意义在于平衡——8 组 KV 头比 32 组省了 75% 显存,但质量损失几乎为零;而继续压缩到 1 组,质量下降就会让用户明显感知。就像演唱会速记员从 32 人减到 8 人无损,但减到 1 人就真的记不过来了。
为何常见:看到 MQA 显存更小就认为更好,忽略了模型生成质量这个核心指标。优化是显存、速度、质量三方的权衡,不是单指标最大化。
误区GQA 看起来和 MHA 差不多,只是把“头”改叫“组”而已。应该不用改变代码,直接调参就行吧?
正解GQA 在实现上有本质区别:MHA 的每个查询头都有独立的 KV 投影矩阵,而 GQA 的 KV 投影矩阵只有 n_kv_heads 组。这意味着模型参数量本身也减少了(KV 侧投影的参数量降为 1/4),前向计算时的 K、V 张量形状完全不同,需要做 repeat_interleave 展开。这不仅是超参数改变,更是架构层的修改。
为何常见:从结构图上看 MHA 和 GQA 很像,容易产生“只是参数不同”的错觉。实际上 GQA 是模型类级别的改动,不是配置文件里改个数就行。
误区我已经有一个训练好的 MHA 模型了,能不能直接把它的注意力头合并成几组,变成 GQA 模型直接使用?
正解不能直接转换。MHA 中每个 KV 头是独立训练的,直接合并会破坏模型的注意力模式,导致性能骤降。正确的做法是:从零训练一个 GQA 架构的模型,或者用 MHA 模型的权重初始化后做持续预训练(upcycling)。Llama 2 的 GQA 模型就是从零训练的。
为何常见:看到 GQA 和 MHA 结构相似,就联想到“剪枝”这类可以直接压缩的技术。但注意力头的共享涉及权重矩阵的重新组织,不是简单的参数删除。
误区GQA 是 70B 这种超大模型才需要用的技术,我现在做的小模型根本用不上。
正解GQA 的价值与模型规模相关,但并非只适用于大模型。即使是 1B 规模的模型,在长序列场景下 KV Cache 也可能占用过半的显存。边缘设备上部署小模型时,GQA 同样能带来显著的显存节省和速度提升。只是在模型很小时,KV Cache 的占比较小,优化收益相对有限。
为何常见:看到 Llama 2 只有 34B/70B 版本才用 GQA,就误以为这是“大模型专属”。实际上 GQA 对小模型、长序列、低资源设备同样有价值。
误区把 32 组 KV 合并成 8 组,信息丢失这么多,模型一定变笨了。所以 GQA 只能在要求不高的场景用。
正解实验表明,GQA 在 $G=8$ 时与 MHA 的性能差异极小(困惑度增加约 0.1%),几乎可以忽略。原因是 Transformer 底层中,多个头共享 KV 并不等于丢失信息——查询头仍然可以从共享的 KV 中提取不同的信息组合。真正的质量下降要到 $G \leq 4$ 时才逐渐显著。GQA 是一个巧妙利用了注意力机制冗余性的高效方案,不是牺牲质量的妥协之举。
为何常见:直觉上“共享=丢失”,忽略了注意力头的分工不是独立存储信息,而是并行从同一信息源提取不同特征这一本质。
面试题
Q1. GQA 中“分组”指的是什么?
- A.将序列中的 token 分组成几个部分
- B.将多个查询头分成几组,每组共享一组键值头← 正确答案
- C.将权重矩阵按行分块处理
- D.将隐层维度划分到不同设备上
答案:B。GQA 的分组是指查询头层面的分组:原本每个查询头配一组KV,现在把 $H$ 个查询头分成 $G$ 组,每组共享 $1$ 组KV。A 是对序列的分组,C 是矩阵并行分块,D 是张量并行,均与 GQA 无关,故选 B。
Q2. 假设模型有 32 个查询头、8 个 KV 头,KV Cache 相对 MHA 节省多少显存?
- A.50%
- B.65%
- C.75%← 正确答案
- D.90%
答案:C。MHA 的 KV 头数等于查询头数 32,GQA 为 8。KV Cache 大小与 KV 头数成正比,因此节省比例为 $1 - 8/32 = 75%$,故选 C。A 对应 $G=16$,B 映射无直接对应,D 对应 $G \approx 3$,均与 8/32 不符。
Q3. 为什么 MQA 的质量往往不如 GQA?
- A.MQA 使用的训练数据更少
- B.MQA 仅剩一组 KV,注意力头无法捕捉多样信息← 正确答案
- C.MQA 的计算量更小所以精度更低
- D.MQA 不适用于自回归模型
答案:B。MQA 将所有查询头共用一组 KV,注意力头从不同角度查询信息的特性被抹杀,信息多样性下降,导致质量受损。A 与架构无关,C 计算量小不必然低质,D 自回归场景同样可用 MQA,只是效果不如 GQA,故选 B。
Q4. 在解码阶段,GQA 提速的主要原理是?
- A.减少模型参数量使计算更快
- B.减少需要读取的 KV Cache 数据量← 正确答案
- C.增加批大小以提高 GPU 利用率
- D.跳过注意力计算直接输出
答案:B。解码阶段受内存带宽瓶颈制约,每步生成需读取全部 KV Cache。GQA 减少 KV 头数,使每步读取的数据量缩减(如 75%),从而显著提升生成速度。A 参数减少的收益有限,C 是 GQA 的间接结果而非原理,D 与注意力机制相悖,故选 B。
Q5. Llama 2-70B 使用 GQA 而非 MHA,最核心的工程动因是?
- A.GQA 让训练过程更快收敛
- B.GQA 让 70B 模型在有限显存下可推理长序列← 正确答案
- C.GQA 大幅减少预训练所需的数据量
- D.GQA 支持无监督学习
答案:B。70B 模型在 MHA 下 KV Cache 占用天文数字(数十 GB),单卡无法承载长序列推理。GQA 将 KV Cache 压缩为 1/4,使有限显存可用。训练速度和数据量在预训练阶段并非 GQA 的主要优势,故选 B。
Q6. 以下是 GQA 演进的基础机制是?
- A.量化感知训练
- B.KV Cache 机制← 正确答案
- C.模型蒸馏
- D.混合精度训练
答案:B。GQA 本质上是在减少 KV Cache 的体量,KV 头数与缓存大小直接相关。理解 KV Cache 的 I/O 开销规律是理解 GQA 的基石。量化、蒸馏、混合精度与 GQA 属于不同方向,故选 B。
Q7. GQA 中 KV 头数 $G$ 的选择受到哪些因素影响?
- A.目标 GPU 的显存容量← 正确答案
- B.序列长度预计范围← 正确答案
- C.期望的推理吞吐量← 正确答案
- D.嵌入维度是否为偶数
答案:ABC。$G$ 越小 KV Cache 占用越低,显存受限场景应选更小 $G$;序列越长、吞吐量要求越高,越倾向于小 $G$。嵌入维度奇偶不影响 $G$ 选择,因为 GQA 的维度分割本就要求整除关系,与偶数无关,故 D 错。
Q8. 关于 GQA 的分组数与模型质量的关系,下列说法正确的是?
- A.$G$ 减小时质量持续线性下降
- B.$G$ 从 32 降至 8 时质量损失几乎可忽略← 正确答案
- C.$G$ 越小,KV Cache 压缩率越高但质量风险越大← 正确答案
- D.$G$ 从 8 降至 2 时质量下降更显著← 正确答案
答案:BCD。质量与 $G$ 不是线性关系。32→8 约损失 0.1%,几乎无损;8→4 开始可感知;4→2 降幅更明显。因此 A 的“持续线性下降”错误,B、D 正确反映了分阶段退化的规律,C 描述了两者此消彼长的权衡关系,也正确。
Q9. GQA 中“重复 KV 头”操作(repeat_interleave)的目的是什么?
- A.增加模型参数量
- B.将 KV 头复制以匹配查询头的数量← 正确答案
- C.提高注意力分数的数值稳定性
- D.降低内存带宽压力
答案:B。GQA 中查询头数为 $H$、KV 头数为 $G$,二者数量不同。计算注意力时,需要将 $G$ 组 KV 按分组映射复制为 $H$ 份(每个查询头各一份),才能与 $H$ 个查询头对齐。复制不增加参数和带宽,也不用于数值稳定,故选 B。
Q10. 与 MHA 相比,GQA 在推理阶段带来哪些改进?
- A.KV Cache 显存占用大幅降低← 正确答案
- B.解码阶段内存带宽压力减小← 正确答案
- C.批处理吞吐量得以提升← 正确答案
- D.训练所需样本量减少
答案:ABC。GQA 主要改善推理性能:KV Cache 下降 → 显存下降、带宽压力减小 → 吞吐量提升。训练收敛速度不是 GQA 的核心优势,所需样本量也不因架构而显著改变,故 D 错误。