音乐
暂未播放
DeepSeek-V4 混合注意力完全拆解:CSA + HCA + 滑窗,百万上下文如何便宜 10 倍
背景:百万上下文为什么这么贵#
2026 年 4 月,DeepSeek 发布了新一代模型系列 DeepSeek-V4(技术报告 DeepSeek-V4: Towards Highly Efficient Million-Token Context Intelligence 于 2026 年 6 月公开在 arXiv),包含两个 MoE 模型:DeepSeek-V4-Pro(总参 1.6T、每 token 激活 49B)与 DeepSeek-V4-Flash(总参 284B、激活 13B),两者都原生支持 100 万 token 上下文。V4-Pro 正式版已于 8 月 13 日全量上线。
为什么”百万上下文”需要专门的架构创新?先算一笔账。以 V3.2 的 DSA 配置(MQA 潜空间 KV,头维 512、64 个查询头)为参照,1M 上下文下全注意力(dense attention)的代价是:
- Prefill 计算:注意力分数矩阵是 1,000,000×1,000,000 级别,即使分块,FLOPs 也随序列长度的平方增长;
- KV Cache 内存:每层要为 100 万个 token 各存一份 K/V。用最朴素的 GQA-8(8 个 KV 头、头维 128、BF16)配置算,单层 KV 就是 106×8×128×2×2=4,096 MB——一层 4 GB,61 层就是 244 GB,远超单卡显存,只能靠多卡切分或 offload 硬扛;
- Decode 带宽:解码阶段每个新 token 都要把全部历史 KV 从 HBM 读一遍(即使只做 top-k 选择,也要先扫描索引),上下文越长,单 token 延迟越高。
V3.2 的 DSA(DeepSeek Sparse Attention,站内 NSA 系列第二篇 讲过它的落地)用”闪电索引器打分 + top-k 选 2048 个 token”把注意力计算降下来了,但 KV Cache 还是要按原始 token 存储——1M 上下文下每层仍有 100 万个 KV 条目,索引器自己也要为每个 token 存一份键。
V4 的回答是:与其选哪些 token 参与注意力,不如先把 token 压成更少、更高质量的条目,再在这个”压缩后的世界”里做稀疏选择。 这就是本文要拆解的混合注意力架构——CSA(Compressed Sparse Attention,压缩稀疏注意力)与 HCA(Heavily Compressed Attention,重度压缩注意力)交错排布,外加滑窗分支。论文给出的效率数字是:1M 上下文下,V4-Pro 的单 token 推理 FLOPs 只有 V3.2 的 27%,KV Cache 只有 V3.2 的 10%;V4-Flash 更进一步,FLOPs 降到 10%、KV Cache 降到 7%。相对”BF16 + GQA8 + 128 头维”这种常见稠密基线,V4 系列的 KV Cache 只有它的约 2%。
本文只讲推理侧最核心的部分:混合注意力的每个公式、每个设计决策,以及它给推理系统(KV Cache 布局、磁盘缓存、内核设计)带来的连锁改造。训练侧的 mHC 连接与 Muon 优化器不在本文范围。
总体架构:两条压缩路径 + 一条滑窗#
先看 V4 系列的整体结构(技术报告 Figure 2):

图里的关键信息有三层:
- 注意力层不是一种,而是两种交替。V4-Pro 共 61 层:前 2 层是纯 HCA,之后 CSA 与 HCA 交错排布;V4-Flash 共 43 层:前 2 层是纯滑窗注意力(SWA),之后同样 CSA/HCA 交错。也就是说,模型中同时存在”压缩率 4:1 的稀疏路径”和”压缩率 128:1 的稠密路径”;
- FFN 仍然用 DeepSeekMoE:共享专家 + 细粒度路由专家(V4-Pro 是 1 个共享专家 + 384 个路由专家、每 token 激活 6 个,V4-Flash 是 1 + 256、激活 6 个)。前 3 个 MoE 层改用 Hash 路由(按 token id 的哈希函数决定专家,不经过路由网络,零路由开销)——这是从 V3 继承的配置;
- 残差连接换成了 mHC(Manifold-Constrained Hyper-Connections):把残差流宽度从 d 扩到 nhc×d(nhc=4),并要求层间映射矩阵落在双随机矩阵流形上(Sinkhorn-Knopp 归一化)。这是为深层稳定训练服务的架构件,推理时它只是多两条线性映射,成本极低,本文不展开。
另外两处继承自 V3 的推理相关设计:MTP(Multi-Token Prediction)深度为 1——推理时 MTP 模块兼当自投机解码的草稿模型(站内 MTP 完全拆解 已讲过机制,V4 原样沿用);以及 FP8/FP4 混合精度部署。
CSA:压缩稀疏注意力#
CSA 是 V4 的主力注意力,思路一句话:先把每 4 个 token 的 KV 压成 1 个条目,再用 DSA 式的稀疏选择挑出每个 query 最相关的压缩条目。技术报告 Figure 3 给出了它的完整数据流:

对照图,从上到下依次是:压缩 KV 条目生成(左侧两路 C^a / C^b)、闪电索引器打分(中间橙色部分)、top-k 选择、共享 KV 的 MQA 核心注意力、分组输出投影(右侧)。逐块拆解。
3.1 压缩 KV 条目:softmax 门控的加权池化#
设 H∈Rn×d 是输入隐藏状态序列(n 为序列长度,d 为隐藏维度)。CSA 先算出两组”原始 KV 条目”和对应的”压缩权重”:
Ca=H⋅WaKV,Cb=H⋅WbKV,Za=H⋅WaZ,Zb=H⋅WbZ,其中 WaKV,WbKV,WaZ,WbZ∈Rd×c 都是可训练参数,c 是注意力头维(V4 中 c=512)。注意这里有两个并行的投影流 a 和 b,原因下面马上说。
接下来每 m 个(V4 中 m=4)KV 条目压成一个压缩条目 CiComp∈Rc:
[Smi:m(i+1)−1a;Sm(i−1):mi−1b]=Softmaxrow([Zmi:m(i+1)−1a+Ba;Zm(i−1):mi−1b+Bb]),CiComp=j=mi∑m(i+1)−1Sja⊙Cja+j=m(i−1)∑mi−1Sjb⊙Cjb,其中 ⊙ 是逐元素乘(Hadamard product),Ba,Bb∈Rm×c 是可学习的位置偏置。这个式子值得逐点解释:
- 加权池化而非平均池化:每个原始 token 对压缩条目的贡献由 S(softmax 归一化后的权重)决定,S 又由独立的投影 Z=H⋅WZ 加位置偏置 B 得到。这让压缩操作学会”这个块里哪个 token 值得被保留”——对指令、数字、专有名词这类关键 token 给高权重,对填充词给低权重。如果换成简单的平均池化,重要信息会被稀释;
- softmax 沿行归一化:Softmaxrow 把 2m 个(a 流 m 个 + b 流 m 个)权重归一化为和为 1,所以压缩是加权平均,尺度稳定;
- 可学习位置偏置 B:B∈Rm×c 的第 j 行只依赖”该 token 在块内的相对位置”,不依赖内容——相当于一个逐通道的”相对位置权重模板”。为什么需要它?块内不同位置的 token 通常重要性不同(比如靠近块尾的 token 往往与后续 query 更相关),但 Z 只看到内容、看不到位置,B 补上这层先验;
- 为什么是两条流 a、b?——交叠压缩:看索引范围,CiComp 实际用了 Ca 的 [mi,m(i+1)) 和 Cb 的 [m(i−1),mi),共 2m 个原始 token。第 i 个压缩条目覆盖的区间是 [m(i−1),m(i+1))——与第 i−1 个条目(覆盖 [m(i−2),mi))共享 m 个 token。效果是:输出条目数仍是 n/m(压缩率 1/m 不变),但每个条目能看到 2m 个 token 的信息。为什么要交叠?如果不交叠,块边界处的 token 会被”硬切”进某个块,跨块的信息关联(比如一个词被切到两个块里)在压缩后永久丢失;交叠让相邻压缩条目共享一半感受野,边界信息不会断裂。注意当 i=0 时 b 流没有前一个块,论文规定用负无穷填充 Zb(softmax 后权重为 0)、用零填充 Cb,保证边界处理正确;
- 压缩率确实只有 m 而不是 2m:虽然每个条目看了 2m 个 token,但 n 个 token 只产生 n/m 个条目(因为相邻条目交叠了 m 个),KV 条目数除以 m,这是论文反复强调的”压缩到 1/m 倍”。
压缩完成后,1M 上下文的 CSA 层只有 106/4=250,000 个压缩 KV 条目,KV Cache 体量直接除以 4。但 25 万个条目对单个 query 来说还是太多,下一步做稀疏选择。
3.2 闪电索引器:低秩打分 + 加权投票#
稀疏选择沿用 DSA 的”先过滤、后计算”思路,但索引器本身做了低秩改造。V4 的索引器叫 Lightning Indexer(闪电索引器),流程分三步。
第一步,生成压缩后的索引器键。 对历史序列做与 CSA 相同的压缩操作,得到压缩索引键 KIComp∈Rmn×cI,cI 是索引器头维(V4 中 cI=128)。注意:压缩权重用的是索引器自己的投影,与主注意力解耦——索引器只关心”相关性”,不需要知道”内容长什么样”,128 维足够打分用。
第二步,低秩生成索引器查询。 对 query token t,用两次投影生成 nhI(V4 中 64)个索引器查询头:
ctQ=ht⋅WDQ,[qt,1I;qt,2I;…;qt,nhII]=qtI=ctQ⋅WIUQ,其中 ht∈Rd 是 query token 的隐藏状态,dc 是查询压缩维度(V4-Pro 中 dc=1536,V4-Flash 中 1024),WDQ∈Rd×dc 是降维投影、WIUQ∈Rdc×cInhI 是升维投影。先压到低维潜空间、再升维出头——这就是 MLA 的 down-projection / up-projection 结构(站内 MLA 完全拆解 讲过同一手法)。直接为 64 个头各生成 128 维查询需要 d×8192 的矩阵,而低秩路径把参数量压到 d×dc+dc×8192,且潜向量 ctQ 还与核心注意力的查询共享——它是后续公式(18)里注意力查询的来源。索引器查询和注意力查询出自同一个低秩潜向量,这个设计让索引器”感知”主注意力的查询分布,两者不会各说各话。
第三步,打分与选择。 索引分数 It,s(query token t 对压缩块 s 的相关性)定义为:
[wt,1I;wt,2I;…;wt,nhII]=wtI=ht⋅Ww,It,s=h=1∑nhIwt,hI⋅ReLU(qt,hI⋅KsIComp),CtSprsComp={CsCompIt,s∈Top-k(It,:)}.Ww∈Rd×nhI 为每个索引器头学一个标量权重 wt,hI。三个细节各回答一个”为什么”:
- 为什么是点积 + ReLU? 每个头的 qt,hI⋅KsIComp 就是”这个头视角下 query 与块 s 的相似度”,ReLU 把负分截为 0。相似度是负的意味着”反相关”——在稀疏选择语境里,反相关块不是候选而是噪声,直接清零既保证稀疏性(大量无关块得 0 分),又让 top-k 排名不被负分干扰;
- 为什么每个头还要乘一个可学习权重 wt,hI? 不同索引器头可能擅长捕捉不同类型的依赖(有的抓词汇匹配、有的抓句法结构),wtI 让 query 动态决定”这次主要信哪个头”——相当于对 64 个打分器做加权投票,而不是简单求和;
- 为什么 s 只取 s<⌊t/m⌋? 因果性:query 只能看到自己之前完成的压缩块。query 所在的当前块尚未压缩完(见下文”未压缩尾部”),也不可见。
选择完成后,每个 query 只保留 top-k 个压缩条目:V4-Pro 的 k=1024,V4-Flash 的 k=512。作为对比,V3.2 的 DSA 是每 query 选 2048 个原始 token。V4 把选择粒度从”token”升级到了”4:1 压缩条目”,再用更小的 k——选择数量本身降了一半,而每个条目承载的信息量是原来的 4 倍。
索引器还带来一个精度红利:打分只需要”相对排序正确”,不需要高精度数值,所以索引器注意力全程用 FP4 计算。FP4 的索引器键(128 维 × 0.5 字节 = 每条目 64 字节)比主 KV 小得多,1M 上下文下索引器扫描 25 万个条目的成本被压到可以接受的范围。
3.3 共享 KV 的 MQA 核心注意力#
选出 CtSprsComp 后,核心注意力在 MQA(Multi-Query Attention,Shazeer 2019)模式下进行。注意力查询同样来自低秩潜向量:
[qt,1;qt,2;…;qt,nh]=qt=ctQ⋅WUQ,ot,i=CoreAttn(query=qt,i,key=CtSprsComp,value=CtSprsComp),其中 nh 是查询头数(V4-Pro 为 128,Flash 为 64),WUQ∈Rdc×cnh 是查询的升维投影。这里的关键是第二行:CtSprsComp 同时充当 key 和 value——每个压缩条目既当键又当值。这与 MLA 的”潜空间 KV 被所有头共享”是同一原理:压缩条目本身是 K 和 V 的联合表示(它是”内容”的压缩,不分键值角色),所有查询头共享同一份。kernel 层面这是决定性的:每个 KV 条目只从 HBM 读一次,被 128 个头复用(NSA 论文在 kernel 设计里论证过同样的原则——键值条目必须被多个 query 共享,计算效率才成立)。
在压缩条目上做注意力的算术强度(arithmetic intensity)分析能看清收益:CSA 层每个 query 要处理 k+nwin=1024+128=1152 个条目(窗口条目来自下文的滑窗分支),每个条目 512 维。对 128 个查询头,FLOPs 约 2×512×1152×128≈1.5 亿/层;而 V3.2 的 DSA 是 2048 个原始 token、同样 512 维潜空间:2×512×2048×128≈2.7 亿/层。单看注意力,CSA 层就比 DSA 少了约 45% 的 FLOPs,再加上 HCA 层和 FP8/FP4 精度,才有了论文里 27% 的总数字。
3.4 分组输出投影:别让输出矩阵变成新瓶颈#
注意力输出 ot=[ot,1;…;ot,nh]∈Rcnh 的维度是 512×128=65,536——比隐藏维度 d=7168 大 9 倍。如果直接用一个 cnh×d 的输出投影矩阵 ot⋅WO 压回 d 维,这一层的矩阵乘参数就是 65,536×7168≈4.7 亿,几乎和整个注意力主体一样贵,完全抵消压缩省下的算力。
V4 的分组输出投影(Grouped Output Projection)把这一步拆成两段:
ot,iG′=ot,iG⋅WiOG,ot,iG∈Rcgnh,ot,iG′∈Rdg,o^t=[ot,1G′;…;ot,gG′]⋅WO,先把 nh 个输出头分成 g 组(V4-Pro 中 g=16,每组 8 个头、c⋅nh/g=4096 维),每组内部投影到 dg=1024 维的中间表示;再把 g 组中间表示拼接(16×1024=16,384 维)投影到最终的 d 维。总参数量从 65536×7168 降到 16×(4096×1024)+16384×7168≈1.85 亿,省了约 60%。
为什么分组有效?注意力头的输出之间相关性很高(同一 query 的不同头关注的是同一上下文的侧面),先把组内合并、再全局合并,相当于给投影矩阵做了一个隐式的低秩结构——信息没有损失多少,参数和 FLOPs 却大幅下降。注意分组投影只影响输出侧,query 的生成仍是完整的低秩升维,因为 dc=1536×512×128 的查询升维参数(约 1 亿)相对可以接受,且查询必须保持逐头独立才能做多头注意力。
HCA:重度压缩注意力#
HCA 是 CSA 的极端版本:压缩率 128:1,且不做稀疏选择——每个 query 对全部压缩条目做稠密注意力。技术报告 Figure 4:

4.1 压缩公式#
HCA 的压缩与 CSA 同构,只是没有交叠(单流),且压缩率大得多:
C=H⋅WKV,Z=H⋅WZ,Sm′i:m′(i+1)−1=Softmaxrow(Zm′i:m′(i+1)−1+B),CiComp=j=m′i∑m′(i+1)−1Sj⊙Cj,其中 m′=128,B∈Rm′×c 是可学习位置偏置。每 128 个 token 加权池化成一个条目,序列压缩到 1/128。HCA 的 query 生成、共享 KV 的 MQA、分组输出投影与 CSA 完全一致(公式 24–26 与 18–19 相同),不再重复。
4.2 为什么 HCA 敢于”稠密”?#
“每 query 都要看全部 KV”在全注意力里是灾难,但在 128:1 压缩后不是。算一下:1M 上下文下,HCA 层一共只有 106/128=8,192 个压缩条目。对 8192 个条目做稠密注意力,FLOPs 约 2×512×8192×128≈10.7 亿/层——单层比 CSA 层贵 7 倍,但注意两件事:
- HCA 层在整个模型里占比小(前 2 层纯 HCA,其余与 CSA 交错),且它消灭了索引器的全部成本:没有索引器键要存、没有打分要算、没有 top-k 要排。对一个已经压到 8192 条目的序列做全量注意力,比”再维护一套打分器去挑选”更便宜;
- 更重要的:稀疏选择有信息丢失风险。top-k 只保住了”最相关”的块,全局背景(如整个文档的主题、任务指令的上下文)可能不在任何单块的 top-k 里。HCA 的稠密路径保证每个 query 都能看到全部全局信息,代价是固定的、可控的——压缩率 128 就是”全局信息”的定价。
论文给 HCA 的定位可以类比为广角镜头:CSA 是长焦(仔细看选中的区域),HCA 是广角(便宜的全局视野)。两者交错,让模型同时拥有”细节”和”全景”两种注意力。这也是 V4 相对 V3.2 的关键区别——V3.2 只有 DSA 一条稀疏路径,全局信息依赖滑窗和 top-k 的运气;V4 用架构上的双路径保证全局信息不丢失。
4.3 为什么压缩率差 32 倍(4 vs 128)?#
这是整个设计里最值得琢磨的参数选择。两个压缩率差 32 倍,各自承担不同的粒度:
- CSA 的 4:1:压缩率小,条目保留的信息密度高,配合 top-k 选择做”精确检索”。4:1 也恰好是 V4 选择的最”温和”的压缩——再小(比如 2:1)就省不了多少 KV,再大(比如 16:1)条目就太粗糙,top-k 选择失去意义;
- HCA 的 128:1:压缩率大,条目是”超级概念块”(论文原话是 super concept block),丢掉大部分细节,但保留全局统计信息。128 的选择让 1M 上下文压缩到 8192 个条目——正好是普通 LLM 处理”中等长度序列”的规模,稠密计算完全可承受。
两条路径的压缩率差 32 倍还有一个工程上的连锁反应:压缩块的大小(见”KV Cache 管理与内核协同设计”一节)必须是 m 和 m′ 的公倍数,才能让一个 kernel 同时处理两类层——论文选择 lcm(4,128)=128 作为统一的块大小基准,这个数字不是巧合。
混合注意力的小机关:RMSNorm、部分 RoPE、滑窗与 Attention Sink#
除了两大注意力主体,技术报告 2.3.3 节还列出了四个”容易读漏”的细节,每个都对应一个实际工程问题。
5.1 查询与 KV 条目的 RMSNorm#
在核心注意力之前,对每个查询头向量和唯一的 KV 条目(压缩条目)各做一次 RMSNorm。为什么不直接算点积?压缩条目是”内容的压缩平均”,其范数随块内 token 的分布变化(某些块激活大、某些块激活小);如果不归一化,点积分数会被 KV 条目的范数主导——大范数的块总是得高分,注意力退化成”谁大选谁”。RMSNorm 把分数拉回由”方向相似度”决定,既稳定训练也稳定推理。
5.2 部分 RoPE:只旋转最后 64 维#
位置编码只施加在向量的最后 64 维上。为什么是”部分”?回到共享 KV 的设定:压缩条目同时是 K 和 V。K 需要位置信息(否则注意力分不清”同一内容的两次出现”),V 不该有位置信息(值就是内容本身,混合了位置反而污染输出)。全量 RoPE 会直接把位置混进 V;部分 RoPE 是折中——最后 64 维携带位置,其余 448 维保持”纯内容”。
还有个反直觉的细节:输出也要做逆旋转。注意力输出是 KV 条目的加权和,每个 KV 条目都带着自己的位置旋转,求和后输出里混入了”绝对位置”信息。解决办法是在输出上再施加位置为 −i(i 是当前 query 的位置)的旋转——因为 RoPE 的旋转矩阵满足可加性(旋转 θ 再旋转 −θ 抵消),这样每个 KV 条目对输出的贡献里只剩下”query 位置减 KV 位置”的相对位置信息,与标准 RoPE 的相对位置语义一致。这个”输出端逆旋转”手法与 MLA 把 RoPE 维度从潜空间里解耦出来是同一问题的两种解法:MLA 是”物理隔离”(RoPE 维度单独存单独算),V4 是”事后抵消”(旋转后逆旋回来)。
5.3 滑窗分支:本地依赖的兜底#
压缩条目只能覆盖”历史”,query 所在块内部的 token(比如一个 128 长块中间的 token)对它不可见——严格因果性要求 query 看不到未来的、甚至当前块里”晚于自己”的 token,而压缩是按块整体做的,块内信息被混在一起。同时,语言建模里最近的 token 通常最相关(局部句法、指代、指令的延续)。这两个问题用同一个方案解决:滑窗分支——每个 query 额外得到最近 nwin=128 个未压缩的原始 KV 条目,与选出的压缩条目拼接后一起进入核心注意力。
注意这个滑窗与 CSA/HCA 的关系是”补充”而不是”替代”:压缩条目提供长程结构化记忆,滑窗提供短程高保真记忆。之前讲的算术强度计算里”每 query 1152 个条目”就包含这 128 个窗口条目。
5.4 Attention Sink:允许注意力”什么都不看”#
标准 softmax 注意力强制每行分数和为 1,即 query 必须把注意力”用光”。长上下文稀疏场景下这会出问题:某 query 与任何历史块都不相关(比如新文档的开头、切换话题后的第一个 token),强制归一化会把权重摊到一批低相关条目上,引入噪声。V4 的处理是引入可学习的 sink logits {z1′,…,znh′}:
sh,i,j=∑kExp(zh,i,k)+Exp(zh′)Exp(zh,i,j),分母里多了一项 Exp(zh′)(第 h 个头的学习常数)。效果是每头注意力分数之和不再必须等于 1——模型学会”这个 query 没有相关历史时,把注意力权重收走”(分数和可以接近 0),而不是被迫分给无关条目。这是 StreamingLLM 里 attention sink(真实存在的 sink token)的泛化:V4 不需要真的往序列里插一个 sink token,而是给每个头一个”想象中的 sink”。代价只是分母多一个标量项,数值上完全免费。
效率账本:1M 上下文下注意力到底便宜了多少#
把上面的机制汇总,看 1M 上下文下 V4-Pro 每层 KV Cache 的实际构成(FP8 存主维、BF16 存 RoPE 64 维、索引器键 FP4):
| 条目类型 | 条目数/层 | 每条目大小 | 每层 KV 占用 |
|---|---|---|---|
| CSA 压缩条目 | 106/4=250,000 | 448×1B+64×2B=576B | 约 144 MB |
| HCA 压缩条目 | 106/128=8,192 | 576 B | 约 4.7 MB |
| 滑窗 KV(未压缩) | 128 | 576 B | 约 0.07 MB |
| 索引器键(FP4) | 250,000 | 128×0.5B=64B | 约 16 MB |
对照 GQA8/BF16 基线的一层 4,096 MB:V4-Pro 的 CSA 层(含索引器)约 160 MB,是基线的约 4%;混合了 HCA 层后,整体 KV Cache 是基线的约 2%——这正是论文 2.3.4 节给出的数字。再对照 V3.2(每层 100 万个 FP8 潜空间条目,约 576 MB/层),V4 的 10% 意味着 KV Cache 从”勉强塞进显存”变成”只占 1/10 的显存”,剩下的空间可以放更大的 batch、更长的上下文或多路复用。
FLOPs 侧:V3.2 的 DSA 每层每 query 要处理 2048 个原始 token 条目;V4-Pro 的 CSA 层是 1152 个条目(其中 1024 个是 4:1 压缩条目),HCA 层虽然看 8192 个条目但层数少、且没有索引器开销。论文报告的端到端数字(覆盖全部注意力与 MoE):Pro 单 token FLOPs 为 V3.2 的 27%,Flash 为 10%。注意 Flash 的数字更好看,因为它的 top-k(512)和头数(64)都更小——省下的 FLOPs 与模型容量是一对矛盾,Flash 的容量小,需要的注意力精度也低,这解释了为什么”更小的模型省得更多”。
另外要澄清一个常见误解:这些数字是”单 token 推理”(解码)视角。Prefill 阶段每个 token 都是 query,总 FLOPs 还要乘序列长度,但压缩同样生效(KV 条目数除以 4/128),所以 Prefill 的加速比与解码同源。论文强调的 1M 上下文”可行”,正是因为在两个阶段都同时压缩了计算与存储。
推理系统:异构 KV Cache 如何管理#
混合注意力给推理框架出了个难题。vLLM 的 PagedAttention 及后续变体(站内 PagedAttention 完全拆解 讲过)建立在两个假设上:KV 一经写入就保留到请求结束(支持共享与写时复制),以及 所有 token 的 KV 形状相同(统一块大小)。V4 的 KV Cache 同时违反这两条:
- 滑窗 KV 会过期:窗口外的 KV 对后续 query 不再可见,但 PagedAttention 的块表假设”活着”的块一直有用;
- 条目类型异构:同一序列的同一时刻,存在 4:1 压缩条目、128:1 压缩条目、原始滑窗 KV、索引器键四种物理形态,大小、更新频率、访问模式全不一样;
- kernel 对齐约束:高性能注意力 kernel 要求块内 token 数固定,而压缩条目对应 m 个原始 token,普通分块粒度对不上。
技术报告 Figure 6 给出了官方 KV Cache 布局:

6.1 两种缓存、两套管理策略#
图里把 KV Cache 切成两个互不相干的组件:
经典 KV Cache(classical KV cache)——管 CSA/HCA 的压缩条目。每个请求分配多个块,块表管理方式与 PagedAttention 类似,存的是”永远不过期”的压缩历史。因为压缩条目一旦生成就不再变化(压缩是确定性的历史函数),它可以像普通 KV 一样被共享、复用、写时复制。
状态缓存(state cache)——管两类”当前状态”:滑窗 KV 与未压缩尾部。为什么需要尾部?压缩操作要凑齐 m 个 token 才能执行,序列末尾不足 m 个的 token 没法压缩,必须先以原始形态存着,等凑够了再压成条目迁入经典缓存。滑窗 KV 也一样是”只与当前位置有关的滚动状态”——第 t+1 个 token 到来时,窗口头部被踢出,窗口尾部加入新 KV,它就是一个随时在变的滚动缓冲区。论文的洞察是:这两类数据的共同点是它们都是”序列状态”而不是”序列历史”——只依赖当前位置,一旦更新,旧值就没用了。因此状态缓存按序列分配固定大小的块(窗口大小 nwin=128 加上不满一个压缩块的最多 m−1 个尾部 token),序列结束即整体回收,不需要块表、不需要引用计数、不需要驱逐策略——SWA 的”过期”问题在结构上被消灭了:窗口不存在的部分就不分配缓存。
6.2 稀疏注意力 kernel 的协同设计#
第二个障碍(kernel 对齐)的解法是缓存布局与 kernel 一起设计。标准稀疏注意力 kernel 假设每块固定 B 个 token;在 CSA 层,一块 B 个压缩条目对应 B⋅m 个原始 token,HCA 层对应 B⋅m′ 个。如果两类层的块大小不一致,kernel 要为两种块各写一套。论文的方案是让”原始 token 数”成为 m 与 m′ 的公倍数——因为 lcm(4,128)=128,任何”128 的倍数”个原始 token 在 CSA 层正好是 32 个压缩条目、在 HCA 层正好是 1 个压缩条目,同一个 kernel 骨架可以服务两类层。配套的工程细节还包括:块内 padding 对齐 cache line(缓存行对齐减少跨行访问)、每块压缩条目的个数固定以便静态分配共享内存等。
6.3 磁盘 KV Cache:共享前缀的三套 SWA 策略#
V4 的服务端继续用磁盘 KV Cache 消除共享前缀的重复 prefill(这是 V3 推理系统就有的机制):请求命中已缓存前缀时,直接读盘复用 KV,跳过整段 prefill。但 V4 的异构 KV 让”存什么”成了选择题:
- CSA/HCA 压缩条目:全量落盘。它们体量小(1M 上下文下每层约 150 MB,远小于原始 KV),且永远有效,无脑存就是最优解。命中前缀时读取到最后一个完整压缩块为止;前缀尾部不足一个压缩块的 token 不存盘,命中时需要重算(因为它们对应的未压缩 KV 不属于任何完整块);
- 滑窗 KV:体量是压缩条目的约 8 倍(每层、每 token 都有,不压缩),全存浪费、不存就要重算。论文给出三种策略,在存储与计算之间取不同的平衡点:
| 策略 | 存储内容 | 命中前缀时的开销 | 适用场景 |
|---|---|---|---|
| Full SWA Caching | 全部 token 的滑窗 KV | 读最后 nwin 个 token 的缓存,零重算 | 存储充足;但 SSD 写入模式差(每个请求只读窗口尾部,其余写入浪费) |
| Periodic Checkpointing | 每 p 个 token 存一个”窗口状态检查点” | 加载最近的检查点 + 重算剩余 <p 个 token | 通过调节 p 在存储与重算间滑动,默认折中方案 |
| Zero SWA Caching | 不存任何滑窗 KV | 用缓存的 CSA/HCA 压缩条目重算最后 nwin⋅L 个 token | 存储紧张;重算量 ≤128×L 远小于全 prefill |
第三种策略的计算依据值得展开:滑窗 KV 的生成是”局部”的——第 l 层第 t 个 token 的滑窗 KV 只依赖第 l−1 层最近 nwin 个 token 的输出(因为注意力窗口只有 nwin 宽)。所以从缓存好的 CSA/HCA 压缩条目出发,重算最后 nwin 个 token 的滑窗 KV,需要向前传播 nwin 个 token × L 层——1M 前缀下是 128×61≈7,808 个 token 的重算量,仍然只有全 prefill(100 万 token)的 0.8%。这就是”共享前缀便宜 100 倍”的数学来源。
社区侧(Together AI 的服务博客)用另一个视角印证了同一问题:V4 的异构缓存打破了”一个逻辑前缀对应一份连续 KV 缓冲”的假设,SGLang 生态的做法是引入 Shadow Radix——逻辑前缀树的一个节点同时投影到 SWA、C4 压缩、C128 压缩、索引器键、压缩状态环五个物理池,前缀复用变成”五池同步命中”;配套的 HiSparse 把不活跃的 C4 稀疏 KV 卸载到 CPU 内存,换取 5–10 倍的解码 batch 扩容。这些是论文之外的系统层补充,细节以各家实现为准。
模型配置速查与系列定位#
两个模型的注意力超参数汇总(技术报告 4.2.1 节):
| 配置项 | V4-Pro | V4-Flash |
|---|---|---|
| 层数 / 隐藏维 d | 61 / 7168 | 43 / 4096 |
| 前 2 层注意力类型 | HCA | 纯滑窗注意力 |
| 之后 | CSA / HCA 交错 | CSA / HCA 交错 |
| CSA 压缩率 m | 4 | 4 |
| HCA 压缩率 m′ | 128 | 128 |
| 索引器头数 nhI / 头维 cI | 64 / 128 | 64 / 128 |
| 注意力 top-k | 1024 | 512 |
| 查询头数 nh / 头维 c | 128 / 512 | 64 / 512 |
| 查询压缩维 dc | 1536 | 1024 |
| 输出投影组数 g / 中间维 dg | 16 / 1024 | 8 / 1024 |
| 滑窗大小 nwin | 128 | 128 |
| MoE(共享 + 路由 / 激活) | 1 + 384 / 6 | 1 + 256 / 6 |
| MTP 深度 | 1 | 1 |
| 总参 / 激活参 | 1.6T / 49B | 284B / 13B |
把 V4 放回 DeepSeek 的注意力演化主线看:MLA(2024,V2)解决了”每头存 KV”的内存问题,用低秩潜空间让所有头共享 KV;DSA(2025,V3.2)解决了”KV 太多”的问题,用索引器只让少数 token 参与计算;CSA + HCA(2026,V4)则同时迈出两步——先把 KV 压缩 4 倍/128 倍再稀疏选择,并且用双路径(稀疏 + 稠密压缩)把”全局信息不丢失”写进架构。每一步都建立在”KV 条目必须被多查询共享”的 kernel 效率原则之上:MLA 共享于潜空间,DSA 共享于 token 选择,V4 共享于压缩条目。这也是 kernel 侧(FlashMLA、DeepGEMM 的稀疏注意力内核)一直能吃到架构红利的原因。
小结#
- CSA 把每 4 个 token 用 softmax 门控池化压成 1 个条目(交叠 4 个 token 保持边界信息),再用低秩闪电索引器(64 头、FP4)打分、top-k 选出 1024 个条目,在共享 KV 的 MQA 模式下做核心注意力,输出走分组投影控制参数规模;
- HCA 以 128:1 压缩、不做选择、稠密注意全部压缩条目,为全局信息提供保底路径;4:1 与 128:1 的 32 倍差距不是随意定的,它们分别对应”精确检索”与”全景背景”两种粒度,也让统一块大小 lcm(4,128)=128 成为 kernel 对齐的基础;
- 四个细节机制:查询/KV 归一化防范数主导、部分 RoPE(最后 64 维 + 输出逆旋转)调和”K 要位置、V 不要位置”、滑窗分支兜底本地依赖、Attention Sink 允许注意力分数和不为 1;
- 效率结果:1M 上下文下 V4-Pro 单 token FLOPs 为 V3.2 的 27%、KV Cache 为 10%(Flash 为 10% / 7%),相对 GQA8 BF16 基线 KV Cache 仅约 2%;
- 推理系统:压缩条目进经典块式缓存,滑窗与未压缩尾部进固定大小的”状态缓存”;磁盘缓存中压缩条目全存、SWA 按 Full / Periodic / Zero 三档策略在存储与重算之间权衡(Zero 策略的重算量只有全 prefill 的约 0.8%)。
参考资料#
- DeepSeek-V4: Towards Highly Efficient Million-Token Context Intelligence(arXiv 技术报告)
- DeepSeek-V4 技术报告 HTML 全文(架构图与公式出处,arXiv 页面可读版)
- Hugging Face Blog: DeepSeek-V4 — a million-token context that agents can actually use
- Together AI: Serving DeepSeek-V4 — why million-token context is an inference systems problem
- DeepSeek-V3.2-Exp 开源仓库(DSA 技术报告,CSA 的稀疏选择直接继承自 DSA)
- NVIDIA Developer Blog: Build with DeepSeek V4 using NVIDIA Blackwell
- Multi-Query Attention(Shazeer 2019,MQA 原文)
- DeepSeek-V4 模型仓库(Hugging Face,含推理实现目录)
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分内容可能已过时
评论区
分享你的想法,与大家交流讨论
音乐
暂未播放



