Kimi Delta Attention 完全拆解:固定循环状态如何替代不断增长的 KV Cache

7135 字
36 分钟
Kimi Delta Attention 完全拆解:固定循环状态如何替代不断增长的 KV Cache

KV Cache 为什么会成为长上下文的硬上限#

标准因果自注意力在生成第 tt 个 token 时,需要拿当前查询向量与此前 tt 个键向量做匹配,再对对应的值向量加权求和。历史 KK、VV 因而不能丢:每生成一步,KV Cache 都随上下文长度继续增长。若某层有 HkvH_{kv} 个 KV 头、单头维度为 dhd_h、每个元素占 bb 字节,长度为 TT 时,该层缓存约为

MKV=2THkvdhb.M_{KV}=2T H_{kv}d_h b.

前面的系数 2 分别对应 Key 和 Value。这个表达式揭示了两个彼此叠加的问题:缓存容量与 TT 成正比;decode 每一步还要把这些历史向量从显存读入计算单元。长上下文下,后者常常比乘加本身更先成为瓶颈。PagedAttention 可以减少碎片,量化可以降低每个元素的字节数,稀疏注意力可以少读一部分 token,但它们通常仍保留一份随序列增长的历史表示。

Kimi Delta Attention(KDA)选择了另一条路线:不保存每个 token 的独立 KK、VV,而把过去压缩进固定大小的矩阵状态。KDA 出自月之暗面团队于 2025 年 10 月发布的技术报告 Kimi Linear: An Expressive, Efficient Attention Architecture。它不是简单地把 softmax 去掉,而是把线性注意力重新解释成一张可在线改写的关联记忆,再用 Delta Rule 解决旧映射覆盖不干净的问题,用逐通道门控控制不同记忆方向的寿命。

这里先区分两个容易混淆的名字:KDA 是一个注意力算子;Kimi Linear 是围绕它搭建的混合模型架构。后者按 3:1 交错放置 KDA 层和全局 MLA 层,因此不是纯线性注意力模型。这个混合结构保留少量精确的全局检索通道,同时让大多数层不再维护随长度增长的 KV Cache。

Kimi Linear 的 KDA、MLA 与 MoE 交错架构
Kimi Linear 的 KDA、MLA 与 MoE 交错架构

图 1:Kimi Linear 以 3 个 KDA 层配 1 个 MLA 层,KDA 内部还包含短卷积、逐通道遗忘门和输出门。来源:MoonshotAI/Kimi-Linear 官方仓库。

这张图也解释了论文所说“KV Cache 最多减少 75%”的来源:在足够长的序列上,3/4 的 token-mixing 层只保留定长状态,只有 1/4 的 MLA 层继续保存逐 token 缓存。它不是把整个模型的缓存凭空压成四分之一,因为 KDA 状态、短卷积状态和 MLA 的固定开销仍然存在;“最多”描述的是长序列极限下的架构比例。

从线性注意力到 Delta Rule#

线性注意力是一张矩阵形式的关联表#

先看最朴素的递推式。设单个注意力头在时刻 tt 产生查询、键和值 qt∈Rdk\mathbf q_t\in\mathbb R^{d_k}、kt∈Rdk\mathbf k_t\in\mathbb R^{d_k}、vt∈Rdv\mathbf v_t\in\mathbb R^{d_v},矩阵状态为 St∈Rdk×dv\mathbf S_t\in\mathbb R^{d_k\times d_v}:

St=St−1+ktvt⊤,ot=St⊤qt.\mathbf S_t=\mathbf S_{t-1}+\mathbf k_t\mathbf v_t^{\top}, \qquad \mathbf o_t=\mathbf S_t^{\top}\mathbf q_t.

外积 ktvt⊤\mathbf k_t\mathbf v_t^{\top} 把“键 kt\mathbf k_t 应当读出值 vt\mathbf v_t”写进状态。查询时,St⊤qt\mathbf S_t^{\top}\mathbf q_t 根据查询和各个已写入键方向的相似程度组合值。将递推式展开可得

ot=∑i=1t(qt⊤ki)vi.\mathbf o_t =\sum_{i=1}^{t} \left(\mathbf q_t^{\top}\mathbf k_i\right)\mathbf v_i.

这就是线性注意力的关键交换律:先累计 kivi⊤\mathbf k_i\mathbf v_i^{\top},再与查询相乘,不再显式形成 T×TT\times T 的注意力矩阵。decode 时,每一步只需更新并读取固定大小的 S\mathbf S。

问题是,同一键方向被多次写入时,外积只会不断相加。假设“Paris”先与一个旧值关联,后文又给出新值,朴素更新不会删除旧映射,两个值会在同一方向叠加。序列越长,状态里的关联干扰越严重。

Delta Rule 先计算写入误差,再做定向修正#

DeltaNet 不再最大化无界的键值相关性,而是把当前状态在键 kt\mathbf k_t 上读出的结果逼近目标值 vt\mathbf v_t。其重构损失为

Lt(S)=12∥S⊤kt−vt∥22.\mathcal L_t(\mathbf S) =\frac{1}{2} \left\|\mathbf S^{\top}\mathbf k_t-\mathbf v_t\right\|_2^2.

其中,S⊤kt\mathbf S^{\top}\mathbf k_t 是写入之前从同一键方向读出的旧值。对 S\mathbf S 求梯度并以步长 βt\beta_t 做一次更新:

∇SLt=kt(S⊤kt−vt)⊤,St=St−1−βtkt(St−1⊤kt−vt)⊤.\begin{aligned} \nabla_{\mathbf S}\mathcal L_t &=\mathbf k_t \left(\mathbf S^{\top}\mathbf k_t-\mathbf v_t\right)^{\top},\\ \mathbf S_t &=\mathbf S_{t-1} -\beta_t\mathbf k_t \left(\mathbf S_{t-1}^{\top}\mathbf k_t-\mathbf v_t\right)^{\top}. \end{aligned}

整理后得到经典 Delta Rule:

St=(I−βtktkt⊤)St−1+βtktvt⊤.\mathbf S_t =\left(\mathbf I-\beta_t\mathbf k_t\mathbf k_t^{\top}\right) \mathbf S_{t-1} +\beta_t\mathbf k_t\mathbf v_t^{\top}.

这条式子可拆成“擦除”和“写入”两步。第一项从旧状态中减去与当前键方向重合的部分;第二项把新键值映射写回去。若 ∣kt∣2=1|\mathbf k_t|_2=1 且 βt=1\beta_t=1,左乘 I−ktkt⊤\mathbf I-\mathbf k_t\mathbf k_t^{\top} 会完全去除旧状态在 kt\mathbf k_t 方向的投影,然后写入新值。若 0<βt<10<\beta_t<1,更新更保守,相当于只修正一部分预测误差。

这也是官方实现先对 qq、kk 做 L2 归一化的原因之一:键范数会直接影响状态转移矩阵的特征值;归一化后,βt\beta_t 才能更可控地表达更新强度。

Gated DeltaNet 仍只有一个遗忘旋钮#

Delta Rule 会覆盖与当前键相似的旧关联,却没有主动衰减长期不再访问的内容。Gated DeltaNet(GDN)在每个头上增加一个标量门 αt∈[0,1]\alpha_t\in[0,1]:

St=αt(I−βtktkt⊤)St−1+βtktvt⊤.\mathbf S_t =\alpha_t \left(\mathbf I-\beta_t\mathbf k_t\mathbf k_t^{\top}\right) \mathbf S_{t-1} +\beta_t\mathbf k_t\mathbf v_t^{\top}.

αt\alpha_t 小,整个头的旧记忆一起快速衰减;αt\alpha_t 接近 1,整个头一起长期保留。它比完全不遗忘更稳定,但一个标量必须同时决定 dkd_k 个键通道的寿命。只要一个头里并存“应长期保存的实体关系”和“应迅速丢弃的局部语法状态”,这一个旋钮就会冲突。

KDA 的核心:让遗忘从一个标量细化为逐通道向量#

KDA 把 GDN 的标量 αt\alpha_t 换成向量 αt∈[0,1]dk\boldsymbol\alpha_t\in[0,1]^{d_k},形成对角矩阵 Diag⁡(αt)\operatorname{Diag}(\boldsymbol\alpha_t):

St=(I−βtktkt⊤)Diag⁡(αt)St−1+βtktvt⊤,ot=St⊤qt.\mathbf S_t =\left(\mathbf I-\beta_t\mathbf k_t\mathbf k_t^{\top}\right) \operatorname{Diag}(\boldsymbol\alpha_t) \mathbf S_{t-1} +\beta_t\mathbf k_t\mathbf v_t^{\top}, \qquad \mathbf o_t=\mathbf S_t^{\top}\mathbf q_t.

KDA 的对角门控、Delta 修正与外积写入
KDA 的对角门控、Delta 修正与外积写入

图 2:KDA 先用对角门逐行衰减旧状态,再做键方向上的 rank-1 修正,最后写入新的键值外积。来源:Kimi Linear 技术报告 第 3 节。

矩阵 S\mathbf S 的行对应键空间的通道,列对应值空间的通道。因此左乘 Diag⁡(αt)\operatorname{Diag}(\boldsymbol\alpha_t) 会逐行缩放状态:第 jj 个 αt,j\alpha_{t,j} 控制第 jj 个键通道保留多少历史。KDA 不是给每个历史 token 分配一个遗忘权重,而是给压缩状态中的不同方向分配不同时间尺度。

令

S~t−1=Diag⁡(αt)St−1,\widetilde{\mathbf S}_{t-1} =\operatorname{Diag}(\boldsymbol\alpha_t)\mathbf S_{t-1},

则 KDA 更新可写成

St=S~t−1−βtkt(S~t−1⊤kt−vt)⊤.\mathbf S_t =\widetilde{\mathbf S}_{t-1} -\beta_t\mathbf k_t \left(\widetilde{\mathbf S}_{t-1}^{\top}\mathbf k_t-\mathbf v_t\right)^{\top}.

这个形式最容易理解:先按通道遗忘得到衰减状态,再用当前键查询它,计算“旧预测与新目标之间的误差”,最后沿 kt\mathbf k_t 方向修正。αt\boldsymbol\alpha_t 决定哪些记忆方向能进入本轮更新,βt\beta_t 决定当前键值对要把对应映射改写到什么程度,两者分工不同。

几个退化情形可以帮助定位 KDA:

  • 当 αt=1\boldsymbol\alpha_t=\mathbf 1 时,它退化为 DeltaNet;
  • 当向量所有分量相等时,它退化为标量门控的 GDN;
  • 当 βt=0\beta_t=0 时,本轮只遗忘、不写入;
  • 当去掉 Delta 修正项时,它更接近只做逐通道衰减的 GLA,而不再显式修正键冲突。

门控与 Delta 变换的顺序不能随意交换。一般情况下,Diag⁡(αt)\operatorname{Diag}(\boldsymbol\alpha_t) 与 ktkt⊤\mathbf k_t\mathbf k_t^{\top} 不可交换;KDA 的定义是先衰减旧状态,再以当前键为基准纠错。这个顺序同时决定了模型表达能力和后续 chunkwise 推导的结构。

一次 KDA 前向究竟发生了什么#

论文中的递推式只描述核心状态机,实际 KDA 层还要从隐藏表示生成 qq、kk、vv、两个门,并在输出前做归一化和门控。设输入为 xt∈Rd\mathbf x_t\in\mathbb R^d,第 hh 个头的主要参数化为

qth,kth=L2Norm⁡(Swish⁡(ShortConv⁡(Wq/khxt))),vth=Swish⁡(ShortConv⁡(Wvhxt)),αth=f(Wα↑Wα↓xt),βth=Sigmoid⁡(Wβhxt).\begin{aligned} \mathbf q_t^h,\mathbf k_t^h &=\operatorname{L2Norm} \left(\operatorname{Swish} \left(\operatorname{ShortConv} \left(\mathbf W_{q/k}^h\mathbf x_t\right)\right)\right),\\ \mathbf v_t^h &=\operatorname{Swish} \left(\operatorname{ShortConv} \left(\mathbf W_v^h\mathbf x_t\right)\right),\\ \boldsymbol\alpha_t^h &=f\left(\mathbf W_{\alpha}^{\uparrow} \mathbf W_{\alpha}^{\downarrow}\mathbf x_t\right),\\ \beta_t^h &=\operatorname{Sigmoid} \left(\mathbf W_{\beta}^h\mathbf x_t\right). \end{aligned}

ShortConv 是沿序列方向的小型深度卷积,论文实现采用长度为 4 的卷积核。固定状态擅长概括历史,却未必擅长无损表达刚刚出现的几个 token;短卷积相当于额外提供一个极小的局部窗口。消融实验中,去掉卷积后验证困惑度从 5.65 上升到 5.70,说明即使混合架构中已有全局注意力,局部归纳偏置仍有作用。

αt\boldsymbol\alpha_t 通过先降维、再升维的投影生成,避免直接使用一个巨大的全秩门控矩阵。官方 FLA 实现通常在对数域生成门值:代码中的 g 保存 log⁡α\log\boldsymbol\alpha,累积衰减由 cumsum 完成,随后再取指数。这比连续直接相乘许多接近 1 的 BF16 数更稳定。

核心递推完成后,各头输出还会经过 RMSNorm、Sigmoid 输出门和线性投影:

yt=Wo[Sigmoid⁡(Wg↑Wg↓xt)⊙RMSNorm⁡(KDA⁡(qt,kt,vt,αt,βt))].\mathbf y_t =\mathbf W_o \left[ \operatorname{Sigmoid} \left(\mathbf W_g^{\uparrow}\mathbf W_g^{\downarrow}\mathbf x_t\right) \odot \operatorname{RMSNorm} \left(\operatorname{KDA} \left(\mathbf q_t,\mathbf k_t,\mathbf v_t, \boldsymbol\alpha_t,\beta_t\right)\right) \right].

这个输出门控制“已经从记忆中读出的内容有多少送往残差流”,它与状态遗忘门不是同一个门。论文消融中,去掉输出门的验证困惑度为 5.67,改用 Swish 门则恶化到 5.81,而默认 Sigmoid 为 5.65。这里的信号是:状态内部如何记忆与层输出向残差流暴露多少信息,需要分开控制。

按 token 观察,KDA 的 decode 路径可以概括为以下顺序:

输入 x_t
├─ 短卷积与投影得到 q_t、k_t、v_t
├─ 生成逐键通道遗忘门 alpha_t 与更新率 beta_t
├─ S_decay = Diag(alpha_t) S_prev
├─ old_value = S_decay^T k_t
├─ S_t = S_decay + beta_t k_t (v_t - old_value)^T
├─ memory_output = S_t^T q_t
└─ RMSNorm、输出门与 W_o 投影

在因果语义上,式中输出使用更新后的 St\mathbf S_t,因此当前位置可以看到自己;实现必须让并行 prefill 路径与逐 token recurrent 路径保持同样的因果掩码,否则两种模式会产生不同结果。

固定状态如何改变推理复杂度#

缓存从 O(T)O(T) 变成 O(1)O(1),但常数并不为零#

每个 KDA 头只需保存 dk×dvd_k\times d_v 的状态矩阵,缓存量为

MKDA=Hdkdvb,M_{KDA}=H d_k d_v b,

它不含序列长度 TT。以 H=32H=32、dk=dv=128d_k=d_v=128、BF16 为例,核心状态约为

32×128×128×2=1,048,576 bytes,32\times128\times128\times2=1{,}048{,}576\ \text{bytes},

即每个 KDA 层约 1 MiB,另有很小的短卷积缓存。作为量纲对比,同样头数和维度的普通多头 KV Cache 在 1M token 时单层约为 16.4 GB,也就是约 15.3 GiB。这个对比不是 Kimi Linear 与 MLA 的实测值,因为 MLA 本身已经压缩了 KV 表示;它只用来说明“按 token 存档”和“维护关联矩阵”在渐近行为上的差异。

固定状态也带来不可逆的信息瓶颈。KV Cache 像一摞仍可逐页检索的原始卡片,KDA 状态更像反复改写的索引表:容量不会增长,但两个键方向过于相似时会互相干扰,已经被门控衰减的信息也无法在后续恢复。Kimi Linear 保留周期性 MLA 层,正是为了给精确检索留出未压缩路径。

Decode 的每 token 代价不再依赖上下文长度#

标准全注意力 decode 中,当前查询要读取并点乘 TT 个历史键和值,单头主项为 O(Tdh)O(Td_h)。KDA 需要读写 dk×dvd_k\times d_v 状态,单头主项为 O(dkdv)O(d_kd_v)。当 dkd_k、dvd_v 固定时,后者与 TT 无关。

不过,“线性注意力”并不意味着任意长度都更快。短上下文时,FlashAttention 对规则的矩阵运算优化非常成熟,而 KDA 需要生成门、运行短卷积、更新矩阵状态;这些固定开销可能抵消理论优势。论文实测中,4K 到 16K 的 Kimi Linear 与 MLA 延迟接近,长度进入 128K 以上后差距才持续扩大。

Prefill 仍是线性复杂度,但要用并行形式才能跑快#

若严格按递推式处理长度为 TT 的输入,虽然总 FLOPs 是线性的,却形成 TT 步串行依赖,GPU 会因并行度不足而低效。论文给出单头、头维 dhd_h、chunk 大小 C=64C=64 时 KDA chunk kernel 的理论 FLOPs:

FLOPs⁡KDA=6Tdh2+3TCdh+TC2.\operatorname{FLOPs}_{KDA} =6Td_h^2+3TCd_h+TC^2.

全局注意力的主项则为

FLOPs⁡Attn=2T2dh.\operatorname{FLOPs}_{Attn}=2T^2d_h.

KDA 关于 TT 是一次项,全注意力是二次项;但 KDA 的一次项系数包含 dh2d_h^2 和 chunk 内辅助计算,所以真正的交叉点由形状、精度、Kernel 和硬件共同决定,不能只看大 O 记号。

Chunkwise 并行与硬件感知 Kernel#

为什么不能直接做 prefix scan#

将 KDA 写成一般状态空间形式:

St=AtSt−1+Bt,\mathbf S_t=\mathbf A_t\mathbf S_{t-1}+\mathbf B_t,

其中

At=(I−βtktkt⊤)Diag⁡(αt),Bt=βtktvt⊤.\mathbf A_t =\left(\mathbf I-\beta_t\mathbf k_t\mathbf k_t^{\top}\right) \operatorname{Diag}(\boldsymbol\alpha_t), \qquad \mathbf B_t=\beta_t\mathbf k_t\mathbf v_t^{\top}.

状态转移可以组合,所以理论上可做并行扫描;但若显式构造并相乘每个 dk×dkd_k\times d_k 的 At\mathbf A_t,计算和存储成本会吞掉线性注意力的优势。KDA Kernel 的任务是利用“对角矩阵减 rank-1 矩阵”的特殊结构,把一段 token 的连续更新压成少量密集矩阵乘。

chunk 内并行、chunk 间递推#

把序列切成长度 CC 的块。块与块之间传递边界状态 S[n]\mathbf S_{[n]},这部分仍按块顺序推进;一个块内部的 CC 个 token 则通过 WY 表示和 UT 变换批量计算。令 γ[n]i→j\boldsymbol\gamma_{[n]}^{i\rightarrow j} 表示从块内第 ii 个位置到第 jj 个位置逐通道门值的累积乘积:

γ[n]i→j=∏r=ijα[n]r.\boldsymbol\gamma_{[n]}^{i\rightarrow j} =\prod_{r=i}^{j}\boldsymbol\alpha_{[n]}^r.

这里的乘积是逐元素乘法。论文把一连串对角加 rank-1 转换压缩成辅助矩阵 W[n]\mathbf W_{[n]} 和 U[n]\mathbf U_{[n]},边界状态更新写成

S[n+1]=Diag⁡(γ[n]C)S[n]+(Γ[n]i→C⊙K[n])⊤(U[n]−W[n]S[n]).\mathbf S_{[n+1]} =\operatorname{Diag} \left(\boldsymbol\gamma_{[n]}^C\right)\mathbf S_{[n]} +\left(\boldsymbol\Gamma_{[n]}^{i\rightarrow C} \odot\mathbf K_{[n]}\right)^{\top} \left(\mathbf U_{[n]}-\mathbf W_{[n]}\mathbf S_{[n]}\right).

Γ\boldsymbol\Gamma 堆叠了块内各位置的累积衰减,⊙\odot 是逐元素乘法。括号中的

U[n]−W[n]S[n]\mathbf U_{[n]}-\mathbf W_{[n]}\mathbf S_{[n]}

可以看作块内所有 Delta 修正后的“伪 Value”。它已经扣除了边界状态对当前各键的旧预测,因此能够一次参与矩阵乘法,而不必逐 token 修改状态。

块内所有输出由两部分组成:

O[n]=(Γ[n]1→C⊙Q[n])S[n]+Tril⁡[(Γ[n]1→C⊙Q[n])(K[n]Γ[n]1→C)⊤](U[n]−W[n]S[n]).\begin{aligned} \mathbf O_{[n]} ={}& \left(\boldsymbol\Gamma_{[n]}^{1\rightarrow C} \odot\mathbf Q_{[n]}\right)\mathbf S_{[n]}\\ &+\operatorname{Tril} \left[ \left(\boldsymbol\Gamma_{[n]}^{1\rightarrow C} \odot\mathbf Q_{[n]}\right) \left( \frac{\mathbf K_{[n]}} {\boldsymbol\Gamma_{[n]}^{1\rightarrow C}} \right)^{\top} \right] \left(\mathbf U_{[n]}-\mathbf W_{[n]}\mathbf S_{[n]}\right). \end{aligned}

第一项让块内查询读取进入该块之前的历史状态,属于 inter-chunk 部分;第二项只用下三角矩阵,让每个位置读取同一块中不晚于自己的更新,属于 intra-chunk 部分。这样既维持因果性,又把主要工作变成 Tensor Core 擅长的矩阵乘。

KDA 为什么比一般 DPLR 更容易做成快 Kernel#

一般 DPLR 转移写作

St=(Dt−atbt⊤)St−1+ktvt⊤.\mathbf S_t =\left(\mathbf D_t-\mathbf a_t\mathbf b_t^{\top}\right) \mathbf S_{t-1}+\mathbf k_t\mathbf v_t^{\top}.

KDA 是它的受约束特例:

Dt=Diag⁡(αt),at=βtkt,bt=kt⊙αt.\mathbf D_t=\operatorname{Diag}(\boldsymbol\alpha_t), \quad \mathbf a_t=\beta_t\mathbf k_t, \quad \mathbf b_t=\mathbf k_t\odot\boldsymbol\alpha_t.

一般 DPLR 的 at\mathbf a_t、bt\mathbf b_t、kt\mathbf k_t 彼此独立,chunk 化时会产生更多相关矩阵,并涉及累积衰减的倒数。很小的门值连乘后容易下溢,直接求倒数又可能放大误差,常需二级分块和更高精度计算。KDA 把 rank-1 修正的两个方向绑定到同一个 kt\mathbf k_t,让若干项能够合并。论文报告,它相对一般 DPLR 少做两次二级分块和约三次矩阵乘,长度不超过 64K 时 Kernel 速度接近 2 倍。

这体现了 KDA 的硬件协同取舍:它没有追求最自由的 DPLR 参数化,而是选择足以表达逐通道遗忘与 Delta 改写、同时能被稳定 chunk 化的受约束形式。

与全注意力、Gated DeltaNet 和 MLA 的边界#

把几种机制放在同一个状态视角下,KDA 的位置会更清楚。

机制长期表示遗忘粒度是否修正键冲突Decode 历史读取量精确历史检索
全注意力逐 token KV Cache不主动遗忘不适用随 TT 增长强
MLA每个 token 的低秩潜向量与位置分量不主动遗忘不适用随 TT 增长,但常数更小强
GLA固定矩阵状态逐键通道无 Delta 修正与 TT 无关受状态容量限制
Gated DeltaNet固定矩阵状态每头一个标量有与 TT 无关受状态容量限制
KDA固定矩阵状态每头、每键通道有与 TT 无关受状态容量限制

KDA 与 MLA 解决的不是同一个问题。MLA 仍保留每个历史位置,只是先把多个头的 KV 压到低秩潜空间;KDA 则不保留 token 级档案,只维护固定大小的动态状态。关于 MLA 的低秩吸收与缓存结构,可参考 MLA 完全拆解。两者在 Kimi Linear 里并非竞争关系,而是组成分工明确的混合层:KDA 承担大部分便宜的历史压缩,MLA 周期性恢复精确的全局内容寻址。

KDA 与 Gated DeltaNet 的差别看似只是 αt\alpha_t 从一个数变成 dkd_k 个数,实际同时扩大了记忆控制和位置表达的自由度。把递推完全展开后,第 ii 个值对时刻 tt 输出的贡献包含从 i+1i+1 到 tt 的状态转移连乘:

ot=∑i=1t[qt⊤(∏j=i+1t(I−βjkjkj⊤)Diag⁡(αj))ki]vi.\mathbf o_t =\sum_{i=1}^{t} \left[ \mathbf q_t^{\top} \left( \prod_{j=i+1}^{t} \left(\mathbf I-\beta_j\mathbf k_j\mathbf k_j^{\top}\right) \operatorname{Diag}(\boldsymbol\alpha_j) \right) \mathbf k_i \right]\mathbf v_i.

历史 token 的权重不再只由 qt⊤ki\mathbf q_t^{\top}\mathbf k_i 决定,还取决于中间每一步如何遗忘和改写状态。这个数据相关的转移链会自然编码顺序与距离。GDN 的标量衰减只能让整个键空间按同一速率缩放;KDA 的不同通道可以形成不同有效时间尺度,作用类似“可学习、内容相关的位置变换”。这也是 Kimi Linear 的全局 MLA 层使用 NoPE 的原因:模型把主要的位置感知职责交给 KDA,而不是再向 MLA 注入固定 RoPE 频率。

不能据此把 KDA 简化成“另一种位置编码”。它同时执行三件事:逐通道衰减、键方向上的 Delta 纠错、键值关联写入;位置感知是状态转移连乘产生的结果,而不是独立附加的向量。

Kimi K3 把这条路线扩展到更大模型。其官方模型说明列出 93 个注意力层,其中 69 层为 KDA、24 层为 Gated MLA,比例仍接近 3:1。这说明 KDA 在 K3 中不是一个外围优化开关,而是大多数注意力层的 token mixer;但 K3 还加入 Attention Residual、Gated MLA、Stable LatentMoE 和多模态组件,不能把 K3 的整体能力或效率全部归因于 KDA。

部署时真正要处理的问题#

Prefill 与 decode 必须走不同 Kernel#

同一个数学递推有两种适合硬件的执行形态:prefill 输入很长,首要目标是并行,应该使用 chunkwise KDA;decode 每次只有一个或少量新 token,直接使用 fused recurrent KDA 更合适。前者把块内依赖改写成矩阵乘,后者避免为单 token 构造 C×CC\times C 辅助矩阵。

Flash Linear Attention 的 KDA 层实现把模式明确分为 chunk 和 fused_recurrent。在 Hugging Face 中只让默认 PyTorch 路径跑通,并不能代表生产性能;Transformers 的 Kimi Linear 文档也明确说明,默认是纯 PyTorch 实现,安装并启用自定义 Kernel 后才适合长序列。吞吐评测若没有报告具体后端,很容易把框架开销误判成算法性能。

Cache 管理从“页表”变成“状态快照加少量 KV”#

混合模型同时维护两类缓存:MLA 层的逐 token KV 表示,以及 KDA 层的矩阵状态和短卷积尾部。请求进入、暂停、迁移或被抢占时,服务框架必须把两类状态当作一个一致的请求快照处理。只保存 KDA 矩阵而漏掉短卷积的最近几个输入,恢复后的前几个 token 就会错误。

前缀缓存也不再只是共享 KV block。若多个请求共享长度为 pp 的前缀,KDA 可以复用前缀结束处的状态 Sp\mathbf S_p;但这个状态只代表完整处理到 pp 的结果,不能像 KV block 那样从中删除中间某段。前缀任何位置被编辑,编辑点之后的循环状态都失效,必须从最近的有效快照重新 prefill。系统需要在“多存状态检查点以提高命中率”和“少占显存”之间权衡。

对 KDA 而言,一个状态快照的大小与前缀长度无关,却与 KDA 层数、头数和 dkdvd_kd_v 成正比。因此短前缀未必值得缓存状态,长前缀则可能非常划算。这与普通 KV 前缀缓存“越长占用越大”的成本模型不同,淘汰算法也不能简单照搬。

数值路径必须以 recurrent 版本为基准校验#

Chunkwise 公式包含门值累计、指数、除法、下三角求逆和多个混合精度矩阵乘。实现通常在对数域累积 α\boldsymbol\alpha,对部分辅助量使用 FP32,再把主要 GEMM 放回 BF16/FP16。若所有步骤都强制低精度,长块上的累积衰减可能下溢;若全部使用 FP32,又会失去 Tensor Core 吞吐。

可靠的 Kernel 验证至少应检查三类一致性:

  1. 短序列上,chunkwise 输出与逐 token recurrent 输出接近;
  2. 带非零初始状态时,分两段计算与整段计算接近;
  3. 变长批处理、左填充和状态恢复后,单样本结果不受同批其他请求影响。

递推实现是最慢却最直接的数值 oracle。误差应按序列长度、门值范围和状态范数分桶观察,不能只在随机短输入上测一个全局余弦相似度。

并行策略要把固定状态算进通信和显存规划#

KDA 各头拥有独立状态,天然适合按头做张量并行:每张卡保存本地头的 S\mathbf S,输出投影处再按模型的张量并行规则聚合。相比之下,序列并行需要在分段边界传递状态;通信量不随段长增长,但必须严格保持顺序。上下文并行若想并行多个序列段,则需要扫描式合并状态转移,而不能只把 token 区间平均切开。

生产部署还要为短请求保留回退路径。短上下文下,自定义 KDA Kernel 的启动与门控开销可能占主导;长上下文才逐渐显出固定状态的带宽优势。调度器应根据 prefill 长度、decode batch、硬件和已编译 shape 选择 Kernel,而不是只用一种路径覆盖所有请求。

实验结果应该怎样解读#

Kimi Linear 的质量、解码加速与长上下文 TPOT
Kimi Linear 的质量、解码加速与长上下文 TPOT

图 3:左图比较 4K MMLU-Pro 与 128K RULER 上的质量—速度位置,右图展示 TPOT 随解码上下文增长的变化。来源:MoonshotAI/Kimi-Linear 官方仓库。

论文最有价值的实验不是某个孤立榜单分数,而是三组对照共同回答了“收益究竟来自哪里”。

第一组是相同规模和相同配方下的 MLA、混合 GDN 与 Kimi Linear 对照。在 1.4T token 的可比实验中,Kimi Linear 在 128K RULER 上为 84.3,MLA 为 81.3,GDN-H 为 80.5。它说明固定状态不必然以长上下文质量大幅下降为代价,也说明逐通道门相对标量门有实际收益。不过,结果属于完整的 3:1 混合模型,不能解读成“纯 KDA 在任何任务都超过纯全注意力”。

第二组是混合比例消融:

KDA训练困惑度验证困惑度含义
0:19.455.77全 MLA 基线
1:19.295.66质量接近最佳,但全注意力层更多
3:19.235.65论文采用的质量—效率折中
7:19.235.70训练拟合不差,分布外验证变弱
15:19.345.82全局精确检索通道过少

3:1 并不是从复杂度公式直接算出的常数,而是特定模型规模与实验设置下的经验折中。7:1 的训练困惑度与 3:1 相同,验证困惑度却更高,提示更多 KDA 层可以拟合语料,却可能因有限状态干扰而损失泛化和检索能力。

第三组是速度口径。论文的 batch size 为 1 测试中,1M 输入的 prefill 相对 MLA 约快 2.9 倍,decode TPOT 约快 2.2 倍;在利用节省的缓存扩大有效 batch 后,图 3 报告 1M 上下文下 TPOT 从 11.48 ms 降到 1.84 ms,对应 6.3 倍。前一个数字主要反映单请求 Kernel 路径,后一个数字还包含内存节省带来的批处理增益。两种结果都成立,但回答的是不同问题。

4K 到 16K 区间的曲线接近也很重要:固定状态并不会自动保证所有长度都加速。只有当全注意力读取长 KV Cache 的代价足够大时,KDA 的门控、状态更新和混合层固定开销才被摊薄。

局限与适用场景#

KDA 最适合长 prefill、长时间 decode、并发批处理受 KV Cache 容量限制的负载。工具调用轨迹、代码仓库分析、长文档多轮问答和持续生成都可能受益,因为上下文继续增长时,大多数层的状态大小不变。

它的边界同样清晰。

第一,固定状态是有损压缩。模型不能从 St\mathbf S_t 中重新展开全部历史 token。随机访问式的精确复制、针尖检索和大量相似键并存时,状态碰撞仍可能发生。逐通道门改善了“记多久”,Delta Rule 改善了“如何覆盖”,但都没有消除有限容量本身。

第二,KDA 把算法复杂度转移给了 Kernel 和运行时。没有 chunkwise 并行时,prefill 会退化成低并行度循环;没有 fused recurrent Kernel 时,decode 会产生过多小算子;没有混合缓存管理时,抢占、前缀复用和连续批处理都可能出错。论文级公式只是起点,系统实现决定理论收益能否兑现。

第三,状态缓存并非处处更小。它与序列长度无关,却有 HdkdvH d_kd_v 的固定底座;极短请求、很大的状态维度或极强 KV 量化下,KDA 的缓存优势会缩小。混合架构还保留 MLA 层,所以总显存不会真正变成常数。

第四,现有全注意力模型不能只替换一个推理算子就变成 KDA。两者的状态语义、权重参数化和位置建模不同。Kimi Linear 所说的基础设施兼容,主要指模型已经按 KDA 架构准备好后,可以接入标准生成接口和服务框架;它不意味着无需适配权重便能把任意 Transformer 无损转换。

第五,实验中的质量来自 KDA、3:1 混合、NoPE、短卷积、输出门和其他模型组件的共同作用。若只在另一个模型里移植递推式,却改变头维、门参数化或全注意力比例,论文中的质量与速度数字都不能直接外推。

小结#

KDA 的核心不是“把注意力近似成线性复杂度”这一句口号,而是一套从记忆规则到 GPU Kernel 连贯闭合的设计:

  1. 用 dk×dvd_k\times d_v 的固定矩阵状态替代逐 token KV Cache;
  2. 用 Delta Rule 先擦除当前键方向上的旧预测,再写入新关联;
  3. 用逐键通道门控制不同记忆方向的寿命,补足 GDN 标量门的粗粒度;
  4. 把对角加 rank-1 的状态转移压成 chunk 内矩阵乘,prefill 使用 chunkwise Kernel,decode 使用 recurrent Kernel;
  5. 在模型层面保留周期性 MLA,用少量全注意力弥补固定状态对精确检索的不利影响。

因此,KDA 的真正价值不只在 O(1)O(1) 状态,也不只在某个长上下文加速数字。它展示了一种更完整的推理侧架构方法:先选择可控的记忆更新规则,再约束数学结构以适配 Tensor Core,最后让调度器同时管理循环状态与全注意力缓存。模型、Kernel 和服务系统三层必须一起成立。

参考资料#

  1. Kimi Linear: An Expressive, Efficient Attention Architecture
  2. MoonshotAI/Kimi-Linear 官方仓库
  3. Flash Linear Attention:KDA 官方算子实现
  4. MoonshotAI/FlashKDA 高性能 Kernel
  5. Gated Delta Networks: Improving Mamba2 with Delta Rule
  6. Linear Transformers Are Secretly Fast Weight Programmers
  7. Transformers are RNNs: Fast Autoregressive Transformers with Linear Attention
  8. Hugging Face Transformers:Kimi Linear 模型文档
  9. MoonshotAI/Kimi-K3 官方仓库

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Kimi Delta Attention 完全拆解:固定循环状态如何替代不断增长的 KV Cache
https://pinghaoyang.com.cn/aigc/posts/kimi-delta-attention/
作者
平昊阳
发布于
2026-10-12
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
平昊阳
乘长风,破巨浪, 展鸿图于未央!
--
总访问量
--
访客数
公告
欢迎来到我的个人博客!欢迎关注交流吖!
更多相关公告,见
「社交-留言」。
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
站点统计
文章
212
分类
26
标签
291
总字数
2,407,536
运行时长
0 天
最后活动
0 天前

文章目录