Attention Variants · Source Walkthrough

Linear Attention, From Scratch to Kimi 3 (Kimi Delta Attention)

Start with a two-dimensional associative memory. Add error-correcting writes, adaptive forgetting, and KDA's channel-wise gate. Then work through one complete KDA update using Kimi K3's real dimensions as the reference.

Kimi K3 does not replace every attention layer with one new trick. Its text backbone alternates a fixed-state recurrent memory, Kimi Delta Attention (KDA), with periodic full Multi-head Latent Attention (MLA). Understanding it means answering three separate questions: what the state stores, how one token edits it, and why a few layers still keep a token-growing cache.

Where this mechanism is used

KDA is the central linear-attention mechanism in Kimi Linear and Kimi K3. Its direct predecessor, Gated DeltaNet, combines Mamba-2-style decay with DeltaNet's associative-memory update. The concrete reference throughout this post is Kimi K3's official Hugging Face code plus the production kernels in Flash Linear Attention (FLA).

1.Where KDA sits in Kimi K3

Kimi K3's text backbone has 93 decoder layers. The configuration marks 69 as KDA layers and 24 as full MLA layers. The dominant rhythm is:

KDA
fixed recurrent state
KDA
fixed recurrent state
KDA
fixed recurrent state
↓ every fourth layer
Full MLA
global token-to-token attention
repeat 3:1 through most of the stack; the final layer is also full attention

These layers solve different problems. KDA compresses the past into a fixed matrix, so decode cost does not grow with context length. MLA can still look directly at every cached token, which helps exact long-context retrieval. K3 spends most layers on the cheap recurrent path and periodically restores full global interaction.

Keep the names separate: KDA is one attention mechanism. Kimi K3 is the larger hybrid architecture containing KDA, MLA, MoE blocks, and—in the multimodal model—a vision tower.

2.Why ordinary attention caches tokens

For one query \(q_t\), ordinary causal attention compares it with every earlier key and mixes the corresponding values:

\[ o_t=\sum_{i\le t} \operatorname{softmax}_i\!\left(\frac{q_t^\top k_i}{\sqrt{d_k}}\right)v_i. \]

The useful feature is direct access: token \(t\) can assign a fresh weight to every earlier token. The cost is that decoding must preserve every earlier \(k_i\) and \(v_i\).

new token produces   q_t, k_t, v_t
↓ append
KV cache:   (k_1,v_1), (k_2,v_2), ... , (k_t,v_t)
↓ next query scans all cached tokens
cache size and decode work grow with context length

Softmax prevents us from simply computing \(K^\top V\) once and reusing it: the normalization depends on the current query and all of its scores. Linear attention changes the interaction so the history can be summarized before the query arrives.

3.Linear attention as a fixed associative memory

What are we trying to do? Ordinary attention keeps every past key and value as a growing list. Linear attention tries to replace that list with one fixed matrix \(S\). Each arriving key–value pair writes into \(S\); a later query reads from it.

The two-dimensional example below is not yet DeltaNet or KDA. It is the minimal baseline that shows what the matrix stores, how an outer product writes a key–value association, and how a query retrieves a mixture from that memory.

Start with the smallest useful case. Keys and values are both two-dimensional. Store this first pair:

\[ k_1=\begin{bmatrix}1\\0\end{bmatrix}, \qquad v_1=\begin{bmatrix}3\\4\end{bmatrix}. \]

The outer product writes a matrix:

\[ S_1=k_1v_1^\top = \begin{bmatrix}1\\0\end{bmatrix} \begin{bmatrix}3&4\end{bmatrix} = \begin{bmatrix}3&4\\0&0\end{bmatrix}. \]

Now store a second pair, \(k_2=[0,1]^\top\), \(v_2=[5,6]^\top\):

\[ S_2=S_1+k_2v_2^\top = \begin{bmatrix}3&4\\5&6\end{bmatrix}. \]
Read the memory

Ask with \(q=[0.25,0.75]^\top\). The output is:

\[ S_2^\top q = 0.25\begin{bmatrix}3\\4\end{bmatrix} + 0.75\begin{bmatrix}5\\6\end{bmatrix} = \begin{bmatrix}4.5\\5.5\end{bmatrix}. \]
The query mixes rows of the memory. Each row is a complete value-space vector associated with one key-space coordinate.

Now generalize. Let a key have width \(K\), a value have width \(V\), and use column vectors:

\[ k_t,q_t\in\mathbb{R}^{K}, \qquad v_t\in\mathbb{R}^{V}, \qquad S_t\in\mathbb{R}^{K\times V}. \]

Causal linear attention is the recurrence:

Add an association, then read it
\[ S_t=S_{t-1}+k_tv_t^\top, \qquad o_t=S_t^\top q_t. \]

The state remains \(K\times V\) whether the sequence has 10 tokens or one million. That is the storage win.

The most important shape correction

\(S_t\) is not a token-by-token attention matrix. It has no sequence dimension. In K3, one head's state is \(128\times128\): key coordinates by value coordinates. The sequence has already been compressed into that matrix.

What plain additive memory gets wrong

The same fixed state is also the bottleneck. Outer products from many tokens overlap. If a new token uses a key similar to an old key, it adds another value on top; it cannot deliberately replace the old association. Nothing is forgotten, so interference accumulates.

4.DeltaNet: write the error, not the whole value

Suppose the memory currently maps \(k=[1,0]^\top\) to \([3,4]^\top\), but a new token wants that same key to map to \([9,2]^\top\). Adding the whole new value would leave both associations superposed. DeltaNet first asks what the memory already returns:

\[ \text{recalled value}=S_{t-1}^\top k_t. \]

Then it computes only what is missing:

\[ \text{value error}=v_t-S_{t-1}^\top k_t. \]
A half-strength correction

Old recall: \([3,4]\). New target: \([9,2]\). The error is \([6,-2]\).

With update strength \(\beta=0.5\), write half the error: \([3,-1]\). The new recall becomes:

\[ [3,4]+[3,-1]=[6,3]. \]
The memory moves halfway from its old answer to the new target. It does not blindly add another full value.

The general delta-rule update is:

DeltaNet
\[ e_t=v_t-S_{t-1}^\top k_t, \qquad S_t=S_{t-1}+\beta_tk_te_t^\top. \]

Here \(\beta_t\in(0,1)\) is a learned update strength. If \(k_t\) is unit-normalized, then reading with the same key after the update interpolates toward the target:

\[ S_t^\top k_t =(1-\beta_t)S_{t-1}^\top k_t+\beta_tv_t. \]

That is why the delta rule is naturally described as an online learning step: \(S\) is a fast-weight map, and each token trains it on one \(k_t\mapsto v_t\) example.

One crisp correction: DeltaNet does not “subtract the current value.” It subtracts the value already recalled from memory from the new target value, then writes the residual.

5.Gated DeltaNet to KDA: learn when each channel forgets

DeltaNet can surgically revise the current key direction, but it has no fast way to clear broadly stale context. Gated DeltaNet (GDN) adds one data-dependent retention scalar \(\alpha_t\) per head. KDA changes that scalar into a vector with one retention value per key dimension.

Mamba-2adaptive scalar decay; additive state update in the SSD view
DeltaNettargeted error-correcting write
GDNdelta write + one decay per head
KDAdelta write + one decay per key channel
MechanismForget gateTargeted delta correction?
Mamba-2 / SSD viewone scalar per token and headno
DeltaNetnoneyes
Gated DeltaNet\(\alpha_t\in\mathbb{R}\) per token and headyes
KDA\(\boldsymbol{\alpha}_t\in\mathbb{R}^{K}\) per token and headyes

See the diagonal gate before the formula

Take a four-row state and four retention values:

\[ S= \begin{bmatrix} 1&2&3&4\\ 5&6&7&8\\ 9&10&11&12\\ 13&14&15&16 \end{bmatrix}, \qquad \boldsymbol{\alpha}= \begin{bmatrix}0.5\\0.9\\0.1\\1.0\end{bmatrix}. \]

KDA scales each whole row:

\[ \operatorname{Diag}(\boldsymbol{\alpha})S = \begin{bmatrix} 0.5&1&1.5&2\\ 4.5&5.4&6.3&7.2\\ 0.9&1&1.1&1.2\\ 13&14&15&16 \end{bmatrix}. \]

The first key coordinate is halved, the third is nearly cleared, and the fourth is retained. Implementations do not construct a diagonal matrix; they broadcast the four retention values across the rows and multiply elementwise.

GDN versus KDA
\[ \text{GDN:}\quad \bar S_t=\alpha_tS_{t-1} \] \[ \text{KDA:}\quad \bar S_t=\operatorname{Diag}(\boldsymbol{\alpha}_t)S_{t-1}. \]

The subsequent delta update is the same kind of error-correcting write. The core algorithmic change from GDN is the granularity of this decay; making it train efficiently requires KDA's new chunkwise formulation.

Why not one independent gate for every state element?

The most general recurrence could predict \(G_t\in(0,1)^{K\times V}\) and apply \(G_t\odot S_{t-1}\). The GLA paper explicitly starts from this form. In practice, full gates are factorized as \(G_t=\alpha_t\otimes\gamma_t\), or reduced to KDA's key-side vector.

An unconstrained K3 gate would grow from \(96\times128=12{,}288\) outputs per token to \(96\times128\times128=1{,}572{,}864\)—128 times larger. It would also give every value coordinate different temporal weights, preventing the value dimensions from sharing the same efficient chunkwise attention calculation. GLA reported only marginal gains from adding the second factor. Recent extensions such as Gated DeltaNet-2 instead add structured key-side erase and value-side write vectors while preserving the rank-one update.

6.The complete KDA recurrence

We can now read the entire recurrence without hiding any operation. Assume \(q_t\) and \(k_t\) have already been normalized.

1Forget across key coordinates[K,V] → [K,V]
\[ \bar S_t=\operatorname{Diag}(\boldsymbol{\alpha}_t)S_{t-1}. \]
This is broad forgetting. It affects all rows according to 128 learned channel decisions. It is predicted from the current token, but it is not restricted to the current key direction.
2Read what the decayed memory currently says[K,V]ᵀ × [K] → [V]
\[ \hat v_t=\bar S_t^\top k_t. \]
3Compute and scale the value error[V]
\[ e_t=v_t-\hat v_t, \qquad \tilde e_t=\beta_te_t. \]
Kimi's \(\beta_t\) is one scalar per token and head. It is shared across all 128 key and value coordinates.
4Write the correction along the current key[K] × [V] → [K,V]
\[ S_t=\bar S_t+k_t\tilde e_t^\top. \]
5Read the updated state with the query[K,V]ᵀ × [K] → [V]
\[ o_t=S_t^\top q_t. \]

Combining Steps 1–4 gives the report's compact equation:

Kimi Delta Attention
\[ \boxed{ S_t= \left(I-\beta_tk_tk_t^\top\right) \operatorname{Diag}(\boldsymbol{\alpha}_t)S_{t-1} + \beta_tk_tv_t^\top } \] \[ o_t=S_t^\top q_t. \]

The state transition is diagonal plus rank one:

\[ \left(I-\beta_tk_tk_t^\top\right)\operatorname{Diag}(\boldsymbol{\alpha}_t) = \operatorname{Diag}(\boldsymbol{\alpha}_t) - \beta_tk_t(k_t\odot\boldsymbol{\alpha}_t)^\top. \]

This structure is the engineering key: KDA gains channel-wise forgetting without giving up the matrix operations needed by its specialized chunkwise training algorithm.

Exactly how K3 computes the forget values

K3 does not use the raw projected gate directly. For head \(h\) and key channel \(j\), let \(g_{t,h,j}\) be the token-dependent projection, \(b_{h,j}\) the learned channel bias, and \(A_{\log,h}\) one learned sensitivity scalar for the whole head. K3 computes:

\[ \alpha_{t,h,j} = \exp\!\left[ -5\, \sigma\!\left( e^{A_{\log,h}} \left(g_{t,h,j}+b_{h,j}\right) \right) \right]. \]
QuantityShape in K3Role
raw \(g\)[B,T,96,128]dynamic signal from the current token
dt_bias[96,128]static baseline / threshold per channel
\(A_{\log}\)[96]positive head-level sensitivity through \(e^{A_{\log}}\)
final \(\alpha\)[B,T,96,128]actual retention factor applied to the state

The sigmoid \(\sigma(\cdot)\) maps into \((0,1)\). Multiplying by \(-5\) creates a log-retention in \((-5,0)\), and the outer exponential produces:

\[ \alpha\in(e^{-5},1)\approx(0.0067,1). \]
Sigmoid outputLog-retentionActual retentionMeaning
near 0near 0near 1retain
0.5-2.50.082strong decay
near 1near -50.0067almost erase
KDA recurrence versus K3 gate parameterization

The original Kimi Linear formulation uses \(\log\alpha=-e^{A_{\log}}\operatorname{softplus}(g+b)\), which has no fixed lower bound. Kimi K3 keeps the same KDA state recurrence but switches to the bounded \(-5\,\sigma(e^{A_{\log}}(g+b))\) “safe gate” above. The mechanism is KDA in both cases; the checkpoint-specific way of producing \(\alpha\) is different.

The update strength has a simpler path. K3 projects the hidden state to 96 raw values and applies sigmoid inside the kernel:

\[ \tilde\beta_{t,h}=\operatorname{b\_proj}(x_t)_h, \qquad \beta_{t,h}=\sigma(\tilde\beta_{t,h}). \]

7.One Kimi K3 KDA layer, end to end

Now follow the real tensors. K3's text hidden width is 7168. Every KDA layer uses 96 heads with key and value width 128.

hidden 7168 heads 96 key width 128 value width 128 projected width 12,288
hidden states   [B,T,7168]
↓ three independent linear projections
Q   [B,T,12288]
K   [B,T,12288]
V   [B,T,12288]
↓ separate depthwise causal Conv1d, width 4, then SiLU
channels:   ch 0, ch 1, ... , ch 12287   (shape unchanged)
↓ reshape 12288 = 96 × 128
q   [B,T,96,128]
k   [B,T,96,128]
v   [B,T,96,128]
↓ KDA recurrence + fixed state
o   [B,T,96,128]
↓ per-head RMSNorm × sigmoid(output gate)
join heads   [B,T,12288]   → output projection →   [B,T,7168]

The short convolution is temporal, not channel mixing

Each of the 12,288 channels owns its own four-tap filter. For channel \(c\):

\[ y_{t,c} = \operatorname{SiLU}\!\left( w_{c,0}x_{t-3,c} +w_{c,1}x_{t-2,c} +w_{c,2}x_{t-1,c} +w_{c,3}x_{t,c} \right). \]

There is no sum across other channels because the convolution uses groups=12288. Q, K, and V each have a separate convolution and a separate four-slot cache:

conv_state_q  [B, 12288, 4]
conv_state_k  [B, 12288, 4]
conv_state_v  [B, 12288, 4]

During decode, the one new projected token joins those cached values. These are pre-convolution recent activations, not past attention keys and values.

Q/K normalization and query scaling

Before the KDA recurrence, each head's Q and K vectors are L2-normalized. The query is additionally scaled by \(1/\sqrt{128}\):

\[ k_t\leftarrow\frac{k_t}{\lVert k_t\rVert_2}, \qquad q_t\leftarrow \frac{q_t}{\lVert q_t\rVert_2\sqrt{128}}. \]

Kimi does not pass an explicit scale from the attention module; FLA defaults to k.shape[-1] ** -0.5. The scaling keeps the state readout from growing with key width.

The output gate is not the forget gate

After KDA returns \(o=S^\top q\), K3 reuses the Python variable name g for a completely different gate. Because use_full_rank_gate=true, it projects directly from 7168 to 12,288 output logits:

\[ g^{\text{out}}=\operatorname{Linear}_{7168\rightarrow12288}(x). \]

For every head and value channel:

\[ o'_j = \frac{o_j}{\sqrt{\frac1{128}\sum_r o_r^2+\epsilon}} \;w_j\; \sigma(g^{\text{out}}_j). \]

The learned RMSNorm weight \(w\in\mathbb{R}^{128}\) is static and shared across tokens and heads; the sigmoid output gate is dynamic per token, head, and channel.

8.A complete KDA update, with numbers

We have met every KDA operation separately. Now let one token perform the whole sequence: forget the old state, check what the current key already recalls, write half of the error, then read the updated state.

Use a two-dimensional state so every number stays visible. Before the current token arrives:

\[ S_{t-1}= \begin{bmatrix} 2&0\\ 0&1 \end{bmatrix}. \]
Inputs from the current token

Retention: \(\boldsymbol{\alpha}_t=\begin{bmatrix}0.5\\1\end{bmatrix}\) — halve row 1; keep row 2.

Key and query: \(k_t=q_t=\begin{bmatrix}1\\0\end{bmatrix}\) — address the first key coordinate.

Target value: \(v_t=\begin{bmatrix}3\\2\end{bmatrix}\) — the association this token wants.

Update strength: \(\beta_t=0.5\) — write half of the error.

1Forgetscale state rows
\[ \operatorname{Diag}(\boldsymbol{\alpha}_t) = \begin{bmatrix} 0.5&0\\ 0&1 \end{bmatrix} \] \[ \bar S_t = \operatorname{Diag}(\boldsymbol{\alpha}_t)S_{t-1} = \begin{bmatrix} 1&0\\ 0&1 \end{bmatrix}. \]
The first key-coordinate row is halved. The second row is retained.
2Recall at the current key[2,2]ᵀ × [2] → [2]
\[ \hat v_t = \bar S_t^\top k_t = \begin{bmatrix}1\\0\end{bmatrix}. \]
After forgetting, memory currently answers \([1,0]^\top\) for this key.
3Compute and scale the errortarget − recall
\[ e_t = v_t-\hat v_t = \begin{bmatrix}3\\2\end{bmatrix} - \begin{bmatrix}1\\0\end{bmatrix} = \begin{bmatrix}2\\2\end{bmatrix}, \] \[ \beta_te_t = \begin{bmatrix}1\\1\end{bmatrix}. \]
The target is \([3,2]^\top\), but \(\beta=0.5\), so this token writes only half of the missing value.
4Write along the current keyouter product
\[ k_t(\beta_te_t)^\top = \begin{bmatrix}1\\0\end{bmatrix} \begin{bmatrix}1&1\end{bmatrix} = \begin{bmatrix} 1&1\\ 0&0 \end{bmatrix}, \] \[ S_t = \bar S_t+k_t(\beta_te_t)^\top = \begin{bmatrix} 2&1\\ 0&1 \end{bmatrix}. \]
Only the row selected by \(k_t=[1,0]^\top\) receives the correction.
5Read with the query[2,2]ᵀ × [2] → [2]
\[ o_t = S_t^\top q_t = \begin{bmatrix}2\\1\end{bmatrix}. \]
What changed?

After forgetting, this key recalled \([1,0]^\top\). The new target was \([3,2]^\top\). A half-strength delta update moved the answer exactly halfway to \([2,1]^\top\). That is one complete KDA token update.

9.Prefill, chunks, and packed requests

Training, prefill, and decode use the same KDA recurrence. They differ in how it is scheduled.

SituationKimi pathWhy
trainingchunk_kdahas a backward pass and exposes large matrix multiplications
prefill, many tokenschunk_kdaparallel work within fixed-size chunks
cached decode, one tokenfused_recurrent_kdaone direct recurrent step with the cached state

The attention module makes the decode choice with:

mode = "fused_recurrent" if use_cache and q_len == 1 else "chunk"

The fused kernel is capable of looping over multiple tokens, but Kimi normally sends prefill to the chunk implementation. Chunking compresses products of KDA's diagonal-plus-rank-one transitions into dense operations that tensor cores can execute efficiently.

What cu_seqlens means

Variable-length requests can be unpadded and concatenated into one packed token axis. The original batch dimension is then represented by boundary offsets.

Three requests with lengths 3, 5, and 2
packed tokens: [request 0 ...][request 1 ........][request 2 ..]
cu_seqlens:    [0, 3, 8, 10]

Request 0 occupies packed positions [0,3), request 1 occupies [3,8), and request 2 occupies [8,10).

This is not “decode mode,” and it is not kernel tile padding. It is simply the boundary map that prevents the short convolution and recurrent state from crossing from one packed request into the next.

10.Why Kimi K3 keeps MLA layers

KDA is efficient because it compresses the entire past into one fixed matrix. That compression can lose exact token-level detail. MLA is full attention: it can still look directly at every cached token, but its cache grows with context length.

KDA
fixed \(128\times128\) state per head
constant decode memory per layer
lossy associative compression
MLA
one cache entry per token
direct global token-to-token lookup
memory grows with context

K3 therefore repeats three KDA layers followed by one MLA layer: KDA supplies the efficient recurrent path, while MLA periodically restores direct global access. That is MLA's role in this architecture. Its internal design is already covered in Understanding MLA and MLA Decoding: Materialize vs Absorb.

11.Cache size: KDA versus MLA

The comparison is simple: an optimized MLA layer keeps a compact entry for every token, while a KDA layer keeps one fixed recurrent state for the whole sequence.

LayerPersistent decode memoryContext dependence
KDAone fp32 recurrent matrix plus three short-convolution statesfixed
MLA576 bf16 cached values per token in the optimized K3 layoutgrows linearly

KDA's fixed state

One KDA recurrent state contains:

\[ 96\times128\times128=1{,}572{,}864 \]

fp32 values, exactly 6 MiB per request per layer. The three four-slot bf16 convolution caches add about 0.281 MiB. This does not change when the context grows.

The hybrid crossover

An all-MLA 93-layer comparison model has 93 token-growing caches. K3 has only 24; its other 69 layers carry fixed KDA and convolution state. Under the assumptions shown below, the fixed-state overhead is larger at very short contexts, then the hybrid crosses over near 5.7K tokens and approaches a 74% reduction.

93-layer all-MLA cache
K3: 24 MLA + 69 KDA
All MLA
100%
K3 hybrid
What the calculator assumes

One request; bf16 MLA entries of width 576; fp32 KDA recurrent state; bf16 four-slot Q/K/V convolution state; no allocator padding, cache quantization, speculative branches, or batch sharing. It compares cache mechanisms, not total model memory. Production kernels and cache formats can move the exact numbers.

At K3's maximum 1,048,576-token context, this simplified estimate gives roughly 112.3 GB for 93 all-MLA layers versus 29.4 GB for K3's hybrid cache. Almost all of the hybrid's long-context growth comes from its 24 MLA layers; the 69 KDA layers remain at about 0.45 GB combined per request.

12.Source map and final mental model

Kimi K3 ships its architecture as remote code in the model repository. The multimodal wrapper eventually hands text processing to KimiLinearForCausalLM. For source-first reading, use this order:

ReadWhy
modeling_kimi_linear.pyK3's MLA, KDA layer, projections, caches, mode selection, and output gates
fla/ops/kda/naive.pythe recurrence in the most readable executable form
short_conv.pydepthwise causal short convolution and its four-slot state
Kimi Linear reportderivation, DPLR chunk algorithm, ablations, hybrid design, and efficiency results

The misconceptions worth keeping cleared

Tempting interpretationCorrect mental model
\(S\) is a token attention matrix\(S\) is a fixed \(K\times V\) associative map with no sequence axis
KDA's rows “are values”each row is one key-coordinate's complete contribution in value space
the forget gate acts only along \(k_t\)forgetting scales every key row; the delta correction is the operation targeted by \(k_t\)
\(\beta\) is per channel in K3\(\beta\) is one dynamic scalar per token and head; \(\alpha\) is per key channel
short-conv state is a KV cacheit stores four recent pre-convolution projected values per Q/K/V channel
prefill and decode use different attention mathsame recurrence, different schedules: chunk-parallel versus fused recurrent
K3 has no growing cacheKDA layers are fixed-state; the 24 MLA layers still grow with context
KDA in one continuous thought

Forget broadly by key channel. Read the old value at the current key. Write only the error. Read the updated memory with the query.

\[ \bar S_t=\operatorname{Diag}(\boldsymbol{\alpha}_t)S_{t-1}, \quad e_t=v_t-\bar S_t^\top k_t, \quad S_t=\bar S_t+\beta_tk_te_t^\top, \quad o_t=S_t^\top q_t. \]

References

How to cite this post

Dong, S. (2026). Linear Attention, From Scratch to Kimi 3
(Kimi Delta Attention).
https://simondong1.github.io/linear-attention.html