☰ inferserver 的数学基础

9. GQA:多个 Q 头共用一个 K/V 头

9.1 定义

多头注意力(MHA)里 Q、K、V 的头数相同,都是 \(H\)。GQA(grouped-query attention)让 K/V 只有 \(H_{kv}\) 个头,且 \(H\) 是 \(H_{kv}\) 的整数倍。第 \(h\) 个 Q 头使用第

\[ g(h)=\left\lfloor \frac{h}{\,H/H_{kv}\,}\right\rfloor \]

个 K/V 头。Qwen3-0.6B:\(H=16\)、\(H_{kv}=8\),组大小 \(G=2\),所以 Q 头 0、1 用 KV 头 0,Q 头 2、3 用 KV 头 1,依此类推。

写成公式,对每个 Q 头 \(h\):

\[ o_h=\operatorname{softmax}\left(\frac{q_hK_{g(h)}^\top}{\sqrt d}+M\right)V_{g(h)} . \]

同一组内的两个 Q 头共享 K/V,但 \(q_h\) 不同,所以它们的注意力权重仍然不同;共享的只是“被查询的键值内容”。

9.2 省了什么

KV cache 每 token 的字节数:

\[ \underbrace{2}_{K,V}\times L\times H_{kv}\times d\times \text{bytes}. \]

MHA 则是 \(H\) 而不是 \(H_{kv}\),所以 GQA 把 KV 内存和 K/V 投影的计算量降为 \(H_{kv}/H=1/2\)。Qwen3-0.6B fp32:

\[ 2\times28\times8\times128\times4=229{,}376\text{ B}=224\text{ KiB/token}, \]

MHA 会是 448 KiB/token。Q/K/V 投影里,k_proj、v_proj 的输出也从 2048 降到 1024。

注意:注意力的 QK^T / AV 计算量并没有按这个比例下降,因为 16 个 Q 头仍然各自算分数;省的是 KV 的存储、读取和 K/V 投影。GQA 是“内存/带宽”优化,不是“注意力 FLOPs”优化。

极端情况 \(H_{kv}=1\) 叫 MQA,省得更多但质量通常下降;GQA 是两者之间的折中。

映射写错不会报错

如果写成 \(h \bmod H_{kv}\) 或 \(h\cdot H_{kv}/H\),形状仍然对,但每个 Q 头读到了错误的 K/V,输出全错。正确是整数除法 \(h/G\)。

对应代码:model/config.go 的 KVGroupSize,model/attn.go 的 kvHead := h / group。