想象一下,你走进一座巨大的图书馆。每位研究员(查询头 Q)都需要查阅一批参考书(Key 和 Value),来撰写自己的报告(注意力输出)。在传统的多头注意力机制下,每个研究员不仅要亲自去书架取书,还要把自己看过的每一页都复印一份带走,甚至每一组座位都配一套完全相同的复印本。结果,整座图书馆的复印机一刻不停地运转,书架被塞满,大家在走廊里挤来挤去,阅读速度慢得惊人。 后来,管理员想出了两个妙招。第一招叫 MQA:既然大家查的都是同一批参考书,那就干脆只留一套复印本,所有人排队轮流看——速度快了,但热门区域总是挤成一团,有人等得不耐烦,报告质量就打了折扣。第二招叫 GQA:把研究员分成几个小组,每个小组共享一套复印本,既不像原来那样人手一套浪费资源,也不像只留一套那样挤得水泄不通。Llama 2 等现代大模型正是采用了 GQA 这个折中方案,把推理吞吐量提升了 2~3 倍。
理解 KV 共享,是理解大模型推理性能优化的关键。掌握 GQA/MQA,你就能读懂 Llama 2、Mistral 等主流模型的架构选择,并为实际部署加速方案提供理论依据。
可视化演示
展示SCAN电梯调度算法的磁头移动过程:磁头沿一个方向移动到端点后反向,依次处理请求队列中的磁道访问请求。
本节配有交互动效(进入视口后自动加载,约 3KB)。
SCAN\u7535\u68af\u8c03\u5ea6 \u8bf7\u6c42\u961f\u5217 \u5f53\u524d\u78c1\u5934 \u5df2\u5904\u7406 SCAN \u78c1\u5934\u6cbf\u65b9\u5411\u79fb\u52a8\u81f3\u7aef\u70b9\u540e\u53cd\u5411
背景动机
图书馆管理员的烦恼,从每一个研究员都要求“人手一套复印本”开始。在标准多头注意力机制中,模型有多个查询头,每个查询头都独立地对同一个序列进行注意力计算,相当于每个研究员都要为同一批参考书单独复印一套。这套“复印本”在推理时就是 KV 缓存——每个 token 的 Key 和 Value 向量都要被保存下来,供后续 token 查询。序列越长,复印本就越多,书架的负担就越重。
自回归推理是另一个放大器。大模型生成每个新 token 时,都要重新读取之前所有 token 的 Key 和 Value,这就像每写一句话,都要把前面写过的所有参考书重新翻一遍。当序列长度达到几千甚至几万时,KV 缓存的大小就会爆炸。
更深层的瓶颈在于内存带宽。解码阶段每次只生成一个 token,计算量比训练时小得多,此时真正限制速度的不是算力,而是把 KV 缓存从显存搬到计算单元所需要的时间——就像图书馆里最慢的不是复印机本身,而是来回搬书的走廊。KV 缓存越大,搬书时间越长,推理就越慢。这就是为什么缩小 KV 缓存体积成为优化推理性能的第一突破口。
2 代表 Key 和 Value 各一份,L 是层数,H_kv 是 KV 头数,D 是每个头的维度,S 是序列长度。- 层数 LTransformer 有多少层,每层都要存储一份 KV 缓存。这相当于图书馆的每一层楼都有一套复印本。
- KV 头数 H_kv在标准 MHA 中,KV 头数等于查询头数;在 GQA 中则被分组缩减。这是最关键的压缩旋钮。
- 头维度 D每个注意力头向量的维度,决定了单本“参考书”的厚度。
- 序列长度 S已经生成的 token 数量,每生成一个新 token,KV 缓存就多一行。这就是为什么长上下文场景下缓存猛增。
- 系数 2Key 和 Value 各占一份,复印了两套。
MQA方案
管理员的第一个激进方案是 MQA,把复印本从人手一套缩减为全馆共享唯一一套。MQA 的核心理念是:既然所有查询头查询的是同一个序列,为什么不让它们共享完全相同的 Key 和 Value?于是,所有查询头“排队”查阅同一组 KV。这一改动将 KV 缓存压缩到原来的 1/H(H 为查询头数)。
缓存体积的压缩直接解除了显存焦虑。以前需要 32 套复印本,现在只需要 1 套。在批处理大小为 B 的场景下,MQA 的显存优势更加明显,因为每个 batch 都只需要维护一套 KV。这意味着可以塞进更大的 batch,吞吐量随之提高。
然而,MQA 的激进换来了质量的下降。当所有查询头被迫共享同一组 K 和 V 时,模型表达不同“关系”的能力被削弱了——就像所有研究员都必须在同一本参考书上做笔记,但每个人的笔记重点不同,挤在一起难免互相干扰。实践中,MQA 在部分任务上会出现精度下降,尤其在需要精细区分多种关系的场景里。
MQA 的另一个问题是训练不稳定和实现复杂。虽然推理时缓存大幅缩小,但训练时多个查询头共享 K 和 V 会让梯度传播路径变得复杂,影响收敛速度。因此,MQA 虽然提出来得更早,但在实际大模型中的采用率远不如后来提出的 GQA。
1/√d_k 起到缩放作用,避免点积过大导致 softmax 进入饱和区。- 查询投影 QW^Q每个查询头将自己的输入投影成“问题向量”,相当于每位研究员写下自己的问题。
- 共享键投影 KW^K所有查询头复用同一个 Key 投影,相当于全馆只保留一份目录索引。
- 缩放点积将问题与目录做匹配,除以 √d_k 控制数值范围。
- Softmax 归一化把匹配度转成权重,决定每个 token 被关注多少。
- 共享值投影 VW^V用同一份 Value 加权求和,产出最终输出。
GQA分组
GQA 是管理员深思熟虑后的折中方案:与其让 8 个研究员挤 1 本参考书,或者每人复印 1 本,不如把他们分成小组,每组共享一套复印本。具体来说,GQA 将查询头分成 G 组,每组共享一组独立的 K 和 V 头。当 G = 1 时退化为 MQA,当 G = H 时等价于 MHA,因此 GQA 是两者之间的连续插值。
\begin{aligned}
O &= \text{softmax}\left(\frac{Q G^Q (K G^K)^\top}{\sqrt{d_k}}\right) V G^V \\
\end{aligned}
1. 分组重塑 Q:将查询头按组规整成新的形状,组数为 G。
2. 广播 K 和 V:让每组共享同一套 K 和 V,通过 expand 操作实现零拷贝广播。
3. 组内注意力:每个查询头只与所在组的 KV 做缩放点积注意力,把计算限制在小组内部。
分组数 G 是一个超参数,直接决定了“多人共享程度”。G 越大,KV 缓存越大,模型表达能力越强;G 越小,KV 缓存越小,速度越快。Llama 2 70B 选择了 G = 8、H = 64,将 KV 缓存压缩为原来的 1/8,获得了速度与质量的双赢。
GQA 的巧妙之处在于它几乎不牺牲质量。研究发现,组内共享 KV 带来的表达力损失远小于全部共享,因为多数注意力模式在组内就能被充分表达。这个结果像极了图书馆的“小组讨论区”——几个人的小组共享参考书,互相补充笔记,效率和质量都有保障。
KV缓存
KV 缓存是 GQA 的直接受益者。在推理时,每生成一个新 token,模型都需要把新 token 的 Key 和 Value 追加到缓存中,并在下一次生成时读取全部缓存。这个“写入 + 读取”的过程是内存带宽的主要消费者。KV 缓存的大小直接决定了单条请求的显存占用,进而决定了显卡能同时容纳多少条请求(batch size)。
应用 GQA 后,KV 头数从 H 降到 G,缓存体积相应变为:
压缩效果有多显著?以 Llama 2 70B 为例,该模型有 80 层、每层 8 个 KV 头、每个头 128 维。一条 4096 token 的序列,其 KV 缓存大约是 2 × 80 × 8 × 128 × 4096 ≈ 670 MB。如果不采用 GQA 而是用标准 64 个 KV 头,这个数字会膨胀到约 5.4 GB——一张 A100 80G 的显卡瞬间被吃掉了一小半。
KV 缓存的缩小还带来一个连锁收益:推理引擎可以在缓存中容纳更长的上下文。长上下文场景(如多轮对话、文档分析)是 KV 缓存压力的重灾区,GQA 让这些场景从“显存爆掉”变为“勉强可跑”甚至“流畅运行”。这也是为什么当前主流大模型几乎全部采用 GQA 而非 MHA。
- 原始缓存MHA 的缓存体积正比于查询头数 H。
- GQA 缓存GQA 的缓存正比于分组数 G。
- 压缩比两者相除,得到压缩比例 G/H。
性能收益
共享 KV 的最终目标不是省显存本身,而是把省下来的资源转化为吞吐量。推理吞吐量 = 每秒生成的 token 数,它受到两个因素制约:单次请求的延迟和并发请求的批处理规模。GQA 在这两个维度上都有贡献。
从延迟维度看,KV 缓存变小意味着每个生成步骤需要从显存搬运的数据量减少,内存带宽瓶颈被显著缓解。自回归解码本质上是“内存受限”任务,计算量只是附属,搬运 KV 缓存的时间常常占了大头。缓存缩到 1/8,搬运时间也近似缩到 1/8,单次生成延迟大幅下降。
这个 2~3 倍的吞吐量提升,是实测数据支撑的结论。Google 的 PaLM、Meta 的 Llama 2、Mistral 7B 等模型在采用 GQA 后,都报告了相似的收益。对于服务提供商而言,吞吐量翻倍意味着同样的显卡可以服务两倍的用户,成本直接减半。
GQA 的均衡策略还体现了一个重要理念:性能优化往往不是“快就好”,而是在多个目标之间寻找最佳平衡点。MQA 最快但质量受损,MHA 质量最好但速度受限,GQA 选择了中间地带,用略高一点的 KV 缓存换来了接近无损的质量,这才是工业界最需要的方案。
- 批大小 B单次推理同时处理的请求数。KV 缓存越小,显存越宽裕,B 可以开得越大。
- 计算时间矩阵乘法和注意力计算本身的时间,在 GQA 中基本不变。
- 内存访问时间读取和写入 KV 缓存消耗的时间,GQA 大幅削减了这个分量。
- 整体吞吐在计算时间不变、内存时间缩短、批处理变大的三重作用下,吞吐量获得 2~3 倍提升。
工程应用
GQA 在工程界的普及速度令人瞩目。Meta 在 Llama 2 系列中首次大规模应用 GQA,其 70B 版本采用 8 个 KV 头,而 7B 版本则用 32 个 KV 头(即接近 MHA)。这种“大模型更加需要压缩”的设计思路,恰好印证了 KV 缓存压力随模型规模增大而急剧上升的规律。
主流推理引擎对 GQA 的支持已经相当成熟。Hugging Face Transformers、vLLM、TensorRT-LLM 等都内置了 GQA 的实现,并且在 KV 缓存管理上做了精细优化:内存池化、连续显存分配、Cache Manager 动态调度。对于应用开发者而言,无需手动实现 GQA,只需在加载模型时指定正确的配置,引擎会自动构建共享 KV 的注意力计算图。
在长上下文部署场景中,GQA 的地位不可替代。例如部署一个支持 32K 上下文的对话模型,如果使用 MHA,KV 缓存可能占据超过 20GB 显存,导致 batch size 被压到个位数;而使用 GQA 后,缓存缩减使 batch size 可以扩大数倍,服务吞吐量实现质的飞跃。
除了 Llama 2,Mistral 7B 和 Mixtral 8x7B 也采用 GQA,其 7B 模型使用 8 个 KV 头,配合滑动窗口注意力(Sliding Window Attention),在长文本任务上取得了极佳的效果。这代表了当前大模型架构的一个明确趋势:在注意力机制上做“减法”,用结构优化换取效率。
对于 AI 工程师而言,理解 GQA 不仅是为了读懂模型配置,更是为了在实际部署中做出正确决策:何时选择 GQA(推理成本敏感型场景)、如何设置分组数(在质量预算内尽量压缩)、怎样配合其他优化手段(如 KV 量化、PagedAttention)形成组合拳。这些知识直接决定了推理服务的成本与体验。
AI 应用场景
高并发对话服务
阶段:推理部署
解决的问题:在线对话服务需要同时服务大量用户,每个用户都有多轮历史对话,KV 缓存占用巨大,导致单卡只能服务少量并发。传统 MHA 架构下,一条 2000 token 的对话请求就要占用数百 MB 显存,GPU 上只能同时跑几个请求,服务成本居高不下。采用 GQA 后,KV 缓存缩小到 1/8,单卡并发数提升数倍。
具体案例:某 AI 创业公司部署 Llama 2 70B 提供客服机器人服务。将模型的 KV 头从 64 缩减到 8 后,在同样的 A100 集群上,每张卡的并发请求数从 4 提升到 12,整体推理成本降低了约 60%,同时客服回答质量在人工评估中几乎没有差异。
长文档分析
阶段:模型应用
解决的问题:长上下文场景(如论文解读、合同审查)要求模型一次性处理数万 token,KV 缓存随序列长度线性增长,极易超出显存限制。GQA 压缩缓存后,长序列推理才能在消费级 GPU 上运行。
具体案例:一家法律科技公司需要处理平均 8000 词的法律合同,每个合同约 12000 token。使用 MHA 架构的模型需要 48GB 显存才能处理,而切换到 GQA 架构后,显存需求降到 16GB,公司得以使用更便宜的 GPU 完成同样任务,月成本从 20 万元降至 7 万元。
KV缓存服务
阶段:系统架构
解决的问题:在 Prompt Cache、前缀复用等高级推理优化中,KV 缓存是需要被跨请求复用的核心资产。GQA 缩小了缓存体积,使得缓存服务可以在相同显存下缓存更多的前缀,提高前缀命中率。
具体案例:某大模型 API 服务商观察到大量请求共享相同的系统提示词。在引入 GQA 并配合 Prefix Caching 后,系统提示词的 KV 缓存命中率达到 90%,有效生成时间缩短了 40%,用户感知的响应速度明显提升。
Python 演示
import torchimport torch.nn as nnimport torch.nn.functional as F class GroupedQueryAttention(nn.Module): """分组查询注意力 GQA:分组共享 KV,可退化为 MQA/MHA""" def __init__(self, d_model=512, h_q=8, h_kv=2, dropout=0.0): super().__init__() self.h_q = h_q self.h_kv = h_kv self.d = d_model // h_q self.w_q = nn.Linear(d_model, d_model, bias=False) self.w_k = nn.Linear(d_model, h_kv * self.d, bias=False) self.w_v = nn.Linear(d_model, h_kv * self.d, bias=False) self.w_o = nn.Linear(d_model, d_model, bias=False) self.drop = nn.Dropout(dropout) def forward(self, x, past_kv=None): b, s, _ = x.shape q = self.w_q(x).view(b, s, self.h_q, self.d).transpose(1, 2) k = self.w_k(x).view(b, s, self.h_kv, self.d).transpose(1, 2) v = self.w_v(x).view(b, s, self.h_kv, self.d).transpose(1, 2) if past_kv is not None: pk, pv = past_kv k = torch.cat([pk, k], dim=2) v = torch.cat([pv, v], dim=2) # 核心:将 q 的 head 维度按组 reshape,再将 kv 扩展回原头数 group = self.h_q // self.h_kv q = q.view(b, self.h_kv, group, s, self.d) k_e = k.unsqueeze(2).expand(b, self.h_kv, group, -1, self.d) v_e = v.unsqueeze(2).expand(b, self.h_kv, group, -1, self.d) score = (q @ k_e.transpose(-1, -2)) / (self.d ** 0.5) attn = F.softmax(score, dim=-1) attn = self.drop(attn) out = (attn @ v_e).transpose(1, 2).contiguous() out = out.view(b, s, self.h_q * self.d) return self.w_o(out), (k, v) def main(): torch.manual_seed(0) model = GroupedQueryAttention(d_model=64, h_q=8, h_kv=2) x1 = torch.randn(1, 5, 64) x2 = torch.randn(1, 3, 64) out1, kv = model(x1) out2, kv2 = model(x2, kv) # 验证拼接后长度 assert kv2[0].shape[2] == 8, "KV 缓存应增大到 8" print("GQA 单步输出:", out1.shape) print("GQA 增量输出:", out2.shape) print("KV 缓存形状:", kv2[0].shape) if __name__ == "__main__": main()
q = self.w_q(x).view(...).transpose(1, 2)将 Q 投影后拆成多个查询头,shape 变为[batch, 头数, 序列, 维度]。self.w_k = nn.Linear(d_model, h_kv * self.d, bias=False)关键设计:仅投影出 h_kv 组 K(如 2 组),而不是 h_q 组,从源头压缩了 KV 缓存。torch.cat([pk, k], dim=2)在序列维度拼接历史 KV 和新 token 的 KV,这是缓存追加的标准操作。q.view(b, self.h_kv, group, s, self.d)将 h_q 个查询头按组 reshape,例如 8 个头 → 2 组 × 4 个头,为后续广播做准备。k.unsqueeze(2).expand(...)零拷贝广播 KV 到每个组内全部查询头。expand 不复制数据,这正是 GQA 高效的原因。(q @ k_e.transpose(-1, -2)) / (self.d ** 0.5)缩放点积注意力,计算每个查询头与其组内 KV 的匹配度。F.softmax(score, dim=-1)对最后一个维度归一化,得到注意力权重。return self.w_o(out), (k, v)返回输出与 KV 缓存,后者将在下一次生成时复用。
常见误区
误区GQA 和 MQA 听起来都像“让多个查询头共享 KV”,很多人认为它们只是同一种技术的不同名字,用哪个都一样。
正解GQA 与 MQA 有本质区别。MQA 是“所有人共用一套 KV”的极限共享,8 个查询头全部指向 1 组 K 和 V;GQA 是“分组共享”,8 个查询头分成 4 组或 2 组,每组共享一套 KV。可以这样理解:MQA 是整座图书馆只有一套复印本,GQA 是每个小组各有一套。MQA 是 GQA 在分组数 = 1 时的特例,MHA 则是分组数 = 查询头数时的另一极端。
为何常见:两者名字中都带“查询”与“共享”,教程中也经常放在一起讲,初学者容易把“部分共享”和“全部共享”混为一谈。
误区既然 MQA 会掉点,那 GQA 作为“变小版的 MHA”也必然会明显损害生成质量,只是比 MQA 轻一点。
正解GQA 在多数任务上质量与 MHA 几乎持平。实验表明,当分组数 G 在 4~8 之间时,PPL(困惑度)仅增加不到 0.1%,这对于实际应用完全可接受。质量下降主要出现在极端压缩(如 G=1 的 MQA)或任务对多种关系建模特别敏感时。GQA 的设计哲学就是“以极小的质量代价换取巨大的效率收益”。
为何常见:人们容易从“MQA 掉点”线性外推,认为凡是共享 KV 都会掉点,忽略了分组带来的缓冲作用。
误区KV 头从 64 个减到 8 个,肯定省了很多参数,模型变小了,所以推理变快了。
正解GQA 减少的是 KV 缓存体积,而不是模型参数量。在标准实现中,KV 投影矩阵本身占比不大,真正的大头是 FFN(前馈网络)和 Embedding。GQA 提速的核心机制是:推理时 KV 缓存变小 → 每次生成 token 需要从显存搬运到计算单元的数据变少 → 内存带宽瓶颈缓解。这类似于图书馆真正拥挤的不是书架上的书本身,而是搬书的走廊。
为何常见:人们习惯于用“参数少=速度快”的直觉做判断,忽略了自回归推理实际上是内存带宽受限而非计算受限的事实。
误区论文说 GQA 能让吞吐量提升 2~3 倍,那我部署服务时直接用 GQA,所有场景的速度都会自动翻倍。
正解GQA 的提速效果受序列长度、batch size、硬件特性影响显著。短序列 + 小 batch 场景下,KV 缓存占比本来就小,GQA 收益有限;长序列 + 大 batch 场景下(如 4K 以上上下文、多用户并发),缓存压缩的收益才会充分显现。GQA 是“给系统创造提速条件”而非“无条件提速”,需要配合足够的并发负载才能兑现。
为何常见:2~3 倍是一个吸引眼球的数字,但它是特定配置下的实验结论,初学者容易把它当成普适常量。
误区KV 缓存缩小,最直接的好处就是显存占用变少,所以 GQA 的作用就是让模型能在更小的显卡上运行。
正解节省显存只是表层收益,更深层的价值是突破内存带宽瓶颈。自回归解码中,每个 token 的生成都要读取全部 KV 缓存,缓存越大,搬运时间越长。GQA 压缩缓存后,搬运时间缩短,单 token 延迟下降,同时显存腾出空间后可以增加 batch size,两者共同提升吞吐量。显存只是物理容器,带宽才是真正的速度瓶颈。
为何常见:显存占用是看得见的数字,而内存带宽是隐藏在系统深处的运行机制,初学者更容易关注前者。
面试题
Q1. MQA 的核心思想是什么?
- A.所有查询头共享一组 K 和 V← 正确答案
- B.所有查询头各自保留独立的 K 和 V
- C.查询头分成多组共享 K 和 V
- D.取消 Key 只保留 Value
答案:A。MQA 是极端的共享策略,所有查询头共用同一组 K 和 V。B 是 MHA 的做法,C 是 GQA,D 是无效设计。
Q2. GQA 中 KV 缓存压缩比例约为多少?
- A.1(不压缩)
- B.H/G(H 为查询头数,G 为分组数)
- C.G/H← 正确答案
- D.常数 2
答案:C。KV 缓存正比于 KV 头数。GQA 从 H 个 KV 头变为 G 个,缓存变为原来的 G/H。若 H=32、G=4,则压缩到 1/8。
Q3. 以下关于 GQA 的说法正确的有哪些?
- A.G 等于查询头数时退化为 MHA← 正确答案
- B.G 等于 1 时退化为 MQA← 正确答案
- C.GQA 完全不损失模型质量
- D.GQA 可缓解内存带宽瓶颈← 正确答案
答案:ABD。GQA 是 MHA 与 MQA 的连续插值,G=H 即每头独立共享(MHA),G=1 即全共享(MQA)。GQA 虽能大幅提升效率,但仍有可能造成轻微的精度下降,C 过于绝对。
Q4. Llama 2 70B 采用 GQA 后,推理吞吐量大约提升多少?
- A.10%以内
- B.50%
- C.2~3 倍← 正确答案
- D.10 倍以上
答案:C。官方报告与业界实测显示,采用 GQA 后 KV 缓存大幅压缩,内存带宽瓶颈缓解,推理吞吐量可提升约 2~3 倍。10 倍以上在纯软件层面很难实现。
Q5. 自回归解码阶段的主要性能瓶颈是什么?
- A.矩阵乘法的计算量
- B.内存带宽和 KV 缓存搬运← 正确答案
- C.数据加载与预处理
- D.网络通信延迟
答案:B。解码阶段每个 token 都要读取全部历史 KV 缓存,计算量相对小,但搬运量随序列长度线性增长,因此瓶颈在显存带宽而非算力。
Q6. KV 缓存大小的计算公式中,符号 H 代表什么?
- A.模型总层数
- B.序列长度
- C.KV 头数← 正确答案
- D.隐藏层维度
答案:C。缓存公式为 2×L×H×D×S,其中 H 是 KV 头数。L 是层数,D 是头维度,S 是序列长度。GQA 正是在 H 上做压缩。
Q7. 关于 GQA 分组数 G 的选择,以下哪些说法是正确的?
- A.G 越大,KV 缓存越大← 正确答案
- B.G 越小,表达能力越弱← 正确答案
- C.分组数应当固定为 8,不可调整
- D.G 的选择需在速度与质量之间权衡← 正确答案
答案:ABD。G 是超参数,并非固定值。G 越大,缓存越多、质量越好;G 越小,缓存越少、速度越快。工业界需要结合任务质量要求和算力预算做权衡。
Q8. 以下哪个模型架构明确采用了 GQA?
- A.GPT-3
- B.BERT
- C.Llama 2 70B← 正确答案
- D.T5 base
答案:C。Llama 2 系列是 GQA 的典型代表,其 70B 版本使用 8 个 KV 头。GPT-3、BERT、T5 base 都基于标准 MHA 设计。
Q9. GQA 相比 MHA 最主要的改进是什么?
- A.增加注意力头数
- B.减少 KV 缓存体积← 正确答案
- C.增大模型参数量
- D.改进激活函数
答案:B。GQA 将多个查询头分组共享 KV,直接减少了 KV 头数,从而压缩缓存体积,缓解显存和带宽压力。B 是 GQA 的设计初衷。
Q10. GQA 可以看作哪两种机制的插值?
- A.MHA 每头独立 KV← 正确答案
- B.MQA 全部共享 KV← 正确答案
- C.线性注意力
- D.滑动窗口注意力
答案:AB。GQA 在分组数 G=H 时等于 MHA,G=1 时等于 MQA,因此它是 MHA 与 MQA 之间的广义插值。C 和 D 是其他注意力变体,不属于 GQA 的定义范畴。