Back to Notes

DeepSeek V4 技术细节解读:共享 K=V 后为什么还要 inverse RoPE

聚焦 DeepSeek V4 的一个很小但关键的注意力细节:在 CSA 中共享 K=V 以后,为什么还要用 inverse RoPE 把位置信息从 value 路径里“拿掉”。

12 min read
阅读人数--Source

这篇 Note 只看 DeepSeek V4 的一个细节:CSA 里把 key 和 value 共享成同一个向量以后,为什么还要做 inverse RoPE

我不打算概括整篇论文。整篇 V4 把注意力、MoE、FP8 训练、超长上下文、推理部署都放在一起,范围太大;如果每个点都讲一点,最后反而什么都没真正理解。这里先把一个局部问题拆清楚。

资料来源和可信度

主要来源:

来源我用它确认什么
DeepSeek V4 技术报告页面CSA 的公式、共享 K=V、partial RoPE、inverse RoPE 的设计描述
Hugging Face Transformers DeepSeek V4 文档V4 模型类型和官方集成状态
DeepSeek-V4-Pro Hugging Face 模型页模型发布时间、用途说明、开源入口
vLLM 的 DeepSeek V4 部署笔记部署侧对 shared K=V、inverse RoPE、CUDA graph 约束的解释
DeepSeek V3.2-Exp 模型页V4 之前 DeepSeek Sparse Attention 的上下文
苏剑林关于 inverse RoPE 的讨论理解“从 value 中移除位置相位”的数学直觉

注意:这篇笔记写于 2026-05-08。DeepSeek V4 是新模型,工程实现和推理框架支持可能继续变化,所以这里把结论限定在当前公开资料能支持的范围内。

先把问题缩小

标准 attention 可以写成:

Attn(Q,K,V)=softmax(QKd)V\operatorname{Attn}(Q,K,V) = \operatorname{softmax}\left(\frac{QK^\top}{\sqrt{d}}\right)V

这里有两条不同路径:

  1. QKQK^\top 决定每个 query 应该看哪些 token。
  2. VV 决定被看的 token 向输出贡献什么内容。

通常 KKVV 是两个不同的投影结果。V4 的 CSA 做了一件更激进的事:让 key 和 value 共享同一个 latent vector。这带来的直接收益是 KV cache 更小,因为缓存里不再同时保存 K 和 V 两份大向量。

但共享以后会出现一个麻烦:如果同一个向量既当 key 又当 value,那么加在 key 上的位置信息也会被 value 带进输出路径。

这就是 inverse RoPE 要处理的问题。

CSA 做了什么

V4 的 CSA 可以粗略理解成三步:

  1. 把历史 token 的 KV 信息压缩到更短的 latent 表示。
  2. 用 query 和 compressed key 做相关性打分。
  3. 只选 top-k 个 compressed slot 做 attention。

如果把论文里的符号简化,query token tt 只会和一个选出来的集合 StS_t 交互:

St=TopK(qtkj, jt)S_t = \operatorname{TopK}\left(q_t^\top k_j,\ j \le t\right)

然后 attention 不是扫完整上下文,而是在 StS_t 上做:

ot=jStsoftmax(qtkjd)vjo_t = \sum_{j \in S_t} \operatorname{softmax}\left(\frac{q_t^\top k_j}{\sqrt{d}}\right)v_j

如果上下文长度是 nn,完整 attention 的核心成本和 nn 成正比;CSA 把每个 query 实际看的位置压到 kk 个候选上。长上下文里,关键不是让模型“记住所有 token”,而是让它能快速找到值得看的 token。

这里的工程诱因很明确:百万 token 上下文时,KV cache 和 attention 计算都会变成主要瓶颈。V4 的 CSA 不是单纯稀疏化,也不是单纯压缩 KV,而是把二者绑在一起。

共享 K=V 的收益

假设每层、每个 token 需要缓存:

KRh×d,VRh×dK \in \mathbb{R}^{h \times d}, \quad V \in \mathbb{R}^{h \times d}

传统做法要缓存两份:

cache2nhd\operatorname{cache} \approx 2nhd

如果用共享 latent 表示 cjc_j 同时服务 key 和 value,缓存近似变成:

cachenhdc\operatorname{cache} \approx nhd_c

这里 dcd_c 可以小于原来的 dd。直观上,这相当于把“用于检索的位置索引”和“用于输出的信息载体”压进一个共享空间。对长上下文模型来说,这非常诱人,因为 KV cache 是推理时持续增长的状态。

但这个收益不是免费的。

麻烦:RoPE 本来只应该帮助匹配位置

RoPE 的作用可以理解为:把位置 pp 编进 query/key 的相位里,让 qpq_pkjk_j 的点积感知相对位置。简化写成:

q~p=Rpqp,k~j=Rjkj\tilde{q}_p = R_p q_p, \quad \tilde{k}_j = R_j k_j

attention 分数变成:

q~pk~j=qpRpRjkj\tilde{q}_p^\top \tilde{k}_j = q_p^\top R_p^\top R_j k_j

由于 RpRjR_p^\top R_j 和相对位置有关,模型就能在打分阶段知道两个 token 的距离关系。

这对 key 是合理的。key 参与“该看谁”的匹配,位置当然重要。

但 value 的角色不同。value 是“看到了以后把什么内容加进输出”。如果把同一个带 RoPE 的向量也当 value,那么输出就会混入被选 token 的绝对位置相位:

op=japjv~jo_p = \sum_j a_{pj}\tilde{v}_j

如果 v~j\tilde{v}_j 里也含有 RjR_j,输出内容就不只是语义内容,还夹带了位置旋转。这样会让共享 K=V 变得尴尬:为了让 key 可检索,我们给它加了位置;但 value 又不一定应该携带这个位置相位。

inverse RoPE 的直觉

inverse RoPE 的目标可以用一句话概括:

在打分时保留 RoPE 的位置感,在取 value 时把位置相位抵消掉。

如果共享向量经过了位置旋转:

c~j=Rjcj\tilde{c}_j = R_j c_j

那么作为 key 使用时,可以直接用 c~j\tilde{c}_j

scorepj=q~pc~j\text{score}_{pj} = \tilde{q}_p^\top \tilde{c}_j

但作为 value 使用时,可以先做反向旋转:

vj=Rj1c~jv_j = R_j^{-1}\tilde{c}_j

再进入加权求和:

op=jSpapjRj1c~jo_p = \sum_{j \in S_p} a_{pj} R_j^{-1}\tilde{c}_j

因为 Rj1Rj=IR_j^{-1}R_j = I,理想情况下:

Rj1c~j=cjR_j^{-1}\tilde{c}_j = c_j

这就恢复了“不带位置相位的 value 内容”。所以 inverse RoPE 不是一个花哨技巧,它是在共享 K=V 以后补上的结构性修正。

为什么不是直接不给 value 加 RoPE

如果 K 和 V 是分开的,最简单的做法就是:key 加 RoPE,value 不加。很多 Transformer 变体本来就是这么做的。

但 CSA 的关键收益来自共享:

KVCK \approx V \approx C

共享以后,同一个 cached latent 既要服务 retrieval,又要服务 aggregation。你不能既说“这个缓存向量作为 key 时带位置”,又说“同一个缓存向量作为 value 时完全不带位置”,除非在读取 value 时做一次变换。

inverse RoPE 就是这个读取时变换。

这也是为什么这个细节值得单独看:它不是为了提高 benchmark 的独立 trick,而是由“共享 K=V”这个更底层的内存优化推出来的。

partial RoPE 为什么也出现在这里

V4 资料里还提到 partial RoPE。它的意思不是把所有维度都旋转,而是只对一部分维度注入位置:

cj=[cjpos,cjcontent]c_j = \left[ c_j^{\text{pos}}, c_j^{\text{content}} \right]

其中:

c~jpos=Rjcjpos\tilde{c}_j^{\text{pos}} = R_j c_j^{\text{pos}}

而另一部分维度保留内容信息:

c~jcontent=cjcontent\tilde{c}_j^{\text{content}} = c_j^{\text{content}}

这种设计可以减轻一个冲突:同一个 latent 既要作为检索索引,又要作为内容载体。如果所有维度都强行承担位置旋转,value 路径的内容纯度会受影响;如果完全不加位置,key 路径又不够好用。partial RoPE 给了模型一个折中空间。

一个可以带走的理解

CSA 的核心不是“少算一点 attention”这么简单。它真正难的地方在于:当你压缩 KV 并共享 K=V 时,原本在标准 attention 中天然分开的功能会被绑在一起。

标准 attention 里:

对象主要职责
Key被 query 检索,适合携带位置信息
Value被 attention 权重汇聚,主要携带内容信息

CSA 共享 K=V 后:

对象新问题
Shared latent同时要可检索、可汇聚、可缓存、可稀疏选择

inverse RoPE 解决的是这个新问题里的一个局部矛盾:

retrieval wants positionbutaggregation wants content\text{retrieval wants position} \quad \text{but} \quad \text{aggregation wants content}

所以我的理解是:

shared K=V 是为了长上下文推理的内存效率;inverse RoPE 是为了不让这个内存优化破坏 value 路径的语义干净度。

和 DeepSeek V3.2 的关系

DeepSeek V3.2-Exp 已经把 sparse attention 作为长上下文方向公开出来,资料里提到它用于探索训练和推理效率。V4 的 CSA 可以看作进一步把稀疏选择、KV 压缩、共享 K=V 放到同一个注意力模块里。

这不是简单从 dense attention 改成 sparse attention,而是把长上下文推理时最贵的几件事同时处理:

  1. 不想为每个 query 看完整上下文。
  2. 不想缓存完整 K 和完整 V。
  3. 不想因为共享 K=V 而污染 value 的内容路径。

前两点是效率问题,第三点是结构一致性问题。

以后读这类论文时我会问的问题

  • top-k selection 的候选集合如何构造,是否需要额外 indexer。
  • compressed latent 的维度是多少,跨层是否共享策略。
  • inverse RoPE 是训练时学会适配,还是推理时纯函数式地抵消。
  • 稀疏选择在长上下文 recall 上是否存在失败模式。
  • 推理框架如何处理动态 top-k 带来的 CUDA graph 或 batching 问题。

小结

这篇 Note 的结论很窄:

V4 的 inverse RoPE 可以理解为共享 K=V 之后的必要配套。

如果 key 和 value 分开,key 加 RoPE、value 不加就够了;但 CSA 为了节省 KV cache 把它们共享以后,同一个 latent 会同时进入“匹配路径”和“内容路径”。inverse RoPE 让这个 latent 在匹配时保留位置,在输出时尽量还原为内容。

这个设计背后的取舍,比“模型支持百万 token”这个结论本身更值得学习。

如何引用

引用这篇 Note 时,可以使用下面自动生成的文本格式或 BibTeX。

Plain text

Baiyuan Qiu. (2026). DeepSeek V4 技术细节解读:共享 K=V 后为什么还要 inverse RoPE. Baiyuan Qiu. Updated 2026-05-08. https://www.brianchiu.top/notes/deepseek-v4-shared-kv-inverse-rope/

BibTeX
@online{qiu2026deepseekv4sharedkvinverserope,
  author = {Baiyuan Qiu},
  title = {DeepSeek V4 技术细节解读:共享 K=V 后为什么还要 inverse RoPE},
  year = {2026},
  month = {may},
  url = {https://www.brianchiu.top/notes/deepseek-v4-shared-kv-inverse-rope/},
  urldate = {2026-05-08}
}