(四)KV Cache 放不下?MLA 把它压成 512 维

地基打完,开始拆第一道墙:放不下
上一篇我们把标准注意力从零拆了一遍,最后落在三块地基上:注意力怎么算、为什么是 N²、KV Cache 是个什么东西。地基篇留的钩子很具体——凭什么 GLM-5.2 敢把每个 token 的 K/V 压到只存一个 512 维的潜在表示?
这篇就来兑现这个承诺。它破的是墙 2:放不下。
进度感:本篇破墙 2(放不下 KV Cache)
墙 1(算不动 N²)→ 已铺垫,第 5 篇破
墙 2(放不下)→ 本篇 MLA 在破 ★
墙 3(推不动)→ 第 6 篇 MoE
墙 4(蹦得慢)→ 第 7 篇 MTP
先把墙 2 量化:KV Cache 到底多大
上一篇讲了 KV Cache 的设计:模型生成时,每个历史 token 在每一层都要存一份 K 和 V,供后面的 token 来查,免得每蹦一个字都把前面全重算一遍。聪明的设计,代价是占地方。
占多大?拆 config 算一笔账。GLM-5.2 有 64 个注意力头,每个头的 K/V 维度不小:
qk_head_dim: 256 # 每个头的 K 总维度(含位置编码分量)
v_head_dim: 256 # 每个头的 V 维度
num_attention_heads: 64
单个 token 在单层的 K 和 V 合起来:64 头 × 256 维 × 2(K 和 V)≈ 3.3 万个数。再乘上层数和上下文长度:
每 token 每层: 64 头 × 256 维 × 2(K,V) ≈ 3.3 万个数
× 78 层 × 100 万 token(1M)
→ 约 2.5 万亿个数
→ bf16 下约 5 TB
5 TB。 这里按每头完整 256 维算(nope 192 + rope 64);地基篇算的 3-4TB 用的是每头 192 的纯内容维度,没算位置分量。两种都是真实账,差异只在算不算那 64 维位置编码。这还只是 KV Cache,没算模型权重本身——一块 H100 只有 80GB 显存,5TB 要 60 多块卡才装得下光是”记忆历史”这件事。这就是墙 2,算得动是一回事,存得下是另一回事。

这对你用 AI 意味着什么: 为什么长对话聊到后面,AI 开始”忘掉”前面说过的话?很多产品不是不想记住,是 KV Cache 把显存吃满了,只能截断或清缓存。你感受到的”健忘”,本质是显存账本上的取舍。 懂了这点,你就明白为什么压缩 KV Cache 直接等于”AI 能记住更久”。
GLM-5.2 破这道墙的武器叫 MLA(Multi-head Latent Attention,多头潜在注意力)。它能把这 5TB 压到能在普通多卡上跑。怎么压?答案藏在一个你其实早就熟悉的直觉里。
压缩的底牌:数据有冗余
先建立直觉——凭什么数据能被压缩?因为它有冗余。
例子一:JPEG 图片
一张 4096×4096 的照片有 1600 万个像素值。但相邻像素的颜色通常很接近——天空一片蓝,皮肤一片黄。这种”相邻相似”就是冗余。JPEG 利用它,把 1600 万个数字压成几百 KB;解压时重建图片,不是 100% 相同,但肉眼看几乎一样。
例子二:身高体重表
假设你有 1000 个人的身高和体重,表面上是 1000 行 × 2 列 = 2000 个数字。但身高和体重高度相关(高的人通常更重),所以这 2000 个数字可以这么压:
压缩版:
1000 个人的身高(1000 个数字)
+ 一个公式: 体重 ≈ 0.9 × 身高 − 88
→ 1001 个数字,能恢复出近似的 2000 个
2000 个压成 1001 个,靠的就是”体重和身高相关”这层内在结构。
这就是”低秩”的直觉:数据有内在结构(相关性),所以能用更少的信息近似恢复。

例子三(最关键):4 维压成 2 维
这个例子直接对应 MLA 的原理。假设一个 4 维的向量 K,其实是由两个”基础方向”组合出来的:
基础方向 A = [1, 1, 1, 1]
基础方向 B = [1, 2, 3, 4]
K = 3 × A + 1 × B = [4, 5, 6, 7]
K 是 4 维,但**只需要存 2 个系数(3 和 1)**就能完整恢复它——因为 A 和 B 是预先知道的(训练学出来的):
压缩: K = [4, 5, 6, 7] → 只存系数 [3, 1] (4 维 → 2 维)
恢复: [3, 1] × {A, B} → [4, 5, 6, 7] (2 维 → 4 维)

这就是 MLA 压缩的数学本质:把高维向量,分解成”少数基础方向的线性组合”。 存系数(低维),用时再恢复(高维)。4 维能压成 2 维,是因为 K 本来就躺在一个 2 维的子空间里——它”看起来”是 4 维,“实际上”的有效自由度只有 2。
把原理套到 GLM-5.2:MLA 怎么压
现在把这个原理套到真实参数上。
要压的东西有多大
上面算过,每个 token 在每层要存的 K,展开是 64 头 × 256 维 = 16384 维。这个 16384 怎么来的,拆 config 看得很清楚:
qk_nope_head_dim: 192 # 不带旋转位置编码的部分
qk_rope_head_dim: 64 # 带旋转位置编码的部分
# 两者相加 = 256,就是每头的 K 总维度(qk_head_dim: 256)
64 头 × 256 = 16384
顺便说一句,地基篇里我提过 head_dim: 192——那是标准多头注意力里”每头纯内容”的维度(即这里的 nope 部分)。GLM 每头的 K 实际还多带 64 维的 RoPE 位置分量,合起来才是 256。所以 16384 要用 64×256 来算,不是 64×192。
MLA 的两步:压、再还原
MLA 发现:这 16384 维的 K,其实可以用一个 512 维的”潜在向量”近似恢复出来。512 这个数同样是 config 写死的:
kv_lora_rank: 512
具体分两步走。
压缩(存进 Cache 前):
上一层的输出 x(6144 维)
│
│ × 压缩矩阵 W_DKV(6144 → 512)
▼
潜在向量 c(512 维) ← 这才是真正存进 KV Cache 的东西
恢复(要用 K 时):
潜在向量 c(512 维)
│
│ × 恢复矩阵 W_UK(512 → 12288) ← 只还原内容部分(64 头 × 192 nope)
▼
恢复出的 K'(近似 K 的内容部分,nope)
V 用对应的恢复矩阵 W_UV 同理还原。注意这里只还原了 nope 内容部分——那 64 维的 rope 位置分量走的是另一条路(不压缩、单独存),不进这条压缩链路。这正是下面”解耦设计”要讲的事,也解释了为什么前面说每头完整是 256 维、却只压 192 维的内容。
关键:压缩和恢复矩阵是训练学出来的
W_DKV 和 W_UK 不是人手设计的,是训练学出来的。训练给它们的目标很明确:让恢复出的 K’ 和原始 K 尽量接近,同时潜在向量 c 尽量小。
打个比方,JPEG 算法也是”学”出来的——它发现”丢掉人眼不敏感的高频信息损失最小”。MLA 的两个矩阵是训练自己摸索出”用 512 维怎么最优地压缩 16384 维”,模型自己学会了哪些信息该留、哪些可以丢。
顺带一提:MLA 对 Q 也做了类似的低秩压缩(config 里
q_lora_rank: 2048),但 Q 用完即弃、不进 KV Cache,所以不影响显存账。本篇聚焦影响存储的 K/V 侧。

这对你用 AI 意味着什么: MLA 的压缩矩阵是训练学出来的,意味着这套压缩是”为 GLM-5.2 自己量身定做”的——它在自己的数据分布上学会了最优的低秩结构。所以 MLA 压缩的收益不能简单外推到别的模型:同样标 1M 上下文,用 GQA 的模型没有这种压缩,显存账完全不一样。这也是为什么”标称长度”背后藏着完全不同的成本结构。
收益算账:5TB 压到能装下
把压缩前后的账算清楚。压缩后,每个 token 在每层不再存完整的 K/V,只存那个 512 维的潜在向量:
| 标准注意力(不压缩) | MLA | |
|---|---|---|
| 每 token 每层存什么 | K(16384) + V(16384) | 潜在向量 c(512) |
| 1M × 78 层 总量 | 约 5 TB | 约 88 GB |
| 压缩比 | — | 约 57 倍 |
(这里的 5TB、88GB、57 倍是我按 config 维度估算的,非官方数字;第 2 篇算路线时用的也是同一套口径。)
KV Cache 从 5TB 降到约 88GB。 这个量级意味着——配合权重的合理切分,普通的多卡集群就能扛住整个 1M 的历史,不再需要 Google 级别的专用集群。这就是 MLA 这道墙被拆掉后的样子。

这对你用 AI 意味着什么: 为什么同样是”1M 上下文”,有的只能按 API 调用、有的能自己买卡部署?压缩 KV Cache 直接决定了部署门槛。MLA 把记忆从”需要 TPU 海洋”压到”几张消费级/企业级卡能扛”,这是开源模型能让人人跑起来的前提之一。
压成这样,质量不会崩吗
你一定会问:16384 维压成 512 维,信息丢了这么多,模型不就傻了吗?
理论上确实有损。 512 维不可能完美编码 16384 维的全部信息,恢复出的 K’ 只是近似。这一点 MLA 从不回避。
但实际损失很小,原因有三层:
- 训练让矩阵学会”丢最不重要的信息”。 就像 JPEG 丢的是人眼不敏感的高频,MLA 丢的是对注意力影响最小的那些维度——不是平均地丢,是挑着丢。
- MLA 从预训练就嵌进去了。 模型从第一天训练就在适应这种压缩,而不是训完再硬塞一个压缩层。整个模型是”知道自己在被压缩”的前提下长出来的。
- 它已在多代模型上跑通。 MLA 从 DeepSeek-V2 起就在生产里验证,被 DeepSeek、GLM 多代模型沿用至今,效果稳定在第一梯队——如果压缩真的伤筋动骨,这些模型不可能长期保持竞争力。
核心权衡一句话:用一点点精度,换 KV Cache 压缩约 57 倍。 在 1M 上下文这个场景里,这笔账绝对划算——光靠后面 DSA 省计算不够,还得 MLA 省存储,两者联手才能让 1M 从”装不下”变成”装得下”。
MLA vs GQA:开源阵营的内部分化
KV 压缩不是只有 MLA 一条路。这条路上,开源阵营自己就分成了两派,做法完全不同。
GQA:让多个头共享一组 K/V
Llama 3、Qwen3 走的是这条。把 64 个查询头分成几组(比如 8 组),每组共享同一组 K/V:
MHA(标准): 64 个 Q 头,64 组 K/V → 不省
GQA: Q 头分组,每组共享一组 K/V → 压约 4-8 倍(视头数配置)
MQA: 所有 Q 头共享 1 组 K/V → 压约 64 倍(质量差)
GQA 是”共享头”的思路——不改变每份 K/V 的内容,只是让多份共用,实现简单,压缩比约 4-8 倍(视头数配置)。
MLA:用低秩矩阵压扁 K/V
DeepSeek、GLM-5.2 走的是这条。不共享头,而是把每个 K/V 用矩阵压成 512 维的潜在向量,用的时候再恢复。压缩比约 57 倍,但实现复杂——要存压缩矩阵、恢复矩阵,推理时还要解压。
为什么 GLM-5.2 不选更简单的 GQA
一句话:冲 1M,GQA 的几倍不够,必须 MLA 的约 57 倍。 表里各方案的压缩比按各自头数计,跨模型不可直接横比,只看量级。
| MHA | MQA | GQA | MLA | |
|---|---|---|---|---|
| 做法 | 每头独立 K/V | 全共享 1 组 | 分组共享 | 低秩压缩 |
| 压缩比 | 1× | ~64× | ~4-8× | ~57× |
| 质量 | 最好 | 差 | 良好 | 接近 MHA |
| 代表 | 早期模型 | 较少用 | Llama 3 / Qwen 3 | DeepSeek / GLM-5.2 |
GQA 适合中等上下文(128K 量级)——简单够用。但要做 1M,KV Cache 的基数太大,几倍压缩后还是装不下,只有 MLA 这种”把每份 K/V 真正瘦身”的狠招才行。

这对你用 AI 意味着什么: 选模型时,“用 GQA 还是 MLA”是个隐形的分水岭。用 GQA 的模型(Llama、Qwen)在中等长度上性价比高,但往 1M 冲会吃力;用 MLA 的模型(GLM、DeepSeek)为超长上下文而生,代价是实现更复杂。你想跑多长的上下文,决定了哪一派更适合你。
一个关键细节:MLA 和 RoPE 的解耦
讲一个略深、但挺关键的细节。
上一篇提过,GLM-5.2 用 **RoPE(旋转位置编码)**给 token 注入位置信息。问题来了:RoPE 是”旋转”,属于非线性变换,没法被低秩压缩——硬把 RoPE 部分也塞进潜在向量,旋转信息就丢了。
GLM-5.2 的解法是把 K 拆成两半,各走各的路:
K 向量 = [nope 部分 192 维(可压缩)] + [rope 部分 64 维(单独存,不压缩)]
- nope 部分(192 维):不带位置编码的纯内容,交给 MLA 的低秩压缩。
- rope 部分(64 维):带旋转位置编码,单独存储,不参与压缩。
这就是 config 里那两个参数的来历:
qk_nope_head_dim: 192 # 可压缩的内容部分
qk_rope_head_dim: 64 # 不可压缩、单独存的位置部分
这个”解耦设计”让 MLA 和 RoPE 能协同工作——既吃到了 MLA 的压缩红利,又没丢掉 RoPE 编码位置的能力。两个本来打架的设计,靠”分家”和平共处。这是 GLM-5.2 架构里值得多看一眼的一处取舍。

墙 2 拆完了,但 N² 还在
MLA 把墙 2(放不下)拆了:KV Cache 从 5TB 压到约 88GB,普通多卡装得下了。
但墙 1(算不动)纹丝没动。 MLA 让 Cache 小了,可每次算注意力时,该做的点积一个也没少——1M 上下文还是一万亿次点积。压缩省的是”存”,没省”算”。
这就是下一篇的主题。GLM-5.2 怎么把这一万亿次点积,砍成每个 token 只算 2048 次?答案是它最核心的架构创新——DSA(一种稀疏注意力方案)+ IndexShare。关键是一个叫 **indexer(索引器)**的东西,像一个”机场安检的粗筛台”,先用低成本快速筛出每个 token 该关注哪 2048 个,再用完整注意力只对这 2048 个精算。
更妙的是,GLM-5.2 还让每 4 层共享一个 indexer,把粗筛成本也省掉 3/4。这套组合拳是整个连载的重头戏。
下篇预告:《注意力 N² 太贵怎么办?DSA + IndexShare》
本篇涉及的 config 字段——qk_head_dim: 256、qk_nope_head_dim: 192、qk_rope_head_dim: 64、v_head_dim: 256、kv_lora_rank: 512、q_lora_rank: 2048、num_attention_heads: 64、num_hidden_layers: 78、hidden_size: 6144、dtype: bfloat16——均来自 GLM-5.2 公开的 config.json。KV Cache 的 5TB / 88GB / 57 倍为笔者按 config 维度估算(bf16;57 倍 ≈ 5TB ÷ 88GB),非官方数字,第 2 篇路线对比用的是同一套口径。MLA 的低秩分解、W_DKV/W_UK/W_UV 压缩-恢复机制、解耦 RoPE 设计引自 DeepSeek 论文公开的 MLA 方案,GLM-5.2 直接沿用;MLA 自 DeepSeek-V2 起被 DeepSeek、GLM 多代模型沿用。GQA/MQA/MHA 为业界通用方案,Llama 3 / Qwen 3 用 GQA 据其公开资料,实际压缩比视头数配置(Qwen3 系列约 4 倍、Llama 3 70B 约 8 倍)。无任何闭源或未公开信息。