Lesson 0005 · Phase 1 地基 · 约 45 分钟

GQA 与 MLA:KV Cache 的压缩竞赛

读完本课你将能:算清 KV Cache 的显存账单,解释 GQA 的分组共享与 MLA 的潜空间压缩,读懂 config 里的 num_key_value_heads / kv_lora_rank

前三课把注意力层"怎么算"讲完了。这一课换一个问题:怎么"存"。第一课对照表"注意力机制"那一行——DeepSeek-V3 写着 MLA、GLM-4.5 写着 GQA、Kimi K2 写着 MLA——两个缩写争的是同一件事:生成的时候,历史信息要在显存里放多少。这一仗没有硝烟,却直接决定了"多长的对话装得下、多大的批次跑得动"。

一、KV Cache:逐 token 生成的显存账单

先回忆第一课的生成方式:一次吐一个 token。生成第 t 个 token 时,模型要让它看到前 t−1 个 token——那些 token 的 K、V 在之前的步骤里已经算过一遍。如果不留底,每生成一个字就得把全部历史的 K/V 重算一遍(第 1000 步 = 重算 999 个 token 的投影),纯浪费。所以推理引擎把每个 token 的 K、V 缓存下来:这就是 KV Cache——用显存换时间。

把账单算清楚(本课的全部数字都从这里长出来)

每个 token、每一层,要缓存的是所有 KV 头的 K 和 V

每 token 每层缓存 = n_kv × (head_dim_K + head_dim_V) × 2 字节(bf16)

拿 DeepSeek-V3 的规模假想一个"不做任何压缩"的 MHA(128 个头,head_dim 128 + 128):

账目算式结果
每 token · 每层128 × (128+128) × 2 B64 KB
每 token · 全模型 61 层64 KB × 613.9 MB
32K 长对话 · 全模型3.9 MB × 32768≈ 131 GB

131 GB——你的 3080 只有 10 GB。权重都放得下,缓存先爆了。这就是压缩竞赛的赌注,也是 GQA 和 MLA 要解决的全部问题:让每 token 每层的那 64 KB 变小

要点:三幕分别是"账单爆炸 → GQA 的 12:1 分组 → MLA 的 576 元素潜空间"。动画数字全部来自下面两节的 config 一手数据。

二、GQA:读者共享笔记(GLM 路线)

压缩的第一个想法很自然:K/V 真的需要那么多份吗?沿这条想法往前走是一整个光谱:

GLM-4.5 的选择(config 一手)num_attention_heads = 96num_key_value_heads = 8——96 位读者、8 份共享笔记,每 12 个 query 头看同一份 K/V(12:1)。账单:

GQA = 8 × (128+128) × 2 B = 4 KB / token / 层     (MHA 假想 48 KB → 12× 压缩)

数学上它改了什么?几乎没有:attention 公式一字不改,只是第 g 个 query 头做点积时,用的 K/V 从"自己的第 g 份"换成"组里共享的那份"(⌊g/12⌋ 号)。为什么质量几乎不掉?因为训练出来的多个头本来就常学到相似的关注模式——共享笔记丢掉的多数是冗余。脚本 B1(分组 == 组内广播,零差)和 B2(与 transformers 官方 repeat_kv 对拍,零差)把这件事钉死。

三、MLA:把笔记压进潜空间(DeepSeek/Kimi 路线)

GQA 是"少存几份",MLA 是更激进的一步:把 K/V 的信息压缩进一个低秩的"潜空间",只缓存压缩件

压缩管线(对照动画面板第 3 幕)

c_kv  = x · W_DKV          ← (d_model → 512) 低秩投影,【只缓存它】
k_nope = c_kv · W_UK       ← 用时升维:512 → 每头 128 维,重建 K
v      = c_kv · W_UV       ← 同理重建 V
k_rope = c_kv · W_KR       ← 64 维位置专用通道(见下)

为什么这样是"无损"的?因为 K/V 本来就是从 x 线性投影出来的——x 的信息先压到 512 维、再展开成 128 个头的 K/V,数学上是同一个线性映射拆成两步。脚本 B3 证明:分项打分(nope + rope)与"拼出完整 K 一次点积"恒等;B4 证明:只凭 576 元素的缓存重建的 K/V,跑出的 attention 输出与全量路径零差。压掉的不是信息,是存储冗余

回收第四课:那条 64 维的 rope 通道是什么?

这里有个精心设计的细节。c_kv 是全头共享的低秩向量——它没法"每头转一个不同角度"(第四课的 RoPE 要求每个头的 K 有自己的旋转)。DeepSeek 的解法:从同一个 latent 再投出一条 64 维的位置专用通道 k_rope,全头共享,专门负责旋转打分;每个头的 K 分成两段——[128 维内容段(不旋转), 64 维位置段(旋转)],打分 = 内容内积 + 位置内积,缩放按总维数。这就是 config 里 qk_rope_head_dim = 64 的身份:RoPE 在 MLA 里的落点。脚本 B3/B4 用的就是同构的小规模实现。

MLA 缓存 = kv_lora_rank(512) + qk_rope_head_dim(64) = 576 元素 = 1.12 KB
对照 DeepSeek 自身 MHA 假想(128 头):57× 压缩;32K 长对话 131 GB → 2.3 GB

代价:每步生成多两次小矩阵乘(升维重建);实现复杂度也高(生产引擎里还有"矩阵吸收"等优化把升维折叠进 attention——工程深水区,知道有这回事即可)。

四、三家对照表:第一课那一行,闭环

模型路线config 一手数字每 token·每层缓存
GLM-4.5 GQA num_heads 96 / num_key_value_heads 8 / head_dim 128 / 92 层 4 KB(12×)
DeepSeek-V3 MLA kv_lora_rank 512 / qk_rope_head_dim 64 / qk_nope_head_dim 128 / 61 层 1.12 KB(57×)
Kimi K2 MLA kv_lora_rank 512 / qk_rope_head_dim 64 / rope_theta 50000(本课脚本新证)/ 61 层 1.12 KB(57×)

两个流派各有拥趸:GQA 胜在简单(推理零额外计算、所有推理框架原生支持);MLA 胜在压缩率(57× vs 12×)且质量实验上打平甚至更好——代价是工程复杂。第一课的"注意力机制"行到此完全展开:MLA 不是玄学,是低秩压缩 + 位置专用通道

五、动手:可自判卷的实现

conda activate py310_qwenpaw
cd <课程工作区目录>
python scripts\kvcache_from_scratch.py

脚本干四件事(无需写代码,但请逐行读懂 A 部分——注释里标了 shape 流动):

  1. A · 从零实现:GQA(repeat_kv 分组广播 + causal attention)+ 简化 MLA(W_DKV/W_UK/W_UV/W_KR 管线,DeepSeek 同构小规模版)
  2. B · 五道判卷:B1 GQA 分组正确性(组内广播零差)→ B2 官方对拍(transformers repeat_kv)→ B3 MLA 等价性(分项 vs 拼接)→ B4 缓存重建(只凭 576 元素跑出零差输出)→ B5 压缩账(12× / 57×)
  3. C · 两张图cache_bytes.png(三方案每 token 每层字节,log 尺度)+ cache_vs_ctx.png(总缓存 vs 上下文长度,128K 处标注)
  4. D · 三家 config 直连:GLM-4.5 / DeepSeek-V3 / Kimi-K2 的 KV 字段当场对账
观察任务(带结果回来)
  1. cache_bytes.png:三根条从左到右差了多少倍?哪一段压缩是"数量级"级别的?
  2. cache_vs_ctx.png:128K 处三条线各多少 GB?你的 3080(10GB)——哪条线装得下、哪条根本没戏?
  3. B1 的 Δ 是 0.0e+00,B3 的 Δ 是 4.77e-07:同样是"等价"判卷,为什么一个是精确零、一个是机器误差级?(提示:B1 是同一数学的两种分组写法;B3 是不同计算顺序的浮点求和)
  4. D 部分输出:GLM 有 num_key_value_heads 字段、DeepSeek/Kimi 没有——两种路线在 config 字段上的"指纹"差异说明什么?

六、检索练习

七、本周深读任务(一手来源)

本周必做
  1. 动手(20 分钟):跑通脚本,五道判卷全绿,完成第五节 4 个观察任务。
  2. 读源码(15 分钟,本地可达):打开 .../site-packages/transformers/models/deepseek_v3/modeling_deepseek_v3.py,找 kv_a_proj_with_mqa(= W_DKV+W_KR 合体)与 kv_b_proj(= W_UK/W_UV)——你会认出本课管线里的每一个零件,只是名字换成了生产版。
  3. 选读:DeepSeek-V2 论文(arXiv: 2405.04434)§ MLA——潜空间压缩与吸收优化的原始出处。

判卷出现 FAIL、看不懂 latent→K/V 的 shape 流动、config 对账有出入——直接问我