Ch 04 · CSA
第一部分 · 架构 · 04

CSA — 长上下文的快通道

先把 1M 序列粗筛成 250K 个"超级 KV",再让每个 query 只精读其中 1024 个 —— V4 长上下文的命脉。

名词速通 · 一分钟看懂 CSA

CSA = Compressed Sparse Attention(压缩 + 稀疏 注意力)

把"每个 query 都要扫完 1M 个 KV"这个 $O(n^2)$ 的炸弹拆成两步: (1) 把每 $m$ 个原始 KV 压成 1 个"超级 KV"($n \to n/m$,$m=4$), (2) 用一套轻量化打分器从 $n/m$ 个超级 KV 里选 top-$k$($k=1024$)。 每个 query 的核心注意力只处理 $k$ 个压缩条目;每个条目由相邻窗口中的原始 token 学习聚合而成,不能把它等同为逐字读取 $k \cdot m$ 个原始 token。

MLA(Multi-head Latent Attention,V3 的方案)
把 KV 通过低秩投影压到 $d_c$ 维 latent 缓存(V3 取 $d_c=512$,$d=7168$,缓存压 14×)。解决了"单 token KV 缓存大",但没解决"序列变长后每 query 仍要扫全部 $n$ 个 token"。1M context 下还是 $O(n^2)$。
Token-Level Compressor(CSA 第一步)
以步幅 $m$ 生成压缩条目(V4 paper 称 $C^{\text{Comp}}$),所以 $n=$ 1M $\to n/m=$ 250K。对内部条目而言,a 流取当前 $m$ 个位置,b 流取前一组 $m$ 个位置,合计形成覆盖 $2m$ 位置的重叠窗口;序列边界需按实现约定处理。
Lightning Indexer(CSA 第二步)
一套更轻的 query/key 投影,用 ReLU(不是 softmax)给 250K 个压缩 key 打分。按报告配置,Indexer 的 query-key 点积宽度为 $64\times128=8192$,是 Pro 核心注意力 $128\times512=65536$ 的 1/8;这不等于端到端参数量或耗时恰好为 1/8。报告没有进一步解释“Lightning”命名或声称无需实例化全部分数。
Top-$k$ Selection
每个 query 用 indexer 分数选出最高的 $k$ 个压缩条目,只对它们做核心 attention。Pro 取 $k=1024$,Flash 取 $k=512$。这些条目来自重叠窗口的学习型聚合,$k\cdot m$(4096 / 2048)只能帮助理解压缩尺度,不能解释为实际逐字读取的 token 数。
Shared-KV MQA(共享 KV 的多查询注意力)
$n_h=128$ 个 head 共享同一份 KV,省去每 head 一份 KV 的开销。"Shared" 还有第二层意思:indexer 与核心 attention 共用同一份 query latent $\mathbf{c}^Q$,省一次大矩阵乘。
Grouped Output Projection(分组输出投影)
核心 attention 的输出维度 $n_h \cdot c = 128 \times 512 = 65536$,直接投回 $d=7168$ 约需 4.70 亿参数 / 层。把 head 切成 $g=16$ 组,先在各组投到 $d_g=1024$,再拼接并投回 hidden dimension,约需 1.85 亿参数 / 层,按矩阵尺寸估算减少约 60.7%。
SWA(Sliding Window Attention,滑动窗口分支)
除了 top-$k$ 超级 KV,每个 query 额外读最近 $n_{\text{win}}=128$ 个原始未压缩 KV。压缩 + top-$k$ 容易把"最近几十个 token"融成一两个超级 KV,丢掉局部细节;SWA 兜住这一点。
一句话定位:压缩把候选 KV 数量降到约 $n/m$,Indexer 为每个 query 扫描这些压缩 key,再让核心注意力只处理 top-$k$ 条目。单 query 的主要量级是 $O(n/m+k)$;压缩条目是原始 token 的学习型聚合,不能等同于逐字读取 $k\cdot m$ 个原始 token。
CSA 架构总览:Token-Level Compressor + Lightning Indexer + Top-k Selector + Sliding Window + Shared KV MQA 的数据流
图 4-1 · CSA 整体架构。底层 Hidden States of KV Tokens 同时喂两条路:左路 Token-Level Compressor 输出 Compressed KV Entries(参与 Top-k 选择 + 主 attention),右路另一个 Compressor 输出 Compressed Indexer Keys,与 Indexer Queries 在 Lightning Indexer 内做 Multi-Query Attention 算 Index Scores → Top-k Selector 挑出 Selected Compressed KV Entries。Sliding Window KV Entries(左侧黄条)直接 concat,喂入最上层的 Shared Key-Value Multi-Query Attention来源:DeepSeek-V4 技术报告 §2.3.1 Compressed Sparse Attention,Figure 3,p. 9。

1. V3 的 MLA 在 1M 上为什么不够

V3 的注意力是 MLA(Multi-head Latent Attention):通过低秩投影把 KV 压到 $d_c$ 维 latent,每个 head 再升回。它解决的是"每个 token 的 KV 缓存太大"这一项。

  • 常规多头注意力需要为各 KV head 保存 key 与 value;
  • MLA 改为缓存低维 latent 与位置相关分量,显著降低每 token 的缓存量。

但 MLA 没解决的有三件事:

  • 计算复杂度仍是 $O(n^2)$:每 query 仍要与所有 $n$ 个 KV 做点积;
  • 缓存总量仍 $O(n)$:即使单 token 表示更小,序列增长仍会线性增加 KV cache;
  • 每个 query 仍扫描全部历史:长上下文 decode 的内存读取与点积数量继续随 $n$ 增长。
数值演练 · 为什么 1M 跑不动 对长度为 $n$ 的整段 prefill,dense attention 的 score 数量是 $n^2$;生成下一个 token 时,单层 attention 则需要让一个 query 扫描约 $n$ 个历史 key。序列长度从 64K 增至 1M:

整段 prefill:score 数量从约 $4.3 \times 10^9$ 增至 $10^{12}$,放大约 244 倍; 单 token decode:每层扫描的历史位置从 64K 增至 1M,放大约 15.6 倍。

KV cache 仍随 $n$ 线性增长。具体字节数取决于 latent、RoPE 维度、数据类型和层布局;V4 报告用 Figure 1 给出相对基线的整体结果,因此不能仅凭 hidden size 推算固定字节数。

因此,V4 不只压缩每个 KV 的表示,还通过压缩与稀疏选择减少 core attention 实际处理的 KV 数量。

2. 两个独立想法,单独用都不够

想法 A · 稀疏注意力:让每个 query 只看 $k$ 个位置(如 Longformer 的 sliding window、BigBird 的 random+global+window)。

问题:要决定"哪 $k$ 个位置最相关",需要先对所有 $n$ 个位置打分 —— 打分本身就是 $O(n)$ per query,总成本仍是 $O(n^2)$。靠规则强行选 $k$(如固定窗口)会丢真正相关的远端信息。

想法 B · 压缩注意力:把每 $m$ 个 KV 加权平均成 1 个超级 KV,序列长度 $n \to n/m$。

问题:超级 KV 是多个原始 token 的加权平均,query 无法精确读出某一具体 token 的 KV —— 细粒度访问能力丢了

CSA 的关键洞察

把两个想法串起来,正好对消彼此的弱点

  • 先压缩($n \to n/m$),把"打分代价"从 $O(n)$ 降到 $O(n/m)$
  • 再用稀疏选择,让每个 query 的核心 attention 只处理 top-$k$ 个压缩条目。

最终每个 query 的核心注意力处理 $k$ 个压缩条目。每个条目由多个相邻 token 聚合而成,因此它携带这些位置的混合信息,但不提供对 $k\cdot m$ 个原始 token 的逐项访问。

数值演练 · 算一笔账 1M 序列下 V4-Pro 的两步走($m=4, k=1024$):

Step 1(Indexer 打分):每 query 对 $n/m = 250\text{K}$ 个超级 KV 打分。Indexer 用 1/8 量级的小 head,每分数代价约 $n^I_h \cdot c^I = 64 \times 128 = 8192$ FLOPs。一个 query 的总打分代价 $= 250\text{K} \times 8192 = 2 \times 10^9$ FLOPs。

Step 2(Core attention):每 query 在 $k=1024$ 个超级 KV 上做核心 attention,代价 $\approx 1024 \times n_h \cdot c = 1024 \times 128 \times 512 = 6.7 \times 10^7$ FLOPs。

对比 dense MLA:每 query 对 1M 个 token 做核心 attention,代价 $\approx 10^6 \times 128 \times 512 = 6.7 \times 10^{10}$ FLOPs。

以上是按简化维度进行的本文估算,用于展示 Indexer 扫描与 core attention 的相对量级;实际 FLOPs 还取决于投影、双流压缩、数据类型与融合 kernel。技术报告给出的是相对 V3.2 的整体计算曲线,不给出“单层省 33×”这一拆分结论。

3. Token-Level Compressor:每 $m$ 个 KV 压成 1 个

朴素压缩:把序列切成 $\lfloor n/m \rfloor$ 段,每段简单平均。

致命问题:边界丢信息。位置 $m-1$ 和 $m$ 是相邻的,但它们落进不同段的超级 KV —— query 看任一段都只看到一半边界。

V4 的双流重叠设计:生成两组并行 KV 投影 $C^a, C^b$ 配两组打分 $Z^a, Z^b$。每个超级 KV $C^{\text{Comp}}_i$ 是两段相邻区间的混合

$$ \big[S^{a}_{mi:m(i+1)-1};\,S^{b}_{m(i-1):mi-1}\big] \;=\; \mathrm{Softmax}_{\text{row}}\!\Big(\big[Z^{a}_{mi:m(i+1)-1}+B^{a};\, Z^{b}_{m(i-1):mi-1}+B^{b}\big]\Big) $$
$$ C^{\text{Comp}}_i \;=\; \underbrace{\sum_{j=mi}^{m(i+1)-1} S^{a}_j \odot C^{a}_j}_{\text{右半段 a 流贡献}} \;+\; \underbrace{\sum_{j=m(i-1)}^{mi-1} S^{b}_j \odot C^{b}_j}_{\text{左半段 b 流贡献}} $$

每个符号的含义:

符号形状 / 角色
$C^a, C^b$$n \times c$,两套独立的 KV 投影
$Z^a, Z^b$$n$ 维,每个 token 在自己流里的"重要性"打分
$B^a, B^b$$m$ 维的可学习段内位置偏置
$S^a, S^b$把当前 a 段与前一 b 段拼成 $2m$ 个 logit 后共同做 softmax;两部分权重合计为 1
$\odot$逐元素乘 —— $S_j$ 是标量、$C_j$ 是向量;内部压缩条目是 $2m$ 个双流向量的加权组合
📖 公式白话翻译

双流重叠这两条公式翻成大白话就是"每个原始 token 同时投影到两份独立的"信道",再交叉打包"

  1. 每个 token 投两份:$C^a_j$ 和 $C^b_j$ 是同一个 token 的两套独立 KV 投影 —— 像同一份文档拍两张不同角度的照片;
  2. $Z$ 给每个 token 打"重要性分":$Z^a, Z^b$ 是两个流各自的打分;当前 a 段与前一 b 段的 $2m$ 个 logit 拼接后共同归一化;
  3. 压缩条目 $i$ = 当前 a 段 + 前一 b 段的加权组合:相邻内部条目的输入窗口彼此重叠;序列首尾的边界行为不由这组简化公式单独规定;
  4. 位置偏置 $B$:让压缩权重能够显式区分段内相对位置;报告没有规定中心 token 默认权重更高。

整体效果:输出序列的压缩率仍是 $1/m$;从窗口几何上看,内部条目近似于 stride=$m$、kernel=$2m$ 的双流可学习池化。

具体例子 · $m=4$ 段是什么样 假设原始序列 $\{x_0, x_1, \dots, x_{11}\}$(12 个 token),$m=4$,输出序列长度为 3。下面只展示公式覆盖到的内部关系;首个条目的左侧边界需要由实际实现处理。

按双流公式:
$C^{\text{Comp}}_1 = \sum_{j=4}^{7} S^a_j C^a_j + \sum_{j=0}^{3} S^b_j C^b_j$
$C^{\text{Comp}}_2 = \sum_{j=8}^{11} S^a_j C^a_j + \sum_{j=4}^{7} S^b_j C^b_j$

注意 token $x_4$(第二段开头):通过 a 流进入 $C^{\text{Comp}}_1$,又通过 b 流进入 $C^{\text{Comp}}_2$。这展示了相邻内部条目的重叠关系。

4. Lightning Indexer:怎么便宜地为 $n/m$ 个超级 KV 打分

选 top-$k$ 之前要先对所有 $n/m$ 个超级 KV 打分。直接用核心 attention 的全 head 算太贵 —— Indexer 用一套更轻的参数:

$$ \mathbf{c}^Q_t \;=\; \mathbf{h}_t \cdot W^{DQ},\qquad [\mathbf{q}^I_{t,1};\dots;\mathbf{q}^I_{t,n^I_h}] \;=\; \mathbf{c}^Q_t \cdot W^{IUQ} $$

每个符号:

  • $\mathbf{h}_t$:第 $t$ 位的 hidden state($d=7168$ 维);
  • $W^{DQ}$:把 $\mathbf{h}_t$ 投到 query 低秩 latent $\mathbf{c}^Q_t$(维度 $d_c = 1536$);
  • $W^{IUQ}$:把 $\mathbf{c}^Q_t$ 上升回 $n^I_h \cdot c^I = 64 \times 128$ 个 indexer query;
  • $n^I_h = 64$(vs 核心 $n_h = 128$),$c^I = 128$(vs 核心 $c = 512$)—— Indexer 的"信道数 $\times$ 每信道维度"是核心的 $1/8$。
维度对比 · Indexer 与核心 attention 只比较 query-key 内积维度:
  • 核心 attention:$n_h \cdot c = 128 \times 512 = \mathbf{65{,}536}$
  • Lightning Indexer:$n^I_h \cdot c^I = 64 \times 128 = \mathbf{8{,}192}$,是前者的 1/8
这只是单个候选位置的点积维度对比,不包含 query 投影、压缩、top-k、数据类型或 kernel 利用率,因此不能直接当成端到端成本比例。

Indexer 的关键设计:用 ReLU 而非 softmax 计算打分

$$ I_{t,s} \;=\; \sum_{h=1}^{n^I_h} w^I_{t,h} \cdot \mathrm{ReLU}\!\big(\mathbf{q}^I_{t,h} \cdot K^{\text{IComp}}_s\big) $$
📖 公式白话翻译

这条打分公式对每对 (query $t$, 超级 KV $s$) 算一个数 $I_{t,s}$,意思就是"这个 query 对这个超级 KV 有多感兴趣"

  1. 每个 indexer head 算一个内积:$\mathbf{q}^I_{t,h} \cdot K^{\text{IComp}}_s$ —— 64 个 head 各算一次;
  2. 过 ReLU:负相关的直接归 0,保留正相关那部分原始强度;
  3. head-wise 加权:$w^I_{t,h}$ 是模型学的"哪些 head 更可信",重要 head 票权更大;
  4. 所有 head 累加得到最终分数。

ReLU 在这里作用于每个 indexer head 的 query-key 内积:负值被截为 0,正值再乘以该 head 的动态权重并求和。报告给出了这一公式,但没有把选择 ReLU 的原因归结为 softmax 的内存占用或长序列“稀薄化”。

不要把它解释成“softmax 稀薄化”

对同一组原始分数,softmax 是单调变换,top-k 排名不会因为归一化而改变;实现也不必长期保存所有概率。因此,仅凭“概率绝对值变小”不能解释论文为何在 Indexer 中使用 ReLU;报告也没有给出 ReLU 与 softmax 的独立消融。

$w^I_{t,h}$ 是head-wise 重要性,让模型学会哪些 indexer head 更可信。最终 top-$k$ 选择:

$$ C^{\text{SprsComp}}_t \;=\; \big\{\, C^{\text{Comp}}_s \,\big|\, I_{t,s} \in \mathrm{Top}\text{-}k \,\big\} $$

V4-Pro 取 $k = 1024$,V4-Flash 取 $k = 512$。

5. 为什么 $k$ 取 1024、$m$ 取 4

$k\cdot m$ 可用作压缩条目所覆盖原始位置数量的粗略尺度:Pro 为 4096,Flash 为 2048。但每个条目是学习型聚合,不能把这个乘积解释成模型逐 token 精读的数量。

尺度律 · $k$ 和 $m$ 各司其职
  • 论文配置:两款模型均取 $m=4$;Pro 的 $k=1024$,Flash 的 $k=512$。
  • 可解释的权衡:增大 $m$ 会减少候选条目但提高单条目的压缩损失;增大 $k$ 会提高核心注意力成本并保留更多候选。
  • 报告没有公布“4096 已足够”、Indexer 上限为 250K 或 1M–10M 尺度律公式,因此不把这些解释成实验结论。

6. Shared-KV MQA + Grouped Output Projection

top-$k$ 选完后,每 query 要在 $k=1024$ 个超级 KV 上做核心 attention。还有两个工程问题要处理。

问题 ①:标准多头 attention 每个 head 一份 KV,太重

解法是 MQA(Multi-Query Attention):所有 $n_h=128$ 个 query head 共享一份 KV。这样 KV 计算与缓存只算一次,head 间的差异完全靠 query 表达。

"Shared-KV" 还有一层意思:indexer 与核心 attention 共享同一个 query latent $\mathbf{c}^Q$ —— 不需要为 indexer 单独投一份 query,省一次大矩阵乘。

问题 ②:输出投影维度爆炸

核心 attention 的 head 总输出维度是 $n_h \cdot c = 128 \times 512 = 65536$。直接投回 $d=7168$ 需要的参数:

$$ 65536 \times 7168 \;\approx\; 4.7 \times 10^{8}\;\;\text{参数 / 层} $$
数值演练 · 直接输出投影的参数量 61 层每层 4.7 亿参数 = $\mathbf{2.87 \times 10^{10}}$ = 287 亿参数 / 仅输出投影
V4-Pro 的总参数是 1.6T,不是 800 亿;287 亿约占总参数的 1.8%。不过这部分是每个 token 都会经过的稠密投影,其计算负担不能用占总 MoE 参数的比例来衡量。报告只定性称直接投影成本很高。

Grouped Output Projection:把 $n_h$ 个 head 切成 $g=16$ 组,每组先投到 $d_g=1024$ 维,最后 concat 16 组共 $g \cdot d_g = 16384$ 维,再投到 $d=7168$。

$$ \begin{aligned} \text{Step 1:}\;\; & \mathbf{o}_g \;=\; \mathrm{concat}_h(\text{head}_h)_{h \in \text{group}\,g} \cdot W^{O,1}_g, \quad W^{O,1}_g \in \mathbb{R}^{(n_h/g \cdot c) \times d_g} \\ \text{Step 2:}\;\; & \mathbf{o} \;=\; \mathrm{concat}_g(\mathbf{o}_g) \cdot W^{O,2}, \quad W^{O,2} \in \mathbb{R}^{(g \cdot d_g) \times d} \end{aligned} $$
📖 公式白话翻译

"分组输出投影"做的事情就是把一次大矩阵乘拆成两层小矩阵乘,两层之间夹一个"瓶颈":

  • Step 1(组内压缩):把 128 个 head 分 16 组,每组 8 个 head(合计 $8 \times 512 = 4096$ 维)通过 $W^{O,1}_g$ 投到 $d_g=1024$ 维 —— 每组是一次小 4096→1024 投影,16 组并行;
  • Step 2(跨组融合):把 16 组的 1024 维结果 concat 成 16384 维,再用 $W^{O,2}$ 投回 $d=7168$ —— 这一步让组与组之间能互相通信

这是一种带分组第一阶段的两级投影:原本 65536 → 7168 一步完成,现在各组先降到 1024 维,拼接为 16384 维后再投回 7168。报告说明其目的是降低直接输出投影的计算负担,但没有给出与直接投影的独立精度消融。

参数量:$g \cdot (\tfrac{n_h}{g} c \cdot d_g) + (g d_g) d = 16 \times (8 \cdot 512 \cdot 1024) + 16384 \cdot 7168 \approx 67.1\text{M} + 117.4\text{M} = 184.5\text{M}$。相比直接投影的约 469.8M,按矩阵尺寸估算减少约 60.7%

7. Sliding Window 分支:保住最近 $n_{\text{win}}$ 个 token 的细节

压缩 + top-$k$ 有个隐性弱点:最近的几十个 token 大概率被压在某一两个超级 KV 里,细粒度局部信息被融掉。但这些 token 对生成连贯性最关键。

CSA 给每个 query 额外加一条 SWA(Sliding Window Attention)分支:

$$ \mathbf{o}_{\text{CSA}}(t) \;=\; \mathrm{Attn}\big(q_t, \mathrm{TopK}(C^{\text{Comp}})\big) \;+\; \mathrm{Attn}\big(q_t, \{K_{t-n_{\text{win}}+1:t}, V_{t-n_{\text{win}}+1:t}\}\big) $$

$n_{\text{win}} = 128$(V4-Pro)。这条分支用原始未压缩 KV,dense 跑(128 个 token cheap),与 top-$k$ 结果直接相加。

📖 公式白话翻译

CSA 的最终输出 = "远场注意力(top-$k$ 个超级 KV)"+ "近场注意力(最近 128 个原始 KV)"

  • 远场:top-$k$ 选出来的 1024 个超级 KV,承载 1M 范围内的"哪些段相关";
  • 近场:滑窗 128 个原始未压缩 KV,承载"上一句 / 上一段的细节"。

只比较 query-key 点积项,近场分支处理 128 个原始 token,约为 $128 \times n_h \cdot c \approx 8.4 \times 10^6$ 次乘加量级;核心分支处理 1024 个压缩条目,对应约 $6.7 \times 10^7$。这项简化比较约为 1/8,但没有计入投影、索引、访存和其他 kernel,不能直接当作端到端耗时比例。

8. 代价 / 还没解决的问题

  • Indexer 是近似:低秩 + ReLU 的打分有可能漏掉真正相关的远端 token。这是 sparse attention 系列方法的共性失误模式,CSA 没根本解决,只是把"漏选率"做到很低。
  • 压缩权重训练目标与最终任务对齐有 gap:$Z^a, Z^b$ 是与主 loss 一起端到端训练的,目标是下游任务好不是检索准。所以超级 KV 不是 retrieval 优化解。
  • Indexer 有额外开销:它需要扫描压缩 key。报告没有给出 5% 的比例,也没有说推理时会按序列长度动态切回 dense;预训练则是在 dense warmup 后于 64K 阶段引入 sparse attention。
  • Top-$k$ 的非 differentiable 性质:选哪 $k$ 个本身是离散决策。V4 的处理方式是"在前向选 top-$k$,反向不传梯度到选择本身,只传给被选中的 KV"—— 这是稀疏 attention 流派的标准做法,但意味着 indexer 的训练信号比理想稀薄。

CSA 先把 KV 压缩为约 $n/m$ 个条目,再由 Lightning Indexer 为每个 query 选择 top-$k$,核心注意力只读取入选条目。Shared-KV MQA、Grouped Output Projection 与 128-token SWA 分支进一步控制参数和局部信息损失。需要注意:压缩条目不是原始 token,Indexer 扫描仍带来与 $n/m$ 成正比的成本,top-$k$ 选择也可能漏掉相关内容。

flowchart LR H["Hidden States
n × d"] --> TC1["Token-Level
Compressor (a/b 双流)"] TC1 --> CC["Compressed KV
n/m × c"] CC --> TC2["Compressor 2
(为 indexer)"] TC2 --> IK["Indexer Keys"] Q["Query Token h_t"] --> Down["Latent c^Q"] Down --> IQ["Indexer Queries"] IK --> Score["ReLU 打分"] IQ --> Score Score --> TopK["Top-k Selector
k=1024 (Pro)"] CC --> TopK TopK --> SelComp["Selected Compressed KV"] Down --> CoreQ["Core Queries"] SelComp --> Core["Shared-KV MQA"] CoreQ --> Core Win["Sliding Window
n_win=128 raw KV"] --> Core Core --> GOP["Grouped
Output Projection"] GOP --> Out["CSA 输出"] classDef hot fill:#71a4e1,stroke:#71a4e1,color:#fff class TopK,Core,GOP hot

图 4-2 · CSA 完整流程。Lightning Indexer 是"大脑",Grouped Output Projection 是"压缩器",Sliding Window 是"短期记忆"。