文章启蒙来自苏神的缓存与效果的极限拉扯:从MHA、MQA、GQA到MLA,拜谢Orz。
attention
首先attention的公式我们都知道如下:
我们尝试用数学来表述清楚这个问题,首先假设输入为一条token的x,输出为一条token的o,结合上面的公式得到QKV矩阵:
其中分母是缩放因子,通常可以人为设定,这里我们后面再提
MHA
那么对于多头注意力机制,也就是attention all you need的原文中提到的,是attention的一种并发形式,我们假设输入为:
假设有h个注意力头组成一个多头注意力,那么在t时刻的输出为:
那么在第s个头内,进行的计算有:
其架构如下:

上面QKV切分之后的size都是[seq, d_i],那么QK^T就是[seq, seq],完整QKV经过softmax之后的size为[seq, d_i],也就是说每个head_i产出的size都是[seq, d_i],拼起来就是[seq, seq] ,最后通过一个合并头将所有头的输出合并回一个大的张量。
这里需要注意的是,在MoE架构中,多头模型的权重太大了,进行TP的时候会把权重矩阵进行切分,分配到不同的GPU上,具体可以看vllm v1 源码解析 —— 单机八卡推理
还需要注意的是,这里的qkv在存储上并非对称,详情请看为什么加速LLM推断有KV Cache而没有Q Cache?
综上所述,得出的attention如下式:
上面公式省略了原公式分母的缩放因子,实践中常见的缩放因子设计为d_{k}=d_{v}=d/h。LLAMA2-7b有d=4096,h=32,d_{k}=d_{v}=128
,而deepseek v3.2的h = 128,d=7168,d_{k}=56。
MQA和GQA
MQA
MQA就是舍弃了多个KV,只用一份KV。

GQA
GQA没有那么极端,只是分成了G组,组内共享同一个KV。

GQA提供了MHA到MQA的自然过渡,当g=h时就是MHA,g=1时就是MQA,当1<g<h时,它只将KV Cache压缩到g/h,压缩率不如MQA,但同时也提供了更大的自由度,效果上更有保证。GQA最知名的使用者,大概是Meta开源的LLAMA2-70B,以及LLAMA3全系列。(苏神文章原话)
在llama2/3-70B中,GQA的g=8,其他用了GQA的同体量模型基本上也保持了这个设置,这并非偶然,而是同样出于推理效率的考虑。我们知道,70B这个体量的模型,如果不进行极端的量化,那么不可能部署到单卡(A100/H100 80G)上。单卡不行,那么就能单机了,一般情况下一台机可以装8张卡,刚才我们说了,Attention的每个Head实际上是独立运算然后拼接起来的,当g=8时,正好可以每张卡负责计算一组K、V对应的Attention Head,这样可以在尽可能保证K、V多样性的同时最大程度上减少卡间通信。(苏神文章原话)
MLA Multi-head Latent Attention
参考文献: