DeepSeek-V4.1 模型结构(3):序列维度(下),Hierarchical Sparse Indexer 与 FP4 KV cache

DeepSeek-V4.1 模型结构(3):序列维度(下),Hierarchical Sparse Indexer 与 FP4 KV cache

💡 原文中文,约15200字,阅读约需37分钟。
📝

内容提要

DeepSeek-V4.1 采用分层稀疏索引器降低解码开销:第20层先全量打分,选出最多16384个候选位置,后续Reindex层仅在候选池内打分,使计算量与上下文长度无关。同时,main KV改用FP4存储,每token从584字节压缩至288字节,并结合跨层共享,全局KV总量从V4-Flash的3514字节降至890字节。

🔎

延伸解读

候选池如何降低解码计算量

在长上下文解码时,Reindex 层原本需要为每个 token 给所有可见 KV 打分,计算量随上下文线性增长。Hierarchical Sparse Indexer 让第 20 层先全量打分并选出最多 16384 个候选位置,后续 Reindex 层只在这个固定大小的池内打分,使这部分计算量与上下文长度无关。1M 上下文下,4 个 Reindex 层的打分量从约 1.7×10^10 次乘加降至 2.7×10^8 次,整体 indexer 计算量约为无候选池时的四成。

FP4 量化如何压缩 KV 存储

V4.1 将 main KV 从混合精度(448 维 FP8 + 64 维 BF16)改为 FP4 存储,每条从 584 字节压缩到 288 字节。FP4 采用 E2M1 格式,配合每 16 个通道一个 E4M3 scale,使每个数平均占 4.5 bit。相比 indexer K 使用的 E8M0 scale,E4M3 能更精确地逼近理想 scale,充分利用 E2M1 的 8 档分辨率。结合跨层共享,全局 KV 总量从 V4-Flash 的 3514 字节/token 降至 890 字节/token。

实现细节与边界条件

候选池机制仅在 decoder 中生效,第 20 层和 encoder 的 Full 层仍全量扫描。候选池是一张每个 query 独立的布尔表,最新位置所在的块被强制保留。当上下文短于 16384 个 token 时,候选池不起剪枝作用。参考实现中 Reindex 层仍先计算全部位置分数再用表遮盖,实际省计算需依赖部署 kernel 只取池内 KV。此外,FP4 的 main KV 在读取时会反量化,不参与 4 bit 乘法,滑窗 KV 仍保留 FP8。

❓

Q&A

DeepSeek-V4.1 的 Hierarchical Sparse Indexer 是怎么降低解码时 indexer 打分计算量的?

它分两级:第 20 层先对全部可见位置打分,选出 top-512,同时把位置按每 8 个一块分组,取每块最高分,保留分数最高的 2048 块,形成最多 16384 个位置的候选池;后续第 24、28、32、36 层(Reindex 层)只在这个候选池内用自己的 indexer query 重新打分选 top-512。这样 Reindex 层的打分量从随上下文长度线性增长变为常数(16384×4096≈6.7e7 次乘加),1M 上下文下是原来的 1/64。

候选池在实现上有哪些关键细节?

根据官方代码 select_candidate_blocks:1) 最新的一块强制保留,因为当前 query 最新的位置所在的块可能还没填满,会被更早的满块比下去,所以把它的分数设成正无穷;2) 候选池是一张布尔表,每个 query 一张,用它的层把池外位置的分数设成负无穷再取 top-512;3) 上下文短的时候(块数不到 2048)候选池就是全部位置,超过 16384 个 token 后才真正开始剪枝。

DeepSeek-V4.1 的 main KV 为什么改用 FP4 存储?精度如何保证?

改用 FP4 是为了减少存储:每条 main KV 从 584 字节压到 288 字节。精度保证措施包括:使用 E2M1 格式(1 位符号、2 位指数、1 位尾数)表示 15 个值;每 16 个数配一个 E4M3 的 scale(比 E8M0 更精确,能几乎正好等于 max|x|/6);去掉 NVFP4 的全局 scale,因为 E4M3 最大值 448 与 E2M1 最大值 6 相乘得 2688,远大于 RMSNorm 后每个分量绝对值上界 22.6(实测最大约 10),范围足够;RoPE 不改变这个界;并通过量化感知训练(QAT)在 post-training 阶段引入,性能只有很小下降。

DeepSeek-V4.1 的全局 KV 每 token 890 字节是怎么算出来的?和 V4-Flash 的 3514 字节相比如何?

V4.1-Flash 每个 Full 层存一条 main KV(288 字节)和一条 indexer K(68 字节),共 356 字节。encoder 的 3 个 Full 层 m=2,每 token 1.5 条,贡献 1.5×288=432 字节 main KV 和 1.5×68=102 字节 indexer K;decoder 第 20 层 m=1,贡献 288+68=356 字节。合计 432+102+288+68=890 字节/token。V4-Flash 为 3514 字节/token(21 层 CSA m=4 贡献 3066+357,20 层 HCA m=128 贡献约 91),V4.1-Flash 是其约 1/3.9。

Hierarchical Sparse Indexer 在训练时也生效吗?它只用在哪些层?

论文说这个机制是 training-aware 的,在 post-training 阶段引入,训练和推理用同样的候选范围,深层的 indexer 是在它推理时面对的那个范围里被优化的。它只用在 decoder,目的是减少 decode 时的重复打分。第 20 层自己仍然全扫(候选池是它扫完才有的),encoder 的 3 个 Full 层也仍然全扫,因为 encoder 里没有 Reindex 层。

FP4 的 main KV 和 indexer K 在格式与使用方式上有什么不同?

两者都用 E2M1 的 4 bit 数,但 scale 格式不同:indexer K 每 32 维一个 E8M0 scale(只能是 2 的整数次幂),每个数平均 4.25 bit;main KV 每 16 个数一个 E4M3 scale(普通 FP8 数),每个数平均 4.5 bit。使用方式上,indexer 的 FP4 是为了算得快,query 和 K 直接用 4 bit 做矩阵乘法,需要硬件原生支持 MXFP4;main KV 的 FP4 是为了存得少,读出来先反量化成更高精度再做注意力,不参与 4 bit 乘法。滑窗 KV 则保留 FP8。

🏷️

标签

➡️

继续阅读