我们从DeepSeek V4的技术报告出发,以此为蓝本来查漏补缺,并进一步建立相关知识储备。万丈高楼,始于平地。

Speculative Decoding与MTP

在进入核心的模型创新点之前,我们首先回顾一下Speculative Decoding以及从DeepSeek V3一并沿用至V4的MTP策略。需要明确的是,MTP是一种 训练目标或模型结构,speculative decoding 是一种推理加速方法。在DeepSeek的模型中,MTP本质上是为了更好地支持speculative decoding,同时还能提升模型能力。

对于一个标准的autoregressive LLM,在给定了$x_{\lt t}$的上下文之后,一次forward只生成一个token,训练目标为$p(x_t|x_{\lt t})$。这个策略所存在的问题是显然的:在每一次inference的时候,每一个token都会经历一个完整的forward过程。而为了提升效率,在训练时就引入MTP作为训练目标,也即每一个step都预测多个token:在DeepSeek-V3的架构中,除了主模型之外还引入了MTP modules专门用于预测主模型之后的token。

而在推理时,一般情况下的speculative decoding可以理解为先用便宜模型或模块草稿式地猜几个 token,再让大模型一次性检查这些token是否正确。注意:speculative decoding的操作可以保证其token的概率分布和朴素策略一致,推导如下

假设目标模型,也就是原本的大模型,其条件分布为$p(x_t \mid x_{\lt t})$;而 draft model 或 MTP module 给出的近似分布为$q(x_t \mid x_{\lt t})$,在普通 decoding 中,每一步都直接从$p$中采样:$x_t \sim p(\cdot \mid x_{\lt t})$;因此,生成$K$个 token 需要进行$K$次目标模型 forward:$x_t, x_{t+1}, \ldots, x_{t+K-1}$. 而在 speculative decoding 中,我们首先用较便宜的draft model一次性生成一段候选 token:$\hat{x}_t, \hat{x}_{t+1}, \ldots, \hat{x}_{t+K-1} \sim q$,随后,目标模型$p$并不是逐个重新生成这些token,而是对整段draft token进行一次并行验证。对于第$i$个draft token $\hat{x}_{t+i}$,目标模型会计算$p(\hat{x}_{t+i} \mid x_{\lt t}, \hat{x}_t, \ldots, \hat{x}_{t+i-1}),$并将其与 draft model 的概率$q(\hat{x}_{t+i} \mid x_{\lt t}, \hat{x}_t, \ldots, \hat{x}_{t+i-1})$进行比较。接受该 token 的概率通常写作:

$$ \alpha_i = \min\left( 1, \frac{ p(\hat{x}_{t+i} \mid x_{\lt t}, \hat{x}_{t:t+i-1}) }{ q(\hat{x}_{t+i} \mid x_{\lt t}, \hat{x}_{t:t+i-1}) } \right). $$
直觉上,如果draft model给出的token在目标模型看来也足够合理,即 $p/q$ 较大,那么该token就会被接受;如果draft model过于自信地提出了一个目标模型并不认可的token,那么该token就更可能被拒绝。一个关键点是,标准speculative decoding并不是简单地“猜对就用,猜错就丢”,而是通过接受-拒绝采样保证最终生成分布仍然与直接从目标模型$p$解码一致。为了看清这一点,可以先考虑单个token的情况。draft model先采样:$\hat{x} \sim q(\cdot)$,若 $\hat{x}$ 被接受,则输出 $\hat{x}$。此时某个 token $x$ 被接受并输出的概率为:
$$ q(x) \cdot \min\left(1, \frac{p(x)}{q(x)}\right) = \min(p(x), q(x)). $$
如果draft token被拒绝,则不能直接重新从$p$中采样,否则会破坏分布一致性。正确做法是从residual distribution中采样:
$$ p_{\text{res}}(x) = \frac{(p(x)-q(x))_+}{ \sum_{x'} (p(x')-q(x'))_+ }, $$
其中$(a)_+ = \max(a, 0)$;因此,一个token $x$最终被输出的总概率为:
$$ \underbrace{\min(p(x), q(x))}_{\text{accepted from draft}} + \underbrace{(p(x)-q(x))_+}_{\text{sampled from residual}} = p(x). $$
这说明,只要接受-拒绝和residual sampling的过程设计正确,speculative decoding的最终输出分布就和直接从目标模型$p$中采样保持一致。它改变的是计算方式,而不是目标分布。

回到DeepSeek的MTP设计,其作用正是在这里体现出来。MTP module相当于为主模型提供了一个轻量级、与主模型高度对齐的internal drafter。由于MTP module在训练阶段就被要求预测未来token,并且与主模型共享部分表示或输出空间,它给出的draft token往往比一个外部小模型更加贴近主模型分布。

DeepSeek MoE

接下来是MoE的部分,我们一步步回顾一下目前已经成为基模主流的MoE设计的基本原理以及经典的优化与改进策略。我们知道,一个标准的Transformer层包含了attention和FFN的部分,而FFN往往占据了大量的参数和计算,因此MoE就希望不让每一个token都经过一个巨大的FFN,而是准备多个expert FFN,并让router为每一个token都选择少数几个expert。具体而言,一个标准的FFN可以写成

$$ \text{FFN}(h)=W_2\sigma(W_1h) $$
而MoE层把一个FFN替换成多个expert:$E_1,\dots, E_N$,并通过router产生每一个expert的权重$g_i(h)=\text{Softmax}(W_rh)$,实际计算时只选择top-$K$的结果,记为$\mathcal{T}(h)$(注意最终的hidden feature还需要加上residual connection的结果,在这里我们只考虑FFN转换成MoE的逻辑):
$$ \text{MoE}(h)=\sum_{i\in \mathcal{T}(h)}g_i(h)E_i(h) $$
但MoE往往会遇到以下类型的问题,严重影响训练的效率以及性能:
  • expert collapse:router总是偏向少数expert,导致部分expert过载,部分expert几乎不被使用。

  • expert redundancy:多个expert 学到相似知识,没有形成足够清晰的分工。

  • routing instability:训练早期router不稳定,导致token-expert分配波动较大。

  • communication overhead:在分布式训练中,不同token被送到不同expert,通常需要all-to-all communication,这会带来额外系统开销。

所以MoE的核心难点不是“把 FFN 拆成很多expert”这么简单,而是如何让expert既被均衡使用,又真正形成specialization。DeepSeek MoE针对这个需求提出了以下两点改进方案:

  • Fine-Grained Expert Segmentation:本质上就是把较大的expert拆成较小的expert,例如原来是8个专家选2个,那么拆分之后可能变成64个专家用16个,这自然也就意味着模型可以为不同的token构造更加细粒度的expert组合,从而增强specialization
  • Shared Expert Isolation: 虽然上述操作能让不同的expert学到更加细粒度的知识,但有些通用知识实际上所有的expert都会需要,如果不加处理的话就可能会导致不同expert有部分参数需要处理类似的知识。因此,DeepSeekMoE更进一步引入了$K_s$个shared expert(我们记这一集合为$\mathcal{S}$),不经过router选择而对所有token都激活

最终得到的MoE层可以写成

$$ \text{DeepSeekMoE}(h)=\sum_{i\in \mathcal{S}}E_i^{\text{shared}}(h)+\sum_{j\in \mathcal{T}(h)}g_j(h)E_j^{\text{routed}}(h) $$
在负载均衡的设计逻辑上,DeepSeekMoE引入了Expert-Level balance loss和Device-Level balance loss来防止MoE坍缩到特定的几个expert或者设备上。而对于DeepSeek V4,除了基础细节修改之外基本沿用了DeepSeekMoE的设计思想与架构。

Manifold-Constrained Hyper-Connection

接下来就是mHC这一个核心的贡献,我们从Transformer中使用的最基本的residual connection出发,一步步推导mHC的来历和原理。对于一个普通的Transformer层,可以抽象地写成

$$ x_{l+1}=x_l+F_l(x_l) $$
残差连接之所以在深层网络中非常有效,一个重要原因在于它保留了一条identity path,使得浅层特征可以较为直接地传递到深层,同时也为反向传播提供了一条直接的梯度通路,从而缓解梯度消失或梯度爆炸等训练不稳定问题。

这也可以帮助理解为什么当前许多大模型训练更倾向于采用 Pre-Norm 结构,即

$$ x_{l+1}=x_l+F_l(\text{Norm}(x_l)) $$
如果采用Post-Norm,那么我们实际上有
$$ x_{l+1}=\text{Norm}(x_l+F_l(x_l)) $$
这实际上就破坏了$x_l$到$x_{l+1}$的无损传播路径,归一化参数更新时的梯度变化有可能导致梯度消失或爆炸

我们把Identity Mapping逐层展开,得到的结果如下所示,可以发现最初的信号$x_0$能够一直传递到深层。

$$ x_L=x_0+\sum_{l=0}^{L-1}F_l(x_l) $$
然而,普通residual的结构其实很死板,包含了两个固定的假设:残差项的权重永远是1,且层输出$F_l(x_l)$也直接加入进去,也就是说,它无法学习形如$x_{l+1}=a_lx_l+b_lF_l(x_l)$这样的结构,更不能学习$l+1$层对于$l-2,\dots, l-k$层的直接依赖关系。在Hyper-Connection的论文中,也指出Pre-Norm在传统的residual connection的设定下很容易导致深层相似的问题。为了解决这个挑战,HC把一条residual stream扩展为了$n$条。需要明确的是,residual stream依然是对应于每一个layer,相当于每一个layer同时维护多条残差流;并不是$x_{l+1}$就直接依赖$x_{l}, \dots, x_{l-n+1}$,具体而言,我们设多条residual stream为

为了解决这个挑战,HC把一条 residual stream 扩展为了$n$条。具体来说,我们记第$l$层开始前的多条residual streams为

$$ X_l= \begin{bmatrix} x_l^{(1)}\\ x_l^{(2)}\\ \vdots\\ x_l^{(n)} \end{bmatrix} \in \mathbb{R}^{n\times d}, $$
其中$x_l^{(i)}\in\mathbb{R}^d$表示第$i$条 residual stream。普通Transformer block $F_l$本身仍然只接收一个$d$-维输入,因此HC首先需要从这$n$条stream 中读出当前层真正要处理的输入。我们记这一读入权重为$\alpha_l\in\mathbb{R}^{n}$,于是当前层的输入可以写成$\alpha_l^\top X_l$,同时记
$$ y_l=F_l(\alpha_l^\top X_l)=\sum_{i=1}^{n}\alpha_{l,i}x_l^{(i)}. $$
接下来,HC 还需要决定如何把当前层输出$y_l$写回到$n$条 residual streams 中。我们记写回权重为$\beta_l\in\mathbb{R}^{n}$,并引入一个 residual stream 之间的混合矩阵$R_l\in\mathbb{R}^{n\times n}$,于是HC的整体更新可以写成
$$ X_{l+1}=R_lX_l+\beta_l y_l. $$
这里需要注意,$\beta_l y_l$表示把同一个层输出$y_l$按照不同权重写入不同的stream,即
$$ \beta_l y_l = \begin{bmatrix} \beta_{l,1}y_l\\ \beta_{l,2}y_l\\ \vdots\\ \beta_{l,n}y_l \end{bmatrix}. $$
将上式按stream展开,可以得到
$$ x_{l+1}^{(i)} = \sum_{j=1}^{n}R_{l,ij}x_l^{(j)} + \beta_{l,i} F_l \left( \sum_{j=1}^{n}\alpha_{l,j}x_l^{(j)} \right). $$
从这个形式可以看出,HC 相比普通的残差连接多出了三类可学习的连接关系:第一,$\alpha_l$决定当前层从哪些residual stream中读取信息;第二,$\beta_l$决定当前层输出被写回到哪些residual streams;第三,$R_l$ 决定不同residual stream之间如何混合和传递。

另一种理解HC的方式是回忆LSTM的结构:LSTM中$h_{t}$其实同时依赖当前的输入$x_{t-1}$以及$h_{t-1}$;原始的$h_{t-1}$只是一个$d$维的向量,而HC的思想相当于$h_{t-1}$变成了一个$d\times n$的矩阵,其中$n$为residual条数。普通的residual connection自然就可以看成是HC的一个特例

接下来我们可以证明,HC在提供了相比于普通残差连接更加丰富通路的同时,却也重新引入了梯度消失和梯度爆炸的问题:如果我们HC的更新函数进行展开,会得到

$$ X_L=R_{L-1}R_{L-2}\dots R_0X_0 + \sum_{l=0}^{L-1}(R_{L-1}\dots R_{l+1})\beta_lF_l(\alpha_l^\top X_l). $$
我们不难发现,对比普通残差连接的展开式,HC的展开式中能发现早起层的信息不再只能沿着一条固定的identity path向后传播,而是可以通过$R_l$在不同residual stream之间不断混合,并在后续层中被不同的$\alpha_l$重新读取。但如果$R_l$是一个完全不受约束的学习矩阵,那么多层复合矩阵$R_{L-1}R_{L-2}\dots R_0$就可能在深层网络中产生不稳定的放大与衰减,普通residual connection中恒等映射所保证的稳定性就会被破坏。

因此,DeepSeek-V4中所引入的mHC正是为了解决这一问题,它通过约束residual stream mixing matrix中$R_l$的取值,使其满足$R_l\in\mathcal{B}_n$,其中$\mathcal{B}_n$被称为Birkhoff Polytope,为所有$n\times n$双随机矩阵的集合:

$$ \mathcal{B}_n=\{R\in\mathbb{R}_+^{n\times n}|R\mathbf{1}=\mathbf{1},\quad R^\top\mathbf{1}=\mathbf{1}\} $$
这种情况下,我们能够证明$R_{L-1}R_{L-2}\dots R_0\sim \mathbf{I}$,类似residual connection保留一条稳定的通路;在这里,我们依然可以利用简单的线性代数来推导一下:

假设我们只考虑skip path:

$$ X_{l+1}^{\text{skip}}=R_lX_l $$
我们定义所有residual streams的平均表示为
$$ \tilde{x_l}=\frac{1}{n}\mathbf{1}^\top X_l. $$
那么我们有
$$ \tilde{x}_{l+1}=\frac{1}{n}\mathbf{1}^\top X_{l+1}^{\text{skip}}=\frac{1}{n}\mathbf{1}^\top R_lX_l $$
由于$R_l\in\mathcal{B}_n$,因而我们有
$$ \tilde{x}_{l+1}=\frac{1}{n}\mathbf{1}^\top R_lX_l=\frac{1}{n}\mathbf{1}^\top X_l=\tilde{x}_l $$
这说明,在mHC中,residual stream mixing并不会改变多条streams的平均表示,只是对于信息的重新分配。进一步地,我们可以证明对于任意深度的复合residual mapping,$A_{0\rightarrow L}=R_{L-1}R_{L-2}\dots R_0$,依然有$A_{0\rightarrow L}\in\mathbb{B}_n$

在实际操作时,mHC通常不会直接学习一个已经满足双随机约束的矩阵,而是先学习一个uncontrained matrix,然后再进行投影和归一化操作来进行近似,使其符合双随机矩阵的性质。

Compressed Sparse Attention (CSA)

接下来我们分析DeepSeek-V4中另一个核心结构:Hybrid Attention with CSA and HCA。对于标准的attention,当序列长度为$n$时,每一个query token都需要和前面所有KV tokens做attention,因此整体复杂度随序列长度近似呈二次增长。为了解决这一问题,DeepSeek-V4 引入了Compressed Sparse Attention以及Heavily Compressed Attention,也即CSA和HCA。在本章节中,我们主要讨论CSA的思路:CSA同时结合了KV压缩和稀疏选择这两种思想:它首先把每$m$个token的KV cache压缩成一个compressed KV entry,然后再通过DeepSeek Sparse Attention的方式为每个query选择top-$k$ 个compressed KV entries参与核心attention计算。

我们先来看KV压缩的部分,这一块可以通俗理解为$m$个token的信息融合为一个token的pooling过程,但不同于最naive的average pooling,这里我们希望模型自己去判断不同token之间的重要性以及融合的策略。由于这一块之前接触的不是很多,因此我们先从high-level的角度对比一下传统的KV Cache以及CSA的思想

在标准attention里,我们通常有$K=[k_1,\dots, k_n]$,$V=[v_1,\dots, v_n]$,其中$n$为token总数,同时我们有

$$ o_t=\text{softmax}(q_tK^\top)V $$
因此在标准的KV Cache中,会分别缓存$K_{1:t}$以及$V_{1:t}$来重复利用;但CSA作为一个KV Cache压缩的策略,并不是分别生成$K^{\text{comp}}$以及$V^{\text{comp}}$,而是生成一个$C^{\text{comp}}\in\mathbb{R}^{\frac{n}{m}\times c}$,其中每一行$C_i^{\text{comp}}\in\mathbb{R}^c$表示第$i$个压缩之后的KV entry。如果我们想要和标准的KV Cache对齐,我们可以粗略理解为$K_i^{\text{comp}}=C_i^{\text{comp}}$,$V_i^{\text{comp}}=C_i^{\text{comp}}$,也即$C^{\text{comp}}$同时作为压缩之后的key matrix和value matrix。

总结而言,对于传统的KV Cache,我们是考虑每一层的$K$以及$V$;但在CSA中,我们是直接利用$H$来计算$C^{\text{comp}}$。我们将在之后的推导中,逐步展开这一套混合注意力机制的完整流程

设其输入的hidden states为$H\in\mathbb{R}^{n\times d}$,其中$n$为序列长度,$d$为hidden size。CSA不直接为每一个token都保存完整的key和value,而是先通过线性映射得到两组候选KV Entries:

$$ C^a=HW^{aKV}, \quad C^b=HW^{bKV} $$
其中$C^a,C^b\in \mathbb{R}^{n\times c}$,这里的$c$就可以理解为compressed KV entry的维度,实际上先做了一层降维的操作。与此同时,模型还会生成两组compression weight logits:
$$ Z^a=HW^{aZ}, \quad Z^b=HW^{bZ} $$
直观上而言,$C^a, C^b$提供要被压缩的内容;而$Z^a,Z^b$则决定每一个token在压缩时应该占有多大的权重。进一步地,为了让这种压缩操作不仅依赖token本身的hidden state,还能感知token在当前压缩窗口中的相对位置,CSA还引入了两组learnable positional bias,记为$B^a, B^b\in \mathbb{R}^{m\times c}$,其中$m$为压缩窗口的大小。也即对于一个长度为$m$的token block,这个learnable positional bias可以让模型区分block内部不同位置的token。

接下来我们看$i$个压缩之后的KV entry是如何得到的,它对应的主窗口为$\{mi,mi+1,\dots, m(i+1)-1\}$。一个朴素的想法可能是直接把这$m$个token的候选KV entries聚合成一个向量,但CSA的设计逻辑稍微复杂一些:它同时使用两组候选entries $C^a$和$C^b$,并在生成第$i$个compressed entry时,使用$C_{mi:m(i+1)-1}^a$以及$C_{m(i-1):mi-1}^b$,分别负责当前block以及前一个block。对应的compression logits也就变成了$Z_{mi:m(i+1)-1}^a+B^a$以及$Z_{m(i-1):mi-1}^b+B^b$。将这两者拼接起来,可以得到一个大小为$2m\times c$的压缩打分矩阵,然后CSA对这个矩阵在token维度上做softmax操作,就得到了对应的压缩权重

$$ \left[ S^a_{mi:m(i+1)-1}; S^b_{m(i-1):mi-1} \right] = \mathrm{Softmax}_{\mathrm{token}} \left( \left[ Z^a_{mi:m(i+1)-1}+B^a; Z^b_{m(i-1):mi-1}+B^b \right] \right). $$
需要特别注意的是这个地方softmax函数的含义,并不是给每个token一个单独的标量权重,而是对于每一个feature channel上都单独生成一组token-level的权重,也就意味着不同的feature channel可以关注不同的token。在得到了压缩的权重之后,第$i$​个压缩后的KV entry的结果就可以通过加权求和来得到:
$$ C_i^{\mathrm{comp}} = \sum_{j=mi}^{m(i+1)-1} S_j^a\odot C_j^a + \sum_{j=m(i-1)}^{mi-1} S_j^b\odot C_j^b. $$
最终得到的$C_i^{\text{comp}}\in \mathbb{R}^c$。此时我们也就能理解,$C^a,C^b$相当于提供了两套不同的压缩视角,分别从当前block以及前一个block中获取信息,从而减轻block boundary所带来的信息割裂问题。最后需要注意的是,标准意义上的KV Cache是一个纯inference优化的策略,但是CSA的KV压缩是架构上的创新,在训练时也会涉及。

要特别注意的一点是,对于CSA而言,attention中KV侧的组织方式已经和原始的Transformer有了明显的区别。在标准的Transformer中,每一个token都会对应一组token-level的$k_j$以及$v_j$,当前query通常需要在所有可见历史token的$K,V$上做attention。而现在这里我们得到的$C^{\text{Comp}}$可以理解为基于CSA层的输入$H$所得到的一个memory representation,而不是主干网络中继续向后传播的hidden feature,它的作用是作为后续attention计算中被query读取的压缩KV memory。

接下来,我们将进一步考虑attention的具体计算过程。假设原始序列长度为$n$,CSA经过压缩之后得到的compressed KV entries的数量大约是$n/m$,在超长上下文的context之下依然可能非常大。如果每一个query在计算attention的过程中依然需要dense attend(也即query和每一个compressed entry都要交互),那么attention的计算量依然很高。因此,CSA在得到了$C^{\text{comp}}$之后还会引入一个轻量级的sparse selection模块,也即论文中的Lightning Indexer,用来为每一个query token动态选择最相关的top-$k$个compressed KV entries

那么如何设计这个indexer的逻辑呢?我们首先需要明确这个indexer的输入:原始给到attention层输入自然是$H=[h_1,\dots, h_n]$,但是既然此处的目标是选择对应的query token和哪些Compressed Token来计算attention,因此这个indexer的输入应该也是Compressed之后的KV Entry。但是,这里并没有直接使用$C_i^{\text{comp}}$,而是使用刚刚介绍的一整套压缩策略来构造出另一组Compressed Token $K^{I,\text{Comp}}\in\mathbb{R}^{n/m \times c_I}$, 其中$c_I\ne c$为indexer的head dimension,一般$c_I\lt c$(Index中每一个token的特征维度会更小,这一设计也相对符合直觉)。也就是说,$K^{I,\mathrm{Comp}}$ 只用于计算index score,负责判断“哪些 compressed block 值得被选中”;而真正进入后续 core attention、作为key/value被读取的,仍然是对应位置上的$C^{\mathrm{Comp}}$

在得到了用于indexing的KV indexer $K^{I,\text{Comp}}$之后,接下来就需要考虑query token的计算了。这里同样要注意区分的是,相比于原始Transformer中的$Q=W_QX$的基本计算,在这里用于选择到底哪些KV Cache比较重要的query并不是原始值,而也是专门的index query,计算方式如下:

$$ c_t^Q = h_t\cdot W^{DQ} $$
$$ \left[q_{t,1}^I,q_{t,2}^I,\dots, q_{t,n_h^I}^I\right]=q_t^I=c_t^Q\cdot W^{IUQ} $$

其中$h_t\in \mathbb{R}^d$是query token所对应的hidden state,$c_t^Q\in \mathbb{R}^{d_c}$是query压缩之后得到的结果,$d_c\lt d$为query压缩维度;$n_h^I$则定义了用于索引的query一共有几个head;$W^{DQ}\in\mathbb{R}^{d\times d_c}$以及$W_{IUQ}\in\mathbb{R}^{d_c\times c^In_h^I}$为索引query的投影矩阵,注意到最终投影得到的维度是$c^In_h^I$,也即$n_h^I$个head,每一个head所对应的维度$c^I$与前面计算得到的$K^{I,\text{Comp}}$是对齐的。这样,我们也就可以计算query token $t$以及第$s$个compressed block之间的index score $I_{t,s}\in\mathbb{R}$,其实就是$n_h^I$个head所对应的$q_{t,r}^I$分别与前面专门用于index的第$s$个索引$K_s^{I,\text{Comp}}$计算点积($s\lt\text{Floor}(\frac{t}{m})$,相当于选取的是query token的$t$所对应的前一个完整的block),外部再加一层可学习的权重即可,具体而言有:

$$ [w_{t,1}^I;w_{t,2}^I;\dots;w_{t,n_h^I}^I]:=w_t^I=h_t\cdot W^w $$
$$ I_{t,s}=\sum_{r=1}^{n_h^I}w_{t,r}^I\cdot \text{ReLU}\left(q_{t,r}^I\cdot K_s^{I,\text{Comp}}\right). $$

其中$W^w\in\mathbb{R}^{d\times n_h^I}$为一个可学矩阵,$w_{t,h}^I\in\mathbb{R}$也即表示第$h$个索引头的权重。在上述推导中,需要注意$q_{t,r}^I\in \mathbb{R}^{c_I}$,$K_s^{I,\text{Comp}}\in \mathbb{R}^{c_I}$;在得到了index query $t$与compressed block $s$之间的分数之后,对于第$t$个token而言就只会保留top-$k$个compressed block来用于计算:

$$ C_t^{\text{SprsComp}}=\left\{C_s^{\text{Comp}}\Big|I_{t,s}\in \text{Top-k}(I_{t,:})\right\}. $$
这样,我们也就完成了对于每一个token各自需要关注的KV Entry的选择。CSA之后计算attention的方式就类似于MQA的思想(用共同的一组$K,V$来处理不同head的query),并更进一步地设置$K=V=C_t^{\text{SprsComp}}$。对于一个query token $t$,现在我们才从latent representation $c_t^Q$中计算最终传统意义上的Query值
$$ [q_{t,1};q_{t,2};\dots;q_{t,n_h}]:=q_t=c_t^Q\cdot W_{UQ} $$
其中$n_h$为query head的个数,$W^{UQ}\in\mathbb{R}^{d_c\times cn_h}$为上采样矩阵。我们注意到$c_t^Q$在这里同时用于query $q_t$以及索引query $q_t^I$的计算。最后,输出的attention就定义为
$$ o_{t,i}=\text{CoreAttn}(\mathtt{query}=q_{t,i},\mathtt{key}=C_t^{\text{SprsComp}},\mathtt{value}=C_t^{\text{SprsComp}}) $$
其中$o_{t,i}$表示第$i$个head在token $t$处的输出值;在MQA的最后,会涉及到多个head合并的output projection的操作。注意到$o_t\in\mathbb{R}^{cn_h}$,而希望重新投影回$d$,因此在投影的时候额外引入了一个trick:先把$n_h$个head的输入分成$g$个group,然后对于每一个group $o_{t,r}^G\in \mathbb{R}^{c\frac{n_h}{g}}$,我们将其投影到$d_g$维度的中间层$o_{t,r}^{G'}\in \mathbb{R}^{d_g}$,其中$d_g\lt c\frac{n_h}{g}$,最终再把中间结果进一步投影到$\hat{o}_t\in \mathbb{R}^d$。

Heavily Compressed Attention (HCA)

在这一章中,我们将讨论HCA:HCA的思想与CSA基本类似,但是使用更加激进的压缩比,同时删除了CSA中使用的前后block组合压缩以及sparse selection的思想。假设输入的hidden states为$H\in\mathbb{R}^{n\times d}$,HCA先计算出待压缩的原始KV entry $C\in\mathbb{R}^{n\times c}$以及对应的weight logit $Z\in \mathbb{R}^{n\times c}$:

$$ C = H\cdot W^{KV} $$
$$ Z = H\cdot W^Z $$

其中$W^{KV}, W^Z\in\mathbb{R}^{d\times c}$为可训练参数。类似CSA的思路,$C$中每$m'\gg m$个token都会根据压缩权重以及新引入的learnable positional bias $B\in\mathbb{R}^{m'\times c}$被压缩到一个token之中,得到$C^{\text{Comp}}\in \mathbb{R}^{\frac{n}{m'}\times c}$,其中每一个压缩之后项$C_i^{\text{Comp}}\in \mathbb{R}^c$如下计算:

$$ S_{m'i:m'(i+1)-1}=\text{Softmax}_{\text{token}}(Z_{m'i:m'(i+1)-1}+B), $$
$$ C_i^{\text{Comp}}=\sum_{j=m'i}^{m'(i+1)-1}S_j\odot C_j. $$

通过这一操作,HCA就把KV表征的序列长度压缩到到原来的$\frac{1}{m'}$;之后,HCA也类似CSA来进行KV共享的MQA以及output projection。在这里,由于没有了sparse selection的环节,因此query token会和所有满足因果约束的历史的compressed KV entries来计算attention(而不是CSA中的其中一个子集)。具体而言,对于一个给定的token $t$,我们有

$$ c_t^Q = h_t\cdot W^{DQ} $$
$$ [q_{t,1};q_{t,2};\dots;q_{t,n_h}]:=q_t=c_t^Q\cdot W^{UQ} $$

其中$h_t\in\mathbb{R}^d$为token t的hidden state;$n_h$表示query head的数量;$W^{DQ}\in\mathbb{R}^{d\times d_c}$以及$W^{UQ}\in\mathbb{R}^{d_c\times cn_h}$为可学的投影矩阵。最终,我们的便可以得到输出:

$$ o_{t,i}=\text{CoreAttn}\left(\mathtt{query}=q_{t,i},\mathtt{key}=C^{\text{Comp}},\mathtt{value}=C^{\text{Comp}}\right), $$
最后projection也先采用分组降维再投影的方式,以减少计算量。

总结

在第一部分中,我们总结了DeepSeek-V4在模型结构层面的核心设计逻辑;而在之后的分析中,我们将进一步深入DeekSeek-V4的更多设计细节,例如Muon优化器的使用,推理框架以及预训练和后训练的框架。