(五)一万亿次点积砍到两千次:DSA 怎么破 N²

ClawLihai · · 共 3,350 字 · 约 11 分钟读完

DSA 稀疏注意力封面

MLA 拆了墙 2,但墙 1 还在

上一篇我们用 MLA 把 KV Cache 从约 5TB 压到 88GB,墙 2(放不下)拆了。但结尾我说了句实话:MLA 只省存储,没省计算。墙 1(算不动)纹丝没动。

这篇就拆墙 1,也是整个连载最该认真读的一篇——GLM-5.2 能冲到 1M,杀手锏就在这里。

进度感:本篇破墙 1(算不动 N²)★ 核心篇
墙 1(算不动)→ 本篇 DSA + IndexShare 在破
墙 2(放不下)→ 第 4 篇 MLA 已破
墙 3(推不动)→ 第 6 篇 MoE
墙 4(蹦得慢)→ 第 7 篇 MTP

墙 1 量化:一万亿次点积,乘 78 层

地基篇讲过,标准注意力的 Q·K 点积是 N²——每个 token 要和序列里所有 token 算一遍。把这个数算到 1M:

1,000,000 × 1,000,000 = 1,000,000,000,000(一万亿次)
→ 这还只是一层的量
× 78 层 = 78 万亿次点积(只算 Prefill 一次)

一万亿次点积,一层。 78 层叠起来 78 万亿。这个量级在任何单机上都跑不动,哪怕是最强的 GPU。这就是墙 1——不是”慢一点”,是”根本算不完”。

墙 1:1M 上下文一次注意力 = 一万亿次点积,一层

这对你用 AI 意味着什么: 为什么很多模型把上下文标到 128K 就不再往上冲了?因为在标准注意力下,长度翻倍,计算量翻四倍。从 128K 到 1M,计算量要涨 60 多倍(长度约 7.8 倍,平方 ≈ 60 倍)——不做架构改造,硬冲就是烧钱烧到不划算。墙 1 是长上下文最硬的一道,破不了它,1M 就是空谈。

核心洞察:大部分注意力是浪费的

DSA 的起点是一个观察,简单到有点反直觉:

经验上,大部分注意力计算是浪费的。

回到老例子”我想吃苹果”。假设句子里有个「的」字——这个字,真的需要和句子里其他所有 token 都算一遍相关性吗?

显然不需要。「的」只关心它修饰的那个词,其他 99% 的 token 和它毫无关系。在 1M 上下文里,这个浪费被放大到极致:「的」要和 100 万个 token 都算一遍,其中绝大部分是噪声。

DSA 的思路一句话:别全员对照,每个 token 只挑最相关的几个算。 拆 config,“几个”是定死的:

index_topk: 2048

每个 token 先粗筛出 2048 个最相关的,只对这 2048 个做完整注意力。计算量从 1M×1M 直接降到 1M×2048——约省 500 倍(上下文越长,省得越多)。

大部分注意力是浪费的:「的」不需要和 100 万 token 都算

这对你用 AI 意味着什么: 为什么你把一份 50 页文档丢给 AI,它有时像”只认真看了”开头几页?因为稀疏注意力做的本来就不是逐字精读,而是相关性筛选——和你问题语义无关的部分,大概率被粗筛跳过了。把关键信息放在和问题语义相近的位置,比埋在第 47 页更容易被它捞到。

难题:谁来挑?这不就绕回来了吗

这里有个看似无解的矛盾,也是 DSA 最核心技术难点:

每个 token 怎么知道该挑哪 2048 个?你得先算一遍相关性才知道谁相关,可算一遍相关性本身就是那一万亿次点积——这不绕回 N² 了吗?

如果解不开这个结,“只算 2048 个”就是一句空话。GLM-5.2 的解法是引入一个新角色:indexer(索引器)

indexer:机场安检的粗筛台

indexer 本质上是一个轻量版的注意力,专门用来快速筛选,不做精确计算。用机场安检打比方:

【第一关:indexer 粗筛】
   乘客(token)出示简易信息卡,快速扫一遍所有人
   → 判断"哪几个人可能相关"
   → 选出 top-2048 个"嫌疑人"

【第二关:完整注意力 精查】
   只对这 2048 个做详细检查(完整 Q·K·V)
   → 其余 99 万多个直接放行
  • indexer = 粗筛(快但糙)
  • 完整注意力 = 精查(慢但准,但只查 2048 个)

粗筛怎么快?靠”低维”

你肯定要问:“粗筛不也算点积吗?那不还是 N²?”

对,粗筛确实也要对全部 token 算一遍,但它用的是低维版的点积,所以快得多。拆 config:

index_head_dim: 128    # indexer 每个头的维度
index_n_heads: 32      # indexer 的头数

完整注意力是 64 头 × 256 维的高维精算;indexer 用 32 头、每头 128 维的低维点积来快速估算”大致相关程度”。维度更低、头数更少,单次成本远低于完整精查。 它要算的次数确实还是 N(每个 token 都扫一遍),但每次便宜得多,而且只为了排出个名次、取前 2048。

indexer 粗筛:低维点积(32 头 × 128 维) vs 完整精查(64 头 × 256 维)

凭什么”低维点积”能当筛选标准

这是最深的一个疑问:凭什么低维点积大的,就一定是真正相关的? 低维丢了那么多信息,凭什么还能预判?

答案:因为 indexer 的投影矩阵是训练出来的。

indexer 把 token 投影到低维用的那几个矩阵,不是人设计的,是训练学的。训练给它定的目标很明确——让”真正相关”的 token 对,在 128 维的低维空间里点积也大。换句话说,模型自己学会了”用 128 维精炼信息,预判完整的相关性”。

再用一个比喻。完整注意力是面试官一对一深度面试,每人两小时、问 256 个问题,精确但慢;indexer 是 HR 看简历快速筛,每人五分钟、瞄几个关键字段,不准但快。HR 的”筛选标准”(看简历哪几个字段)是训练学出来的——学会了”哪几个字段最能预判面试结果”。

所以筛选的根据是低维点积值,而低维点积之所以能当标准,是因为训练赋予了它”低维近似完整相关性”的能力。这不是循环论证,是训练学出来的本事。

机场安检两关:indexer 低维粗筛 top-2048,再完整精查

DSA 的完整流程

把粗筛和精查串起来,就是 DSA 在一层里干的事:

当前 token 进入注意力层


┌─────────────────────────────────┐
│ 第一关:indexer 粗筛              │
│  - 低维 Q·K 点积(快)            │
│  - 取 top-2048                  │
└────────────────┬────────────────┘

┌─────────────────────────────────┐
│ 第二关:完整注意力(只对 top-2048)│
│  - 完整 Q·K·V(64 头 × 256 维)   │
│  - softmax → 加权混合 V          │
└────────────────┬────────────────┘

           输出新向量

一个必须澄清的分工:被跳过的 token,K/V 还在不在 Cache

在。 这是最容易混淆、也最容易张冠李戴的一点:

  • 计算上:被跳过的 token,这一轮不算注意力。
  • 存储上:它们的 K/V 仍然好好地躺在 KV Cache 里——因为别的 token 可能在自己的 top-2048 里选到它们。

所以 DSA 省的是计算,不是 KV Cache 存储。KV Cache 的体积问题归 MLA 管,注意力的计算量问题归 DSA 管,两者分工明确,谁也不抢谁的活。 想压缩存储找 MLA(上一篇),想砍计算量找 DSA(本篇)。

DSA 完整流程:indexer 粗筛 top-2048 → 完整精查;被跳过的 K/V 仍在 Cache

IndexShare:每 4 层共享一个 indexer

讲到这里你可能有个新疑问:既然 indexer 这么好用,为什么不每层都跑一个?反而要”每 4 层共享”?

答案:因为 indexer 本身也要算,75 层各自算也不便宜。

先看 78 层的真实身份

拆 config 的两个数组,78 层每层都挂着两个标签——MLP 类型(dense 还是 sparse)和 indexer 类型(自己算 full 还是借用 shared):

mlp_layer_types: 前 3 层 dense + 后 75 层 sparse = 78 层
indexer_types:   21 个 full + 57 个 shared = 78 层

也就是说,用 DSA 的 sparse 层有 75 个。如果每个 sparse 层都自己跑一个 indexer,那就是 75 份粗筛。粗筛虽然每次是低维,但乘以 75 也不少。

IndexShare 的解法:相邻层”该关注谁”差不多

关键洞察:相邻几层选出来的”该关注谁”,其实差不了多少。

如果第 6 层算出”苹果该关注吃”,那第 7、8、9 层觉得”苹果该关注吃”的概率也很大——语义不会突变。没必要每层都从头粗筛一遍。

于是 GLM-5.2 让每 4 层共用一个 indexer:一组里只有 1 层(full)真正算 indexer,其余 3 层(shared)直接复用它的结果。

班级行政图:真实 config 数据

把 78 层想成一个班级,每 4 人一组,组里 1 个”课代表”。(前 3 个 dense 层 layer 0/1/2 各自挂 indexer、不进共享组;下面的共享组从 sparse 层 layer 3 开始。)

第 1 组:[layer 3, 4, 5]   ← 课代表 layer 6  (full,自己算 indexer)
第 2 组:[layer 7, 8, 9]   ← 课代表 layer 10
第 3 组:[layer 11,12,13]  ← 课代表 layer 14
... (后面每 4 层一组,1 个 full + 3 个 shared)
  • full 层(21 个):课代表,自己算 indexer,得出 top-2048。
  • shared 层(57 个):组员,直接用同组课代表的索引,自己不算。

省了多少

不用 IndexShare:每个 sparse 层各自算 indexer
用 IndexShare:  每 4 层里 1 个算、3 个复用
省了:粗筛这一步,在 3/4 的层被直接省掉

精确数一下:78 层里 indexer 是 full 的有 21 个(含前 3 个 dense 层)、shared 的有 57 个,57:21 ≈ 3:1,正好对应”每 4 层里 1 个算、3 个复用”。官方原话就是”减少了 3/4 层中索引器点积与 topk 运算的计算量”。

官方博客的原话是”每四个稀疏注意力层复用同一个索引器”,并给出综合效果:1M 上下文下每 token FLOPs 降低 2.9×

IndexShare 班级行政图:78 层分 21 组,每组 1 个 full + 3 个 shared

为什么能复用?训练让它稳定

能复用的前提是”相邻层选的 token 差不多”。这不是巧合,是训练有意为之:GLM-5.2 在训练时就让相邻 4 层共享 indexer 的输入,逼着模型学会”在相邻层保持稳定的 top-k 集合”。模型是被训练得”适合复用”的,不是复用一个本身不稳定的玩意儿。

这对你用 AI 意味着什么: IndexShare 这种”跨层共享”的工程取舍,背后是个朴素的道理——很多计算其实有冗余,相邻步骤的结果高度相似,就不必重复算。 这种思路不只存在于大模型:你写代码时缓存重复计算的结果、数据库里复用查询计划,都是同一回事。GLM 把它用到了注意力层之间。

筛错了怎么办:三重保险

DSA 最大的风险,你一定想到了:万一 indexer 把真正相关的 token 筛掉了怎么办? 粗筛毕竟丢信息,漏一个关键的,结果可能就错了。

GLM-5.2 靠三重保险兜底:

  1. top-2048 够大,有冗余。 1M 里选 2048,不是”只选最相关的 10 个”,而是选前 2048 个,冗余度很高,真正相关的 token 大概率在里面。
  2. 多层覆盖。 78 层里多个 full 层各有自己的 indexer(或通过 IndexShare 复用),不同层可能选出不同的 top-2048。一个 token 在这层被漏,可能在别的层被选到,叠起来覆盖面足够。
  3. 训练让 indexer 足够准。 投影矩阵的训练目标就是”别漏掉真正相关的”,实测质量损失很小。

DSA 本质上是有损压缩——用一点点质量换巨大的速度。这个权衡在 1M 上下文场景下绝对划算:宁可粗筛漏掉极少相关 token,也比一万亿次点积根本算不完强。

三重保险:2048 冗余 + 多层覆盖 + 训练精度

收益算账:到底省了多少

把这笔账算清楚:

方案1M 上下文,每 token 每层算几次
标准注意力1,000,000 次
DSA(无 IndexShare)2,048 次(省约 500 倍)
DSA + IndexShare2,048 次精查 + 极少粗筛(粗筛还省 3/4 层,1M 下每 token FLOPs 综合降 2.9×)

(“省约 500 倍”是注意力精算这一步的对比;“2.9ד是整 token 含 MLA/MLP/粗筛的综合 FLOPs,两者口径不同,不能叠加。)

这就是 GLM-5.2 把上下文做到 1M 的核心原因。 DSA 让”算得动”成为可能,IndexShare 让”粗筛成本”也降到最低。

放回版图:DSA 和别家怎么比

先把溯源说清楚:DSA(DeepSeek Sparse Attention)是 DeepSeek 在 V3.2 提出的稀疏注意力方案,GLM-5.2 把它作为基底沿用——这部分不是 GLM 的新东西。GLM-5.2 真正的原创,是叠在 DSA 之上的 IndexShare(每 4 层共享 indexer)。 别被名字里的”DeepSeek”和 config 里的 glm_moe_dsa 绕晕,DSA 这个机制是 DeepSeek 先做的。

把这套和别家放进稀疏注意力的版图:

方案怎么挑”相关的几个”代表
Sliding Window只看相邻固定窗口Mistral、Longformer
DSA单步 indexer 粗筛 top-kDeepSeek-V3.2 提出
DSA + IndexShareDSA + 每 4 层共享 indexer(GLM 原创)GLM-5.2
CSA + HCA多层分工(CSA 压缩后稀疏选 + HCA 重度压缩抓全局)DeepSeek-V4(自家演进的新方案)

有意思的是分野:DeepSeek 自己在 V4 上把注意力往”更精细的多层分工”推(CSA + HCA),而 GLM 选择在 DSA 这个基底上做”跨层复用省粗筛”的工程优化(IndexShare)。两条路都源自”用 indexer 粗筛”这个核心思想,但延伸方向不同。

所以 GLM-5.2 的独特贡献不是 DSA 本身(那是 DeepSeek 的),而是 IndexShare 这个”每 4 层共享 indexer”的设计。 在我看到的公开材料里,主流 1M 方案没采用相同的层间共享;它把粗筛成本又砍掉一大块,是 GLM 在算力账上的工程贡献。

DSA vs DeepSeek 稀疏注意力:同属稀疏家族,路径不同

这对你用 AI 意味着什么: 同样号称 1M 上下文,GLM 和 DeepSeek 背后是不同的稀疏注意力实现,但思路同源。对你这个使用者,差别不在”能不能到 1M”(两家都到了),而在长程任务上谁更稳、谁的部署门槛更低。GLM 把 DSA + IndexShare + MLA + MoE 全组合起来,主打”开源、普通多卡可部署”——这是它和闭源巨头的分野。

墙 1 拆完了,但墙 3 还在

DSA + IndexShare 把墙 1(算不动)拆了:注意力的计算量从一万亿次降到两千次量级。

但墙 3(推不动)还在。 GLM-5.2 是个 753B(7530 亿)参数的模型——就算注意力算得动了,每次预测要把这 7530 亿参数全跑一遍,前向传播本身也慢到不可接受。

下一篇的主题:MoE 混合专家。GLM-5.2 怎么做到”753B 的知识容量,只激活一小部分算力”?答案是 256 个专家里选 8 个(再加 1 个共享专家),每个 token 只激活约 3.5% 的专家。这就像医院分诊:不是所有医生都看你,分诊台根据症状选最相关的几个科室。

下篇预告:《753B 参数怎么跑得动?MoE 混合专家》


本篇涉及的 config 字段——index_topk: 2048index_topk_freq: 4index_head_dim: 128index_n_heads: 32indexer_types(21 full + 57 shared)、mlp_layer_types(3 dense + 75 sparse)、num_hidden_layers: 78num_attention_heads: 64qk_head_dim: 256——均来自 GLM-5.2 公开的 config.json。“每四个稀疏注意力层复用同一个索引器、1M 下每 token FLOPs 降低 2.9ד引自 GLM-5.2 官方博客。DSA(DeepSeek Sparse Attention)由 DeepSeek 在 V3.2 提出,GLM-5.2 作为基底沿用(config 记作 glm_moe_dsa);IndexShare(每 4 层共享 indexer)是 GLM-5.2 的原创贡献。DeepSeek-V4 演进到 CSA + HCA 混合稀疏注意力(非 NSA——NSA 是 DeepSeek 2025 年的独立稀疏注意力论文),据其公开资料。N² 点积次数(1M → 1 万亿、省约 500 倍)为按 index_topk 直接估算的理论值。Sliding Window / Mistral / Longformer 为业界通用稀疏注意力方案。无任何闭源或未公开信息。

这个话题还有

🖼 图文卡片版 · 点击放大轮播
双击或滚轮缩放 · 拖动平移 · Esc 关闭