从0训练LLM · 现代大模型体系 阅读 + 实验 ≈ 50 分钟 页内实验 ×4
C11 更大更省:MoE、线性/稀疏注意力与长上下文 第 5 课

稀疏注意力省了什么,没省什么?

稀疏注意力:省了什么,没省什么
DeepSeek-V3.2 的 DSA 让每个 query 只读 2,048 条 KV,核心注意力从 L² 降到 L·k——可 KV 一条都没少存(每 token 48,068 字节),负责挑选的索引器也仍要扫全部前文。要省存储,得砍别的维度:V4 把序列压短(3,514 字节),V4.1 再让多数层共用一份、并存成 FP4,到 890 字节。每一刀省了什么、代价是什么,这一课逐刀算清。

本课一问:稀疏注意力省了什么,没省什么?

这一课先不碰:服务端怎么给成百上千个请求分配 KV(C13 的 PagedAttention);FP4 这类格式本身怎么编码(C9 岔路);「窗口 1M」是不是「有效 1M」(C2 第 5 课)。

从已有出发

从已有出发 · 累计数据流 点已学环节看形状与参数
解码层

上一课把锚点 3/4 的层换成了不存 KV 的固定状态;剩下 1/4 的全注意力层,每个 query 仍要和前面所有 token 打分,KV 仍按长度一条条存。C3 第 10 课的 KV 阶梯停在 DeepSeek-V3 的 70,272 字节,末尾留了一张 890 字节的虚线卡——这一课把中间的每一级补上。

这一课换个例子,原因是:稀疏注意力的意思是「每个 query 只读 k 个 key」,只有前文远多于 k 时才有意义;贯穿例句 5 个 token,全部读完也不贵。下面用 64 个 key 的教学构造演示选择过程,真实规模用官方 config 在长度轴上算。

一个框架先摆在前面(旧版课程 M8 的三分法):要让「KV 随长度增长」不再成立,数学上只有三种可能——不增长(固定状态,上一课)、增长得慢(把几个 token 压成一条)、照常增长但少读(全存、只挑 top-k 读)。这一课讲后两种。

先自己设计:每个 query 读全部前文

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案每个 query 读全部 L 个 key

每生成一个 token,它的 query 要和缓存里全部 L 条 KV 打分、加权。计算随 L 线性增长,整段序列就是 L²;KV 也要一条不落地存着。

🎯

试三件事(在改进一侧):① top-k = 8:读的 key 从 64 个变成 8 个,看选中的这些占了多少注意力权重、输出误差多大;② 勾「理想索引器」:直接按真实分数挑,是这一步的上限——真实的索引器维度低、是另外训练出来的,差的就是这一截;③ 换几个 query:有的 query 注意力很集中(少数 key 就占大头),有的很分散,稀疏对后者伤得多。不管哪种,64 个 key 都还在缓存里,索引器也给 64 个都打了分。

改进一:DSA——便宜的索引器挑 top-k

DeepSeek-V3.2(2025-09)的 DeepSeek Sparse Attention:在每个 MLA 层前面加一个 lightning indexer,给前文每个 token 打一个分,只把分数最高的 2,048 个送进真正的注意力。打分式逐项看(旧版课程 M8 的五项解释全部保留):

索引器打分(DeepSeek-V3.2 的 DSA;V4 报告式 16 同形)
)
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
旧版课程说「GLM 的 indexer 要给全部 S 个 token 打分,所以它在层的维度上省」——下面的「改进二」展开。
DSA 的选择链(128K 上下文、V3.2 的真实维度;可切小规模)
o = Attn(q, KV[TopK(ReLU(KI · qI))])
输入输出中间结果 渐变 = 分数(连续值)编号槽 = 索引斜线 = 掩码(留白 = 不读)
边长按对数比例缩放,块边上的数字是真实值;同一维度符号的边在全图等长。
维度含义 真实规模 小规模
L 上下文长度(这里取 128K) 131,072 64
dI 索引器 key 维(index_head_dim) 128 4
k 每个 query 读几条(index_topk) 2048 8
dc MLA 每条缓存的维数(kv_lora_rank + qk_rope_head_dim) 576 16
选择链闭合:连续的分数 → 离散的索引 → 0/1 的掩码 → 取出的值。KV 缓存那一块是 L × d_c,一行都没少;少的只是进入核心注意力的行数。

新的失败有两个。第一,KV 一条不少:V3.2 每层每个 token 存 656 字节(FP8 潜向量 + scale + BF16 的 RoPE 部分)加索引器 key 约 132 字节,61 层合计 48,068 字节——比 V3 的 70,272 字节小,是因为换成了 FP8,不是因为稀疏。第二,索引器本身仍是 O(L²):每个 query 仍要给全部 L 个前文打分,只是每一次打分便宜。1M 上下文时,V3.2 每个 query 每层给 1,048,576 个条目打分,是它读进核心注意力的 512 倍。

改进二:少算几次索引器

两个办法:

每个 query 要碰多少条目 · 五种做法、长度从 4K 到 1M(官方 config 现算)
每个 query 平均每层要碰多少条目(config 现算,对数轴)

🎯

试三件事:① L = 4,096:V3.2 的核心注意力读 2,048 条,只省了一半;稀疏是为长序列设计的;② L 拖到 1M:核心注意力那张图里,四种稀疏做法都早早「躺平」在几百到几千条;差别几乎全在索引器那张图——V3.2 每层要给一百万条打分;③ 对照 GLM-5.3 与 V4.1:一个靠「少算几层」,一个靠「先压缩再粗筛」,斜率不同。这张表只比条目数、不比 FLOPs(索引器一条比核心注意力一条便宜得多),替代了旧版课程那组示意常数。

改进三:把序列压短(DeepSeek-V4 的 CSA 与 HCA)

要省存储,就得让存下来的条目变少。DeepSeek-V4(2026-04)把每 m 个 token 的 KV 压成 1 条:

CSA 的压缩:m 个 token 合成 1 条(DeepSeek-V4 报告式 11–12)
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
更正旧版课程:旧稿说「m 论文正文没给,Raschka 的 4/128 是二手来源」。V4 报告 §4.2.1 写明 V4-Flash 的 CSA 压缩率 m = 4、HCA 的 m′ = 128、top-k = 512;V4-Flash 的 config 里 compress_ratios 也是 4 与 128 交错。

V4-Flash 的 43 层:前 2 层纯滑动窗口,之后 21 层 CSA(每 4 个 token 一条,再用索引器挑 top-512)与 20 层 HCA(每 128 个 token 一条、不挑、全读)交错;每一层另有 128 个 token 的滑动窗口,因为压缩对近处伤害最大。V4 报告的 1M 口径:V4-Flash 的 KV 是 V3.2 的 7%,单 token FLOPs 是 10%;激活参数更多的 V4-Pro 分别是 10% 与 27% 。

改进四:几层共用一份、再压精度(DeepSeek-V4.1)

V4.1-Flash(2026-09)接着砍另外两个维度:

逐字节的账:

KV 阶梯下半段 · 按 FlashMLA 官方缓存格式与 config 逐字节现算
每 token 全局 KV 的阶梯(下半段,对数刻度):灰色三级是 C3 的前半段

代价写在官方报告里:CED 下 decoder 的滑动窗口缓存要靠 Bounded Replay 近似重建,报告原话是「数学上并不等价」,只说内部测试影响可忽略,并把「边界情况下的鲁棒性」列为局限 。官方还报告,上下文从 4K 扩到 1M(256 倍),V4.1 的单 token decode FLOPs 只多了 1/4。

2026 年各家把「省 KV」的刀下在不同维度上(R3 §3.2 的框架):

维度手段谁在用
条目大小GQA 减头;MLA 存潜向量;K = V 共享Qwen3.8(4 个 KV 头);K3、GLM-5.3(MLA 512);DeepSeek V4 / V4.1
序列长度固定状态(不增长);几个 token 压成一条Qwen3.8、K3、GLM-5.3-Flash(上一课);DeepSeek CSA / HCA
层数几层共用一份 KV 或索引V4.1 CSA2 与 CED;GLM-5.3 IndexShare
精度FP8 / FP4 缓存V3.2(FP8);V4.1(FP4 全局 KV)
只读一部分(不省存储)索引器 top-k;块级选择DSA(V3.2、GLM-5.3);Qwen3.8-Flash-Next 的 QSA(索引 key 每 4 个 token 一条,预算 2,048 个 token)
⚠

常见误解一:「稀疏注意力省显存。」 按内容挑 top-k 只省计算;KV 一条不少(V3.2 每 token 48,068 字节)。GLM-5.3 这类「MLA + DSA」的 1M 上下文,KV 加索引 key 约 93 GiB(frontier-models.json 的核对值)。

常见误解二:「索引器很便宜,可以忽略。」 单次打分便宜,可它要给全部前文打分:1M 时每个 query 每层是核心注意力读取量的 512 倍条目。这正是 IndexShare、分层索引、索引 key 压缩出现的原因。

常见误解三:「每 token KV 字节可以直接横比。」 要先对齐:什么精度(BF16 / FP8 / FP4)、含不含索引器 key、滑动窗口和固定状态(与长度无关)有没有另计。本课所有阶梯只算随长度增长的全局部分。

常见误解四:「DSA 是 GLM 提出的。」 DSA 出自 DeepSeek-V3.2;GLM-5 报告原文是「we adopt DSA」,GLM 自己的贡献是 IndexShare。

本章演进图:每一代都被上一代的某个真数字逼出来(本课所在的一代已高亮;其余两条线可展开)
① 更多参数,不要更多算力(MoE)
  1. 朴素 dense 直接放大 算力 ∝ 参数
    解决了结构最简单:所有参数对每个 token 都生效。
    卡在锚点 Qwen3.8-27B 每 token 前向约 51.2 GFLOPs;同家族 2.4T 规模若是 dense,约 4.84 TFLOPs(94 倍)。
  2. 第一代 稀疏门控 MoE Shazeer 2017 → GShard → Switch
    解决了每个 token 只算 top-k 个专家:Qwen3.8-2.4T-A95B 每 token 约 186.5 GFLOPs,是同参数 dense 的 26 分之一。
    卡在路由会自我强化地偏心:8 个专家全挤到 1 个时,Switch 负载损失从 0.01 升到 0.08;所有专家的权重仍要常驻显存。
  3. 改进 ① 辅助损失 α·N·Σf·P Switch 2021(α = 10⁻²)
    解决了把「均衡」写进 loss,路由器被梯度推向均匀。
    卡在α 难调:太小拉不平,太大干扰主 loss(Switch 在 10⁻¹ 到 10⁻⁵ 之间扫;Wang 2024 的出发点)。
  4. 改进 ② 无辅助损失偏置 Wang 2024 → DeepSeek-V3
    解决了偏置只改「选谁」,不进门控、不产生干扰梯度;DeepSeek-V3 前 14.3T token 用 γ = 0.001,最后 500B 设为 0。
    卡在固定步长在近千个专家时太慢或来回震荡(Kimi K3 报告)→ Quantile Balancing;而且统计范围(整批 vs 单条序列)比「有没有 loss」更要紧。
  5. 改进 ③ 细粒度专家 + 共享专家 DeepSeekMoE 2024
    解决了同参数同计算,16 选 2 的 120 种组合变成 64 选 8 的 4,426,165,368 种;共享专家装公共知识。
    卡在显存账不减:Qwen3.8-2.4T 路由专家占主干参数 97.98%,BF16 权重 4.89 TB。
  6. 2026 极高稀疏度与分歧 Qwen3.8 · K3 · V4.1
    解决了Qwen3.8-2.4T 512 选 10 + 1 共享;K3 896 选 16 + LatentMoE + Quantile Balancing;V4.1 图文两套偏置。
    卡在共享专家 0、1、2 个都有人用(Qwen3 一个不设),稀疏度从 32 到 64 都有(GLM-5.3 与 V3 为 32、GLM-5.3-Flash 36、K2 48、Qwen3.8 51、K3 56、V4.1-Flash 64)——都是实验选择。
② 长文本:KV 与计算不再随长度等比例涨
  1. 解决了每个 token 都能精确回看任意一个前文 token。
    卡在KV ∝ 长度:锚点若 64 层全是全注意力,262,144 token 时 64 GiB。
  2. 第一步 线性注意力 Katharopoulos 2020
    解决了去掉 softmax,求和提前累加成一个固定大小的状态,与长度无关。
    卡在只加不减:同一个键写两次,读回的是两个值之和;状态最多装 d 对互不干扰的键值(C0 岔路)。
  3. 第二步 Gated DeltaNet · 3:1 混合 Schlag 2021 → Yang 2024 → Qwen3-Next、3.8
    解决了写前先擦(delta)、每步衰减(门):锚点 48 层状态共 144 MiB(FP32)、与长度无关;KV 降到四分之一(262,144 时 16 GiB 对 64 GiB)。
    卡在剩下四分之一的全注意力层仍按长度涨;容量有限,3:1 是实验选出来的。
  4. 工程化 KDA + 衰减下界 Kimi Linear → K3 → GLM-5.3-Flash 等
    解决了每通道一个衰减;g_min = −5 让 16 token 块内累积不低于 −80,e⁸⁰ ≈ 5.5 × 10³⁴ 在 BF16 范围内(上限约 3.4 × 10³⁸),慢路径删除。
    卡在MiniMax M1 用线性混合、M2 又退回全注意力:大规模下多跳推理缺陷、训练受访存限制、低精度与缓存难题(官方博客)。
  5. 另一条路 稀疏注意力 DSA DeepSeek-V3.2 2025 你在这里
    解决了lightning indexer 每个 query 只挑 top-2048 条做核心注意力。
    卡在省计算不省内存:KV 一条不少,每 token 48,068 B;indexer 本身仍要扫全部前文。
  6. 2026 压序列 + 压层 + 压精度 DeepSeek-V4 CSA + HCA → V4.1 CSA2 + CED 你在这里
    解决了V3.2 每 token 48,068 B → V4-Flash 3,514 B → V4.1-Flash 890 B(1M 时 0.87 GiB)。
    卡在是近似:V4.1 自陈 Bounded Replay「数学上不等价」、选择误差可能在未测过的边界情况退化。
③ 训练 4K,使用 128K
  1. 解决了推理时把新位置折算回训练见过的角度范围。
    卡在零样本外推有代价,方法的超参数带着它被调出来时的规模(C2 第 5 课与项目实测)。
  2. 朴素 从头就用长序列训 最直接的想法
    解决了训练与使用同一个长度,不需要任何折算。
    卡在注意力项随长度涨:Qwen3-32B(64 层全注意力)在 131,072 时注意力项是 6N 的 215%;早期长序列还带来梯度方差极端值(Li 2021)。
  3. 现代做法 先短后长 + ABF BERT → Llama 3 → DeepSeek-V3 · Qwen3
    解决了DeepSeek-V3 的 14.8T token 全在 4K,之后两段各 1,000 步扩到 32K、128K,只占全部训练 token 的 0.84%;Llama 3 405B 六段、约 800B token。
    卡在长序列阶段的数据要专门配(长度上采样、领域均衡、合成跨全文任务)。
  4. 2026 分歧 课程在缩短 Kimi K3 · DeepSeek-V4.1
    解决了K3:8K → 64K → 256K → 1M(NoPE 也照走课程);V4.1 注意力从头就是压缩稀疏的,直接从 64K 起训。
    卡在各家配方大多只公开阶段、不公开每段 token 数,无法横向比较成本。
  5. 岔路 多 token 预测 MTP Stern 2018 → Gloeckle 2024 → DeepSeek-V3
    解决了每个位置多学一个更远的目标;推理时当草稿,接受率 α 时每步期望产出 1 + α 个 token。
    卡在DeepSeek-V3 第 2 个 token 接受率 85%–90%,实测 1.8 倍 TPS;V4.1 预训练干脆去掉 MTP,没有公开消融。
数字口径:锚点、Qwen3.8-2.4T、Qwen3-32B、DeepSeek 各代的数字由 anchor-model.json / frontier-models.json(DeepSeek-V3.2、V4-Flash 取本章补充 config)在构建时现算;KV 字节按 FlashMLA 官方 README 的缓存格式计(indexer key 为推算,见第 5 课);FLOPs 只数矩阵乘,乘加记 2。Switch α、DeepSeek-V3 的 γ 与长上下文两段、K3 的 g_min 与长度课程出自各自原文(本地 PDF 已核);MiniMax 的理由出自其官方博客(2025-10-29)。

证据三问

问本课的回答
① 它解决什么问题?让长上下文里的每个 query 不必读全部前文(省计算);再通过压序列、压层、压精度省存储
② 数学上为什么可能有效?字节与条目数的记账 ;注意力权重通常集中在少数位置,只读 top-k 损失可控是理论动机
③ 实验上为什么最后选了它?V4-Flash 1M 下 KV 为 V3.2 的 7%;V4.1 4K → 1M decode FLOPs 只多 1/4;Qwen QSA 的 RULER、MRCR 提升

一句话

📌

按内容挑 top-k 省的是计算,不是 KV;要省内存得压序列、压层、压精度——DeepSeek 三刀一起砍到 890 字节。

🚦

先停在这里:「只读一部分 ≠ 少存一部分」,记住这一句就够进下一课。
再深一层:对着 FlashMLA 的 README,自己把 V3.2 的 656 字节和 V4.1 的 288 字节算一遍;再读 V4.1 报告的 §2.3。

下一个缺口

推理时的计算和显存都压下来了——那训练要不要一开始就用长序列? → 第 6 课:训练 4K,使用 128K?

你可能会问