想象一座大型图书馆。读者们手里拿着各自的问题清单(Query),走到目录柜前,用问题关键词去比对图书索引卡(Key),再从书架上抽出内容匹配的书籍(Value)阅读。这套「问题→匹配→取书→汇总」的流程,正是一个大模型在字里行间反复上演的日常——只不过它的读者不是人,而是注意力机制中的一个个「查询头」。 标准多头注意力(MHA)的运营方式很简单粗暴:每个读者小组都复印一套完整的目录索引卡,甚至把整个书架都复制一份。32个小组,就意味着32套馆藏副本。生成文本时,每写下一个词,图书管理员都要在32套副本里同步搬书、翻找,内存被塞得满满当当,取书通道也拥堵不堪。 MQA(Multi-Query Attention,多查询注意力)带来了一场极简改革:保留32份各不相同的问题清单(Q),但全馆共享同一套目录卡和书库(K、V)。空间占用骤降到原来的1/32,取书时间大幅缩短,推理速度成倍提升。代价是,所有小组共享同一种索引语言,检索的精细度略有下降,但对绝大多数场景来说,这笔买卖非常划算。

岗位关联

一句话价值:掌握MQA的共享KV思路,你就能看透大模型推理中「显存—速度—质量」三角权衡的底层逻辑,直接指导部署时的吞吐调优与显存预算规划。

目录
  1. 可视化演示
  2. 注意力回顾
  3. MHA痛点
  4. MQA核心
  5. GQA折中
  6. 加速收益
  7. 质量权衡
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示UDP无连接数据报传输过程:发送端直接发送数据包,不建立连接、不确认接收,可能出现丢包和乱序。

本节配有交互动效(进入视口后自动加载,约 3KB)。

UDP无连接传输 发送端直接发送数据报,无需建立连接,传输速度快但不可靠 无ACK · 无重传 · 无序号保证 青色数据包从左向右流动,部分包可能丢失

注意力回顾

想象你走进一座图书馆,手里握着一张写满问题的纸条。你首先跑到中央索引柜前,用纸条上的关键词去比对一张张图书索引卡——卡片上的分类标签(Key)与你的问题(Query)越接近,卡片指向的那本书(Value)就越可能对你有用。你抽出几本匹配度最高的书,把它们的核心内容按匹配程度加权汇总成一份新笔记。这套「问题→匹配→取书→汇总」的流程,就是注意力机制的雏形。

在Transformer模型中,输入序列里的每个词都同时扮演「读者」和「书」两个角色。模型为每个词生成三个向量:查询Q、键K、值V。当前词的Q会和序列中所有词的K逐一比对相似度,得到注意力权重,再按这些权重对所有位置的V加权求和,最终得到融合了全局信息的新表示。这就像每个词都进了一次图书馆,带走一份按自己需求定制的摘要笔记。

缩放点积注意力的标准公式是:

实际模型不会只派一个读者去查书,而是同时派出多个「读者小组」,每个组关注不同角度——有的盯着语法搭配,有的捕捉指代关系,有的挖掘语义相似性。这就是多头注意力(MHA):h个小组并行检索,每组都用自己独立的投影矩阵生成Q_h、K_h、V_h。同一个词经由不同小组的解读,能同时表达多种含义。

多头并行就像图书馆同时接待多个主题小组,互不干扰、各取所需。但请注意:每个小组都自带一套完整的索引卡和书库——这为后面的「空间爆炸」埋下了伏笔。

Attention(Q,K,V)=softmax(QK⊤dk)V\text{Attention}(\mathbf{Q}, \mathbf{K}, \mathbf{V}) = \text{softmax}\left(\frac{\mathbf{Q}\mathbf{K}^\top}{\sqrt{d_k}}\right)\mathbf{V}
softmax负责把打分变成非负且和为1的权重;√d_k是缩放因子,防止维度变大后点积数值过大导致梯度消失。
  1. 逐头独立投影每个头用自己的投影矩阵把输入映射到各自的Q、K、V子空间。
    Qh=QWhQ,Kh=KWhK,Vh=VWhV\mathbf{Q}_h = \mathbf{Q}\mathbf{W}_h^Q,\quad \mathbf{K}_h = \mathbf{K}\mathbf{W}_h^K,\quad \mathbf{V}_h = \mathbf{V}\mathbf{W}_h^V
  2. 并行执行注意力每个头独立完成缩放点积注意力运算。
    headh=Attention(Qh,Kh,Vh)\text{head}_h = \text{Attention}(\mathbf{Q}_h, \mathbf{K}_h, \mathbf{V}_h)
  3. 拼接融合输出把头输出拼接成完整向量,再过一次输出投影W^O压缩融合。
    Output=Concat(head1,…,headh)WO\text{Output} = \text{Concat}(\text{head}_1, \ldots, \text{head}_h)\mathbf{W}^O

MHA痛点

图书馆的规模越来越大,读者多了、馆藏也多了。为了照顾好每一位读者小组,管理层决定:给每个小组都配一套「私人馆藏」——完整复印一套索引卡,把书也重新搬来一份。32个小组,就是32套一模一样的馆藏副本。读者来查资料时,管理员要在32套房子里来回穿梭,取书路径成倍变长,馆内通道拥挤不堪。

换成标准多头注意力的语言,这个困境发生在推理阶段。模型每生成一个token,都必须重新「看一眼」历史token的全部信息。为了避免反复重算,工程上把历史token的Key和Value缓存起来——这就是KV Cache。可缓存是按层、按头、按序列长度堆积的,数值大得惊人。

单层KV缓存体积的计算公式如下:

解码阶段,每生成一个token都要把全部KV缓存从显存搬进计算单元。搬运的时间远大于计算本身——这就是内存带宽瓶颈。你也许会觉得计算量很大,但真正的瓶颈恰恰是「取书」的物流成本,而不是「读书」的思考成本。图书馆的通道就那么多,搬运32套副本的代价,最终变成了推理速度的拖累。

CacheMHA=2×h×dk×L\text{Cache}_\text{MHA} = 2 \times h \times d_k \times L
2代表Key和Value各存一份;h是注意力头数;d_k是每个头的向量维度;L是当前已生成的token数。这还只是一层,层数再乘上去。
  1. 单个头的缓存每个头要存一份K和一份V,数据量为2×d_k×L。
    per-head=2×dk×L\text{per-head} = 2 \times d_k \times L
  2. 累加全部头h个头全部累加,头数越多缓存越大。
    CacheMHA=h×(2×dk×L)\text{Cache}_\text{MHA} = h \times (2 \times d_k \times L)
  3. 乘以模型层数每一层Transformer各自维护缓存,层数是最外层的放大因子。
    Cachetotal=layers×CacheMHA\text{Cache}_\text{total} = \text{layers} \times \text{Cache}_\text{MHA}

MQA核心

一位新来的图书管理员提出了大胆建议:既然所有小组查的是同一批馆藏,为什么每个人都要搬一套副本?干脆把32套书架合并成一间公共书库——全馆共享同一套索引卡、同一批藏书;每个小组只需要保留自己那份独特的问题清单(Q)。小组之间分工的差异,由「问什么问题」来体现,而查到的「书」始终是同一批。

这个方案就是MQA(Multi-Query Attention)。改动极小却效果显著:h个查询头依然用独立的权重矩阵投影出各自的Q_h,但Key和Value的投影合并为单头,只生成一份K_shared和V_shared。换句话说,MQA从「多Q多K多V」变成了「多Q单K单V」。

共享KV后的单头计算可写为:

从「32套书架」到「1套公共书库+32份问题清单」,KV缓存体量直接缩为原来的1/h。图书馆的空间压力瞬间释放,管理员再也不用在32个房间之间奔波了。

headh=softmax(QhKshared⊤dk)Vshared\text{head}_h = \text{softmax}\left(\frac{\mathbf{Q}_h \mathbf{K}_\text{shared}^\top}{\sqrt{d_k}}\right)\mathbf{V}_\text{shared}
K_shared和V_shared是全局唯一的,h个查询头共享同一组键值。每个头依旧完成「自己的问题去匹配公共索引」的过程。
  1. 查询头独立投影第h个头保留自己的Q投影矩阵,生成专属查询向量。
    Qh=QWhQ\mathbf{Q}_h = \mathbf{Q}\mathbf{W}_h^Q
  2. 键值共享投影所有头共用同一个K投影和V投影,得到全局唯一的共享KV。
    Kshared=KWK,Vshared=VWV\mathbf{K}_\text{shared} = \mathbf{K}\mathbf{W}^K,\quad \mathbf{V}_\text{shared} = \mathbf{V}\mathbf{W}^V
  3. 逐头检索公共库每个头用自己的Q_h去比对共享K_shared,再取回共享V_shared的加权内容。
    Outputh=softmax(QhKshared⊤dk)Vshared\text{Output}_h = \text{softmax}\left(\frac{\mathbf{Q}_h \mathbf{K}_\text{shared}^\top}{\sqrt{d_k}}\right)\mathbf{V}_\text{shared}
  4. 拼接输出全部头的输出拼起来,过输出投影后得到最终结果。
    Output=Concat(Output1,…,Outputh)WO\text{Output} = \text{Concat}(\text{Output}_1, \ldots, \text{Output}_h)\mathbf{W}^O

GQA折中

不过,公共书库也引来了部分读者的抱怨:有些专业领域差异太大,大家共用一套索引卡后,检索粒度不够精细。于是图书馆又尝试了一种更灵活的运营模式——不搞32套,也不搞1套,而是把32个小组分成G个「部室」,每个部室共用一套索引卡和书架。几个小组之间的需求比较相近,共享一套完全够用;不同部室之间仍保持各自的检索特色。

这种折中方案正是GQA(Grouped Query Attention,分组查询注意力)。设分组数为G,每个组包含h/G个查询头,组内共享一套K/V。它是MHA和MQA之间的连续插值:当G=1时,所有查询头共享一套KV,也就是MQA;当G=h时,每组仅一个头,退化为原始的MHA。

GQA的缓存体积公式如下:

图书馆的「部室制」既保留了一定检索灵活性,又把整体空间压了下来。在部署选型时,有了MHA、GQA、MQA三档「馆藏策略」,便可按显存预算和服务质量要求自由挑选。

CacheGQA=2×G×dk×L\text{Cache}_\text{GQA} = 2 \times G \times d_k \times L
G组共享,意味着KV缓存只有G份。实际开源模型中,LLaMA2-70B就采用了8组GQA,在质量与速度之间选了稳妥的中间档。
  1. 按组累计缓存每组存一份K和V,共G份。
    CacheGQA=G×(2×dk×L)\text{Cache}_\text{GQA} = G \times (2 \times d_k \times L)
  2. 与MHA相比MHA是h份,GQA是G份,比值G/h。
    CacheGQACacheMHA=Gh\frac{\text{Cache}_\text{GQA}}{\text{Cache}_\text{MHA}} = \frac{G}{h}
  3. 退化关系验证G=1时退化为MQA,G=h时还原为MHA。
    G=1⇒Cache=1hCacheMHA;G=h⇒Cache=CacheMHAG=1 \Rightarrow \text{Cache} = \frac{1}{h}\text{Cache}_\text{MHA};\quad G=h \Rightarrow \text{Cache} = \text{Cache}_\text{MHA}

加速收益

书库合并之后,最直观的变化并非「省了几个柜子」,而是每天在馆内搬运图书的实际工作量骤减。管理员响应一次查询,只需在唯一一套公共书库里完成取书,不再被迫跑遍32个房间。同一时间能服务的读者数量因此直线上升,队伍的吞吐能力大幅增强。

在多查询注意力中,这背后的机制是内存带宽的释放。自回归解码是逐token串行进行的,每生成一个新token,就要把KV缓存整体读取一遍。这种场景下,读取数据的总字节数决定了延迟上限,计算单元本身多数时间在空等数据。MQA把需要搬运的KV数据量从h份降为1份,内存带宽瓶颈瞬间缓解。

KV缓存缩减倍数可以写成一个简明的关系式:

图书馆的「高峰通行能力」取决于搬运工每天走的路程。共享书库让每条查询路径缩短为原来的1/h,管理员得以接待更多读者,甚至允许大家借阅更厚的长篇著作——也就是支撑更长的上下文窗口。

缓存缩减倍数=CacheMHACacheMQA=h\text{缓存缩减倍数} = \frac{\text{Cache}_\text{MHA}}{\text{Cache}_\text{MQA}} = h
头数h是缩减的倍数。4头缩减4倍,32头就缩减32倍。这是MQA一切加速收益的总根源。
  1. 缓存比值化简分子分母的公共因子约掉后,只留下头数h。
    2hdkL2dkL=h\frac{2 h d_k L}{2 d_k L} = h
  2. 带宽压力同步下降每次解码读取KV的字节数降为原来的1/h。
    BWMQA=BWMHAh\text{BW}_\text{MQA} = \frac{\text{BW}_\text{MHA}}{h}
  3. 并发能力提升释放出的显存可用来扩大batch数或支持更长上下文。
    内存释放量=(1−1h)×CacheMHA\text{内存释放量} = \left(1 - \frac{1}{h}\right) \times \text{Cache}_\text{MHA}

质量权衡

共享书库也并非毫无代价。当每个小组都必须依赖同一套书目分类和藏书布局时,各小组的「检索视角」被强行对齐,它们不能再按各自偏好设计一套最适合自己的索引体系。少数定位特殊的查询,匹配精度会有所下降。训练时,模型的整体表达能力也会受到轻微限制。

不过实验观察表明,MQA相对MHA的精度损失通常远小于直觉预期。原因在于Q矩阵保留了多头的全部差异——不同头依然可以用独特的「提问方式」,从同一批藏书中抽取不同侧面的信息。更意外的是,KV参数量越少,量化时的动态范围越稳定,MQA在INT8/INT4量化下的误差反而更可控。

用一个参数视角来量化共享程度对表达力的影响:

图书馆可以在「每人一套」「分组共享」「全馆共享」三套方案间按预算与体验权衡。显存宽裕、精度敏感,保留MHA;显存紧张、追求吞吐,切换到MQA;想两头兼顾,GQA的旋钮可以精细调节。

Per-head KV参数=2×G×dkh\text{Per-head KV参数} = \frac{2 \times G \times d_k}{h}
这个式子告诉你:总KV参数被h个查询头平均分摊后,每个头能动用的KV资源是多少。G越小,每头能用的K/V参数越少,共享越彻底。
  1. 总KV参数量G份K和V,每份维度为d_k。
    总KV=2×G×dk\text{总KV} = 2 \times G \times d_k
  2. 均摊到每个查询头除以查询头数h,得到每个头的平均可用KV资源。
    每头可用=2×G×dkh\text{每头可用} = \frac{2 \times G \times d_k}{h}
  3. 验证边界情况G=h时每头资源最充足;G=1时每头资源最少。
    G=h⇒2dk(最充裕);G=1⇒2dkh(最紧张)G=h \Rightarrow 2 d_k \text{(最充裕)};\quad G=1 \Rightarrow \frac{2 d_k}{h} \text{(最紧张)}

AI 应用场景

大模型推理服务

阶段:推理部署与性能调优

解决的问题:标准MHA在服务端解码时KV缓存占满显存,batch被迫缩小,并发上不去。MQA/GQA把KV缓存压到1/G,让同一张卡能服务更多用户。

具体案例:某公司部署32层、32头的LLM聊天服务,单请求序列长度2048时KV缓存约1GB。改用8组GQA后缓存降到1/4,同等显存下单卡并发翻倍,实测token吞吐提升约2.5倍,P99延迟下降40%。

长对话流式生成

阶段:解码生成阶段

解决的问题:客服机器人每回答一句都要读取整个对话历史的KV缓存,历史越长搬运越慢。MQA把每一步读取量降为1/h,显著降低首token延迟。

具体案例:智能客服系统接入MQA改造后,20轮对话场景下平均首token延迟从680ms降到220ms,用户的等待感明显改善,同时长会话不再因为显存超限而强制截断。

端侧与边缘部署

阶段:模型压缩与边缘适配

解决的问题:手机、笔记本、消费级显卡显存有限,标准MHA的KV缓存放不下长上下文。MQA配合量化可以把一个大模型塞进小设备。

具体案例:将13B模型以MQA+INT8方案部署到单张24GB消费级显卡,KV缓存占用从约6GB降至0.8GB,释放出的显存让batch从4提到16,本地私有化对话流畅运行。

Python 演示

python74 行
import mathimport torchimport torch.nn as nn  class MHA(nn.Module):    """标准多头注意力: Q/K/V 全部独立多头"""     def __init__(self, d_model, n_heads):        super().__init__()        self.n_heads = n_heads        self.d_k = 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, t, _ = x.shape        q = self.wq(x).view(b, t, self.n_heads, self.d_k).transpose(1, 2)        k = self.wk(x).view(b, t, self.n_heads, self.d_k).transpose(1, 2)        v = self.wv(x).view(b, t, self.n_heads, self.d_k).transpose(1, 2)        scores = q @ k.transpose(-2, -1) / math.sqrt(self.d_k)        attn = torch.softmax(scores, dim=-1)        out = (attn @ v).transpose(1, 2).reshape(b, t, -1)        return self.wo(out)  class MQA(nn.Module):    """多查询注意力: Q 保持多头, K/V 只保留单头共享"""     def __init__(self, d_model, n_heads):        super().__init__()        self.n_heads = n_heads        self.d_k = d_model // n_heads        self.wq = nn.Linear(d_model, d_model)        self.wk = nn.Linear(d_model, self.d_k)        self.wv = nn.Linear(d_model, self.d_k)        self.wo = nn.Linear(d_model, d_model)     def forward(self, x):        b, t, _ = x.shape        q = self.wq(x).view(b, t, self.n_heads, self.d_k).transpose(1, 2)        k = self.wk(x).unsqueeze(1)   # (b,1,t,d_k)        v = self.wv(x).unsqueeze(1)   # (b,1,t,d_k)        scores = q @ k.transpose(-2, -1) / math.sqrt(self.d_k)        attn = torch.softmax(scores, dim=-1)        out = (attn @ v).transpose(1, 2).reshape(b, t, -1)        return self.wo(out)  def kv_cache_bytes(n_heads, d_k, seq_len, n_layers, dtype_bytes=2):    """单层 KV 缓存字节数: 2(Key+Value) * n_heads * d_k * seq_len"""    return 2 * n_heads * d_k * seq_len * n_layers * dtype_bytes  def main():    d_model, n_heads, seq_len, n_layers = 4096, 32, 2048, 32    d_k = d_model // n_heads    for name, heads in [("MHA", n_heads), ("GQA组8", 8), ("GQA组4", 4), ("GQA组2", 2), ("MQA", 1)]:        cache = kv_cache_bytes(heads, d_k, seq_len, n_layers)        ratio = heads / n_heads        print(f"{name:8s} KV缓存: {cache / 1e9:6.2f} GB  占比: {ratio * 100:4.1f}%")     torch.manual_seed(0)    x = torch.randn(2, 16, d_model)    mha = MHA(d_model, n_heads)    mqa = MQA(d_model, n_heads)    print("MHA 输出:", tuple(mha(x).shape))    print("MQA 输出:", tuple(mqa(x).shape))  if __name__ == "__main__":    main()
目的:用PyTorch从零实现MHA与MQA两个注意力模块,对比不同分组数下KV缓存体量,直观感受「共享KV」带来的显存收益。
  1. self.wk = nn.Linear(d_model, self.d_k)MQA的K投影只输出单头维度,全头共享一份,这是节省缓存的关键。
  2. k = self.wk(x).unsqueeze(1)单头K扩展出「组」维度,让后续所有查询头都能广播使用同一份K。
  3. scores = q @ k.transpose(-2, -1) / math.sqrt(self.d_k)每个查询头分别与共享K做缩放点积,完成多头「各自的检索」。
  4. out = (attn @ v).transpose(1, 2).reshape(b, t, -1)注意力权重对共享V加权求和,再把多头结果拼回完整向量。
  5. cache = kv_cache_bytes(heads, d_k, seq_len, n_layers)把缓存公式2×heads×d_k×L代入程序,打印MHA到MQA的缩减梯度。

常见误区

误区经常听说MQA是「共享KV」,就以为它把Q、K、V的权重矩阵全部合并成一份,这样模型肯定什么都学不到,质量会崩掉。

正解MQA只共享K和V投影,Query的投影每个头完全独立。就像图书馆改革:每个小组保留自己独特的问题清单,只共享同一套书架。不同查询头仍可以用不同提问方式从同一本书里提取不同侧面信息,因此表达能力损失有限。

为何常见:「共享KV」四个字太容易被脑补成「共享一切」,忽略了结构描述中Q一直保持多头这个关键细节。

误区有人觉得MQA不过是让KV缓存变小一点,推理时还要把共享的KV广播到多个头,反而增加额外操作,速度不会变快。

正解解码阶段是典型的内存带宽受限场景——每生成一个token必须把全部KV缓存从显存搬到计算单元,搬运量远大于计算量。MQA把每次要搬运的KV数据量削减为1/h,带宽瓶颈大幅缓解,逐token延迟和总吞吐都明显受益。

为何常见:把注意力机制默认理解为「算力密集型」,忽略了自回归解码时「数据搬运才是主角」这一事实。

误区看到GQA出现,就认为MQA是早期的「过渡品」,现在的大模型都该用GQA,MQA没有价值。

正解MQA没有过时,它是GQA的极端特例(分组数G=1)。T5、PaLM、多查询Transformer等经典模型以及轻量部署场景都在用MQA;GQA则是它的平滑泛化,为了在质量与速度之间取平衡更常被选用。二者是包含关系,不是替代关系。

为何常见:把GQA误当成「下一代产品」,忽略了它在数学结构上是对MQA的一般化推广。

误区一听说MQA「有损」,就认定模型效果会肉眼可见地变差,训练都练不好,绝对不能用。

正解经验数据显示MQA相对MHA的质量差距通常是轻微的,因为Q保留多头差异,仍能表达多种检索视角。更妙的是,KV参数量少带来了更小的量化动态范围,配合INT8/INT4量化反而可能追平甚至反超MHA在压缩后的表现。

为何常见:把「质量有损」在脑海里放大成「质量崩坏」,没有用显存收益和速度收益去衡量整体性价比。

面试题

Q1. MQA多查询注意力中,保持多头的部分是?

单选题 · easy

  • A.查询矩阵Q← 正确答案
  • B.键矩阵K
  • C.值矩阵V
  • D.输出投影

答案:A。MQA的典型结构是「Q多头、K/V单头共享」。类比图书馆改革,每个小组保留自己的问题清单(Q),只共享索引卡和书架(K/V)。A正确;B和C是被共享的部分;D输出投影本来就不属于多头范畴。

Q2. 在32头Transformer中,MQA相对MHA的KV缓存大小约为?

单选题 · easy

  • A.二分之一
  • B.四分之一
  • C.三十二分之一← 正确答案
  • D.六十四分之一

答案:C。MHA缓存=2×h×d_k×L,MQA缓存=2×1×d_k×L,比值为1/h=1/32。就像图书馆从32套书架变为1套公共书库,空间瞬间缩为原来的1/32。C正确。

Q3. GQA分组查询注意力中,当分组数等于多少时退化为MQA?

单选题 · mid

  • A.序列长度
  • B.注意力头数
  • C.恰好为1← 正确答案
  • D.隐藏层维度

答案:C。GQA把h个头分成G组,每组共享一套KV。当G=1时所有头共享同一组K/V,完全等价于MQA;当G=h时每组仅一个头,又退化为MHA。C正确。

Q4. MQA加速解码推理的根本原因是?

单选题 · mid

  • A.减少模型参数量
  • B.减少KV内存读取← 正确答案
  • C.增加并行计算核心
  • D.缩小词表大小

答案:B。解码每生成一个token都要把KV缓存整体读一遍,瓶颈在内存带宽。MQA把每次读取的KV量降为1/h,直接缓解带宽压力。B正确;参数量也有减少,但并非速度提升的根本原因。

Q5. 下列关于MQA的说法正确的有?

多选题 · mid

  • A.共享K与V的投影← 正确答案
  • B.Q投影保持多头← 正确答案
  • C.大幅削减KV缓存← 正确答案
  • D.一定能提升精度

答案:ABC。MQA只保留单份K/V投影并让所有查询头共享,A正确;Q投影每个头独立,B正确;KV缓存从h份减到1份,C正确;D错误——MQA的质量通常是轻微下降,不是提升。

Q6. 缩放点积注意力中除以√d_k的作用是?

单选题 · easy

  • A.防止过拟合
  • B.防止分数过大← 正确答案
  • C.加快反向传播
  • D.增大模型容量

答案:B。点积结果会随向量维度的增大而变大,过大的分数让softmax的梯度区域趋于饱和,训练不稳定。除以√d_k把分数拉回适中区间,类似统一索引卡规格再排序。B正确。

Q7. 标准MHA中,KV缓存大小与哪些因素成正比?

多选题 · hard

  • A.注意力头数← 正确答案
  • B.序列长度← 正确答案
  • C.每头向量维度← 正确答案
  • D.训练数据条数

答案:ABC。KV缓存公式为2×h×d_k×L×layers,其中h是头数、d_k是每头维度、L是序列长度,A/B/C均为正比因子;训练数据条数只影响模型参数训练过程,与推理缓存的体量无关。

Q8. 相比MHA,MQA对模型质量的影响正确的有?

多选题 · hard

  • A.表达能力轻微下降← 正确答案
  • B.质量完全不变
  • C.量化误差反而更低← 正确答案
  • D.训练一定不收敛

答案:AC。共享KV使每个头无法维护完全独立的K/V语义空间,表达能力微降,A正确;KV参数量少导致动态范围更小,量化时误差更稳定,C正确;质量完全不变和训练不收敛都是绝对化说法,均错误。

Q9. 32头模型中,要将KV缓存压缩到原来的四分之一,应采用的方案是?

单选题 · hard

  • A.8组GQA← 正确答案
  • B.4组GQA
  • C.MQA结构
  • D.标准MHA

答案:A。GQA的缓存是MHA的G/h倍。要压到1/4,需G=32/4=8,即8组GQA,A正确;4组GQA得到1/8,MQA得到1/32,MHA不压缩。

Q10. 某32头模型隐藏维度4096、序列长度2048、FP16精度,单层KV缓存从MHA改为MQA节省约多少MB?

单选题 · extreme

  • A.0.25 MB
  • B.8.00 MB
  • C.32.0 MB← 正确答案
  • D.256 MB

答案:C。d_k=4096÷32=128。MHA单层KV缓存=2×32×128×2048×2字节=32MB;MQA=2×1×128×2048×2=1MB。节省约31MB,最接近32MB,选C。计算时注意FP16每元素占2字节,且Key和Value要乘2。