稀疏注意力:选择与压缩机制比较#
本页内容5 个部分
1. Full Attention#
1.1 想解决什么问题#
输入是一个 token 序列 (比如 "The cat sat on the mat") , 经过 embedding 后变成:
每个 是一个 维向量, 代表第 个 token 的当前表示。
核心问题: 当模型处理 "sat" 这个词时, 它的含义其实依赖于 "cat" (谁在 sat) 和 "mat" (sat 在哪) 。所以我们需要一种机制, 让 能从其他位置 那里 "取信息"。
- RNN: 按时间步顺序传递信息 (慢, 远距离信息容易丢)
- CNN: 固定窗口卷积 (窗口外看不见)
- Attention: 每个位置都直接和所有位置算一遍相似度, 按相似度加权融合
1.2 Q / K / V: 数据库的软查询#
类比一下数据库:
| 数据库 | Attention |
|---|---|
| 你输入一个查询 (query) | : 位置 "想找什么" |
| 数据库每行有一个 key | : 位置 "我提供什么标识" |
| 匹配上的行返回 value | : 位置 实际 "装了什么内容" |
| 硬匹配: 要么返回要么不返回 | 软匹配: 按相似度加权返回所有 value |
所以 attention 是个可微的、软的数据库查询。
怎么从 得到 Q/K/V?#
三个不同的线性投影:
其中 是学出来的参数矩阵。
为什么要三个不同的投影? 因为同一个 token 在 "我想找什么" (query) 、"我能被找到什么" (key) 、"我能提供什么" (value) 这三种角色下应该呈现不同的特征。让模型自己学这三个投影是 attention 最关键的设计决定。
形状:
1.3 Attention 的前向, 一步一步#
Step 1: 算所有 (query, key) 对的相似度#
= 位置 的 query 和位置 的 key 的点积。这就是 的来源: 你算了一个 的矩阵。
Step 2: 除以 (scaled)#
为什么? 当 大时, 是 项加起来的, 方差是 。如果不除, 数值会很大, 后面 softmax 会被推到极端 (几乎 one-hot) , 梯度就消失了。除以 把方差拉回 。
Step 3: Causal mask (只看 LM)#
在自回归语言模型里, 位置 不能看到未来的 。把上三角设为 :
经过 softmax 后这些位置就变成 0。
Step 4: Softmax (按行)#
, 每一行加起来等于 1。 就是 "位置 给位置 分配多少注意力"。
Step 5: 用注意力权重加权 value#
第 行: —— 把所有位置的 value 按 给出的权重加权平均。
Step 6: 输出投影#
回到 , 可以接残差连接、下一层。
一行公式总结#
1.4 Multi-Head Attention: 并行多个 attention#
动机: 单个 attention 只能学到一种 "关注模式"。你可能想同时关注:
- 语法结构 (主语在哪)
- 语义关联 (代词指代谁)
- 位置远近
- ...
做法: 把 维拆成 个 head, 每个 head 维度 , 并行跑 个独立的 attention, 然后把结果在 维度拼起来。
每个 head 有自己的 。
总参数量和单 head 差不多, 但模型获得了 种不同视角的能力。典型配置: 。
1.5 KV Cache: 推理时的关键优化#
自回归生成时, 你一个 token 一个 token 地往外吐:
step 1: 输入 [x1] → 生成 x2
step 2: 输入 [x1, x2] → 生成 x3
step 3: 输入 [x1, x2, x3] → 生成 x4
...
每次都要重算所有 key 和 value, 但 的 K 和 V 上一步就算过了, 且不会变 (因为前面的 token 不依赖未来) 。
KV cache = 把已经算出的 缓存起来, 新 token 来时只算它自己的 , append 到 cache 上。
代价: cache 占显存。每层、每个 head 都要存, 长度 时:
(2 是 K 和 V 各一份)
这就是为什么 1M context 这么贵 —— KV cache 会爆显存。
1.6 复杂度的两面: 训练 vs 推理#
| 计算量 | 内存 (KV cache) | |
|---|---|---|
| 训练 (并行处理整段序列) | 不需要 cache | |
| 推理 prefill (一次吃完 prompt) | 写入 | |
| 推理 decode (逐 token 生成) | 每步 | 读取 |
无论哪一面, 一大就爆。这就是为什么需要 sparse attention:
- 训练时: 把 降到 (NSA、DSA 都是干这个)
- 推理时: 压缩 KV cache (MLA、CSA、HCA 都涉及)
1.7 一句话总览#
Attention = 每个 token 用自己的 , 在所有 上算相似度 → softmax → 加权融合所有
MHA = 多个 attention 并行, 每个看不同模式
KV cache = 推理时缓存已算过的 , 用显存换计算
核心瓶颈 = 是 , 长上下文必须想办法 sparse / compress
2. NSA (Native Sparse Attention)#
DeepSeek 2025-02 的论文 (arXiv:2502.11089) 。它提出面向硬件效率、支持端到端训练的原生稀疏注意力方案;不把它表述为整个领域的“第一个”方案。
2.1 出发点: full attention 的瓶颈, 以及它给的提示#
Full attention 在长序列上是 。但仔细看注意力矩阵 :
经验上, 一个 query 的注意力分布高度集中 —— 通常 99% 的概率质量落在不超过几百个 key 上, 剩下的全是接近 0 的"无效计算"。
这给了一个明显的优化方向: 别算那 99% 的接近-0 的项。但有两个挑战:
- 怎么知道哪些项接近 0? —— 你得算了才知道。Sparse attention 的核心难点就是 "不算就预测哪些 key 重要"。
- 能不能端到端训练? —— 早期方案 (BigBird、Longformer) 用手工预设的稀疏模式, 推理快, 但 sparse 模式不参与学习, 精度有限。
NSA 的回答: 用一个 "compressed 视角" 先粗略扫一遍, 根据这个粗略结果决定哪些细粒度位置值得精确算。整个过程可微, 可以端到端训练。
2.2 三路并行: 总览#
对于每个 query 位置 , NSA 同时跑三条 attention 分支:
┌────────────────────────────────────────┐
│ │
q_t ────┼──→ Compress branch (粗略 + 全局) ──┐
│ │
├──→ Select branch (精确 + 选择性) ──┼─→ 门控融合 → o_t
│ │
└──→ Window branch (本地 + 永远看) ──┘
每条分支只看序列的一小部分, 但合在一起覆盖了 "全局粗略 + 局部精准 + 本地兜底" 三个层次。
2.3 三条分支的细节#
Compress branch: 粗略的全局视角#
做法: 把 KV 序列按 block 长度 (典型 ) 分成段:
每个 block 通过一个学出来的压缩函数 (就是个小 MLP) 压成 1 个 token:
(注意 的 和 的 是两个不同的学习模块, 不共享参数。)
压缩后, query 对全部 个压缩 entry 做 dense attention:
复杂度: 。当 时, 这一支比 full attention 快 32 倍。
作用: 给 一个全局但模糊的视野 —— 它知道 "远处某个 block 大致讲了啥", 但精度有限 (因为一个 block 被压成 1 个向量了) 。
Select branch: 精确的稀疏定位#
这是 NSA 最关键、最巧妙的一步。
问题: 我想在某些 block 里看原始 token (精确), 但只看少数几个 block (稀疏)。怎么决定挑哪几个 block?
NSA 的做法: 复用 compress branch 的注意力分数。
具体来说: 在 compress branch 算 softmax 时, 我们已经得到了 对每个压缩 block 的注意力 。这个 正好就是 "block 整体对 的重要程度" —— 直接拿来当 block 的 "重要性分数"。
然后 top- 选最重要的 个 block (典型 ):
被选中的 block 里, query 对原始 (未压缩的) token 做 dense attention:
其中 是 selected blocks 里的原始 KV (总长度 ) 。
复杂度: , 典型 , 比 full attention 快 倍 ( 时快 256 倍) 。
关键设计: block 选择本身不需要额外算分, 免费复用 compress branch 已经算好的注意力。这就是 "native" 的含义 —— 稀疏机制天然 baked-in, 不是事后挂上去的。
训练时怎么传梯度过 top-n? top- 选择本身是离散的、不可微的。但 NSA 不需要让 selection 可微 —— 它通过 compress branch 的连续注意力分数 来训练 "该选哪些块"。也就是说, 梯度通过 compress 分支流回 等参数, 这些参数学出来 "压缩特征", 而 top- 只是依赖这些特征的一个确定性后处理。
Window branch: 本地兜底#
做法: 无论上面两支怎么选, 最近 个 token (典型 ) 永远被精确看到:
为什么需要这一支?
- 语言里最近几十到几百个 token 几乎总是相关的 (代词指代、句法关联、刚提过的实体)
- 但 compress branch 把它们和远处一样压成块了, 丢精度
- Select branch 可能因为某个 query 觉得远处更重要而跳过本地
- Window branch 强制兜底 —— 本地永远 dense 看, 绝不漏
复杂度: , 固定大小, 跟 无关。
2.4 三路融合: 门控#
每条分支都给出一个 维向量。怎么合?
NSA 用一个学出来的门控 —— 对 过一个小 MLP, 输出 3 个 sigmoid 标量 :
注意每个位置的门控都不一样 —— 每个 query 自己决定 "我现在更需要全局还是局部还是本地"。这是动态、依赖输入的 (input-dependent) 。
2.5 "三路并行" 严格说不完全独立#
注意 select 依赖 compress 的 softmax 分数, 所以不是数据流意义上的完全独立。真实计算图:
┌─→ compress(t) ─┬─→ attn_cmp(t) ──┐
│ │ │
q_t ────────┤ └─→ scores p_b │
│ │ │
│ ↓ ├─→ 门控融合 → o_t
│ select top-n blocks │
│ ↓ │
│ attn_sel(t) ───────┤
│ │
└─→ attn_win(t) ──────────────────────┘
但这是结构意义上的并行 (类似 MHA 的多 head 并行):
- 横向几个分支, 不是纵向几层 attention 串起来
- compress → select 只是 1 跳的浅依赖, GPU launch overhead 可忽略
- window 跟 compress 真正完全独立, 可以放不同 stream
- 每层只建一次 (不是每个 query 重算), 所有 个 query 共享
所以 "并行" 的准确含义是: 三路是 "宽" 的而不是 "深" 的, 它们汇总成一个输出, 而不是串成一个深链。
2.6 复杂度对比#
设 :
| 分支 | 每个 query 的计算量 | 时 |
|---|---|---|
| Full attention | ||
| Compress | ||
| Select | ||
| Window | ||
| NSA 总和 |
对比 full attention 加速 , 越大优势越明显。
2.7 Hardware-aligned: 为什么这样设计是 "硬件友好" 的#
NSA 论文花了大量篇幅讲 GPU kernel 设计。核心问题是 arithmetic intensity (计算访存比):
- Full attention 在 GPU 上其实是访存瓶颈, 而不是算力瓶颈 —— 太大, 从 HBM 读到 SRAM 比算 还慢
- 朴素的 sparse attention (随机选 token) 会让访存模式完全不规则, GPU 缓存命中率暴跌, 反而比 full attention 还慢
NSA 的三路设计全部都是 block-wise 的:
- Compress branch: 按 block 压, 访存连续
- Select branch: 选整块 block (不是单 token), 每块 个 token 在内存里挨着, 可以一次性读入 SRAM
- Window branch: 连续的最近 个 token, 完美的连续访问
这让 sparse 不只是理论上 FLOPs 少了, 实测 wall-clock 时间也真的快了 (64K 序列上 forward / backward 都比 full attention 快几倍) 。
2.8 跟 full attention 的关系#
可以把 NSA 看作是 "近似 full attention" 的一种方式:
- Full attention 的注意力矩阵 经验上高度集中 → 大部分项接近 0
- NSA 用三个互补的低秩 / 稀疏视角 (compress / select / window) 去近似 真正非零的部分
- 三路加权和 真正的 , 但只算了一小部分
所以 NSA 的精度损失是轻微的 —— 论文实验显示在大模型上 NSA 训出来的精度甚至略好于 full attention (可能因为 sparse 起到了正则化作用)。
2.9 一句话总览#
NSA = 三路并行的可微稀疏 attention: compress 分支给全局粗略视野 (并顺便给出 block 重要性分数), select 分支用这个分数挑 top- 个 block 看原始 token (精准), window 分支强制看最近 个 token (本地兜底)。三路通过学出来的门控融合。
关键创新: 稀疏选择天然集成在压缩分支里 (免费复用注意力分数), 不是事后挂的。block-wise 设计让稀疏在硬件上真的快, 不是只在 FLOPs 上快。
代价: 多三个分支带来一些 overhead; 需要写定制 kernel。
后继: V3.2 的 DSA 把这个简化成了 "lightning indexer + top-k MLA", 牺牲了 compress 分支的全局视野; V4 的 CSA + HCA 又把全局视野通过另一种方式补回来了。
3. DSA (DeepSeek Sparse Attention)#
V3.2 (2025-09) 引入, 把 NSA 思路工业化。核心变化: 砍掉三路并行, 只保留 "select" 这一路, 但选得更准更快, 并和 V3 的 MLA 整合在一起。
3.1 为什么要简化 NSA?#
NSA 在学术上很漂亮, 但工业部署有几个不爽:
- 三路 = 三套 kernel —— compress、select、window 都需要定制 GPU kernel, 工程维护成本高
- Compress 分支的 overhead 不小 —— 给每层每个 token 都建 , 开销在长序列下不可忽略
- 跟现有 KV cache 优化的耦合 —— V3 已经用 MLA 把 KV cache 压到了 1/10, NSA 的 compress 跟 MLA 怎么共存是另一个工程问题
DSA 的取舍: 砍掉 compress 分支 (承认丢了一部分全局视野) , 保留最有效的 select 分支, 把它做到极致。
3.2 先快速过一下 MLA (DSA 的底座)#
DSA 是基于 MLA 改的, 所以得先知道 MLA 是什么。
MLA = Multi-head Latent Attention (V3 引入)#
MHA 的 KV cache 大小 。现代 LLM 这一项非常大 —— 70B 模型在 128K 上, KV cache 可能就要 100GB。
MLA 的核心想法: 和 的信息量并没有 维那么大, 可以投到一个低维 latent 空间:
其中 (典型 ) 。
推理时, cache 的不再是 和 , 而是 —— 一个低维 latent。每次需要算 attention 时, 实时从 升回 和 :
节省: KV cache 从 变成 , 几乎 1/10。
代价: 需要额外的"上投影"矩阵 , 计算稍微多一点; 需要数学技巧把 attention 整合成"在 latent 空间里直接算"以保持高效。
MLA = KV 用低维 latent 缓存, 推理时升回来, KV cache 显存大幅下降
DSA 的稀疏机制就是叠加在 MLA 之上的 —— 不是替换 MLA, 是在 MLA 的基础上再加一层 sparse 筛选。
3.3 DSA 的两个组件#
x_t → q_t ← 跟 MHA 一样
↘
Lightning Indexer → 得分 s(q_t, j) for all j < t
↓
挑 top-k (典型 k=2048)
↓
x_t → c_t (latent KV) ──→ MLA Attention 仅在选中的 k 个 KV 上跑
↓
o_t
两个核心模块: Lightning Indexer 和 Top-K MLA。
3.4 Lightning Indexer: 轻量、FP8 的 "哪些 token 重要" 打分器#
它的角色#
Lightning Indexer 不参与最终 attention 计算, 它只做一件事:
给每个 (query , preceding token ) 算一个索引分数
这个分数告诉真正的 attention "你应该看 还是不看 " 。打分完, top-k 选出来交给 MLA。
为什么叫 "lightning"#
整个 indexer 设计得极便宜, 以至于不构成瓶颈:
| 维度 | full MLA attention | lightning indexer |
|---|---|---|
| Head 数 | 多 (典型 128 heads) | 少 (典型 4 heads) |
| 维度 | (每 head 128 维) | 极低维 |
| 数值精度 | BF16 / FP16 | FP8 |
| 输出 value | 输出加权 () | 不输出 , 只输出标量分数 |
省下了:
- heads
- 数值精度
- 整个 矩阵相关的计算和访存 (因为 indexer 不需要 )
实测 indexer 的 FLOPs 和访存大约是真正 MLA 的 1/100, 是真 · "lightning" 。
怎么算分数#
简化版的写法:
注意: 这个不再除 , 也不 softmax, 只是个排序用的相对分数。
这一步在 FP8 下做大矩阵乘 —— 非常快。
注意 indexer 用的 完全是独立学出来的另一套投影, 不是真正 attention 的 复用。这是它和 NSA 的关键差别 (NSA 复用 compress 分支的 attention 分数, DSA 用独立的小模型) 。
为什么这能学出 "重要性"#
直觉上, lightning indexer 是一个学出来的近似 attention —— 它的目标是模仿 full attention 会给哪些 高分。只要它对 top-k 的预测够准 (不必每个分数都准, 只要排名靠前的几个对就行) , 就够用。
实测: indexer 选出的 top-2048 token, 跟 full attention 真正最关注的 top-2048 重合度很高, 能保留 >99% 的注意力质量。
3.5 Top-K + MLA Attention#
打分完, DSA 选 top-k (默认 , 在 128K context 中) , 然后让 MLA 只在这 k 个 token 上做完整 attention:
其中 是从 latent 空间升回来的 和 , 只对选中的 k 个位置升。
复杂度#
| 复杂度 | 时 | |
|---|---|---|
| Full MLA attention | ||
| Lightning Indexer | () | |
| Top-K MLA | ||
| DSA 总和 |
对比 full MLA 加速 ; 在 1M context 时差距更大。
3.6 训练: Warm-up + Switch-over#
DSA 的训练有个工程细节, 值得专门讲: lightning indexer 不能从零开始训。
问题: 如果一上来就用 indexer 选 top-k, 然后只在 top-k 上算 attention ——
- Indexer 一开始是随机初始化的, 选出来的全是垃圾
- MLA 在垃圾 token 上算 attention, 输出全是垃圾
- 模型 loss 巨大, 梯度信号被噪声污染
- Indexer 永远学不出来 "哪些重要"
DeepSeek 的解法: 两阶段训练。
阶段 1: Warm-up (把 indexer 训到能用)#
- 全模型其他参数冻结 (不动)
- attention 仍然跑 full (dense) 的 MLA, 作为 "教师信号"
- Lightning indexer 跟着跑, 但不影响实际输出
- 训练目标: 让 indexer 的分数 回归到真正 MLA attention 的分数
这一步本质是蒸馏: 用真正 attention 的输出作为标签, 把 indexer 训成它的轻量近似。
阶段 2: Switch-over (切到 sparse, 继续训)#
- Indexer 训得差不多了 → 切换到 sparse 模式
- attention 现在只在 indexer 选出的 top-k 上算
- 整个模型 (包括 indexer 和原模型参数) 继续训练
- Loss 收敛后部署
这种 warm-up 是 sparse attention 工业化的重要实践 —— 别指望 indexer 和模型同时从零冷启动, 先让 indexer 学个谱, 再让全模型适应 sparse 模式。
3.7 NSA vs DSA: 对比#
| 维度 | NSA | DSA |
|---|---|---|
| 分支数 | 3 (compress + select + window) | 1 (只有 select) |
| 选择机制 | 复用 compress 的 softmax 分数 | 独立的 lightning indexer |
| 选择粒度 | block-level (整块 ) | token-level (单 token top-k) |
| 全局视野 | 有 (compress 分支) | 没有 (被砍了) |
| 局部兜底 | 有 (window 分支强制) | 没有硬保证 (期待 indexer 自己学着选近邻) |
| 跟 KV cache 优化的耦合 | 独立设计 | 跟 MLA 深度整合 |
| 训练 | 端到端单阶段 | warm-up + switch-over 两阶段 |
| 工程复杂度 | 三套 kernel | 一套 kernel, FP8 优化 |
| 部署成熟度 | 学术原型 | 已部署到 V3.2 量产模型 |
关键 trade-off#
DSA 砍掉了 NSA 的 compress 和 window 分支, 在 128K 内问题不大 (因为 indexer 选 2048 个已经能覆盖大部分相关信息) , 但:
- 失去全局视野: 推到 1M context 时, 远处的 token 即使是相关的, 如果不在 top-2048 里就完全看不到 —— 没有 compress 分支提供 "模糊但全局" 的信息
- 失去本地兜底硬保证: 理论上 indexer 应该会把最近的 token 都选上, 但没有强制保证
这正是 V4 的 CSA + HCA 要解决的问题 —— 把丢掉的 "全局粗略" 和 "本地兜底" 用不同方式补回来。
3.8 直觉总结#
可以这样理解 NSA → DSA → V4 的演化:
- NSA: 三个不同视角 hard-coded 在每一层 (compress + select + window)
- DSA: 学一个小神经网络 (indexer) 来动态决定看哪些 token, 只保留 selection; 靠 indexer 的学习能力替代 NSA 的手工设计三分支
- V4 (CSA + HCA): NSA 的"多视角"思想是对的, 但不该挤在同层; 专门化的层 + 交替堆叠, 让每层只做自己最擅长的事
3.9 一句话总览#
DSA = Lightning Indexer (轻量、FP8、不输出 V) + Top-K + MLA。
Indexer 学出来是 "用便宜的方式预测哪些 token 重要" 的小神经网络; 选出 top-k 之后, 真正的 MLA attention 只在 k 个位置算。
关键工程: warm-up 阶段先用 dense attention 蒸馏出 indexer, 再切到 sparse 模式继续训。
代价: 砍掉了 NSA 的 compress (全局粗略) 和 window (本地兜底) 分支, 在 1M 上下文时全局视野不够 —— 这就是 V4 要解决的问题。
一行精确版: DSA = NSA 砍掉 compress 输出 + 砍掉 window 兜底 + 用 lightning indexer 替代 "基于 compress 分数的 block 选择", 并把粒度从 block-level 改成 token-level。余下的就是 "indexer 选 → top-k 个 token → MLA dense attention"。
4. V4 (CSA + HCA)#
V4 的 Hybrid Attention 结合压缩、选择和滑窗。技术报告给出的口径是:V4-Pro 在 1M 上下文下单 token 推理 FLOPs 约为 V3.2 的 27%,KV cache 约为 10%;这是特定配置与序列长度下的比较,不是所有负载的固定比例。
核心思想: 别在同一层挤太多功能, 用整个网络的层堆叠本身来获得多视角。
4.1 出发点: DSA 推到 1M 时的问题#
DSA 在 128K 内工作得不错, 但推到 1M context 时:
- top-2048 在 1M 里只有 0.2% —— 远处的相关 token 大概率被漏掉
- 没有 compress 分支提供 "我知道远处大致讲了什么" 的兜底
- 还是 token-level 选择, 1M 里排序成本本身也变贵
NSA 的回答 (三个互补视角) 在概念上是对的, 但 DSA 砍掉了一些。V4 的回答: 把多视角再加回来, 但用一种 GPU 友好的方式。
4.2 关键设计转向: 从 "层内并行" 到 "层间交替"#
NSA 把三路并行塞在同一层里: 每个 attention 模块同时跑 compress + select + window。
V4 反过来:
每一层只做一种 attention, 但不同层做不同种, 然后交替堆叠。
具体来说, V4 有两种 attention 层:
- CSA 层: 轻度压缩 + sparse top-k (精准定位)
- HCA 层: 重度压缩 + dense (全局粗略)
整个网络: CSA 层、HCA 层、CSA 层、HCA 层 …… 交替出现。
为什么这是好主意?
- 每层 attention 模块结构简单一倍 (只一种, 不是三种并行) → kernel 简单、开销小
- 模型仍然能在每两层之内得到 "精准 + 全局" 两种视角
- 信息在残差流里累积 —— CSA 层精准定位的信息流到下一层 HCA, 被全局视角接着处理, 反过来也成立
4.3 CSA (Compressed Sparse Attention): 精准的稀疏查找#
CSA 在概念上最像 NSA, 但参数选择不同。
三段式#
原始 KV (长度 L)
↓ 学出来的压缩函数 φ, 每 m=4 个 token 压成 1 个
压缩后 KV (长度 L/4)
↓ 类 DSA 的 lightning indexer 打分
↓ top-k 选择 (V4-Flash: k=512; V4-Pro: k=1024)
选中的 k 个压缩 entry → query 对它们做 dense attention
外加:
最近 n_win 个 token 的 sliding window → 永远 dense 看
跟 NSA 比#
| NSA | CSA | |
|---|---|---|
| 压缩率 | (轻得多) | |
| top-k 数 | 16 个 block (block size 32 = 512 token) | 512 或 1024 个压缩 entry |
| 选择粒度 | block-level | 压缩 entry-level (等价于每 4 个 token 一组) |
| 是否有滑窗 | 有 (单独分支) | 有 (单独分支) |
| compress 分支输出 | 加到最终输出 | 不加 (只用来给 indexer 提供素材) |
关键差别:压缩率为 4 时,top-512 个压缩 entry 对应约 2048 个原始 token 的分组范围,但不保留这 2048 个 token 的独立 KV。它与 DSA 选取原始 token 的取舍不同,不能据此断言更精细或精度更高。
一句话直觉#
CSA = 轻度压缩 (几乎不丢信息) + 精准 top-k (只看相关的) + 滑窗 (本地兜底) —— 在每一层提供精确的 sparse 检索能力。
4.4 HCA (Heavily Compressed Attention): 廉价的全局俯视#
HCA 的设计理念跟 CSA 正好相反。
两段式#
原始 KV (长度 L)
↓ 学出来的压缩函数 φ', 每 m'=128 个 token 压成 1 个
压缩后 KV (长度 L/128)
↓ 直接对所有压缩 entry 做 dense attention (不 top-k, 不 sparse)
外加:
最近 n_win 个 token 的 sliding window → 永远 dense 看
注意两个关键点:
- 极激进: 1M token 被压成 压缩 entry
- dense attention, 不 sparse: 既然只剩 8K, 直接 dense 也很便宜
为什么这么设计#
回想 NSA 的 compress 分支: 它用 的压缩率给 query 一个 "全局粗略视野"。HCA 把这个想法推到极致:
- 的极端压缩 → 序列足够短, 可以 dense attention
- Dense 比 sparse 简单 (不需要 indexer、top-k) , kernel 实现极简
- 虽然每个压缩 entry 信息保真度低, 但 query 能看到整个 1M context 的全局摘要
代价: 精度上, 远处某个具体 token 的信息已经被压糊了。但这层不是用来精确定位的, 精确定位交给前一层或后一层的 CSA 去做。
一句话直觉#
HCA = 重度压缩 (信息糊但短) + dense (看全部压缩 entry) + 滑窗 —— 在每一层提供廉价的全局俯视。
4.5 滑窗: CSA 和 HCA 都有#
⚠️ 注意: V4 把 NSA 的 window 分支显式恢复了, 而且两种层都有。
每个 attention 层 (无论 CSA 还是 HCA) 都附带一个最近 tokens 的强制 sliding window 分支。这保证:
- 本地 token 永远 dense 看到, 绝不漏
- 不依赖 indexer 是否选近邻
- 句法、代词指代、最近实体引用都有兜底
4.6 整体架构#
V4 有两个版本:
| V4-Flash | V4-Pro | |
|---|---|---|
| 总参数 | 284B | 1.6T |
| 激活参数 | 13B | 49B |
| 第 1-2 层 | 纯 sliding window | HCA |
| 之后所有层 | CSA / HCA 交替 | CSA / HCA 交替 |
| CSA 的 top-k | 512 | 1024 |
为什么前两层特殊? —— 因为最初几层的特征还没成形, sparse 选择会错得多。Flash 用纯滑窗 (只看近邻) , Pro 用 HCA (全局粗略) 做铺垫, 从第 3 层开始稳定的 CSA/HCA 交替。
4.7 为什么 "层间交替" 比 "层内并行" 好#
NSA 在同一层并行三路。V4 把不同视角分到不同层。后者的好处:
(a) 每层 kernel 极简#
NSA 一个 attention 模块要处理 compress + select + window 三种逻辑, kernel 复杂、bug 多、优化空间小。V4 一个层只一种 attention, kernel 跟普通 attention 几乎一样 (就是 KV 是压缩过的) 。这极大降低了工程复杂度和 kernel 开销。
(b) 残差流自然组合多视角#
回想残差: 每层的输出是 。残差流积累所有层的贡献。
所以 CSA 层算出来的 "精准信息" 和 HCA 层算出来的 "全局粗略信息", 会被自动累加到 里, 后续每层都能看到这两种视角的混合体 —— 不需要在同一层显式 concat 或门控。
(c) 计算分配更灵活#
NSA 三路在同一层意味着每层都要付三路成本。V4 一半的层只付 sparse 成本, 一半只付 dense-on-compressed 成本 —— 总成本是两者各一半的平均, 而不是两者相加。
4.8 为什么这能做到 1M context#
把数字过一遍。设 (CSA) , (HCA) , :
| Per-query 计算量 | 备注 | |
|---|---|---|
| 一层 CSA | 个 KV 上 attention | 不依赖 |
| 一层 HCA | 个 KV 上 dense attention | 跟 线性 |
| 一层 sliding window | 个 KV 上 dense | 不依赖 |
CSA 层的成本几乎跟 无关; HCA 层是 。即使 , HCA 层也只是在 8K 长的压缩序列上做 dense —— 可控。
KV cache: cache 的是压缩之后的 entry, 而不是原始 KV。
- CSA cache: 个 entry
- HCA cache: 个 entry
总和远小于 V3.2 的 MLA cache。论文报告 V4 的 KV cache 是 V3.2 的 10%。
4.9 一段总结演化#
Full attention 一种视角: dense, O(L²)
↓
NSA 三种视角并行, 在同一层
↓ 工业化简化
DSA (V3.2) 一种视角: sparse top-k, 丢了全局
↓ 把多视角加回来, 但分散到层间
V4 (CSA + HCA) 两种视角分别专门化为不同层, 交替堆叠
外加每层都有滑窗兜底
4.10 一句话总览#
V4 = CSA 层 (轻压缩 + sparse top-k + 滑窗, 精准) 和 HCA 层 (重压缩 + dense + 滑窗, 全局) 交替堆叠。
每层 kernel 极简, 残差流自动组合两种视角, KV cache 全用压缩后的 entry 来 cache。
核心思想: 不在同一层做三件事, 而是让网络的层堆叠本身扮演 "多视角组合" 的角色。层间交替 > 层内并行, 因为残差流是免费的胶水。
原始资料:NSA、DeepSeek-V4 技术报告。
延伸阅读#
- DeepSeek-V4:路由评分与残差连接:路由的选择分数、混合权重与残差连接分别承担什么作用?