© 2026 Lalit Yadav

From Softmax Attention to Kimi Delta Attention

gradually build intuition for how attention goes from softmax to kimi delta.

Lalit YadavAug 1, 2026

Contents

  • Reframing softmax attention to see the bottleneck
  • The two costs (the reason linear attention exists)
  • Linear attention, folding the past into a fixed state
  • Why the exponential blocks compression
  • What becomes possible without it
  • The outer product is the storage primitive
  • Where are the savings? The keys are not stored
  • S q is attention, in the opposite association order
  • DeltaNet, giving the memory an eraser
  • Note: k_t^\top k_t vs k_t k_t^\top
  • Worked collision example
  • Gated DeltaNet, a fading knob
  • Concept 5: Kimi Delta Attention, per-channel forgetting
  • Numerical contrast
  • Why it matters
  • The full arc
  • Reference

Reframing softmax attention to see the bottleneck#

For a query at position ttt, causal softmax attention computes the output as a weighted sum over all previous tokens:

ot=∑i≤texp⁡(qt⊤ki)∑j≤texp⁡(qt⊤kj) vio_t = \sum_{i \le t} \frac{\exp(q_t^\top k_i)}{\sum_{j \le t} \exp(q_t^\top k_j)} \, v_iot​=∑i≤t​∑j≤t​exp(qt⊤​kj​)exp(qt⊤​ki​)​vi​

the output at ttt is a blend of every past value viv_ivi​, weighted by how well each token's key kik_iki​ matches the current query qtq_tqt​, where the match scores are normalized across all past tokens so the weights sum to 1.

The two costs (the reason linear attention exists)#

Compute. To produce all outputs for a sequence of length TTT, you compute qt⊤kiq_t^\top k_iqt⊤​ki​ for every pair. That's T2T^2T2 dot products. Double the sequence, quadruple the work.

Memory. To compute oto_tot​ you need every past kik_iki​ and viv_ivi​. Nothing lets you throw any away, because the next query might match an old key strongly. So at decode you cache all past keys and values (the KV cache), and it grows linearly with sequence length.

Why you can't compress the cache: the exponential. Because exp⁡\expexp sits between qtq_tqt​ and kik_iki​, the weight for token iii can't be computed until qtq_tqt​ is known. You cannot pre-summarize the past into a small fixed state, because the past has to wait for the future query before it knows how much to contribute.

The question that sets up everything: what if we removed the exp⁡\expexp and replaced it with something that lets us reassociate the math, so the entire past collapses into a single fixed-size state each new token updates in constant work?


Linear attention, folding the past into a fixed state#

Why the exponential blocks compression#

exp⁡(qt⊤ki)\exp(q_t^\top k_i)exp(qt⊤​ki​) is non-separable. There's no way to split it into a piece depending only on qtq_tqt​ times a piece depending only on kik_iki​. Query and key are fused inside one nonlinear function, so no useful precomputation on keys alone is possible.

What becomes possible without it#

Drop the exponential, let the weight be the plain dot product qt⊤kiq_t^\top k_iqt⊤​ki​. Now query and key are only multiplied, which is separable, so the sum can be rearranged:

ot=∑i≤t(qt⊤ki) vio_t = \sum_{i \le t} (q_t^\top k_i)\, v_iot​=∑i≤t​(qt⊤​ki​)vi​

Since qt⊤kiq_t^\top k_iqt⊤​ki​ is a scalar, move it around freely:

(qt⊤ki) vi=vi (ki⊤qt)=(viki⊤) qt(q_t^\top k_i)\, v_i = v_i\,(k_i^\top q_t) = (v_i k_i^\top)\, q_t(qt⊤​ki​)vi​=vi​(ki⊤​qt​)=(vi​ki⊤​)qt​

Substitute back and pull qtq_tqt​ out of the sum (it doesn't depend on iii):

ot=(∑i≤tviki⊤)qto_t = \left( \sum_{i \le t} v_i k_i^\top \right) q_tot​=(∑i≤t​vi​ki⊤​)qt​

Define the state:

St=∑i≤tviki⊤S_t = \sum_{i \le t} v_i k_i^\topSt​=∑i≤t​vi​ki⊤​

This is the compression. StS_tSt​ is a single matrix of fixed size dv×dd_v \times ddv​×d. It does not grow with ttt. It's a running sum:

St=St−1+vtkt⊤ot=St qt\boxed{S_t = S_{t-1} + v_t k_t^\top \qquad\qquad o_t = S_t\, q_t}St​=St−1​+vt​kt⊤​ot​=St​qt​​

Each new token costs constant work: form the outer product vtkt⊤v_t k_t^\topvt​kt⊤​, add it to the running state, read out with qtq_tqt​. No scanning, no growing cache. Compute drops from T2T^2T2 to linear, memory becomes a constant dv×dd_v \times ddv​×d state.

Caveat: real linear attention keeps a small nonlinearity via a feature map ϕ\phiϕ, so the weight is ϕ(qt)⊤ϕ(ki)\phi(q_t)^\top \phi(k_i)ϕ(qt​)⊤ϕ(ki​). Still separable, so the same state trick applies with ϕ(k)\phi(k)ϕ(k) in place of kkk. What matters is separability, not zero nonlinearity.

The outer product is the storage primitive#

Everything about the memory rests on one operation: the outer product vk⊤v k^\topvk⊤. This is what writes a single key-value pair into the matrix. Build up its behavior in three steps.

Step 1: an outer product turns a pair into a matrix. vk⊤v k^\topvk⊤ is a column vector times a row vector, which produces a matrix (not a number). Each entry:

(vk⊤)[r,c]=v[r]⋅k[c](v k^\top)[r, c] = v[r] \cdot k[c](vk⊤)[r,c]=v[r]⋅k[c]

Every component of vvv is paired with every component of kkk. With v=[2,0]v = [2, 0]v=[2,0] and k=[1,0]k = [1, 0]k=[1,0]:

vk⊤=[20][10]=[2⋅12⋅00⋅10⋅0]=[2000]v k^\top = \begin{bmatrix}2\\0\end{bmatrix}\begin{bmatrix}1 & 0\end{bmatrix} = \begin{bmatrix}2\cdot 1 & 2\cdot 0\\ 0\cdot 1 & 0\cdot 0\end{bmatrix} = \begin{bmatrix}2 & 0\\ 0 & 0\end{bmatrix}vk⊤=[20​][1​0​]=[2⋅10⋅1​2⋅00⋅0​]=[20​00​]

Step 2: the key decides where the value is filed. All the content above landed in column 1, because k=[1,0]k = [1,0]k=[1,0] has its weight in position 1. Change to k=[0,1]k = [0,1]k=[0,1] and the same value files into column 2:

[20][01]=[0200]\begin{bmatrix}2\\0\end{bmatrix}\begin{bmatrix}0 & 1\end{bmatrix} = \begin{bmatrix}0 & 2\\ 0 & 0\end{bmatrix}[20​][0​1​]=[00​20​]

Think of filing a document. The key is the address (picks which column/direction the value is written into); the value is the contents (what gets stored there). One outer product is one filled slot, holding the binding "k→vk \rightarrow vk→v."

Step 3: reading a slot back with a query. Multiply the slot by a query vector xxx. It simplifies:

(vk⊤) x=v (k⊤x)(v k^\top)\, x = v\,(k^\top x)(vk⊤)x=v(k⊤x)

k⊤xk^\top xk⊤x is a single scalar measuring how aligned the query is with the stored key. It acts as a volume knob on the value: query along kkk, get vvv at full strength; query perpendicular to kkk (k⊤x=0k^\top x = 0k⊤x=0), get nothing; partial alignment gives a scaled-down vvv. Check with the slot from Step 1, queried by x=[1,0]x = [1,0]x=[1,0]:

[2000][10]=[20]=v\begin{bmatrix}2&0\\0&0\end{bmatrix}\begin{bmatrix}1\\0\end{bmatrix} = \begin{bmatrix}2\\0\end{bmatrix} = v[20​00​][10​]=[20​]=v

Putting many slots together. A real memory holds many pairs. Add their outer products into one matrix:

S=∑iviki⊤S = \sum_i v_i k_i^\topS=∑i​vi​ki⊤​

Addition preserves the shape, so SSS stays fixed-size no matter how many pairs you store. Each pair was filed into its own key-direction, and they coexist in one matrix. Read the whole memory at once by querying with a key kjk_jkj​:

Skj=∑ivi (ki⊤kj)S k_j = \sum_i v_i \,(k_i^\top k_j)Skj​=∑i​vi​(ki⊤​kj​)

Every stored value viv_ivi​ comes back, each weighted by how much its key kik_iki​ aligns with your query kjk_jkj​. Values whose keys point toward kjk_jkj​ dominate; the rest fade out. That is a soft lookup, and it's the entire reason the matrix behaves like an associative memory.

Worked storage example. Store k1=[1,0],v1=[5,0]k_1=[1,0], v_1=[5,0]k1​=[1,0],v1​=[5,0] and k2=[0,1],v2=[0,9]k_2=[0,1], v_2=[0,9]k2​=[0,1],v2​=[0,9]:

v1k1⊤=[5000],v2k2⊤=[0009],S=[5009]v_1 k_1^\top = \begin{bmatrix} 5 & 0 \\ 0 & 0\end{bmatrix}, \quad v_2 k_2^\top = \begin{bmatrix} 0 & 0 \\ 0 & 9\end{bmatrix}, \quad S = \begin{bmatrix} 5 & 0 \\ 0 & 9\end{bmatrix}v1​k1⊤​=[50​00​],v2​k2⊤​=[00​09​],S=[50​09​]

Different keys wrote into different columns, so values never collided. Reading with k1k_1k1​: Sk1=[5,0]=v1S k_1 = [5,0] = v_1Sk1​=[5,0]=v1​. Reading with k2k_2k2​: Sk2=[0,9]=v2S k_2 = [0,9] = v_2Sk2​=[0,9]=v2​. Clean, because the keys are orthogonal.

Interference appears when keys are not orthogonal. If k2=[0.7,0.7]k_2 = [0.7, 0.7]k2​=[0.7,0.7], then querying k1=[1,0]k_1=[1,0]k1​=[1,0] gives v1(1)+v2(0.7)v_1(1) + v_2(0.7)v1​(1)+v2​(0.7): the wrong value leaks in. Non-orthogonal keys write into overlapping columns, so reads mix.

Where are the savings? The keys are not stored#

The key is consumed at write time and discarded. In St=St−1+vtkt⊤S_t = S_{t-1} + v_t k_t^\topSt​=St−1​+vt​kt⊤​, once ktk_tkt​'s contribution is folded in, ktk_tkt​ ceases to exist as a separate object. Retrieval never matches against stored keys, it just multiplies by the query, ot=Stqto_t = S_t q_tot​=St​qt​. The key-matching was already baked into the geometry of SSS at write time.

KV cache (softmax) Linear attention
Stores every (ki,vi)(k_i, v_i)(ki​,vi​), size T⋅(d+dv)T\cdot(d+d_v)T⋅(d+dv​) one matrix SSS, size dv×dd_v \times ddv​×d
Grows with TTT? yes no
Recovery exact, fully separable lossy, superimposed

The keys aren't stored twice, they're stored zero times. They were spent to build SSS and discarded. The price is that summed outer products can't be pulled apart again: lossy compression.

SqS qSq is attention, in the opposite association order#

Substitute St=∑iviki⊤S_t = \sum_i v_i k_i^\topSt​=∑i​vi​ki⊤​ into the read and push qtq_tqt​ inside:

ot=St qt=(∑i≤tviki⊤)qt=∑i≤tvi(ki⊤qt)=∑i≤t(qt⊤ki) vio_t = S_t\, q_t = \left(\sum_{i \le t} v_i k_i^\top\right) q_t = \sum_{i \le t} v_i (k_i^\top q_t) = \sum_{i \le t} (q_t^\top k_i)\, v_iot​=St​qt​=(∑i≤t​vi​ki⊤​)qt​=∑i≤t​vi​(ki⊤​qt​)=∑i≤t​(qt⊤​ki​)vi​

That last form is exactly standard (softmax-free) attention. Same sum, same scores, same weighted blend. The only difference is when you group:

∑i(qt⊤ki) vi⏟score first, then sum=(∑iviki⊤)qt⏟sum first, then score\underbrace{\sum_i (q_t^\top k_i)\, v_i}_{\text{score first, then sum}} \quad=\quad \underbrace{\Big(\sum_i v_i k_i^\top\Big) q_t}_{\text{sum first, then score}}score first, then sumi∑​(qt⊤​ki​)vi​​​=sum first, then score(i∑​vi​ki⊤​)qt​​​

  • Score-first (softmax) can't pre-sum, because scores need the future query. Holds the full list. Memory and work grow with ttt.
  • Sum-first (linear) pre-sums the past into SSS before the query arrives. Holds one fixed matrix. Constant memory and work. The softmax's exp⁡\expexp is exactly what forbids the regrouping. Linear attention insists on a regroupable form and accepts the blur as the cost.

DeltaNet, giving the memory an eraser#

Flaw in plain linear attention: it only ever adds. It never removes. Two problems:

  1. Interference. Non-orthogonal keys leak into each other on read.
  2. No overwrite. The same key with two values stores a smeared superposition vak⊤+vbk⊤v_a k^\top + v_b k^\topva​k⊤+vb​k⊤, with no way to replace the old binding.

DeltaNet's fix: before writing, check what memory already returns for this key, and write only the correction.

v^t=St−1ktδt=vt−St−1kt\hat v_t = S_{t-1} k_t \qquad \delta_t = v_t - S_{t-1} k_tv^t​=St−1​kt​δt​=vt​−St−1​kt​

The gap δt\delta_tδt​ is the delta. Write only that correction, scaled by write strength βt\beta_tβt​:

St=St−1+βt (vt−St−1kt) kt⊤S_t = S_{t-1} + \beta_t\,(v_t - S_{t-1} k_t)\, k_t^\topSt​=St−1​+βt​(vt​−St−1​kt​)kt⊤​

Expand and group the St−1S_{t-1}St−1​ terms:

St=St−1(I−βtktkt⊤)⏟erase+βtvtkt⊤⏟write\boxed{S_t = S_{t-1}\underbrace{(I - \beta_t k_t k_t^\top)}_{\text{erase}} + \underbrace{\beta_t v_t k_t^\top}_{\text{write}}}St​=St−1​erase(I−βt​kt​kt⊤​)​​+writeβt​vt​kt⊤​​​​

The erase operator (I−βtktkt⊤)(I - \beta_t k_t k_t^\top)(I−βt​kt​kt⊤​) strips out existing content along the ktk_tkt​ direction before the fresh value goes in. βt\beta_tβt​ acts like a learning rate: 0 leaves memory untouched, 1 fully wipes and replaces along ktk_tkt​.

Note: kt⊤ktk_t^\top k_tkt⊤​kt​ vs ktkt⊤k_t k_t^\topkt​kt⊤​#

  • kt⊤kt=∥kt∥2=∑ikt[i]2≥0k_t^\top k_t = \|k_t\|^2 = \sum_i k_t[i]^2 \ge 0kt⊤​kt​=∥kt​∥2=∑i​kt​[i]2≥0 is a scalar (squared norm). Never zero for a nonzero vector, so the erase always subtracts something.
  • ktkt⊤k_t k_t^\topkt​kt⊤​ is a matrix (outer product), the thing inside (I−βtktkt⊤)(I - \beta_t k_t k_t^\top)(I−βt​kt​kt⊤​).

Worked collision example#

Tokens (note k3=k1k_3 = k_1k3​=k1​, a deliberate collision), β=1\beta = 1β=1 throughout:

k1=[1,0],v1=[2,0]k2=[0,1],v2=[0,3]k3=[1,0],v3=[5,0]k_1=[1,0], v_1=[2,0] \quad k_2=[0,1], v_2=[0,3] \quad k_3=[1,0], v_3=[5,0]k1​=[1,0],v1​=[2,0]k2​=[0,1],v2​=[0,3]k3​=[1,0],v3​=[5,0]

Plain linear attention (add only):

S1=[2000]→S2=[2003]→S3plain=[7003]S_1 = \begin{bmatrix}2&0\\0&0\end{bmatrix} \to S_2 = \begin{bmatrix}2&0\\0&3\end{bmatrix} \to S_3^{\text{plain}} = \begin{bmatrix}7&0\\0&3\end{bmatrix}S1​=[20​00​]→S2​=[20​03​]→S3plain​=[70​03​]

Column 1 became 7=2+57 = 2 + 57=2+5: the values smeared together.

DeltaNet (erase before write). At token 3, I−k3k3⊤=[0001]I - k_3 k_3^\top = \begin{bmatrix}0&0\\0&1\end{bmatrix}I−k3​k3⊤​=[00​01​]. Apply to S2S_2S2​ first:

[0001][2003]=[0003]\begin{bmatrix}0&0\\0&1\end{bmatrix}\begin{bmatrix}2&0\\0&3\end{bmatrix} = \begin{bmatrix}0&0\\0&3\end{bmatrix}[00​01​][20​03​]=[00​03​]

Column 1 (token 1's stale value) is zeroed; column 2 (token 2) untouched. Then write v3v_3v3​:

S3DeltaNet=[0003]+[5000]=[5003]S_3^{\text{DeltaNet}} = \begin{bmatrix}0&0\\0&3\end{bmatrix} + \begin{bmatrix}5&0\\0&0\end{bmatrix} = \begin{bmatrix}5&0\\0&3\end{bmatrix}S3DeltaNet​=[00​03​]+[50​00​]=[50​03​]

Reading with q=[1,0]q=[1,0]q=[1,0] gives [5,0]=v3[5,0] = v_3[5,0]=v3​ cleanly. The stale v1v_1v1​ is gone, not smeared.

Honest limit: SSS has fixed capacity dv×dd_v \times ddv​×d. It can't hold unlimited separable bindings. Past capacity, something gives: overwrite or interference. This is the fundamental price of refusing to let memory grow, and it's exactly why Kimi Linear is a hybrid (3 KDA layers : 1 full-attention layer): the full-attention layer keeps an exact view as a safety net.


Gated DeltaNet, a fading knob#

Flaw in DeltaNet: the erase only touches the single direction ktk_tkt​. Every other direction sits frozen forever. No general fading, so stale content clogs the fixed-capacity matrix.

Fix: a decay gate αt\alpha_tαt​ that shrinks the whole state before each update:

St=αt(I−βtktkt⊤) St−1⏟DeltaNet erase+βtvtkt⊤\boxed{S_t = \alpha_t\underbrace{(I - \beta_t k_t k_t^\top)\,S_{t-1}}_{\text{DeltaNet erase}} + \beta_t v_t k_t^\top}St​=αt​DeltaNet erase(I−βt​kt​kt⊤​)St−1​​​+βt​vt​kt⊤​​

αt∈(0,1)\alpha_t \in (0,1)αt​∈(0,1) is a scalar computed per token. It uniformly scales down everything stored, before the targeted erase and write.

  • αt=1\alpha_t = 1αt​=1: nothing fades, back to plain DeltaNet.
  • αt=0\alpha_t = 0αt​=0: entire past wiped, restart from this token.
  • αt=0.98\alpha_t = 0.98αt​=0.98: gentle decay, ~2% lost per step, soft half-life.

Because αt\alpha_tαt​ is per token, the model learns when to hold and when to flush (like an LSTM forget gate, applied to a matrix-valued memory).

Decay chain. A value written at step iii and read at step ttt has been multiplied by αi+1αi+2⋯αt\alpha_{i+1}\alpha_{i+2}\cdots\alpha_tαi+1​αi+2​⋯αt​. That product gives each memory a controllable lifespan.

Position for free. How much a value has faded signals how long ago it was written. So the gates encode recency and ordering, which is why these architectures can drop explicit positional encodings like RoPE.

Flaw that motivates KDA: αt\alpha_tαt​ is a single scalar. Every feature dimension decays at the same rate. But different features live on different timescales (document-wide topic vs. local detail). One shared knob is forced into a compromise.


Concept 5: Kimi Delta Attention, per-channel forgetting#

The one change: replace the scalar αt\alpha_tαt​ with a diagonal matrix Diag(αt)\text{Diag}(\alpha_t)Diag(αt​), where αt\alpha_tαt​ is a vector with one decay value per channel:

St=(I−βtktkt⊤) St−1 Diag(αt)+βtvtkt⊤\boxed{S_t = (I - \beta_t k_t k_t^\top)\,S_{t-1}\,\text{Diag}(\alpha_t) + \beta_t v_t k_t^\top}St​=(I−βt​kt​kt⊤​)St−1​Diag(αt​)+βt​vt​kt⊤​​

Diag(αt)\text{Diag}(\alpha_t)Diag(αt​) holds [αt(1),…,αt(d)][\alpha_t^{(1)}, \ldots, \alpha_t^{(d)}][αt(1)​,…,αt(d)​] on its diagonal. Each key-channel of the memory now fades at its own rate. The erase and write are unchanged; only the forgetting got finer.

Numerical contrast#

State S=[2003]S = \begin{bmatrix} 2 & 0 \\ 0 & 3 \end{bmatrix}S=[20​03​].

Gated DeltaNet, scalar αt=0.7\alpha_t = 0.7αt​=0.7 (both channels lose 30%, no choice):

0.7⋅[2003]=[1.4002.1]0.7 \cdot \begin{bmatrix} 2 & 0 \\ 0 & 3 \end{bmatrix} = \begin{bmatrix} 1.4 & 0 \\ 0 & 2.1 \end{bmatrix}0.7⋅[20​03​]=[1.40​02.1​]

KDA, per-channel αt=[0.9, 0.5]\alpha_t = [0.9,\ 0.5]αt​=[0.9, 0.5] (each column its own rate):

[2003]Diag([0.9, 0.5])=[1.8001.5]\begin{bmatrix} 2 & 0 \\ 0 & 3 \end{bmatrix}\text{Diag}([0.9,\ 0.5]) = \begin{bmatrix} 1.8 & 0 \\ 0 & 1.5 \end{bmatrix}[20​03​]Diag([0.9, 0.5])=[1.80​01.5​]

Channel 1 keeps 90%, channel 2 is halved. One update, two forgetting speeds.

Why it matters#

Long-range channels (overall subject) want α≈1\alpha \approx 1α≈1 and persist. Short-range channels (local detail) want small α\alphaα and flush fast. A single scalar can't serve both. Per-channel decay lets each feature choose, making better use of the fixed-size memory, which is KDA's headline for long-context recall.

Richer position. With per-channel decay, each channel carries its own decay timeline, so the memory holds a multi-rate sense of position. This is why KDA needs no explicit RoPE at all, and the positional signal is more expressive than a scalar could give.


The full arc#

Stage Update What it adds
Softmax attention ot=∑isoftmax(qt⊤ki) vio_t = \sum_i \text{softmax}(q_t^\top k_i)\, v_iot​=∑i​softmax(qt⊤​ki​)vi​ exact recall, but growing cache
Linear attention St=St−1+vtkt⊤S_t = S_{t-1} + v_t k_t^\topSt​=St−1​+vt​kt⊤​ fixed matrix via associativity (lossy)
DeltaNet St=St−1(I−βtktkt⊤)+βtvtkt⊤S_t = S_{t-1}(I - \beta_t k_t k_t^\top) + \beta_t v_t k_t^\topSt​=St−1​(I−βt​kt​kt⊤​)+βt​vt​kt⊤​ erase, so writes overwrite not smear
Gated DeltaNet St=αt(I−βtktkt⊤)St−1+βtvtkt⊤S_t = \alpha_t (I - \beta_t k_t k_t^\top) S_{t-1} + \beta_t v_t k_t^\topSt​=αt​(I−βt​kt​kt⊤​)St−1​+βt​vt​kt⊤​ scalar decay, stale memory fades
KDA St=(I−βtktkt⊤)St−1Diag(αt)+βtvtkt⊤S_t = (I - \beta_t k_t k_t^\top) S_{t-1} \text{Diag}(\alpha_t) + \beta_t v_t k_t^\topSt​=(I−βt​kt​kt⊤​)St−1​Diag(αt​)+βt​vt​kt⊤​ per-channel decay

Reference#

Kimi Linear: An Expressive, Efficient Attention Architecture (arXiv:2510.26692)