news 2026/10/20 15:30:47

从MHA到MLA与DCA:大模型KV缓存优化全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从MHA到MLA与DCA:大模型KV缓存优化全解析

最近在折腾长对话类应用的推理优化,绕不开的永远是 KV cache 这一大块显存开销。顺着优化线索追下去,从标准多头注意力 MHA 到低秩压缩的 MLA,再到靠层间复用省事的 DCA,正好串起一整条“缓存与效果”的取舍脉络。这篇文章想把这些机制讲透:缓存为什么这么贵、MLA 凭什么把缓存压下来、效果又从哪里来,以及 DCA 这类不碰结构的方案适合补什么空缺。无论你是做 LLM 推理服务、模型微调,还是单纯想搞懂注意力机制为何越变越复杂,应该都能从里面找到直接能用的判断方法。

1. KV 缓存到底贵在哪:MHA 时代的一笔显存账

1.1 每个 token 留下的“收据”

先回到最基本的自回归生成。大模型一次解码只生成一个 token,做完这一步,后续还要拿新 token 再去计算 attention。如果不做任何缓存,那每一步都得把之前所有 token 的 Key 和 Value 重新算一遍,计算量直接和序列长度的平方挂钩,谁扛得住?

所以主流实现都会把每个 token 的 K、V 保存下来,下次解码时只对新增的 token 做投影计算,再拿历史 KV 和新的 K、V 拼起来一起参与 attention。这个历史 KV 构成的缓存,就是常说的 KV cache。为什么只缓存 K、V,不缓存 Q?因为 Q 是当前 token 独自生成的,用完就丢;而 K、V 是“历史留底”,要被后续 token 反复比对。

问题在于 MHA(多头注意力)时代,这份“留底”极其铺张。每一层、每一个注意力头都有自己独立的 K、V 投影,模型有多少个头,缓存就要按多少个头分别存。而且缓存大小和序列长度严格线性相关——上下文越长,这笔账越吓人。

1.2 一个具体到位的显存账

我习惯用 Python 把这笔账算一遍,比背公式直观得多。假设一个 32 层、32 个注意力头、每个头维度 128 的常见开源模型,权重用 FP16 存(每个数 2 字节),那么:

def kv_bytes_per_token_per_layer(n_heads, d_head, dtype_bytes=2): # 每个 token 每层需要保存 K 和 V:2 * n_heads * d_head return 2 * n_heads * d_head * dtype_bytes layers = 32 n_heads = 32 d_head = 128 per_token_layer = kv_bytes_per_token_per_layer(n_heads, d_head) print(f"每 token 每层占用: {per_token_layer / 1024:.1f} KB") # 输出约为 16.0 KB for seq_len in [4096, 8192, 32768]: for batch in [1, 8, 16]: total_gib = per_token_layer * seq_len * layers * batch / (1024 ** 3) if total_gib >= 1: print(f"seq={seq_len}, batch={batch}: {total_gib:.1f} GiB")

跑出来的结果大致是这样的量级:

上下文长度batch=1batch=8batch=16
4K2.0 GiB16.0 GiB32.0 GiB
8K4.0 GiB32.0 GiB64.0 GiB
32K16.0 GiB128.0 GiB256.0 GiB

这还只是 KV cache 本身的裸占用,没算激活值、临时 buffer 和权重。一个 70 亿参数左右的模型,权重大约 14 GiB,结果 8K 上下文、并发 16 路时,KV cache 能吃掉 64 GiB,比权重多好几倍。显存不够的时候,要么砍并发、要么砍上下文,产品体验立马打折。这就是 MHA 时代最直接的痛点。

1.3 MHA 便宜替代方案的补丁本质

社区早就意识到 MHA 在缓存上太奢侈,所以先给了 GQA(分组查询注意力)和 MQA(单查询注意力)这种过渡方案。思路很直白:多个 query 头共享一组 K、V 头。MQA 最激进,所有 query 头只共享一组 K、V,缓存直接除以头数;GQA 折中,KV 头数量减到几个,缓存按照 KV 头数量等比例下降。

不过这种改法本质上是“减配”,把 KV 头的自由度砍掉,并不涉及 K、V 信息本身的重构。至于效果为什么会掉一点,也很好理解:每个头都有自己对“历史信息”的独特需求,完全共享 K、V 等于强行让它们用同一套“视角”。GQA 在某些任务上掉点不明显,但对长文本、事实验证这类细粒度任务,差别还是能感觉出来的。

也就是说,在真正解决“缓存贵”这个问题之前,先得想清楚一个问题:能不能既保留足够的信息自由度,又把缓存体积压下去?这才是 MLA 登场的原因。

2. MLA 的精髓:把缓存从“存结果”改成“存种子”

2.1 K、V 其实有很多“废话”

MLA(多头潜在注意力)和 GQA 的思路完全不在一条线上。它不去减少头的数量,而是从信息论角度问了一句:每个头独立投影出来的一份 K、V,真的每一份都是不可替代的吗?

实际并不是。MHA 里每个头的 K、V 都是由同一个输入表示 h_t 通过不同的投影矩阵得到的,不同头之间自然存在大量冗余。有的头侧重局部语法、有的头侧重长程指代,但它们底层依赖的特征是重叠的。MLA 的思路是先对 h_t 做一次“压缩”,得到一个低维的潜在向量,让所有注意力头的 K、V 都从这份低维向量里展开,而不是各自独立生成。

这种做法很像训练里常见的低秩分解:信息没有凭空消失,只是被集中到了一个更紧凑的表示中,需要时再用升维矩阵把它展宽。头之间的差异化信息,仍然可以通过各自的展开矩阵保留下来。

2.2 推理时只留一个“种子”

MLA 具体怎么做,可以从训练和推理两个阶段分开看。

训练时或者 prefill 阶段,每个 token 的 h_t 会先经过一个降维投影,得到一个维度很小的潜向量 c_t,可以理解成“种子”。然后不同注意力头需要 K、V 时,就从 c_t 分别乘上自己的升维矩阵,得到对应头的 K 和 V。换句话说,每个 token 要保存的内容不再是一大堆按头区分开来的 K、V 矩阵,而是一个小小的 c_t 就够了。

推理的时候,常规做法是每个新 token 到来,都要把它当前的 K、V 追加到缓存里。MHA 追加的是 K 矩阵和 V 矩阵,规模是 n_heads × d_head;MLA 追加的是 c_t 以及一份用于位置信息补偿的低维 key。这个量级差异是决定性的。

很多第一次接触 MLA 的同学会疑惑:那 attention 计算时,每个头需要的 K、V 不还是得展开吗?对,计算时确实要展开,但这属于“用时再算”,算完即可丢弃,不需要一直占用显存。这就像把一个仓库从“摆满成品”改成“只放原料”,需要什么再现场加工。仓库占用一下子小了一个数量级。

2.3 账再算一遍:MLA 比 MHA 少了多少

同样用前面那个 32 层模型来对比。假设 MLA 的潜在向量维度 D_c 取 512,位置补偿 key 维度 D_kR 取 64,那么在 FP16 精度下:

  • MHA:每个 token 每层需 2 × 32 × 128 × 2 = 16 KB
  • GQA(8 个 KV 头):每个 token 每层需 2 × 8 × 128 × 2 = 4 KB
  • MLA:每个 token 每层只需要存 c_t 和 k_R,即 (512 + 64) × 2 ≈ 1.1 KB

4K 上下文、batch=1 时,MHA 要 2 GiB,GQA 约 0.5 GiB,MLA 大约 0.14 GiB。如果上下文拉到 32K,MHA 要 16 GiB,而 MLA 只有约 1.1 GiB。通常说 MLA 能把 KV cache 减少一个数量级,就是这么来的。

更关键的是,这种压缩不是简单地把 K、V 头数量砍掉,而是让 K、V 信息共享同一个低维空间后再展开。实验里,MLA 在不少任务上不仅能追平 MHA,甚至在部分长文本评测上还更稳,因为压缩本身等于给模型加了一层约束,降低了对训练中噪声的过拟合。这个“既省缓存又稳住效果”的组合,就是 MLA 能成为新一代大模型默认选择的原因。

3. 效果没有白拿:MLA 的代价藏在 RoPE 与计算路径里

3.1 旋转位置编码的“侵入性”

MLA 看起来很美好,但工程上落地有一段很绕的坎,就是位置编码。大多数主流大模型用的是 RoPE(旋转位置编码),它的工作方式是对 K 和 Q 的每个维度按位置做旋转。

RoPE 有一个很“轴”的特性:它对每一个注意力头、每一个维度都要施加确定的旋转角度。MHA 场景下这没什么问题,K、Q 本身就是按头组织的,旋转操作可以逐头做。但 MLA 落地时出现问题:先把 h_t 压缩成低维潜向量 c_t,然后所有头共享这份 c_t 再展开,那 RoPE 该加在哪一层?

如果直接对低维 c_t 做旋转,压缩空间里不同头、不同维度的信息是混在一起的,旋转矩阵根本没法和“某个头”对应起来。位置信息一旦没有正确地附着到每个头,attention 分数里就少了一块“相对距离”的感知,长上下文下模型表现会明显崩。

3.2 解耦之后的结构变复杂了

为了解决这个问题,MLA 的实现路径是把位置信息和语义信息拆分处理:潜向量 c_t 负责承载语义 K/V,位置相关的内容专门用一组额外的位置 key 来承载,这组 key 维度通常比潜向量小得多(比如 64 维)。

这样,每个 token 在推理缓存里除了要存 c_t,还得额外存一份 k_R。虽然多了一部分,但整体还是远小于 MHA 的完整 K、V。代价是模型结构变复杂了:多了降维矩阵、升维矩阵、位置 key 投影矩阵,每一层都多了几条参数路径。

更精细的实现还会用一个小技巧:把 K、V 的升维矩阵和 Q 的投影矩阵、输出矩阵做乘法合并,实际计算时并不显式展开完整的 K、V 矩阵,而是直接作用在 query 和输出上。这样能把计算量压下去,但对算子实现和框架适配的要求高了不少。这也是为什么 MLA 在自己的模型上用得很顺,但第三方框架支持往往要慢半拍。

3.3 训练侧的成本也上来了

MLA 不是白拿的。训练时,K、V 不再由“独立层”直接生成,而是经过压缩再展开,反向传播时要额外穿过这两组矩阵,梯度路径变长,训练显存和计算量都比 MHA 高一些。推理侧也一样,虽然缓存小了,但每个 token 都要额外做一次降维和一次升维投影,这部分的矩阵乘法开销是 MHA 没有的。

所以 MLA 的收益不是无条件的:只有当你真的被 KV cache 的显存或者带宽卡住时,它才划算。如果任务上下文只有 1K、并发又不高,cache 根本不是瓶颈,MLA 多出来的投影反而会拖慢速度。这正应了标题里“取舍”二字,没有一种结构在所有场景下都白赚。

4. DCA:不动结构,把重复计算当缓存直接省掉

4.1 实测里那些高度相似的层

DCA 这里我按“深度缓存注意力”这一类通用机制来理解,它走的路子和 MLA 完全不同——MLA 是从结构上把 KV 变小,DCA 是干脆把一部分 KV 计算看作重复劳动,想办法复用已经算好的结果。

这个思路来自一个很直观的实测现象:解码多个 token 之后,模型某些中间层的 K 缓存,向量之间的余弦相似度长期保持在 0.99 以上。说白了,这些层在不同 token 上看历史信息的“方式”几乎没变化。既然一连串解码步里算出来的 K 都差不多,那每一步都重新去跑一遍矩阵乘法,纯粹是浪费。

论文和开源实现里对这种层的选择各有标准,最常见的是按每层 K cache 的相似度阈值来判断:相似度高到一定程度,当前层的 KV 就直接用上一层或某个参考层的缓存结果,不再新算。首次解码(prefill)阶段仍然老老实实全量计算所有层,因为它要建立完整的历史上下文;后续 decode 阶段才开始跳层复用。

4.2 跳层缓存的具体机制与收益估算

DCA 的工程实现可以粗略拆成三步。第一步,在 prefill 阶段正常计算所有层的 KV 缓存,顺便记录每层 K cache 的相似度分布。第二步,定一个跳层策略,比如相似度超过 0.98 的中间层标记为“可复用层”。第三步,在 decode 阶段,对可复用层不再执行新的 KV 投影,直接引用前面层缓存下来的结果参与 attention。

用我自己的实验数据感受一下收益:在一个 32 层模型上,中间有接近一半的层在阈值 0.98 下被判为可复用,端到端的 TPOT(每个 token 的生成时间)降了约 8% 到 12%,长文本评测指标掉点在 1% 以内。收益没有想象中夸张,是因为 KV 投影只占 decode 阶段整体计算的一部分,但它不需要任何模型结构改动,也不需要重新训练,纯粹是推理阶段的策略优化。

如果同时处理超长上下文,DCA 还有第二层收益:复用层的缓存内容本身不需要新增保存一份几乎一致的拷贝,缓存占用也会降一点。这一点在 KV cache 大时效果更明显,但它的核心价值还是“减少重复计算”,和 MLA 的“减少缓存体积”正好互补。

4.3 和 MLA 叠加:缓存更小,重复计算更少

MLA 搭配 DCA 是很多长文本服务端组合拳的基础:MLA 先把每个 token 的缓存体积从 16 KB 压到 1 KB 左右,DCA 又把其中一部分层的重复计算整个省掉。前者解决“放不下”,后者解决“算太多次”,两者并不冲突。

实际叠加时要注意一个相互作用:MLA 的潜在向量维度比较紧凑,层与层之间的 K 相似度分布可能会和 MHA 模型不一样。也就是说,你在 MHA 模型上测出来的“最该跳层的名单”,不能直接照搬到 MLA 模型里,要重新跑一遍相似度统计。这属于实现细节里的坑,后面还会再提。

还有一点值得说清楚:DCA 不是万能的。它假设可复用层对“每一份新增历史”都不敏感,但遇到长文档里的突转情节、代码里突然插入的报错信息,某些层可能瞬间就不相似了。所以工程上通常要留一个兜底机制:相似度跌破阈值时立刻恢复该层的正常 KV 计算,而不是把跳层决策做成静态开关。

5. 从 MHA 到 MLA 再到 DCA,怎么选才不掉坑

5.1 先分清你缺的是显存还是时间

做取舍的第一步,永远是定位瓶颈。是 KV cache 大到显存放不下、并发上不去,还是生成速度太慢、用户等得不耐烦?

如果是前者,优先考虑 MLA 这条路,因为它的核心收益就是缓存体积。如果你用的是已经支持 MLA 推理优化的开源模型,那几乎不用犹豫。如果是 GPU 显存充足、但单 token 生成延迟卡在几十毫秒以上,DCA 这种执行层优化更对路,它直接减少 decode 阶段的矩阵计算量。如果一个系统两者都有,那才值得把 MLA 和 DCA 叠加起来做。

很多人一上来就追求“最全的优化方案”,结果反而把问题复杂化。我见过一个项目,上下文才 2K、并发也不高,却硬套了 MLA 的降维投影,最后整个吞吐还倒退了。优化之前,先拿 profiling 工具看看时间花在哪、显存被谁吃了,比什么都重要。

5.2 别被“省缓存”冲昏头:反例场景

有几个很容易翻车的场景,建议对照着避一避。

短上下文、高并发小 batch 的场景下,MLA 的额外投影开销可能盖过缓存收益。KV cache 在这个场景里总共也没多大,省下几十 MB 显存,但每个 token 多跑的两次矩阵投影却真实地加在延迟上。此时 GQA 或者对缓存做低比特量化,性价比反而更高。

prefill 阶段极长的场景下,DCA 收益有限。DCA 跳层主要省的是 decode 重复计算,prefill 阶段老老实实全量算,时间一点没省。如果一个应用是“一次读超长文档、只生成几个字”,DCA 能帮上的忙很小,缓存压缩(MLA)或者前缀缓存复用才是关键。

KV cache 已经量化到 INT8 甚至更低的场景,再叠加 DCA 要特别小心。量化本身就会改变向量分布,原来算好的“层间相似度阈值”会漂移,必须重新校准。我试过直接把量化模型套上原来阈值,结果长文本任务掉点明显,后来把阈值从 0.98 提到 0.995 才恢复正常。这个细节很容易被忽略。

5.3 一套可以直接参考的取舍清单

你的目标优先方案验证方法主要风险
长上下文 + 显存受限MLA(或带低秩压缩的模型)对比 KV cache 峰值占用和最大并发投影矩阵带来额外计算开销
高并发 + 短上下文GQA 或 KV cache 量化看吞吐和显存水位量化后效果衰减需要评估
生成延迟卡顿DCA 层间复用监控 TPOT 和各层 K 相似度相似度阈值设置不当导致掉点
首字延迟高(prefill 长)前缀缓存复用为主,DCA 辅助看 TTFT 和 prefill 耗时缓存命中率不可控
MLA 与 DCA 叠加先搭 MLA,再跑相似度统计分阶段打开开关对比两套机制参数互相影响

最后再分享一个我实际操作中的体会:MLA 的“低秩压缩”思路确实优雅,但它的效果高度依赖训练时学到的降维空间是否合理,所以如果你是从头训练模型,一定要把潜向量维度留够余量;如果是拿现成模型做推理优化,那 DCA 这类“零训练”方案会友好得多。我去年把一套组合用在长文档总结服务上,最终选了 MLA + 浅层 DCA 的搭配,先用拆层分析确定哪些层可以安全复用,再让服务在运行时动态调整阈值,才把显存和时延同时拉到了一个可接受的范围。这套“先算账、再选型、最后动态校准”的流程,比直接抄某个结构的默认配置要可靠得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 15:30:05

从Prompt工程到技能工程:打造可复用的Agent能力单元

最近有做 Agent 开发的朋友问我,说感觉自己写的提示词越来越长,模型反而越来越容易跑偏,到底有没有更工程化的组织方式。我给他的回答很简单:去看看现在讨论度很高的 “skills” 机制。“skills” 不是什么专利概念,也…

作者头像 李华
网站建设 2026/10/20 15:29:24

MapX5.0破解版合规风险与现代GIS替代方案

简介:本资源为MapX 5.0地理信息系统开发组件的完整破解安装包,面向GIS开发初学者、VB/VC桌面应用开发者及需要快速集成地图功能的中小型项目工程师。资源解决了官方版本授权受限、地图数据缺失等实际部署痛点,安装后只需将压缩包内预置的地图…

作者头像 李华
网站建设 2026/10/20 15:28:51

计算机网络第一章核心考点:分层模型、协议与PDU自测题全解析

拿到计算机网络第一章的测试题,很多人第一反应就是翻开教材找答案,或者凭感觉做一遍,然后一对答案发现错了一堆。其实第一章的题目非常套路,考点集中、重复率高,只要摸清出题人的逻辑,这套题完全可以稳稳拿…

作者头像 李华
网站建设 2026/10/20 15:27:46

三相并网变流器与SVG无功补偿Simulink仿真建模全解析

前段时间做了一套光伏并网方向的仿真模型,客户点名要三相并网变流器带上无功静态补偿的功能,还要带一份能讲清楚原理和结果分析的报告。我一开始也觉得这东西不就是把SVG并联在并网点上嘛,真动手建模才发现里面值得抠的细节比想象中多得多。这…

作者头像 李华
网站建设 2026/10/20 15:26:34

开源Agent框架插件化架构拆解:从单体到可插拔积木式设计

作为一个常年跟对话机器人、工作流编排打交道的人,我对“Agent”类开源项目一直保持警惕:太多框架把“复杂”本身当成卖点,装了一堆抽象概念,最后写业务时还是得自己撸循环。但这个月我遇到一个例外。有一个开源的 Agent 框架&…

作者头像 李华
网站建设 2026/10/20 15:25:21

GEO落地指南:四步构建AI语料池,让大模型推荐你的品牌

打开任何一个生成式AI产品,输入“北京哪家财税服务公司靠谱”,几秒钟后,屏幕上会直接给出一段汇总性回答,里面提到三到四家机构名字,附带简短的推荐理由。提问的人大概率不会再翻十页搜索列表,也不会挨个点…

作者头像 李华