CS 8803-LLM · Session 17

Linear Transformers as Fast Weight Programmers

Every attention layer is secretly a tiny neural network reprogramming its own weights, one token at a time. Delete the softmax and a 1990s idea called a fast weight programmer reappears underneath — carrying the same memory-capacity limit, and, three decades later, the same fix.

Prerequisites: attention is softmax(QKT/√d)V + matrix-vector multiplication and outer products. Session 08's KV cache material is useful background but not required — this session rebuilds attention from a different angle entirely.
10
Chapters
4
Simulations
0
Assumed Knowledge

Chapter 0: The O(L²) Wall

Session 08 spent ten chapters on a memory problem: the KV cache grows by one entry every token, forever, and eventually a GPU runs out of room to hold it. That problem has a clean fix — bound the cache, keep a handful of sink tokens, done. This session is about a different, more stubborn problem hiding inside the same mechanism: even if you had infinite memory, standard attention would still get catastrophically slower as a conversation grows. The KV cache's size is linear in the number of tokens processed. The compute spent turning that cache into an answer is quadratic. Those are two separate walls, and Session 08 only tore down the first one.

Where the quadratic term actually comes from

Picture generating token number L in a sequence. To compute this token's output, self-attention takes its query vector and compares it against every one of the L previous tokens' key vectors — one dot product per prior token — then uses those comparisons to mix together every prior token's value vector. That is L comparisons for token L alone. Sum that cost over every token from 1 to L in the sequence (because token 2 compares against 1 prior token, token 3 against 2, and so on) and you get the familiar triangular-number sum, proportional to L2. Double the sequence length and the compute for attention roughly quadruples — not doubles. That is the wall this chapter is about.

token 1
0 prior comparisons
token 2
1 prior comparison
token 3
2 prior comparisons
… sum over L tokens → proportional to L²

Make “proportional to” exact: the total number of comparisons across an L-token sequence is 0+1+2+…+(L−1), the classic triangular-number sum, which has a closed form:

0+1+2+…+(L−1) = L(L−1)/2

At L=1,000, that's 1,000×999/2 = 499,500 comparisons — not 1,000, not 1,000,000, but almost exactly half of L2=1,000,000. The constant factor of one-half is why complexity notation drops it (O(L2) absorbs any constant multiplier), but it's worth having seen the exact count once, rather than trusting “quadratic” as an unverified label.

The precise complexity, both forms

Written out formally, a full attention layer processing a sequence of length L with model dimension d costs O(L2d + Ld2) — the first term is the attention comparisons themselves (query-key dot products and the resulting value mixing), the second is the ordinary cost of the linear projections that produce Q, K, and V in the first place. For any sequence longer than the model's own hidden dimension, the first term dominates completely, and that is the term that scales quadratically. This lesson's entire arc is about a family of methods that replace that first term with something that costs O(Ld2) instead — linear in sequence length, no matter how long the conversation runs.

A worked crossover, using Session 08's own numbers

Don't take “linear beats quadratic” on faith — it depends on where you are relative to the model's own dimension. Use Llama-2-7B's hidden size again, d = 4,096, the same architecture Session 08 built its byte arithmetic around. Compare the dominant terms of each approach: full attention’s L2d against a linear recurrent form’s Ld2. Their ratio collapses to something almost embarrassingly simple:

full attention cost ÷ linear attention cost = (L²d) ÷ (Ld²) = L ÷ d

That ratio says everything. When the sequence length L is shorter than the hidden dimension d, the ratio is below 1 — full attention is actually cheaper. Linear attention only wins once L exceeds d, and it wins by exactly the factor L/d, growing without bound as the conversation continues. Work three concrete points on that curve, all at d = 4,096:

Sequence length LRatio L ÷ dWhat it means
512 (short chat turn)0.125full attention is 8× cheaper here
4,096 (= d, the crossover)1.0exactly tied
16,384 (a long document)4.0linear attention is 4× cheaper
4,000,000 (Session 08's headline length)≈977linear attention is ≈977× cheaper

Check the 16,384 row by hand, because a ratio is only convincing once you've multiplied it out. Full attention: 16,3842 × 4,096 = 268,435,456 × 4,096 ≈ 1.10 × 1012. Linear attention: 16,384 × 4,0962 = 16,384 × 16,777,216 ≈ 2.75 × 1011. Divide: 1.10 × 1012 ÷ 2.75 × 1011 = 4.0, exactly matching L/d = 16,384/4,096 = 4. The shortcut formula and the brute-force FLOP count agree, which is the whole point of deriving the ratio in the first place — it lets you skip the arithmetic for every new L you care about.

Where the multi-head split fits into this arithmetic

Real attention layers don't use one giant d-dimensional query/key/value — they split d across H heads, each of width d/H, and run the attention computation independently per head. It's worth confirming this doesn't quietly change the crossover analysis above. Each head's attention cost is O(L2(d/H)), and there are H heads, so the total is O(H · L2(d/H)) = O(L2 d) — the H cancels out exactly, the same way Session 08's Chapter 1 found that head count doesn't change the KV cache's byte cost. Splitting into heads changes what the attention pattern can represent (each head can specialize on a different kind of relationship); it does not change this chapter's big-picture complexity story at all. Every ratio and crossover point derived above holds per-layer, regardless of how many heads that layer is split into.

A non-obvious consequence: bigger models push the crossover point further out

Here's a consequence of the L÷d ratio that's easy to miss: the crossover point scales with the model, not just the sequence. A small model with d=512 crosses over at just 512 tokens — linear attention starts winning almost immediately. A frontier-scale model with d=16,384 doesn't cross over until 16,384 tokens, four times further out than this chapter's Llama-2-7B example. As models get bigger, the sequence length at which linear attention starts paying off gets longer, not shorter — the exact opposite of the naive intuition that bigger models have more to gain from efficiency tricks. This is a genuine tension worth carrying forward: the largest models, which need efficient long-context handling the most, are also the ones where the crossover point is furthest away, meaning they need the longest contexts before this chapter's savings actually kick in.

The honest caveat this chapter opens with, not buries at the end. “Linear attention is always faster” is false. It has a fixed per-token overhead of O(d2) that full attention doesn't pay until the sequence gets long. For short sequences — a single chat turn, a short function signature — ordinary softmax attention is the right tool, and it is also, as later chapters will show, usually the more accurate tool. The case for linear attention is specifically a long-sequence case. Session 08's whole premise — a chatbot running all day, a 4-million-token stream — is exactly the regime where the crossover has been crossed by a factor of a thousand.

One more worked number: a full pretraining run, not just one sequence

Chapter 0's crossover table looked at a single sequence. Pretraining runs process many sequences, and it's worth confirming the same argument scales up rather than resetting somehow. A realistic pretraining corpus is packed into fixed-length training sequences — say 8,192 tokens each, well past the 4,096-token crossover — and a single training run might process on the order of 10 million such sequences to reach 80 billion tokens of data. The per-sequence ratio from Chapter 0's formula at L=8,192, d=4,096:

L ÷ d = 8,192 ÷ 4,096 = 2.0 — linear attention costs half as much, on the attention term, for every single one of those 10 million sequences

Nothing about training at scale changes the per-sequence argument — the savings simply multiply by however many sequences the run processes, because each sequence's attention cost is independent of every other sequence's. This is exactly why a training-efficiency argument that looks marginal at one sequence (a 2× saving is real but not dramatic) can still translate into a meaningfully cheaper multi-week pretraining run once multiplied across millions of sequences — and why the effect compounds even faster for any application that routinely processes much longer contexts than 8,192 tokens, where Chapter 0's ratio grows well past 2.

Why this isn't the same problem Session 08 solved

It's worth being precise about the difference, because both sessions talk about “long sequences are expensive” and it would be easy to conflate them. Session 08's StreamingLLM keeps full quadratic attention's mechanism exactly as-is — softmax, full comparisons — and instead bounds L by evicting old tokens, so the quadratic and linear costs both stay capped at whatever the window size is. It is a patch that keeps the sequence effectively short forever. This session asks a more ambitious question: what if the mechanism itself never needed L2 at all, for any window size, so nothing ever needs evicting? That would mean a model that could, in principle, keep every token's influence available — not through a cache someone truncates, but through an accumulator that costs the same fixed amount whether the sequence is 10 tokens or 10 million.

Put the two sessions side by side in one sentence each: Session 08 answers “how do I keep an existing, unmodified full-attention model running forever?” This session answers “could the attention mechanism itself have been built differently from the start, so that question never needed asking?” Both are legitimate, complementary engineering answers to the same underlying observation — attention doesn't scale — aimed at two different points in a system's lifecycle: one for models already trained and deployed, one for models not yet built.

The crossover, live

Drag the sequence-length slider. The red bar is full attention's dominant cost (L²d); the teal bar is linear attention's (Ld²), both at d = 4,096 (Llama-2-7B). Watch which bar is taller as L crosses d.

sequence length L4,096

Total cost versus per-step cost — two different questions

The crossover table above answered “what does it cost to process a whole sequence of length L from scratch?” That's the right question for training, where the entire sequence is available at once. But generation — the setting Session 08 cared about — asks a different question: having already processed L tokens, what does it cost to produce token L+1? For full attention, that single step still has to compare the new query against every one of the L cached keys, costing O(Ld) — growing linearly in how long the conversation has already run, forever. For a linear-attention recurrence, that single step costs a fixed O(d2), the same amount whether it's token 10 or token 10 million. Total-sequence cost and per-step cost tell different stories, and it's worth holding both:

QuestionFull attentionLinear attention
Cost to process a full sequence of length L (training)O(L²d)O(Ld²)
Cost of one more generation step, after L tokens (inference)O(Ld) — grows with L, foreverO(d²) — fixed, no matter how large L gets

Put a number on the gap. At L=100,000 tokens into a conversation, d=4,096: generating the next single token under full attention costs proportional to Ld = 100,000 × 4,096 = 409,600,000. Under a linear-attention recurrence it costs proportional to d2 = 4,096 2 = 16,777,216 — the same number it would have cost at token 1. The ratio, 409,600,000 ÷ 16,777,216 ≈ 24.4×, is exactly L/d again (100,000/4,096≈24.4) — the same formula from the crossover table, now applied to the cost of a single generation step rather than to the whole sequence. This is the number that determines how laggy a deployed chatbot feels late into a long conversation: under full attention, each new reply takes longer to start generating than the one before it, growing worse the longer the conversation runs; under linear attention, token 100,001 costs exactly what token 2 did.

This second row is the one that matters most for a live chatbot: it says every single token full attention generates gets slower to produce than the one before it, while every token linear attention generates costs exactly the same as the first one did. Session 08's StreamingLLM approximates this second row's flatness by bounding the window — capping L at a fixed value so O(Ld) stops growing. This session's approach gets there differently: it doesn't cap L, it removes L from the per-step formula entirely.

A older name for half of this idea: recurrent neural networks

Before Transformers existed at all, sequence models were dominated by recurrent neural networks (RNNs) — models that read one token at a time and update a fixed-size hidden state, exactly the shape Chapter 1 is about to derive for linear attention. RNNs already had the constant-memory, linear-time property this chapter is chasing. So why did the field move away from them toward quadratic-cost Transformers in the first place?

The answer is training, not inference. An RNN's hidden state at step i depends on its hidden state at step i−1, which depends on step i−2, all the way back to the start — a strictly sequential dependency chain that has to be walked one step at a time even during training, on data the model already has in full. That serial chain is brutal for GPUs, which are built to do enormous numbers of independent operations at once, not thousands of small operations that must happen in strict order. Transformers won not because O(L2) is a good property, but because full attention's parallel form — process every position in the sequence at once, using ordinary matrix multiplication — trains blisteringly fast on exactly this kind of hardware, quadratic cost notwithstanding. This session's entire arc, especially Chapters 6 and 7, is about winning back that RNN-style constant memory without giving back the parallel trainability that made Transformers practical in the first place. That tension — recurrent inference, parallel training — is the single hardest engineering problem this whole lesson is built around, and it resurfaces explicitly once the delta rule enters the picture.

An honest caveat this early: attention isn't the whole layer

Every crossover ratio in this chapter compared attention's own cost, full versus linear. It's worth being upfront that attention is only one piece of a Transformer layer — the feed-forward (MLP) block that follows every attention layer costs O(Ld2) regardless of which attention mechanism sits next to it, and at typical model sizes the MLP block is often the larger of the two pieces per layer. That means the whole-model speedup from swapping in linear attention is always smaller than the crossover ratio this chapter derives — the ratio applies to the fraction of total compute attention itself accounts for, not to the model's total runtime. This doesn't undercut the argument (long-sequence workloads are still dominated by attention's O(L2) term once L grows large enough, MLP cost notwithstanding), but a precise engineer should never quote “977× faster” as a whole-model number without checking what fraction of the model's total FLOPs that ratio actually applies to.

What "linear" is actually promising

Hold two separate promises apart, because this lesson delivers on both but proves them differently. First: linear time — the compute to process a sequence of length L grows proportionally to L, not L2. Second, and just as important: constant memory — the amount of state carried forward doesn't grow with L at all. A bounded KV cache (Session 08) gets you close to the second promise by capping growth, but it's still technically O(window size), and it discards information to get there. What this session builds toward is a mechanism whose memory truly never grows past a single fixed-size matrix, no matter how long the sequence runs, while still — in principle — letting every past token influence that matrix. Whether that promise holds up honestly, and what it costs, is this entire session's subject.

“In principle” is doing real work in that sentence, and it's worth being upfront about why. A fixed- size matrix influenced by every past token is not the same guarantee as a KV cache that stores every past token's key and value individually and separately retrievably. Information from token 1 does influence the state at token 1,000 — it was summed in, it's mathematically still present — but whether it can still be cleanly recovered, rather than blended into an unrecoverable mixture with everything else written since, is exactly Chapter 3's capacity question, not a settled fact this early in the lesson. “Influences the state” and “can be retrieved from the state” are different claims, and conflating them is the single easiest way to misread everything that follows.

Concept → realization. The fix in this session doesn't patch attention's cache-eviction policy the way Session 08's did. It removes one function — softmax — from inside the attention formula, and in doing so restructures the entire computation from “compare against everything, every time” into “maintain a running summary, update it each step.” That restructuring is the whole lesson. Chapter 1 does it explicitly, one line of algebra at a time.
At d = 4,096, roughly how does the compute cost of full attention compare to a linear-attention recurrence when the sequence length L is much smaller than d (say L = 256)?

Chapter 1: Attention Without Softmax

Chapter 0 promised a mechanism that reorganizes attention from “compare against everything” into “maintain a running summary.” This chapter does that transformation, in full, using nothing but algebra you already know: matrix multiplication is associative, meaning you can group the terms in a product however you like without changing the answer. That one fact is the entire trick.

A reminder of what associativity means, in the simplest possible setting, before applying it to matrices: for ordinary numbers, (2×3)×4 = 2×(3×4) — both equal 24, regardless of which multiplication happens first. The same freedom to regroup holds for matrix and vector products, as long as the order of the factors left to right doesn't change (matrix multiplication is associative but, unlike ordinary numbers, generally not commutative — swapping the order of two matrices usually changes the answer, only regrouping is free). That distinction — associative but not commutative — will matter again, sharply, in Chapter 6.

Standard attention, written explicitly

Start from the attention every prior session has used. For output position i, with query qi, and keys/values k1ki, v1vi from every token processed so far:

oi = ∑j=1i vj · κ(kj, qi) ÷ ∑j′=1i κ(kj′, qi)

where κ(k, q) = exp(k·q) is the softmax kernel — a function that turns a raw dot product into a positive number, so that after dividing by their sum, every token's contribution is a proper weight between 0 and 1 that sums to exactly 1 across the row. This is exactly softmax(QKT)V, just written per-token with the normalization made explicit rather than hidden inside a softmax call.

The one substitution that changes everything

The exponential is what breaks the associativity trick — you cannot factor exp(k·q) into separate functions of k and q multiplied together. So replace it. Pick some function φ that maps vectors into a new space where the dot product there approximates the original kernel:

κ(k, q) = exp(k·q)   ←   replaced by   →   κ′(k, q) = φ(k)Tφ(q)

If this move looks familiar from classical machine learning, that's not an accident. Support Vector Machines made exactly this substitution famous decades earlier under the name the kernel trick: replacing an expensive or infinite-dimensional similarity function with an explicit feature map whose dot product approximates it. The direction is usually the opposite of what's happening here, though — SVMs typically use the kernel trick to avoid ever computing φ explicitly (because φ would map into a huge or infinite space), working entirely through the kernel function instead. Here, the whole point is the reverse: compute φ explicitly, in a modest, finite dimension, specifically because having explicit feature vectors is what unlocks the associativity trick below. Same underlying idea, opposite reason for using it.

Substitute κ′ into the attention formula above. The numerator becomes ∑j vj (φ(kj)Tφ(qi)) — and here is the associativity move: because qi is the same vector multiplying every term in the sum, you can pull it outside the sum entirely and group it with each vj first instead:

j=1i vj (φ(kj)Tφ(qi)) = ( ∑j=1i vj ⊗ φ(kj) ) φ(qi)

where ⊗ is the outer product — two vectors combined into a matrix, one column per input dimension, rather than collapsed into a single number the way a dot product would. That parenthesized sum no longer depends on qi at all. It only depends on everything up to position i. Give it a name:

Si = ∑j=1i vj ⊗ φ(kj)      zi = ∑j=1i φ(kj)

and the whole layer collapses to:

oi = Siφ(qi) ÷ ( zi·φ(qi) )

From a sum to a recurrence

Si is a running sum. Running sums update by adding one new term, which means you never need to recompute the whole sum from scratch — you only need what you had a moment ago, plus the newest piece:

Si = Si−1 + vi ⊗ φ(ki)      zi = zi−1 + φ(ki)

This is a recurrent neural network — a model that carries forward a fixed-size hidden state and updates it one input at a time — except the hidden state Si here is not a vector, the way an old-school RNN's hidden state was. It's a matrix, exactly dvalue × ddot in size, and it never grows no matter how many tokens have been processed. That is the constant-memory promise from Chapter 0, delivered: Si costs the same number of bytes at token 10 and at token 10 million.

This matrix-valued generalization is worth pausing on, since it's easy to skim past. Classic RNNs like LSTMs carry forward a hidden state that's a single vector, typically hundreds to a few thousand numbers. Linear attention's state is a full matrix, dvalue×ddot numbers — for a 128-dimensional head, that's already 16,384 numbers, two full orders of magnitude more capacity than a typical LSTM hidden state. That larger, matrix-shaped state is precisely what makes the fast-weight-programmer framing (Chapter 2) the more natural lens than “just another RNN” — a single vector doesn't naturally support the outer-product write-and-read operations this session builds everything else on top of.

token i arrives
produces ki, vi, qi
update the state
Si = Si-1 + vi ⊗ φ(ki) — one outer product added
read the output
oi = Siφ(qi) — one matrix-vector multiply
↻ discard ki, vi forever — nothing to cache

The simplification almost everyone actually uses

φ needs to satisfy a technical property (its output must be positive, so the ratio still behaves like an attention weight) if you want the normalization in Chapter 0's formula to mean something. But a large and important slice of practice — including the delta-rule models this lesson is building toward — drops the normalization entirely and sets φ to the identity function, recovering the plainest possible version:

Si = Si−1 + vikiT      oi = Siqi

No kernel, no normalization, no query projection needed even — just raw keys, values, and queries, wired through outer products. Every worked numeric example for the rest of this session uses exactly this form, because it's the version that makes the underlying arithmetic checkable by hand, and it's also, as Chapter 2 shows, the version with a startling second identity hiding inside it.

If you do want a φ: why ELU+1, not the simpler ReLU

For readers who do want the general kernelized form (Chapter 0's positivity-preserving version, not the identity simplification above), the most common practical choice is ELU(x)+1 rather than the even simpler ReLU(x). Both satisfy the positivity requirement — ReLU clips negatives to exactly zero, ELU+1 smoothly approaches zero from above. The reason to prefer ELU+1 is about training, not inference: ReLU's gradient is exactly zero for every negative input, meaning any key or query component that lands on the negative side of zero contributes nothing to learning at all — a “dead” direction the model can never recover useful gradient signal through. ELU's gradient stays nonzero (though small) everywhere, including deep into negative territory, so gradient descent can still nudge those components rather than abandoning them entirely. It's a small implementation choice, but it's the kind of choice that differentiates a linear-attention variant that trains reliably from one that quietly loses gradient signal in half of its input space.

Two ways to compute the same output, verified against each other

Chapter 1's callout mentions a “parallel form” without proving it agrees with the recurrent form. Check it by hand on a tiny 3-token example, using the identity-φ simplification from above (raw keys, values, queries, no kernel). Keys and values: k1=(1,0), v1=(2,0); k2=(0,1), v2=(0,3); k3=(1,1), v3=(1,1). Query with q3=(1,1), asking for the output at position 3.

Recurrent form — build the state step by step, output only what's needed at step 3:

S1 = v1k1T = [ [2,0],[0,0] ]    S2 = S1 + v2k2T = [ [2,0],[0,3] ]    S3 = S2 + v3k3T = [ [3,1],[1,4] ]
o3 = S3q3 = [ [3,1],[1,4] ](1,1) = (4, 5)

Parallel form — compute the same output directly as a masked attention sum, no running state at all: o3 = ∑j=13 vj(kj· q3). The three dot products: k1·q3=1, k2·q3=1, k3·q3=2.

o3 = 1·(2,0) + 1·(0,3) + 2·(1,1) = (2,0)+(0,3)+(2,2) = (4, 5)

Identical, exactly. This is not a coincidence — it's the same associativity argument from earlier in this chapter, just run in reverse: the recurrent form built the state up token by token, while the parallel form computed the equivalent masked weighted sum all at once. Training uses the parallel form (one large matmul, GPU-friendly); a deployed model generating token by token uses the recurrent form (cheap, constant-size state). Both are the same function, computed two different ways — and this equivalence is the entire reason linear attention can be fast to train and fast to run simultaneously, a combination the RNN era never had.

python
import torch

k = torch.tensor([[1.,0.],[0.,1.],[1.,1.]])
v = torch.tensor([[2.,0.],[0.,3.],[1.,1.]])
q3 = torch.tensor([1.,1.])

# recurrent form: build S step by step
S = torch.zeros(2,2)
for i in range(3):
    S = S + torch.outer(v[i], k[i])
print(S @ q3)                                   # tensor([4., 5.])

# parallel form: one masked matmul, no state at all
scores = k @ q3                                       # dot products against q3
print((scores.unsqueeze(1) * v).sum(dim=0))  # tensor([4., 5.]) -- identical

Both print the same two numbers. This is the check every production linear-attention implementation relies on implicitly: train with the fast parallel form, deploy with the cheap recurrent form, and never worry that the two paths might disagree, because the associativity argument guarantees they can't.

What φ is actually allowed to be

Chapter 1 asserted φ needs a positive codomain for the normalization to behave like real attention weights, and separately showed that dropping normalization entirely lets φ be the identity. There's a second property worth naming, because Chapter 3 depends on it directly: the dimensionality of φ's output, ddot, is a free design choice, and it does not have to equal the original key dimension dkey. A φ that maps into a higher-dimensional space than it started in (ddot > dkey) can, in principle, store more orthogonal directions than the raw keys ever could — a lever for raising the capacity ceiling Chapter 3 is about to run headlong into. The simplest possible φ (identity, or ELU+1 applied elementwise) leaves ddot = dkey, spending none of that lever. Later chapters revisit what happens when you pull it.

In code, so the shapes are undeniable

python
import torch

def full_attention_step(q, K, V):
    # K, V: (i, d) -- every key/value seen so far. Cost grows with i.
    scores = K @ q                              # (i,) -- i comparisons, more every step
    weights = torch.softmax(scores, dim=0)
    return weights @ V                       # (d,)

def linear_attention_step(q, k, v, S):
    # S: (d_value, d_key) -- fixed size, no matter how many tokens came before.
    S = S + torch.outer(v, k)                   # one outer product, O(d^2) work
    o = S @ q                                    # one matrix-vector multiply, O(d^2) work
    return o, S                              # S is EVERYTHING carried to the next step

Read the two functions side by side. full_attention_step needs the entire history K, V passed in every single call — that's the KV cache Session 08 spent a whole lesson pricing out. linear_attention_step needs only the previous step's S, a single fixed-size matrix. Nothing about token 1 through token i−1 individually survives past that one matrix — and whether that's a feature or a cost is a question this lesson keeps returning to.

Why this matters beyond speed. This recurrent form is also fully parallelizable during training, despite looking sequential: because it's a plain sum, you can compute the whole sequence's outputs at once via O = (QKTM)V, where M is a causal mask, exactly like ordinary attention's parallel form, just without the softmax. Training gets to use the fast parallel form; inference gets to use the cheap recurrent form. That dual nature — matters enormously in Chapter 6, when a small change to the update rule turns out to break exactly this property.

The chain of reasoning, compressed to five steps

Before moving on, compress this chapter's whole derivation into a checklist worth being able to reproduce from memory: (1) write softmax attention with its kernel κ(k,q)=exp(k·q) made explicit; (2) replace κ with a factorizable kernel φ(k)Tφ(q); (3) use associativity to pull the query-dependent factor outside the summation over prior tokens; (4) name what's left inside the sum a running state S; (5) notice that a running sum updates by simple addition of one new term, which is exactly a recurrence. Every one of those five moves is reversible and checkable — there is no step in this derivation that requires taking anything on faith, which is precisely why Chapter 2 can claim the resulting object is identical to a decades-old memory model, not merely similar to one.

What algebraic property of matrix multiplication makes it possible to rewrite the attention sum as a running state S that updates one token at a time, once the softmax is replaced by a kernel φ(k)Tφ(q)?

Chapter 2: Fast Weight Programmers

The matrix S from Chapter 1 is not a new idea invented for Transformers. It is, almost move for move, a mechanism proposed in 1991 — three decades before “linear Transformer” was a phrase anyone used — by Jürgen Schmidhuber, under a name that sounds almost like science fiction: Fast Weight Programmers. This chapter is the paper this whole session is built around — Linear Transformers Are Secretly Fast Weight Programmers, by Imanol Schlag, Kazuki Irie, and Jürgen Schmidhuber, IDSIA (Swiss AI Lab), published at ICML 2021 — and its central claim is exactly what the title says: strip the softmax out of attention, and what's left is, in every mathematical respect, a fast weight programmer.

What a fast weight programmer actually is

In an ordinary neural network, the weights are fixed after training — only the activations change as new inputs arrive. The fast weight idea, going back to von der Malsburg's 1981 work on synaptic modulation, is to let the weights themselves change at test time, dynamically, based on the input stream. A slow net — trained the ordinary way, its own weights frozen after training — learns to continually reprogram the weights of a second, fast net, using a sequence of small, differentiable edits. The slow net's job isn't to answer the question directly; its job is to write a tiny, constantly-updating program (encoded as the fast net's weight matrix) that the fast net then executes.

An analogy worth holding onto: the slow net is a programmer, the fast net is the computer running whatever program was most recently compiled. Every token that arrives hands the programmer a small edit to make to the running program — not a full rewrite, just one more instruction appended — and the computer immediately executes the updated version to produce its output. Nobody hand-writes the instructions; the programmer (the slow net) learns, through ordinary gradient descent during training, what instructions to generate at each step in order to make the fast net produce useful outputs. This is the picture Chapter 1's matrix S already matches exactly — it just wasn't described in programming terms until now.

Schmidhuber's 1991–1993 formulation gives the slow net one specific, elegant instruction for rewriting the fast weights: an outer product between two self-generated vectors. For an input sequence {x(1)x(L)}, the slow net produces two vectors per step, a(i) and b(i), and writes them into the fast weight matrix additively:

W(i) = σ( W(i−1) + a(i)b(i) )      y(i) = W(i)x(i)

Compare that to Chapter 1's ending point: Si = Si−1 + vikiT, oi = Siqi. Set σ to the identity function, drop the query projection, and rename av, bk, xq. The two equations are now identical. Not similar — identical, term for term. Linear attention's running state S is a fast weight matrix; its keys and values are the slow net's self-invented programming instructions; its queries are the fast net's own input. Ba et al. (2016) had already noticed a version of this connection to attention specifically; this paper makes the equivalence with modern linear Transformers precise and complete.

python
import torch

# Schmidhuber 1991-93's Fast Weight Programmer, verbatim (sigma = identity)
def fwp_step(x, Wa, Wb, W):
    a, b = Wa @ x, Wb @ x
    W = W + torch.outer(a, b)      # the "programming instruction"
    y = W @ x
    return y, W

# Chapter 1's linear attention recurrence, same shapes, renamed variables only
def linear_attn_step(q, Wk, Wv, S):
    k, v = Wk @ q, Wv @ q
    S = S + torch.outer(v, k)      # a → v, b → k
    o = S @ q
    return o, S

# same weights, same input -- these two functions compute the same thing
d = 4; Wa = Wk = torch.randn(d,d); Wb = Wv = torch.randn(d,d)
x = torch.randn(d); W = torch.zeros(d,d); S = torch.zeros(d,d)
y, _ = fwp_step(x, Wa, Wb, W)
o, _ = linear_attn_step(x, Wk, Wv, S)
print(torch.allclose(y, o))   # True -- not an analogy, the identical function

One more historical detail worth savoring: Schmidhuber's 1993 paper, describing a recurrent version of exactly this mechanism, uses the phrase “internal spotlights of attention” to describe what the fast net's dynamically-changing weights do — written nearly a quarter-century before “attention” became the field's standard vocabulary for this operation. The word choice wasn't a coincidence in hindsight, and it wasn't borrowed from later Transformer papers either — it's independent evidence that the underlying concept these two vocabularies describe was recognizable as “attention-like” even to the researcher who first built it, decades before the term was standardized.

Why this reframing earns its keep. It's not decoration — it hands you an entire pre-existing body of theory to import wholesale. Anything known about fast weight memories' capacity, their failure modes, and how to fix those failure modes — developed independently of Transformers, decades before anyone cared about language models — now applies directly to linear attention. Chapters 3 and 4 are that theory, imported and worked by hand.

The longer lineage, because it didn't start or stop in 1991

The fast weight idea has an even older ancestor and a longer afterlife than a single paper suggests, and both ends of that history are instructive. The ancestor: Christoph von der Malsburg's 1981 work on “synaptic modulation” first proposed that a network's effective weights could be a superposition of ordinary, slowly-changing weights and fast, context-dependent ones — the conceptual seed, though not yet a trainable mechanism. Geoffrey Hinton and David Plaut, in 1987, built a network with two literal sets of weights at different learning rates, but for a different purpose (fast retraining after damage), not for processing a sequence. What Schmidhuber's 1991–1993 papers added, and what made this session's equivalence possible, was the missing piece: a mechanism where the slow net learns, by ordinary gradient descent, to generate the programming instructions for the fast net, from the input stream itself — nobody hand-designs what gets written; the network invents it.

The afterlife matters just as much for understanding why this framing resurfaced in 2021. Fast weights went quiet for two decades, then reappeared repeatedly under different names as different subfields rediscovered pieces of the same idea: hypernetworks (a network that generates another network's weights), dynamic convolution (a convolution kernel computed fresh from the input rather than fixed after training), and meta-learning approaches that use fast weights to let a model adapt within a single task without a separate fine-tuning step. Schlag and Schmidhuber's own 2018 and 2021 follow-up work specifically extended fast weight memories with mechanisms for replacing outdated associations — work that directly seeded the delta rule this session builds toward in Chapter 4. The point worth carrying forward: this session's “linear Transformers are secretly fast weight programmers” is not a one-off cute observation. It's the moment a three-decade-old, independently-rediscovered-several-times idea and a then-brand- new architecture turned out to be the same mathematical object, which is exactly why the capacity theory (Chapter 3) and the delta-rule fix (Chapter 4) could be imported wholesale instead of invented from scratch.

A second, independent bridge: modern Hopfield networks

Fast weight programmers aren't the only 20th-century memory model attention turns out to resemble. A separate 2021 line of work (Ramsauer et al.) showed that softmax attention itself is mathematically close to a modern Hopfield network — an associative memory model dating to John Hopfield's 1982 paper, updated with a continuous, exponential energy function whose update rule turns out to look almost exactly like softmax attention's retrieval step. That's a genuinely different historical lineage than this chapter's fast-weight-programmer connection — Hopfield networks come from statistical physics and associative memory theory, fast weight programmers come from meta-learning and dynamic connectionism — and yet both independently converge on the same object: attention as some flavor of associative memory retrieval. Two unrelated fields, decades apart, both looking at attention and recognizing the same shape underneath it is a strong signal that “attention is a memory-retrieval operation” isn't a cute reframing, but close to attention's actual mathematical nature.

Why this connection took thirty years to notice

It's worth asking why nobody spotted this identity between 1991 and 2021, given how short the algebraic distance turned out to be. Two practical reasons stand out. First, fast weight programmers were a small, specialized research thread throughout the 2000s and 2010s — interesting to a handful of researchers, not mainstream enough for most of the field working on Transformers to have encountered them at all. Second, and more fundamentally, nobody had strong reason to go looking for the connection until after the Transformer's O(L2) cost became a widely felt practical pain point, which only happened once Transformers themselves became the dominant architecture and people started training them on longer and longer sequences, around 2019–2020. The equivalence this chapter derives was, in a real sense, always true and always derivable — nothing about the 1991 equations changed. What changed was that enough people needed a fix for quadratic attention that someone went looking through the literature for prior work on efficient, associative-memory-flavored alternatives to a growing weight matrix, and found a three-decade-old answer waiting. This is a useful pattern to notice for research generally: a genuinely useful old idea can sit unused for decades, not because it was wrong, but because the problem it solves hadn't become painful enough yet for anyone to go looking for it.

A worked memory: two orthogonal keys, hand-computed

Make this concrete with numbers small enough to verify with pencil and paper: a 2-dimensional key space, two stored associations. Let the two keys be the standard basis vectors, and pick two arbitrary value vectors to associate with them:

k1 = (1, 0), v1 = (5, 2)      k2 = (0, 1), v2 = (3, 7)

Build the fast weight matrix by summing the two outer products, exactly as Chapter 1's recurrence would after two steps. Recall vk = vkT is a dvalue × dkey matrix — here 2×2 — built by multiplying each entry of v against each entry of k:

v1k1 = [ [5·1, 5·0], [2·1, 2·0] ] = [ [5, 0], [2, 0] ]      v2k2 = [ [0, 3], [0, 7] ]
W = v1k1 + v2k2 = [ [5, 3], [2, 7] ]

Now retrieve. Query with k1 itself — o = Wk1 = W·(1,0), which picks out exactly W's first column:

W(1,0) = (5·1 + 3·0, 2·1 + 7·0) = (5, 2) = v1, exactly

Query with k2: W(0,1) = (5·0+3·1, 2·0+7·1) = (3, 7) = v2, exactly. Perfect recall, both times, with zero interference between the two stored associations. Now try a query that's a 50/50 blend of the two keys, q = (0.5, 0.5):

W(0.5, 0.5) = (5·0.5 + 3·0.5, 2·0.5 + 7·0.5) = (4, 4.5) = 0.5v1 + 0.5v2

Exactly the average of the two stored values — a graceful, linear blend, not garbage. This is the fast weight memory working exactly as designed: perfect retrieval on the keys you stored, sensible interpolation on anything in between.

Why orthogonality gives exact retrieval, formally

The two numeric checks above are convincing but it's worth seeing why they had to come out exact, not just observing that they did. For a set of keys {k1kn} that are orthonormal (each unit length, all mutually perpendicular), querying the memory W = ∑j vjkj with any stored key ki gives:

Wki = (∑j vjkj)ki = ∑j vj(kj·ki) = ∑j vj·δji = vi

where δji is the Kronecker delta — 1 when j=i, 0 otherwise — which is exactly what “orthonormal” means in dot-product terms: kj·ki is 1 when they're the same vector and 0 whenever they're different. Every term in the sum except j=i vanishes automatically, leaving exactly vi with nothing added or subtracted. This is the precise condition Chapter 3 violates on purpose: the moment any two stored keys have a nonzero dot product, that clean cancellation breaks, and δji stops being 0 for ji — which is exactly the crosstalk term Chapter 3 computes by hand.

Scaling the toy up to a real model's dimensions

This chapter's memory lived in 2 dimensions specifically so the arithmetic could be checked by hand. A real attention head, per Session 08's Llama-2-7B numbers, has dkey = 128 (4,096 hidden size split across 32 heads). Nothing about the mechanism changes at that scale — W is still built by summing outer products, retrieval is still one matrix-vector multiply — only the numbers get too large to track by hand. What does change with scale is exactly the quantity Chapter 3 is about: a 128-dimensional key space has room for 128 mutually orthogonal directions instead of 2, which sounds like a lot of headroom until you remember that a single attention layer processes sequences far longer than 128 tokens, routinely thousands to millions of them. The toy's cliff at 3 stored associations and the real model's cliff somewhere past 128 are the same cliff, just at different heights — Chapter 3 makes that scaling explicit.

The outer-product memory, live

Two keys are fixed at 0° and 90° (orthogonal), holding the two values above. Drag the query angle around the circle and watch the retrieved output blend continuously between v1 and v2 — exact at the two stored angles, a mix everywhere else.

query angle (°)

In code: the whole memory, four lines

python
import torch

k1, v1 = torch.tensor([1., 0.]), torch.tensor([5., 2.])
k2, v2 = torch.tensor([0., 1.]), torch.tensor([3., 7.])

W = torch.outer(v1, k1) + torch.outer(v2, k2)   # [[5,3],[2,7]]

print(W @ k1)   # tensor([5., 2.])  -- exact v1
print(W @ k2)   # tensor([3., 7.])  -- exact v2
print(W @ torch.tensor([0.5,0.5]))  # tensor([4., 4.5]) -- blend

This is what “attention” reduces to once softmax is gone: not a comparison against a growing list, but a lookup in a matrix that was built by summing self-invented key-value edits. Every worked example for the rest of this session builds on exactly this W = ∑ vk mechanism — the next two chapters ask what happens when you keep writing to it forever, and how to fix what goes wrong.

Notice, too, that torch.outer(v1, k1)'s argument order matches this session's math notation exactly — value first, key second, producing vkT — which is worth confirming explicitly rather than assuming, since getting the order backwards would silently produce the transpose of the intended matrix and every subsequent retrieval would be wrong in a way that's easy to miss without a worked numeric check like this one to catch it against.

A quick sanity check: does dvalue have to equal dkey?

Nothing in this chapter's derivation required the value dimension to match the key dimension — worth confirming, since all this chapter's worked examples happened to use 2 for both. W = ∑ vjkj is a dvalue×dkey matrix in general, built from dvalue-dimensional values and dkey-dimensional keys independently. Try dvalue=3, dkey=2: k1=(1,0), v1=(5,2,9). The outer product v1k1 is a 3×2 matrix, [[5,0],[2,0],[9,0]], and retrieval Wk1 returns a 3-dimensional vector, exactly matching v1. The key space (where orthogonality and capacity live, Chapter 3) and the value space (what actually gets retrieved) are two independent design choices — capacity is governed entirely by dkey (or ddot, once a φ is involved), never by how wide the retrieved values happen to be.

In the framing of Fast Weight Programmers, what role do a Transformer's keys and values play?

Chapter 3: When Memory Overflows

Chapter 2's two-key memory was perfect because the two keys were orthogonal — at right angles, dot product exactly zero. That wasn't a lucky choice; it was the entire reason retrieval came out clean. This chapter asks the question Chapter 2 sidestepped: what happens once you try to store more associations than the key space has room for orthogonal directions?

The capacity argument, in one sentence

A d-dimensional space contains at most d mutually orthogonal vectors — that's a basic fact of linear algebra, the same one that says a 2D plane has exactly two perpendicular axes and no third one is possible. Every additional key beyond that count is forced to have a nonzero dot product with at least one key already stored. And a nonzero dot product means the retrieval sum ∑j vj (kj·q) picks up unwanted contributions from those other stored values whenever you query with that key — crosstalk, in the paper's own terminology, borrowed from Smolensky's 1990 tensor product representation theory. Beyond d stored associations, in a ddot-dimensional key space, the memory enters what the paper calls an overcapacity regime, and retrieval quality degrades.

Smolensky's tensor product representation theory, borrowed here, was originally developed for a different question — how connectionist neural networks could represent symbolic, structured information (roles bound to fillers, like a sentence's grammatical structure) using nothing but distributed vector representations, without hand-wiring the structure in. The mathematical object at the center of that theory — a sum of outer products binding “role” vectors to “filler” vectors — turns out to be identical in form to this chapter's fast weight matrix, with keys playing the role of roles and values playing the role of fillers. One difference is worth flagging, since it's a genuine distinction rather than a detail: Smolensky's classical tensor product representations are built with the roles and fillers chosen in advance, by whoever designs the representation. This chapter's fast weight programmers learn both the keys and the values through gradient descent, with nobody specifying in advance what a given key ought to mean. The capacity theorem transfers regardless of that difference — a d-dimensional space still only holds d orthogonal directions whether those directions were chosen by a designer or discovered by training — which is precisely why it was safe for the 2021 paper to import the theorem wholesale rather than re-derive it.

Hand-worked: adding a third key to a 2D memory

Take Chapter 2's exact memory, W = [ [5,3], [2,7] ], built from two orthogonal keys. There is no room for a third orthogonal direction in 2D — so pick a third key that is necessarily non-orthogonal to the first two, say k3 = (0.6, 0.8) (a unit vector, at roughly 53° from the x-axis), paired with v3 = (9, 1):

v3k3 = [ [9·0.6, 9·0.8], [1·0.6, 1·0.8] ] = [ [5.4, 7.2], [0.6, 0.8] ]
Wnew = W + v3k3 = [ [10.4, 10.2], [2.6, 7.8] ]

Query with k3 itself, hoping for v3 = (9,1) back:

Wnew(0.6, 0.8) = (10.4·0.6 + 10.2·0.8, 2.6·0.6 + 7.8·0.8) = (14.4, 7.8) ≠ (9, 1)

Wrong, by a lot. Here's why, made explicit: because k1·k3 = 0.6 and k2·k3 = 0.8 (neither is zero — k3 isn't orthogonal to either earlier key), the retrieval sum pulls in leftover mass from both earlier associations:

o = v1(k1·k3) + v2(k2·k3) + v3(k3·k3) = (5,2)·0.6 + (3,7)·0.8 + (9,1)·1 = (3,1.2)+(2.4,5.6)+(9,1) = (14.4, 7.8)

Matches, term for term — confirming exactly where the corruption comes from. Now check the damage in the other direction: query with k1, which used to retrieve a perfect (5, 2):

Wnew(1, 0) = (10.4, 2.6) ≠ (5, 2)

Also wrong — adding the third association corrupted retrieval of the first one too, even though nothing about k1 or v1 changed. This is the overcapacity regime in miniature: past a fixed capacity, every new write degrades every old memory a little, not just the newest one.

How the real experiment behind this chapter was actually run

This chapter's numbers (the S≈60 threshold at ddot=64, the 128/256/384 DPFP thresholds) come from a controlled synthetic retrieval task designed specifically to isolate capacity, worth understanding in outline. A pool of S unique random key-value pairs is sampled, written into the memory once each (no repeats, unlike Chapter 5's later with-replacement variant), and the model is then queried with one of the stored keys, asked to retrieve its paired value. Every model in the comparison — softmax, plain linear attention, DPFP at three settings, FAVOR+ at three settings — is trained on this exact task, independently, for each value of S from 20 up to 600 in steps of 20, until either the loss falls below a fixed threshold or training visibly stalls. Each is trained fresh at every S, so the reported threshold where a given φ function starts failing is not a single lucky or unlucky run, but the point along an entire swept curve where retrieval quality provably falls apart. That's the same shape of experiment the interactive simulation below approximates in 2D, with random keys instead of a deliberately constructed example.

Is this a toy problem, or does it actually happen in a real deployment?

Connect the synthetic S=20-to-600 sweep to something concrete. A real Llama-2-7B-scale attention head has ddot=128 (Session 08's 4,096 hidden size, split across 32 heads). Under plain linear attention with φ=identity, that head's memory would enter Chapter 3's overcapacity regime the moment a processed sequence carries more than 128 genuinely distinct key directions worth remembering. A single paragraph of ordinary text easily contains far more than 128 tokens, and while not every token's key points in a fully independent direction, real learned key projections spread out enough in practice that meaningful crosstalk shows up well before every one of a document's tokens has been processed — which is exactly why Chapter 5's real experiment (not a toy) shows plain linear attention's perplexity diverging past 260 on long, untruncated text. This isn't a corner case reserved for adversarially constructed sequences; it's the default behavior of the sum rule on any sufficiently long, sufficiently varied stretch of ordinary language.

The misconception this rules out. “Just use a bigger key dimension and this goes away.” Bigger ddot raises the ceiling — it doesn't remove it. The paper's own synthetic experiment (Section 6.1) fixes dkey = 64 and finds ordinary linear attention (φ = identity, so ddot = dkey = 64) starts accumulating retrieval error right around 60 stored associations — just under the theoretical limit of 64, exactly as this chapter's 2D toy predicts. Their proposed feature map DPFP (deterministic parameter-free projection) can inflate the dot-product dimension to push that ceiling higher — ddot = 128, 256, or 384 for DPFP-1, DPFP-2, DPFP-3 — and each one dutifully starts failing right around its own limit. The random-feature method FAVOR+ (Performer) takes a different approach and, in their tests, never manages to reach zero loss at any capacity — a real cost of its randomness. Every method they tried has a wall. None of them has none.
python
import torch

k1, v1 = torch.tensor([1.,0.]), torch.tensor([5.,2.])
k2, v2 = torch.tensor([0.,1.]), torch.tensor([3.,7.])
k3, v3 = torch.tensor([0.6,0.8]), torch.tensor([9.,1.])

W = torch.outer(v1,k1) + torch.outer(v2,k2) + torch.outer(v3,k3)
print(W @ k3)   # tensor([14.4, 7.8])  -- should be [9,1], crosstalk from k1 and k2
print(W @ k1)   # tensor([10.4, 2.6]) -- should be [5,2], corrupted by k3's write too

How DPFP actually buys extra capacity

The callout above treats DPFP as a black box that “pushes the ceiling higher.” It's worth seeing how, in miniature, because the trick is genuinely clever and reusable. Take a 2-dimensional key k = (k1, k2) and map it into 4 dimensions using the rectifier r(a) = max(0, a) applied to four different sign combinations:

φ1(k) = r(k1)r(k2)    φ2(k) = r(−k1)r(k2)    φ3(k) = r(k1)r(−k2)    φ4(k) = r(−k1)r(−k2)

Each of the four output coordinates is the product of two rectified half-plane tests — effectively asking “is this key in quadrant 1? quadrant 2? 3? 4?” Any given 2D key vector falls into exactly one quadrant, so at most one of the four coordinates is nonzero for that key: if k has k1>0 and k2>0 (quadrant 1), only φ1 fires; the other three are forced to zero because they each contain a rectified negative version of a positive coordinate. Two keys landing in different quadrants therefore project to vectors with non-overlapping nonzero entries — automatically orthogonal in the 4D output space, even though they weren't orthogonal in the original 2D space. That's the entire mechanism: trade dimensionality for guaranteed orthogonality, splitting the input space into enough regions that keys landing in different regions can never collide. The general version scales this to any dkey, with a tunable parameter ν controlling exactly how many regions to split into — which is where the 128/256/384 capacity figures in the callout above come from: ddot = 2dkeyν, so ν=1,2,3 at dkey=64 gives exactly those three numbers.

A second real result: the same story in machine translation

The capacity theory isn't confined to synthetic retrieval toys — the paper checks it on WMT14 English-to-German translation too, comparing BLEU scores (higher is better) for the same architecture with different φ functions, at three different ddot values:

Modelddot=64, test BLEUddot=256, test BLEUddot=512, test BLEU
Standard Transformer (softmax)27.7
Linear Transformer (φ=ELU+1)26.8
Performer (FAVOR+)24.425.327.7
DPFP (this paper's φ)26.927.1

Read the Performer row across: it needs ddot=512 — a projection dimension eight times the original key size — before it catches up to a standard softmax Transformer's 27.7 BLEU. DPFP reaches 26.9–27.1 at far more modest dimensions. The mechanism is the same capacity argument this chapter has been making by hand: a small dot-product space starves retrieval quality, and different φ functions buy their way out of that starvation at very different rates.

An honest complication: even softmax attention isn't infinite capacity

It would be tempting to conclude “just use real softmax attention and this whole chapter's problem disappears.” Softmax attention doesn't compress the past into a fixed-size matrix at all — every token's key and value stay separately addressable, so there's no ddot-sized bottleneck to overflow. But the paper's own Setting 1 experiment (Section 6.1.1, the same one that measured the 60/128/256/384 thresholds) reports that even softmax attention “struggles to fully converge with more than 500 keys” on their synthetic retrieval task. The failure mode is different in kind — not the clean, provable crosstalk this chapter derived by hand, but a harder optimization landscape as the number of associations to juggle grows — and it kicks in at a much higher association count. But “no hard capacity ceiling” is not the same claim as “capacity is never a practical concern,” even for the method Chapter 0 is racing to approximate.

Does raising ddot to 3 fix the 2D toy? A quick check

One more useful sanity check on the capacity argument: does adding a third dimension to this chapter's toy memory (not a third association — a third axis) let three keys coexist cleanly, the way two did in 2D? Use three mutually orthogonal 3D keys, k1=(1,0,0), k2=(0,1,0), k3=(0,0,1), each with its own value. Every pairwise dot product among these three is exactly 0 — the Kronecker-delta argument from Chapter 2 applies without modification, and all three retrievals come back exact, no crosstalk at all. The lesson isn't “more dimensions never help” — DPFP's entire strategy in this chapter is exactly “buy more dimensions.” The lesson is narrower and sharper: capacity tracks ddot exactly, one-for-one, with no slack. Three keys need three dimensions, not “a bit more than two.”

Push it one key further to confirm the ceiling is exact, not approximate. Add a fourth key in that same 3D space, necessarily non-orthogonal to at least one of the first three (since 3D allows only 3 mutually orthogonal directions) — say k4=(0.577,0.577,0.577), a unit vector with equal components along all three axes. Its dot product with each of k1, k2, and k3 is 0.577, not zero — so by the exact same crosstalk formula from earlier in this chapter, querying with k4 after it's written pulls in a 0.577-weighted contribution from all three earlier values, and querying with any of k1k3 now picks up a 0.577-weighted sliver of v4 too. The pattern is exact and dimension-independent: the (d+1)-th association in a d-dimensional space always corrupts, by precisely the geometry of how far from orthogonal it necessarily is.

Capacity overflow, statistically

A 2D toy memory (ddot = 2) storing S random unit-vector keys, each with a random value. Drag S up and watch average retrieval error jump right after S exceeds 2 — the same cliff the paper's real ddot = 64 experiment shows at S ≈ 60, just too large a number to hand-verify here.

stored associations S1

What an overloaded memory should do instead

Look back at what actually broke in the hand-worked example: writing v3 at k3 didn't replace anything — it just added a new outer product on top of whatever was already sitting near that direction. An ordinary Transformer's KV cache doesn't have this problem at all, because it stores every (k, v) pair as a separate, immutable slot — concatenation, not summation, so nothing overwrites anything (at the cost, Session 08 showed at length, of the cache growing forever). The fast weight matrix has finite size by design; it has to overwrite. The purely additive update from Chapter 1, Si = Si−1 + vikiT, never subtracts anything — it can add new associations, but it has no notion of deprecating an old one that a new write should replace. That is precisely the gap Chapter 4 closes.

The tradeoff this chapter has been quietly making, named explicitly

Step back and notice what full attention buys by refusing this whole tradeoff: Session 08's KV cache stores every token's key and value as a separate slot, so it never has a capacity ceiling in the sense this chapter means — retrieval quality doesn't degrade as the sequence grows, only the memory bill does (Session 08's entire subject). Linear attention makes the opposite bet: bound the memory bill at a fixed size, and accept that doing so requires actively managing what stays in that bounded space. Chapter 3 has shown what happens when that management is skipped (the sum rule). Chapter 4 is the paper's answer for how to do the managing well. Neither choice is free — one pays in unbounded memory, the other pays in the engineering work of building a memory that knows how to forget correctly.

That framing — unbounded-but-simple versus bounded-but-managed — recurs constantly in systems engineering outside language models too: an unindexed table that never needs maintenance but grows forever, versus a fixed-size cache that stays small but needs an eviction policy to stay correct. Recognizing the capacity problem as an instance of that general pattern, rather than a language-model-specific quirk, is part of what makes Chapter 4's fix easy to accept once you see it: it's the familiar “bounded resource needs an active management policy” story, wearing a delta-rule-shaped costume.

Why does querying the memory with k1 return a corrupted value after a third, non-orthogonal key-value pair is written, even though k1 and v1 themselves never changed?

Chapter 4: The Delta Rule

Chapter 3 diagnosed the disease: a purely additive write can never remove what's already there, so an overloaded memory can only ever get muddier, never cleaner. This chapter is the fix the paper proposes — and it borrows its name from a genuinely old idea in machine learning, the error-correcting delta rule of Widrow and Hoff, 1960, the same update rule behind the very first trainable linear classifiers.

The idea, before the algebra

Instead of blindly adding a new association on top of whatever is already stored at that key, first look at what's currently stored there, then write a value that corrects it — erase the old association's contribution and replace it with the new one, rather than piling the new one on top. Three steps, run at every token:

1 · retrieve
v̄ = Wi-1 · ki — what's currently stored at this key
2 · interpolate
vnew = βvi + (1−β)v̄ — blend toward the new value
3 · write the correction
Wi = Wi-1 − v̄⊗ki + vnew⊗ki

β — a number between 0 and 1, itself produced by a small learned projection of the input, so it's a different value at every token — is a write strength: β = 1 means fully overwrite the old association, β = 0 means leave the memory completely untouched.

Why β is squeezed through a sigmoid

The paper produces βi as σ(Wβxi) — a raw linear projection of the input, passed through the sigmoid function σ(x) = 1/(1+e−x), which squeezes any real number into the open interval (0, 1). This isn't incidental: Chapter 4's interpolation step, vnew = βvi + (1−β)v̄, is only a well-behaved convex combination — guaranteed to land somewhere between the old and new values, never overshooting past either one — if β is bounded within [0, 1]. Without that bound, a large learned projection value could push β above 1 or below 0, and the “blend toward the target” interpretation would break: β>1 would overshoot past the target value entirely, β<0 would push away from it. The sigmoid is what turns an unconstrained linear projection into a number that's always safe to interpret as a mixing weight, the same role a softmax plays in constraining attention weights to sum to 1 — a different constraint, serving an analogous purpose.

Why "delta rule" — a name older than deep learning itself

Bernard Widrow and Marcian Hoff introduced the delta rule in 1960 for a simple linear classifier called ADALINE (Adaptive Linear Neuron), and the core idea there is worth stating because it's the same idea, wearing different clothes: whenever the model's current prediction disagrees with the target, nudge the weights in the direction that reduces the error, by an amount proportional to how wrong the prediction currently is. That's Δw ∝ (target − prediction) × input — error times input, precisely the shape of Chapter 4's update, βi(vi − v̄i) ⊗ ki, just applied to an entire fast-weight matrix instead of a single classifier's weight vector, and with a learned per-token step size βi where ADALINE used one fixed learning rate for every example. This is also, not coincidentally, the same error-times-input shape that shows up at the heart of ordinary gradient descent on a squared-error loss — the delta rule is gradient descent, applied online, one association at a time, to the tiny problem of “make the memory's retrieval at this key equal this value.” A sixty-five-year-old idea for training the simplest possible classifier turns out to be exactly what an overloaded 2021-era memory matrix needed.

The algebra collapses to something simpler

Substitute step 2 into step 3 and the write and remove terms combine (Appendix A.1 of the paper works this out formally):

Wi = Wi−1 + βi(vi − v̄i) ⊗ ki

This is the delta rule: write the difference between what you want and what's currently there, scaled by how strongly you want to write it. When vi = v̄i (the memory already holds the right value), the update term is exactly zero — nothing changes, correctly. When they disagree, the update nudges the memory toward the target, by exactly βi of the gap.

Hand-worked: overwriting an association, cleanly

Return to Chapter 2's clean 2-key memory, W = [ [5,3], [2,7] ], built from k1=(1,0), v1=(5,2) and k2=(0,1), v2=(3,7). Suppose the model now wants to update what's stored at k1 to a new target value, (11,4), with write strength β=0.8.

Step 1 — retrieve. With only two orthogonal keys stored so far, this is exact:

v̄ = W·k1 = W(1,0) = (5, 2)  (= v1, correctly — nothing has overflowed yet)

Step 2 — interpolate:

vnew = 0.8·(11,4) + 0.2·(5,2) = (8.8,3.2) + (1,0.4) = (9.8, 3.6)

Step 3 — write the correction:

ΔW = 0.8·((11,4) − (5,2)) ⊗ (1,0) = 0.8·(6,2) ⊗ (1,0) = (4.8,1.6) ⊗ (1,0) = [ [4.8,0], [1.6,0] ]
W′ = W + ΔW = [ [9.8, 3], [3.6, 7] ]

Verify by re-querying. With k1:

W′(1,0) = (9.8, 3.6)  —  matches vnew from Step 2, exactly

And with k2, which was never touched — because it's orthogonal to k1, the update to k1's slot should leave it entirely alone:

W′(0,1) = (3, 7)  —  still exactly v2, untouched

A clean, selective, verified overwrite: the target association updated exactly as intended, the unrelated one left completely alone.

What the sum rule would have done instead

Now run the naive, purely-additive Chapter-1 update on the same request — no retrieval, no subtraction, just add the new outer product on top:

Wsum = W + (11,4) ⊗ (1,0) = W + [ [11,0], [4,0] ] = [ [16, 3], [6, 7] ]

Query with k1:

Wsum(1,0) = (16, 6)  —  not the target (11,4), and not the old value (5,2) either — the meaningless sum of both

(16, 6) is exactly (5,2) + (11,4) — the old and new values summed together, an answer that means nothing to whatever reads it next. Put the two outcomes side by side and the entire chapter is one comparison:

Update ruleResult at k1What it represents
Delta rule (β=0.8), Ch4(9.8, 3.6)80% of the way to the new target — a correct partial write
Sum rule, Ch1(16, 6)old value + new value, blended into garbage
Concept → realization. “Write-erase instead of write-only” is not a metaphor — it is the literal difference between subtracting v̄ before adding the new value (delta rule) and skipping that subtraction (sum rule). One extra retrieval, one extra subtraction, and an overloaded memory goes from accumulating noise forever to actually correcting itself. The dynamic βi is what lets the model decide, per token, how aggressively to trust and overwrite — a learned analogue of a Kalman gain, deciding how much to trust the new observation versus the existing belief.
python
def delta_rule_step(k, v, beta, W):
    v_bar = W @ k                                # step 1: retrieve current value
    v_new = beta * v + (1 - beta) * v_bar         # step 2: interpolate toward target
    W = W + beta * torch.outer(v - v_bar, k)     # step 3: write the correction (collapsed form)
    return W

# sanity check against the hand-worked numbers above
W = torch.tensor([[5.,3.],[2.,7.]])
W = delta_rule_step(torch.tensor([1.,0.]), torch.tensor([11.,4.]), 0.8, W)
print(W)   # tensor([[9.8, 3.], [3.6, 7.]])  -- matches Step 3 exactly

An honest limit: repairing one key doesn't repair its neighbors

It would be easy to walk away from this chapter thinking the delta rule solves Chapter 3's capacity problem outright. It doesn't — it gives the model a mechanism to selectively correct one association at a time, which is different from raising the ceiling. Prove this with one more hand-worked step, picking up exactly where Chapter 3 left off: the corrupted three-key memory, Wnew = [ [10.4,10.2], [2.6,7.8] ], where k3's addition had corrupted retrieval of both earlier keys.

Use the delta rule, β=1 (full overwrite), to repair k1's association back to its true target (5,2). Retrieve first: v̄ = Wnew(1,0) = (10.4, 2.6) — the corrupted value from Chapter 3. Write the correction:

ΔW = 1.0·((5,2) − (10.4,2.6)) ⊗ (1,0) = (−5.4,−0.6) ⊗ (1,0) = [ [−5.4,0],[−0.6,0] ]
Wrepaired = Wnew + ΔW = [ [5, 10.2], [2, 7.8] ]

Check k1: Wrepaired(1,0) = (5, 2) — exactly repaired, as guaranteed. Now check k2, which was also corrupted by k3's addition but was never targeted by this repair:

Wrepaired(0,1) = (10.2, 7.8)  —  still wrong; the true value was (3, 7)

Untouched, still corrupted. The delta rule repaired exactly the one key it was asked to retrieve-and-correct — it has no way of knowing k2 was collateral damage unless a token specifically retrieves and rewrites k2 too. This is the honest boundary of what Chapter 4 buys: not a cure for overcapacity, but a mechanism that lets the model actively manage a finite memory — deciding, per token, what to overwrite — rather than being stuck with a memory that can only ever accumulate. Chapter 3's hard ceiling (at most ddot genuinely independent associations) doesn't move; what moves is whether the model can behave sensibly within that ceiling instead of degrading uncontrollably past it.

One coping strategy is worth naming, even though this lesson can't verify from the paper alone how strongly a trained model actually relies on it: a model with the delta rule available could, in principle, learn to periodically re-write (retrieve and rewrite with β near 1) an association it predicts will be needed again soon, refreshing it before newer writes have a chance to corrupt it through crosstalk. Nothing forces this behavior — it would have to emerge from training, the way most useful behaviors in a large model do, rather than being hand-coded. Whether real trained DeltaNets actually learn something like this strategy is an interpretability question neither paper in this session directly answers, but it's a natural hypothesis the mechanism makes available, worth flagging as an open question rather than a settled fact either way.

The two extremes worth naming. Set β = 0 at every step and the delta rule reduces exactly to “never write anything” — a frozen, useless memory. Set β = 1 at every step and every write becomes a full, unconditional overwrite, which sounds appealing until you realize it throws away Chapter 2's graceful blending behavior: a key that's similar to but not identical to an existing one would fully stomp the old association rather than blending with it. The learned, per-token βi is what lets the model choose, context by context, where on that spectrum a given write belongs — aggressive correction when a genuine update is needed, gentle blending when it isn't.

Why not a simpler-looking gated update rule?

A different, superficially similar fix might occur to you: instead of retrieving and subtracting, just gate the whole matrix — shrink the old state by (1−β) and add the new association on top, W′ = (1−β)W + βvk. This is, in fact, exactly the update rule a concurrent 2021 paper (Peng et al.) proposed, and it's the same family of mechanism GLA (Chapter 8) is built around. It looks like it should behave similarly to the delta rule — both use a β to control how much writing happens. The paper's own appendix proves it doesn't, with a worked example this lesson can reuse directly.

Take Chapter 2's exact memory again, W = [ [5,3], [2,7] ], from orthonormal k1=(1,0), v1=(5,2) and k2=(0,1), v2=(3,7). Now write a third association at the same key as the second — k3=k2=(0,1), v3=(11,4), β=0.8 — the cleanest possible test, since a good update rule should only touch k2's slot and leave k1's alone entirely.

The gated rule:

W′ = 0.2·[ [5,3],[2,7] ] + 0.8·(11,4)⊗(0,1) = [ [1,0.6],[0.4,1.4] ] + [ [0,8.8],[0,3.2] ] = [ [1, 9.4],[0.4, 4.6] ]

Query k2: W′(0,1) = (9.4, 4.6) = 0.2v2+0.8v3, the correct blend. But query k1, which was never touched:

W′(1,0) = (1, 0.4)  —  but v1 was (5, 2). Shrunk to 20% of its value, for no reason connected to k1 at all.

The delta rule, same scenario: retrieve v̄ = Wk2 = (3,7) = v2 exactly (orthogonal to k1, so retrieval is clean); write the correction:

W′ = W + 0.8·((11,4)−(3,7))⊗(0,1) = W + [ [0,6.4],[0,−2.4] ] = [ [5, 9.4],[2, 4.6] ]

Query k2: W′(0,1) = (9.4, 4.6) — same correct blend. Query k1:

W′(1,0) = (5, 2)  —  exactly v1, completely unchanged

Both rules get k2's update right. Only the delta rule leaves k1 alone. The gated rule's (1−β) factor multiplies the entire matrix — every association currently stored, whether or not it has anything to do with the key being written — while the delta rule's correction term is scaled by k, meaning it only lands on directions the incoming key actually points in. This is precisely why GLA (Chapter 8), built on this gated family, forgets on a schedule that touches everything a little, while DeltaNet forgets conditionally, touching only what a given write's key actually collides with.

Neither rule is strictly better in every situation — the gated rule's uniform, scheduled decay is simpler to implement and, in cases where everything in the state should genuinely fade over time (recency matters more than exact correction), can be exactly the right prior to build in. The delta rule's more surgical, key-conditioned edit is the better choice specifically when the task calls for correcting a particular stale fact without disturbing unrelated ones — which is exactly the recall-heavy setting Chapter 8's benchmarks probe.

Why does the sum rule produce (16, 6) instead of something closer to the target (11, 4) when overwriting k1's association?

Chapter 5: DeltaNet

Chapter 4 built the delta rule from first principles as a fix for the capacity problem. The paper calls a linear Transformer built around exactly this update rule a Delta Network — the model this entire lesson's second half is about, and the name the follow-up paper (Chapter 6 onward) shortens to DeltaNet. This chapter checks the fix against real numbers, not just a 2×2 toy.

Normalization: a detail that turns out to matter

One loose end from Chapters 1–4: none of the hand-worked examples normalized the key vectors, but real key projections aren't guaranteed to have unit-length components summing usefully. The paper tests a straightforward fix — sum normalization — dividing the projected key and query vectors by the sum of their own components before using them, so retrieval behaves like a proper weighted average rather than an unbounded blend. They find this normalization is necessary for their update rule to train stably at all (models diverge without it), but that an additional normalization scheme on top — dividing by an accumulator z, the way Chapter 1's general kernel form does — actually hurts perplexity slightly once sum normalization is already in place. More normalization is not always better; the paper's own ablation (Table 3) shows the best configuration uses sum normalization alone, with neither absolute positional encoding nor the extra attention-normalization term.

Deriving sum normalization, not just naming it

Sum normalization means dividing a projected key vector by the sum of its own components before using it anywhere:

φ′(k) = φ(k) ÷ ∑j=1ddot φ(k)j

Why this specific normalization, and not some other one? The paper's own appendix derives it from a genuine requirement, not a guess: rewrite the delta rule's update in terms of W's individual columns rather than the whole matrix at once, and the update to column i turns out to have a “write” term weighted by ki and a “remove” (crosstalk-correcting) term weighted by ∑jkikj. For the write and remove pieces to be properly balanced — so the remove term doesn't over- or under-correct relative to the write it's paired with — those two weights need to match, which forces ∑jkj = 1: the key's own components must sum to exactly 1. Sum normalization is precisely the operation that enforces that condition on every key, which is why skipping it specifically breaks the delta rule (rather than merely hurting linear attention in general the way skipping normalization on the sum rule would): the delta rule's write/remove balance is a hard requirement the sum rule never had to satisfy in the first place, because the sum rule has no remove term to balance against.

Verify the normalization itself is trivial arithmetic, so it's clear no mystery is hiding in it: take a raw projected key k=(3,1) (components don't need to sum to 1 before normalizing). Sum of components: 3+1=4. Normalized: φ′(k) = (3,1)/4 = (0.75, 0.25). Check: 0.75+0.25=1, exactly the condition the derivation above requires. This is the entire operation — not a learned transformation, not a different kernel, just a division by a scalar computed from the vector's own entries, applied fresh to every key and query at every token.

The full ablation, all four combinations, real numbers

Don't just take “more normalization isn't always better” as an assertion — the paper tests all four combinations of two independent design choices (absolute positional encoding: yes/no; extra attention normalization on top of sum normalization: yes/no) on the medium WikiText-103 configuration, and the full grid is worth seeing:

Positional encodingExtra attn. normalizationValid PPLTest PPL
YesYes30.432.1
NoYes29.231.2
YesNo29.731.5
NoNo28.131.1

The best row drops both extras — no positional encoding, no extra normalization — and it's not a close call; removing positional encoding alone buys more than removing the extra normalization alone does (29.2 vs 29.7 test-adjacent valid numbers), and dropping both compounds the gain rather than cancelling it. The positional-encoding result in particular confirms something Chapter 1 didn't dwell on: DeltaNet's update rule is inherently order-sensitive already — every token's write depends on the exact sequence of retrievals and corrections that came before it, so a recurrence built this way doesn't need an explicit position signal bolted on top the way a permutation-invariant softmax attention layer does.

Real numbers: WikiText-103 language modeling

Why WikiText-103 specifically, and not some shorter benchmark? Its articles run to roughly 3,600 words on average, deliberately chosen (the paper notes) because a benchmark of short, disconnected sentences would never put enough pressure on a memory mechanism to reveal a capacity problem at all — Chapter 3's crosstalk needs enough distinct associations flowing through the state before it becomes visible in the aggregate loss. A dataset of long, internally-coherent documents is exactly the setting where a model that starts corrupting distant associations would show it, and where one that doesn't would earn its perplexity advantage honestly.

Train small Transformer-style models (16 layers, two sizes — “small” at 40M parameters, “medium” at 90M) on WikiText-103, a standard large-vocabulary word-level benchmark, and compare three update rules under an identical architecture and parameter budget. Lower perplexity is better (recall Session 08: perplexity is roughly “how surprised was the model,” and the gap between two perplexity numbers is exponential in the underlying loss difference, not additive):

Model / update ruleSmall, test PPLMedium, test PPL
Standard Transformer (softmax)34.129.6
Linear Transformer, sum rule38.333.0
Delta Network, delta rule35.531.5

Same architecture, same parameter count, same training budget — the only variable is the update rule, and switching from sum to delta closes roughly half the gap between plain linear attention and full softmax attention, in both model sizes. Both configurations are deliberately run in what the paper calls an “overcapacity regime” — the sequence length exceeds the key dimension — exactly the condition Chapter 3 showed breaks the sum rule.

Just how far over capacity, worked from the paper's own architecture numbers

“Overcapacity” is worth quantifying, not just naming, using Chapter 3's exact framework. The small configuration sets total model dimension D=128 split across H=8 attention heads, so each head's key/dot dimension is ddot = D/H = 16. Training context length is L=256:

L ÷ ddot = 256 ÷ 16 = 16× over the theoretical capacity, per head

The medium configuration: D=256, H=8, so ddot=32, trained at L=384:

L ÷ ddot = 384 ÷ 32 = 12× over capacity

Both configurations aren't marginally past the crosstalk cliff Chapter 3 derived — they're an order of magnitude past it. This is precisely why Table 2's gap between the sum rule and the delta rule is as large as it is: at 16× or 12× overcapacity, Chapter 3's crosstalk isn't a subtle effect, it's the dominant failure mode, which is exactly the regime where a mechanism that can correct associations (the delta rule) should outperform one that can only pile them up (the sum rule) by the widest margin. A comparison run well inside capacity would have shown a much smaller gap, because the sum rule's flaw only bites once the shelf is actually full.

The paper is explicit that this is a deliberate experimental choice, not an accident, and it also reports (in an appendix, for a non-overcapacity setting using DPFP's higher-dimensional feature map) that the delta rule's advantage shrinks — unsurprising, given the mechanism this whole chapter has been deriving. When a memory never gets full enough to need active correction in the first place, there's less for a correction mechanism to correct. The headline gap in Table 2 is real, but it's specifically the gap measured under conditions engineered to stress-test the exact failure mode Chapter 3 diagnosed — worth remembering before treating these particular numbers as a universal “delta rule beats sum rule by this much” constant.

The number that matters most: unbounded context

Table 2 caps the context length during evaluation. A more honest test of “constant memory forever” removes that cap entirely and lets the model run over arbitrarily long, untruncated text — exactly Session 08's day-long-chat scenario, but for a fundamentally different architecture than StreamingLLM patches.

ModelParametersState size (constant, in M)Test PPL
Linear Transformer (sum rule)89.8M0.13M> 260
Delta Network89.9M0.13M29.4
Transformer-XL (small memory)90.9M1.05M30.1
Transformer-XL (large memory)90.9M6.29M25.5

Read the top two rows first: the sum-rule Linear Transformer collapses without truncation — a perplexity above 260 is close to broken — exactly Chapter 3's overcapacity prediction playing out at full scale, not a toy. The Delta Network, with the identical 0.13-million-parameter fixed state, stays coherent at 29.4, roughly on par with a full Transformer. Then look at the bottom two rows: Transformer-XL, which keeps a literal window of past hidden states rather than a fixed-size matrix, needs roughly 48× more state (6.29M vs 0.13M) than DeltaNet just to edge past it, at 25.5 vs 29.4. DeltaNet doesn't win outright — but it gets close to a model paying dozens of times more memory, using a state size that never grows no matter how long the text runs.

A detail worth being precise about: which normalization broke which model

This unbounded-context experiment drops the extra attention-normalization term entirely (Chapter 5's own ablation recommended this), but the paper is explicit that the two update rules break for different reasons when normalization choices change, and the asymmetry is instructive. For the Delta Network, keeping the extra normalization term was actively harmful without truncation, because its accumulator z grows without bound as the sequence lengthens — removing it was necessary specifically to avoid that blow-up. For the plain sum-rule Linear Transformer, the paper reports that removing the same term made things worse, not better — perplexity above 1,600, even further broken than the >260 figure in the table above under a different normalization setting. Two different mechanisms, two different failure modes, responding to the same knob in opposite directions. This is precisely why Chapter 5's ablation table earlier in this chapter isn't a universal recipe — “drop the extra normalization” is the right call for the delta rule specifically, for a reason tied to its own accumulator dynamics, not a general-purpose rule that happens to also apply to the sum rule.

Complexity, wall-clock, honestly reported. The paper measures the delta rule's actual overhead against plain sum-rule linear attention in their own CUDA implementation: 66K words/sec and 13GB for the delta rule versus 63K words/sec and 14GB for the sum rule — the delta rule is marginally more expensive per step (one extra retrieval, one extra subtraction), well within noise, and both comfortably beat a standard PyTorch Transformer's 33K words/sec and 17GB at the same scale. The capacity fix from Chapters 3–4 is close to free.

Interestingly, the delta rule is even slightly faster in words/sec than the sum rule here (66K vs 63K), despite doing strictly more arithmetic per step (an extra retrieval and subtraction). The likely explanation is that this comparison measures the sum rule's own separately-implemented CUDA kernel, and small implementation differences between two independently-optimized kernels can easily produce noise of this size — a reminder that a few-percent difference in a wall-clock benchmark is rarely worth over-interpreting, compared to the order-of-magnitude differences (state size, perplexity collapse) this session has spent most of its numeric arguments on.

Checking Chapter 0's crossover against this chapter's own training setup

Chapter 0 derived a crossover at L = d. Chapter 2's “small” configuration here trains at context length L=256 with model dimension D=128. Plug into Chapter 0's ratio:

L ÷ d = 256 ÷ 128 = 2.0  —  linear attention should be roughly 2× cheaper here

That's a real but modest saving — nowhere near the ~977× figure Chapter 0 computed at Session 08's 4-million-token scale, because this training setup sits barely past the crossover point, not deep into linear-attention's favorable regime. This is worth noticing precisely because it's easy to read Chapter 2's perplexity table and assume the compute savings were dramatic too; at this particular context length, they mostly weren't. The efficiency case strengthens as context length grows, which is exactly why Table 4's unbounded-context experiment (next) is the more compelling evidence for why this approach matters, not the bounded 256-token comparison that produced Chapter 2's perplexity numbers.

The synthetic test built specifically to isolate the update rule

Table 2's language-modeling numbers mix together many effects at once — architecture, dataset, capacity regime. The paper also runs a synthetic task built to isolate just the update rule, echoing this lesson's own hand-worked overwrite example from Chapter 4, but at scale and under training. Keys and values are sampled with replacement from a pool of 20 unique key-value pairs, over a sequence of 40 tokens — meaning the same key gets reassigned to a new value multiple times within one sequence, and the correct thing to retrieve is always the most recent value bound to a given key. This is Chapter 4's overwrite scenario, generated automatically and repeated thousands of times during training rather than computed once by hand.

Four update rules are compared under an identical architecture: the plain sum rule (Chapter 1), this paper's delta rule with sum normalization (Chapter 4), and two earlier variants from Schlag and Schmidhuber's own prior work (one using a tanh nonlinearity for keys instead of a φ function, one hybrid combining that variant with DPFP). The result: the sum rule fails to learn the task at all — expected, since Chapter 3's crosstalk argument applies just as much to a memory that keeps getting reassigned as one that keeps growing. This paper's delta rule with sum normalization outperforms every other variant tested, including the authors' own earlier attempts at the same problem. The specific combination — delta rule plus sum normalization, no more, no less — isn't an arbitrary choice; it's the configuration that survived a genuine comparison against real alternatives.

Converting Table 2's gap into bits, the way Session 08 did

Session 08 converted a perplexity gap into bits-per-token to make it viscerally comparable. Do the same here for the medium-config gap between the sum rule (33.0) and the delta rule (31.5). Recall PPL = eloss, loss in nats:

losssum = ln(33.0) ≈ 3.497 nats      lossdelta = ln(31.5) ≈ 3.450 nats
Δloss = 0.047 nats = 0.047 ÷ ln(2) ≈ 0.068 bits per token

A fraction of a bit per token — a real, measurable, but modest improvement, nothing like the thousand-times “PPL 5,158 → 5.4” collapse Session 08's window-attention fix addressed. That scale difference is worth sitting with: Session 08 was fixing a catastrophic failure (attention sinks evicted, model breaks completely); this chapter is describing a genuine but incremental quality improvement (delta rule vs sum rule, both functioning, one somewhat better). Both are real, worthwhile fixes — but conflating their magnitudes would be a mistake this lesson has tried to avoid throughout.

One thread not yet pulled

Table 2 and the state-size table above both share something worth flagging before moving on: every number in this chapter came from a model trained the ordinary way — token by token, sequentially, exactly the recurrent form Chapter 1 wrote down. That's fine for evaluating a trained model, but it says nothing yet about how expensive it was to train it. Chapter 6 asks that question directly, and the answer is less comfortable than this chapter's perplexity numbers suggest.

In the unbounded-context experiment, why does the sum-rule Linear Transformer's perplexity collapse to over 260 while the Delta Network, with the exact same 0.13M-parameter state size, stays around 29.4?

Chapter 6: Why It Resists Parallelism

Chapter 1 ended with a callout worth returning to now: the plain sum-rule recurrence is fully parallelizable during training, because it's just a running sum, and sums can be reorganized into one big parallel matrix multiply, O = (QKTM)V. This chapter is about why that trick quietly stops working the instant you switch to the delta rule — and it's the entire motivation for the second paper this session covers: Parallelizing Linear Transformers with the Delta Rule over Sequence Length, by Songlin Yang, Bailin Wang, Yu Zhang, Yikang Shen, and Yoon Kim, MIT and the MIT-IBM Watson AI Lab, 2024.

Three years separate the two papers this session is built around, and it's worth being clear about what changed in that gap. Chapters 2 through 5 covered the 2021 paper's contribution: the fast-weight-programmer framing, the capacity theory, and the delta rule itself, verified at a scale (40–90M parameters) that a single research group could train in days. This chapter and the next cover what the 2021 paper explicitly flagged as unsolved — the training-parallelism problem — and the 2024 paper's answer to it, which is what unlocks Chapter 8's 1.3B-parameter results. The two papers are not competitors describing different ideas; the second is a direct continuation that made the first paper's idea trainable at a scale worth taking seriously.

A sum versus a product

Write the delta rule's update (Chapter 4) in a slightly different, equivalent form. Starting from Wi = Wi−1 + βi(viWi−1ki) ⊗ ki, distribute the outer product across the subtraction inside the parentheses (v̄i = Wi−1ki, Chapter 4's retrieval step, substituted back in):

Wi = Wi−1 + βivikiT − βi(Wi−1ki)kiT

The last term is Wi−1 multiplied by kikiT (a d×d matrix), which lets Wi−1 factor out of both surviving terms that contain it:

Wi = Wi−1(I − βikikiT) + βivikiT

Notice the shape: this is Wi−1 multiplied by a matrix, not added to one. The matrix (I − βikikiT) is a generalized Householder transformation — a well-known object from numerical linear algebra (it's the building block of the QR decomposition algorithm) that reflects vectors through a hyperplane. Unroll the recurrence back to the start of the sequence and Wt becomes a sum of terms, each one multiplied by a product of every Householder matrix that came after it:

Wt = ∑i=1t βi(vikiT) ( ∏j=i+1t (I − βjkjkjT) )

Compare that to the plain sum rule's St = ∑iviki: no products anywhere, just a sum whose terms can be added up in any order — which is exactly what makes it parallelizable. The delta rule's version has a genuine matrix product tangled through it, and matrix products of different matrices generally don't commute — you can't reorder them the way you can reorder addition. That non-commuting product is precisely what a naive implementation would need to materialize, and doing so is expensive.

What a Householder matrix actually does, in one worked reflection

The name “Householder transformation” sounds intimidating; the operation is simple enough to see in 2D. Take a unit vector k = (1, 0) and β = 1 (a full-strength write). The matrix IkkT:

IkkT = [ [1,0],[0,1] ] − [ [1,0],[0,0] ] = [ [0,0],[0,1] ]

Apply it to an arbitrary vector, say (3, 5): [ [0,0],[0,1] ](3,5) = (0, 5). The component along k (the x-component, 3) is annihilated; the component perpendicular to k (the y-component, 5) survives untouched. That's the entire geometric content of a Householder-style projection: it zeroes out exactly the direction the key points in, and leaves everything orthogonal to that direction alone — the same “erase along one direction, preserve the rest” behavior Chapter 4's hand-worked repair demonstrated numerically (repairing k1 left the orthogonal k2 untouched). Chapter 6's algebra is what makes that connection precise: the delta rule's per-step update is, literally, one of these directional erase-and-write operations, chained token after token.

python
import torch
k = torch.tensor([1.,0.])
H = torch.eye(2) - torch.outer(k,k)
print(H @ torch.tensor([3.,5.]))   # tensor([0., 5.]) -- x-component erased, y untouched

Proving the non-commuting claim, not just asserting it

Chapter 6's central claim is that these Householder-style matrices generally don't commute — order matters. Check it directly with two different keys, k1=(1,0) and k2=(0.6,0.8), both at β=1: H1 = Ik1k1T = [ [0,0],[0,1] ], and H2 = Ik2k2T = [ [1,0],[0,1] ] − [ [0.36,0.48],[0.48,0.64] ] = [ [0.64,−0.48],[−0.48,0.36] ].

H1H2 = [ [0,0],[0,1] ][ [0.64,−0.48],[−0.48,0.36] ] = [ [0,0],[−0.48,0.36] ]
H2H1 = [ [0.64,−0.48],[−0.48,0.36] ][ [0,0],[0,1] ] = [ [0,−0.48],[0,0.36] ]

Different matrices — H1H2H2H1, confirmed entry by entry, not just asserted. This is exactly why the order tokens arrive in cannot be shuffled when computing the delta rule's accumulated state — unlike the plain sum rule, where v1k1T+v2k2T equals v2k2T+v1k1T always, by ordinary commutativity of addition.

Just how expensive, naively

The straightforward way to get each token's contribution right is to actually compute Wi−1ki at every single step, sequentially, because Wi−1 depends on everything before it and there's no shortcut around that dependency without more work. That's an O(d) operation if you already have Wi−1 in hand — but getting to the point where every step's contribution is expressible without ever materializing the full d×d matrix at each step, for all L tokens up front, costs O(L 2d) using the direct approach, and worse, it cannot be parallelized across the sequence at all — step i genuinely needs step i−1's result before it can proceed. This is exactly the recurrent form's bottleneck Chapter 0 wanted linear attention to escape, reappearing from a different direction.

sum rule
St = ∑ vi⊗ki — addition, reorderable, trivially parallel
↓ delta rule adds a per-step matrix multiply
delta rule
Wt = ∑ (…) × ∏(I − βkkT) — a product of Householder matrices, order-dependent
naive training
forced back to token-by-token recurrence — no free parallel form

Every complexity number from this session, side by side

This is a good moment to collect every complexity figure derived so far into one table, because the pattern across rows is the whole story of Chapters 0 and 6 together:

MethodSequential stepsTotal workParallel-friendly?
Full attention (parallel form)O(1)O(L²d + Ld²)yes
Linear attention, sum rule (recurrent)O(L)O(Ld²)no, but doesn't need to be — parallel form exists
Linear attention, sum rule (parallel form)O(1)O(L²d + Ld²)yes
DeltaNet (naive recurrent)O(L)O(Ld²)no, and no free parallel form exists
DeltaNet (naive, materializing state per step)O(L)O(L²d)no
DeltaNet, WY chunkwise (Chapter 7)O(L/C)O(LCd + Ld²)yes, within each chunk

Read the last row against the fourth: WY chunking doesn't reduce DeltaNet's total work below the naive recurrent form by much (both are close to O(Ld2) for reasonable chunk sizes) — the win is entirely in the sequential steps column, from O(L) down to O(L/C). That's the precise, narrow claim Chapter 7 makes, and it's worth being exact about it: chunking doesn't make DeltaNet do less arithmetic, it makes DeltaNet's arithmetic parallelizable, which on GPU hardware is very often the more important axis, exactly per the arithmetic-intensity argument below.

Why not just run the recurrence on the GPU as-is?

It's worth being specific about why a step-by-step recurrence is slow on a GPU, rather than just asserting it. GPUs get their speed from doing enormous numbers of arithmetic operations per byte moved between memory and compute units — arithmetic intensity, in the field's terminology — and specialized hardware (tensor cores) is built specifically to accelerate large, regular matrix multiplications. A token-by-token recurrence does the opposite: each step is a small, elementwise-flavored update, touching a comparatively large state matrix for a comparatively tiny amount of arithmetic, and it must happen strictly after the previous step finishes. Low arithmetic intensity plus forced serialization is close to the worst-case access pattern for this kind of hardware, which is why the recurrent form, despite doing fewer total FLOPs than the parallel form, routinely runs slower in practice — exactly the seeming paradox Chapter 0's crossover table hinted at but didn't fully explain.

Put a rough number on the intensity gap. A single delta-rule step touches a d×d state matrix (reads it once for retrieval, writes it once for the update) while doing on the order of d2 FLOPs of actual arithmetic — a FLOPs-to-bytes ratio close to 1. A well-formed matrix multiply, by contrast, can reuse each loaded value many times across many output elements, routinely reaching FLOPs-to-bytes ratios in the hundreds on modern GPUs. Tensor cores are built to reward that second regime specifically; a workload that spends most of its time near ratio≈1, the way a token-by-token recurrence does, simply can't make use of the hardware that's actually sitting on the chip, regardless of how few total FLOPs it needs.

There's a classical trick for parallelizing recurrences in general, called a parallel scan, that can in principle compute a linear recurrence in O(log L) sequential steps instead of O(L) — and this is exactly the technique Mamba uses internally for its own state-space recurrence (Chapter 8 returns to Mamba). Why doesn't DeltaNet just do the same thing? Because a parallel scan still needs to materialize the state at every one of the L intermediate steps to combine them, and DeltaNet's state is a full d×d matrix, not a modest vector the way Mamba's per-channel state is kept small enough to be. Materializing a dense d×d matrix at every one of L positions is exactly the memory I/O cost this whole chapter is trying to avoid. Chapter 7's WY-representation trick sidesteps that cost a different way — not by scanning faster, but by never materializing the d×d matrix at all until it's genuinely needed.

Why this is a real engineering problem, not a footnote. The original DeltaNet paper (Chapter 5's numbers) trained genuinely small models — 40M and 90M parameters — and the paper's own text says exactly why: its training algorithm “did not parallelize across sequence length,” making it “hardware- inefficient to train,” and it was “not clear how to scale DeltaNet to larger models and datasets.” A brilliant fix for capacity that can't be trained at GPT-scale is a result confined to a paper, not a model anyone deploys. Chapter 7 is the fix for that problem.

What "chunkwise" already means for the easy case

Before tackling DeltaNet specifically, it's worth seeing how ordinary sum-rule linear attention gets its parallel training speed in practice, because the fix in Chapter 7 is a direct extension of it. Split the sequence into chunks of size C. Because the sum rule really is just addition, the state at the end of chunk t+1 is simply the state at the end of chunk t, plus everything written during chunk t+1 — and that “everything written during the chunk” term can be computed as one ordinary matrix multiply over the whole chunk at once, no per-token sequential loop required inside the chunk:

S[t+1] = S[t] + V[t+1]TK[t+1]

This is called the chunkwise parallel form: only L/C sequential steps (one per chunk, carrying the state forward), each one doing a cheap parallel matmul over C tokens at once. Set C = L and you recover the fully parallel form; set C = 1 and you recover the purely sequential recurrence.

Confirm the sum rule's chunkwise identity holds on a tiny case before trusting it: 4 tokens, chunk size 2, using this chapter's earlier 2D keys/values (k1=(1,0),v1=(2,0); k2=(0,1),v2=(0,3); k3=(1,1),v3=(1,1); k4=(1,0),v4=(2,2)). Sequential sum-rule state after all 4: S4 = ∑viki = [ [2,0],[0,0] ]+[ [0,0],[0,3] ]+[ [1,1],[1,1] ]+[ [2,0],[2,0] ] = [ [5,1],[3,4] ]. Chunkwise: chunk 1 (tokens 1–2) gives S[0]=[ [2,0],[0,3] ]; chunk 2 (tokens 3–4) contributes V[1]TK[1] = [ [1,1],[1,1] ]+[ [2,0],[2,0] ] = [ [3,1],[3,1] ]; total S[1] = [ [2,0],[0,3] ]+[ [3,1],[3,1] ] = [ [5,1],[3,4] ]. Identical to the sequential result, confirming the chunkwise identity for the easy (sum-rule) case before Chapter 7 tackles the much harder delta-rule version of exactly this same equivalence.

It works cleanly for the sum rule specifically because addition inside a chunk can be computed all at once, independent of the incoming state — the delta rule's per-token matrix product can't, at least not without one more idea.

An axis that was never the problem: batching across sequences

Session 08 Chapter 1 was careful to separate two axes that get conflated: seq_len (the axis this whole lesson is about) and batch_size (how many independent conversations are processed side by side on the same GPU). It's worth being equally careful here. Everything Chapter 6 has diagnosed as “hard to parallelize” is specifically about the dependency within one sequence — token i needing token i−1's state. Nothing about the delta rule creates any dependency between separate sequences in a batch; a GPU can process 256 independent conversations' worth of DeltaNet recurrences fully in parallel with each other, exactly as it would for any other model. The bottleneck this chapter cares about is real, but it's narrower than “DeltaNet doesn't parallelize” might suggest at a glance — it doesn't parallelize within a sequence without Chapter 7's fix; it parallelizes across sequences exactly as well as anything else does, for free, with no fix required at all.

One more scope note: this chapter is entirely about training

Everything diagnosed in this chapter is a training-time problem specifically. At inference — generating one token at a time from an already-trained model — the plain sequential recurrence from Chapter 1 is exactly what you want to run regardless of update rule: one retrieve, one write, one output, per token, O(d2) work, no chunking needed at all. Chunking exists purely to make training tractable, where the entire sequence is available up front and the goal is maximizing GPU throughput across it. It would be a mistake to read this chapter as saying DeltaNet is slow to run — Chapter 5's own wall-clock numbers (66K words/sec, competitive with the sum rule) already showed inference-shaped throughput is fine. The problem this chapter and the next one solve is specifically about getting from “an untrained model” to “a trained one” without paying an unreasonable compute bill along the way.

Training needs more than a forward pass: the backward pass matters too

One more piece of the training-cost picture worth naming, since it's easy to forget: training doesn't just run this recurrence forward once, it needs to run backpropagation through it as well, computing gradients by walking the same dependency chain in reverse. If every chunk's intermediate state were kept in GPU memory to make that backward pass easy, memory cost would scale with L/C × d2 — defeating much of the memory benefit chunking was supposed to provide. The production implementation handles this with a standard trick from deep learning generally, called gradient checkpointing: discard the intermediate chunk states after the forward pass finishes, and recompute them on the fly during the backward pass instead, trading a modest amount of extra compute (re-running the cheap forward chunk math a second time) for a large reduction in peak memory. This is exactly the kind of implementation detail Chapter 7's clean C=2 example glosses over for pedagogical clarity, but it's real engineering the actual training system has to handle to make Chapter 8's 1.3B-parameter run fit on available hardware at all.

What specifically about the delta rule's update, compared to the plain sum rule, breaks the simple chunkwise-parallel trick used for ordinary linear attention?

Chapter 7: The WY Trick (showcase)

Chapter 6 left DeltaNet with a real problem: training requires either a full sequential pass (slow, one step at a time) or materializing a growing product of Householder matrices (expensive, and it still doesn't parallelize). The 2024 paper's fix borrows a fifty-year-old piece of numerical linear algebra called the WY representation (Bischof and Van Loan, 1987), originally developed for an entirely different problem — computing QR decompositions efficiently on parallel hardware. It turns out to solve DeltaNet's problem too.

Its original purpose is worth a sentence, because the reuse here is a genuinely elegant piece of cross-pollination. QR decomposition — a standard numerical linear algebra routine that factors a matrix into an orthogonal part and a triangular part — is classically computed by applying a sequence of Householder reflections, one per column, each zeroing out the entries below a pivot. Bischof and Van Loan's 1987 contribution was recognizing that this sequence of reflections, needed for early parallel computers, could be represented compactly enough to apply many of them at once instead of one at a time. Nothing in that 1987 paper has anything to do with attention, language models, or memory — it's a numerical-computing paper about matrix factorization. What makes it useful here is purely structural: DeltaNet's per-token update is a Householder reflection (Chapter 6 derived this), so a trick for combining many Householder reflections efficiently transfers over unchanged, thirty-seven years later, to a problem its original authors never imagined.

The trick, in plain words first

A product of many Householder matrices is, in general, a dense d×d matrix that looks like it has no special structure — expensive to build, expensive to store. The WY representation's insight is that this is an illusion: a product of C Householder reflections can always be written compactly as I minus a sum of just C outer products, using two skinny d×C matrices instead of ever forming the full dense d×d product. Concretely for DeltaNet: instead of multiplying together C separate (I − βkkT) matrices one at a time, you compute two small matrices, call them W and U (one row per token in the chunk), and the entire chunk's net effect on the running state can be read off from those two skinny matrices directly — no d×d matrix ever gets materialized at all.

Building W and U: a within-chunk recursion, but a cheap one

Here is the part worth being honest about: computing the rows of W and U for a chunk still has a sequential dependency — row r depends on rows 1 through r−1 — because each new token's correction has to account for how the earlier tokens in the same chunk already modified things:

wr = βr( kr − ∑i=1r−1 wi(ki·kr) )      ur = βr( vr − ∑i=1r−1 ui(ki·kr) )

The crucial difference from Chapter 6's problem: this recursion runs over only C tokens — the chunk size, typically 64 or 128 — and it involves only the small intra-chunk dot products ki·kr, never the full incoming state S[t] at all. It is exactly the kind of small, cheap forward-substitution that GPUs handle well, and because it doesn't touch the incoming state, every chunk's W and U can be computed in parallel with every other chunk's. Only the final carry — combining each chunk's local result with the state handed down from the previous chunk — has to happen in sequence, and that's only L/C steps, not L.

There's a cleaner way to see why this recursion is cheap, for readers comfortable with linear systems: the wr equations above are exactly what you get from a lower-triangular C×C system of equations, built entirely from the chunk's own intra-chunk key dot products ki·kr. Solving a triangular system is a textbook operation — forward substitution, exactly the row-by-row process shown above, or equivalently a single explicit matrix inversion of a C×C matrix. Either way, the size of the problem being solved is C×C, not d×d — and since C (64 or 128) is typically much smaller than d can be for a wide model, this is a genuinely small linear-algebra problem, cheap enough to solve per chunk without becoming a bottleneck of its own.

Hand-worked: a full C=2 chunk, verified against pure sequential DeltaNet

Verify the trick actually reproduces the sequential answer, using small numbers. Take a fresh chunk (starting state S[t] = 0, i.e. the very first chunk of a sequence), with two tokens:

token 1: k1=(1,0), v1=(11,4), β1=0.8      token 2: k2=(0.6,0.8), v2=(9,1), β2=1.0

WY construction, row by row. Row 1 has no earlier rows to correct for:

w1 = 0.8·(1,0) = (0.8, 0)      u1 = 0.8·(11,4) = (8.8, 3.2)

Row 2 corrects for row 1, using k1·k2 = 0.6:

w2 = 1.0·((0.6,0.8) − (0.8,0)·0.6) = (0.6,0.8) − (0.48,0) = (0.12, 0.8)
u2 = 1.0·((9,1) − (8.8,3.2)·0.6) = (9,1) − (5.28,1.92) = (3.72, −0.92)

Chunk-end state. Since S[t]=0, the whole chunk's contribution is just ∑ uiki:

S[t+1] = u1k1 + u2k2 = [ [8.8,0],[3.2,0] ] + [ [2.232,2.976],[−0.552,−0.736] ] = [ [11.032,2.976],[2.648,−0.736] ]

Now the check. Run the same two tokens through Chapter 4's plain sequential delta rule, one at a time, starting from S0=0. Step 1: v̄=0 (empty memory), so vnew=0.8·(11,4)=(8.8,3.2), and S1 = 0.8(11,4)⊗(1,0) = [ [8.8,0],[3.2,0] ] — matches the chunk math's after-row-1 state exactly. Step 2: v̄=S1·(0.6,0.8)=(5.28,1.92), vnew=(9,1) (β=1, full overwrite), and:

S2 = S1 + 1.0·((9,1)−(5.28,1.92))⊗(0.6,0.8) = S1 + (3.72,−0.92)⊗(0.6,0.8) = [ [11.032, 2.976],[2.648, −0.736] ]

Identical, to the last decimal. The chunkwise WY construction and the plain sequential recurrence disagree in how they get there — one via two small parallel-friendly row computations, one via two strictly sequential state updates — but they land on exactly the same state. That equivalence, not just asserted but verified digit for digit here, is the entire point: chunking is a reorganization of the computation, not an approximation of it.

The same C=2 chunk, as code, matching every number above

python
import torch

k = torch.tensor([[1.,0.],[0.6,0.8]])
v = torch.tensor([[11.,4.],[9.,1.]])
beta = torch.tensor([0.8, 1.0])
C = 2

# WY construction: build w_r, u_r row by row (sequential, but only over C rows)
w = torch.zeros(C, 2); u = torch.zeros(C, 2)
for r in range(C):
    corr_k = sum(w[i] * (k[i] @ k[r]) for i in range(r))
    corr_v = sum(u[i] * (k[i] @ k[r]) for i in range(r))
    w[r] = beta[r] * (k[r] - corr_k)
    u[r] = beta[r] * (v[r] - corr_v)

# chunk-end state, starting state = 0 (first chunk of the sequence)
S = torch.zeros(2,2)
for r in range(C):
    S = S + torch.outer(u[r], k[r])
print(S)   # tensor([[11.032, 2.976],[2.648, -0.736]]) -- matches both hand derivations

# sequential check, same two tokens, one state update at a time
Sseq = torch.zeros(2,2)
for r in range(C):
    v_bar = Sseq @ k[r]
    Sseq = Sseq + beta[r] * torch.outer(v[r] - v_bar, k[r])
print(Sseq)  # tensor([[11.032, 2.976],[2.648, -0.736]]) -- identical to S above

Two independent implementations of the same math, agreeing to the last decimal — the WY loop touching only chunk-local quantities (w, u, and the small C×C worth of k[i] @ k[r] dot products), the sequential loop touching the full state at every step. This is the building block a real chunkwise kernel scales up: run the WY loop for every chunk in parallel (none of them depend on each other), then carry the resulting chunk-end states forward across just L/C sequential steps using Chapter 7's Equation 7–8 form.

What the real speedup looks like

This toy used C=2 for hand-verifiability. In production, C is typically 64 or 128, and the payoff scales with sequence length. The paper's own Triton implementation, benchmarked on a single H100 GPU, model dimension 2,048, reports these speedups over the pure sequential form (Figure 1), read directly off the published chart:

Sequence length Lhead dim=64head dim=128head dim=256
5124.0×7.1×10.2×
1,0244.5×7.4×10.2×
2,0485.8×7.2×10.2×
4,0969.5×10.5×17.5×
8,19215.6×15.8×28.7×
16,38423.7×32.8×36.6×

The shape is not a straight climb from the first row. At small L, all three head dimensions sit close together (roughly 4-10×) because the fixed overhead of launching a kernel dominates at that scale, and the head-dim-128 line even dips a hair between 1,024 and 2,048 — noise at that end of the curve, not a real regression. Past L=4,096, GPU parallelism and tensor cores have enough work per launch to actually saturate, and the curves bend upward hard: head-dim 256 goes from 10.2× at L=2,048 to 17.5×, then 28.7×, then a peak of 36.6× at L=16,384, the paper's longest tested sequence and widest tested head dimension. That 36.6× number is the headline result. This is what made training DeltaNet at real scale (1.3-billion parameters, 100 billion tokens — Chapter 8) possible in the first place; the original 2021 paper never trained past 90 million parameters, for exactly the reason Chapter 6 diagnosed.

What the speedup was for: three synthetic recall stress-tests

Faster training is only useful if the model you can now afford to train is actually good at something. Before the 1.3B-scale language modeling results (Chapter 8), the parallelization paper first checks DeltaNet on three synthetic benchmarks purpose-built to isolate in-context recall — the specific skill Chapter 4's delta rule targets. MQAR (multi-query associative recall) tests retrieving many key-value pairs seen earlier in a context, exactly Chapter 4's overwrite scenario at sequence scale; the paper reports DeltaNet performing perfectly on the hardest MQAR setting tested, even without the optional convolution layer described below, beating Mamba specifically in the low-model-dimension regime where capacity is tightest. RegBench tests in-context learning of an unfamiliar formal language from examples given only in the prompt; DeltaNet again performs strongly against Transformer++, GLA, and Mamba baselines.

MAD (Mechanistic Architecture Design) is the most granular of the three — six separate sub-tasks probing different memory behaviors individually, rather than one aggregate score:

ModelCompressFuzzy RecallIn-Context RecallMemorizeNoisy RecallSelective CopyAverage
Transformer51.629.894.185.286.899.674.5
Mamba52.76.790.489.590.186.369.3
GLA38.86.980.863.381.688.660.0
DeltaNet42.235.710052.810010071.8

Read this table for its shape, not just its average column. DeltaNet posts a perfect 100 on In-Context Recall, Noisy Recall, and Selective Copy — three different flavors of “find and return the right thing from context” — and more than doubles the next-best Fuzzy Recall score (35.7 vs 14.4 for the next-closest baseline, not shown in this trimmed table). But on Memorize — a task about storing information for later regardless of any retrieval cue — DeltaNet is the worst model in the comparison, 52.8 against a Transformer's 85.2. This is exactly consistent with everything Chapters 3 and 4 built: the delta rule is a mechanism for selective, error-corrected memory, tuned for retrieval and recall, not a mechanism for maximizing how much can be crammed into a fixed-size state regardless of retrieval structure. A model built around correcting associations is not automatically a model built around remembering everything — those are different objectives, and MAD's granularity is what makes that distinction visible instead of averaged away.

What actually goes into a DeltaNet layer, end to end

Two implementation choices, easy to skip past, turn out to matter enough that the paper ablates them directly. First, the feature map: the original DeltaNet used ELU+1 (Chapter 1's default suggestion) with L1 normalization; this paper switches to the SiLU activation with L2 normalization instead. The full 340M-scale ablation, isolating exactly this choice under otherwise identical training:

Feature map / normalizationWiki PPL ↓LAMBADA PPL ↓Downstream avg
ELU+1, L1-norm (original DeltaNet)31.1255.9640.1
ELU+1, L2-norm28.0337.6242.1
ReLU, L2-norm28.7543.5340.9
SiLU, L2-norm (this paper's choice)28.2437.3742.1

Two separate effects are visible in this grid. Switching the normalization from L1 to L2 (holding the feature map at ELU+1) is what does almost all of the work — WikiText perplexity drops from 31.12 to 28.03, LAMBADA perplexity nearly halves, from 55.96 to 37.62. Switching the feature map itself, with L2 normalization already in place, matters far less and isn't even monotonic (ReLU is slightly worse than ELU+1 on this metric; SiLU is the best of the three tested). The reasoning given for why L2 matters so much: L2-normalized keys make (IkkT) a true projection matrix, which erases information in exactly one subspace while leaving the rest of the state completely untouched — a mathematically cleaner version of exactly the “repair one key without touching its neighbors” property Chapter 4 demonstrated by hand. Second, a lightweight short convolution layer — a small depthwise convolution applied right after the key/query/value projections — is added before the DeltaNet recurrence itself, giving the model a cheap way to look at a token's immediate local neighbors, a kind of positional awareness linear attention doesn't get for free the way softmax attention implicitly does.

Sequential steps vs. chunk size, live

A toy sequence of 16 tokens. Top row: pure sequential DeltaNet, every token a forced serial step. Bottom row: chunkwise, at the chunk size C you pick — green blocks are parallel matmuls within a chunk, the thin connectors between blocks are the only genuinely sequential steps left.

chunk size C4
What C=L and C=1 recover. Push the slider to C=16 (=L) and you get the fully parallel form — one sequential step, but the intra-chunk work now costs O(L2d), Chapter 0's original wall, right back again. Push it to C=1 and you get the pure recurrent form — O(Ld2) total work, but L forced-sequential steps, Chapter 6's bottleneck. The real system lives in the middle, at C=64 or 128, trading a small amount of extra FLOPs (the paper's exact complexity is O(LCd+Ld2)) for a large reduction in how many steps must run one after another.

Why 64 or 128, specifically, and not some other chunk size

The tradeoff behind that choice is worth making concrete with numbers, at a representative L=8,192, d=128 (a realistic per-head dimension):

Chunk size CSequential steps L/CExtra FLOPs term LCd
1 (pure recurrent)8,1921,048,576
1651216,777,216
6412867,108,864
12864134,217,728
51216536,870,912
8,192 (fully parallel)18,589,934,592

Sequential steps fall fast as C grows, but the extra FLOPs term grows linearly right alongside it — there's no free lunch, only a point where the GPU-utilization benefit of fewer sequential launches stops being worth the extra arithmetic. In practice, 64 and 128 sit in the sweet spot where the sequential-step count has already dropped by roughly two orders of magnitude from the C=1 row, while the extra-FLOPs term is still small relative to the O(Ld2) baseline cost that's paid regardless of chunk size. Push much past a few hundred and the extra FLOPs term starts competing with, then dominating, the baseline — which is exactly the diminishing-returns pattern visible in Chapter 7's own measured speedup table, where going from L=2,048 to L=4,096 nearly doubles the speedup, but doubling C alone at fixed L delivers a smaller marginal gain each time.

What does the WY representation actually eliminate the need to do, when computing a chunk's effect on DeltaNet's running state?

Chapter 8: Where Linear Models Stand

Chapter 7's speedup made it possible to train DeltaNet at a scale that actually matters. This chapter reports what happened when the authors did — and holds the result to the same honesty standard Session 08 held StreamingLLM to: what does DeltaNet actually beat, what does it not beat, and where does the whole linear-model family (DeltaNet alongside Mamba, GLA, RetNet) currently sit relative to a full Transformer.

How a DeltaNet layer slots into an otherwise ordinary model

One detail worth being concrete about before the results: building a “DeltaNet model” doesn't mean inventing a new architecture from scratch. The paper follows the standard LLaMA-style Transformer recipe almost exactly — RMSNorm, SwiGLU feed-forward blocks, the usual pre-normalization layout — and swaps out only the token-mixing layer itself, softmax attention out, DeltaNet in. Parameter allocation stays close to a standard Transformer's own split: roughly 4d2 parameters for the DeltaNet layer (matching a standard attention layer's Q/K/V/O projections), 8d2 for the SwiGLU feed-forward block, with the extra scalar βi projection from Chapter 4 adding a negligible sliver on top. This is precisely why the 1.3B-vs-1.3B comparisons in this chapter are a fair fight: the model is not larger, not architecturally different in any other respect, not trained with extra tricks — the token-mixing layer is the only thing that changed, isolating exactly the variable this session has spent nine chapters deriving.

That parameter-matching discipline is worth appreciating precisely because it's easy to get wrong. A comparison between two architectures that differ in total parameter count, training tokens, or auxiliary tricks simultaneously with the mechanism being tested can't cleanly attribute any observed difference to the mechanism alone — the result becomes a statement about the whole bundle of changes, not about DeltaNet specifically. Every table in this chapter benefits from this same discipline having been applied consistently across every row.

The setup: 1.3B parameters, 100B tokens, apples to apples

All models below are trained from scratch on the same subset of SlimPajama, same tokenizer, same parameter budget (roughly 1.3 billion), same 100-billion-token training run — the only thing that changes row to row is the token-mixing mechanism. Mamba is a selective state-space model with data-dependent decay (reparameterizable, the paper notes, as a gated linear transformer). GLA (gated linear attention) is a linear transformer with a learned, data-dependent decay gate instead of a delta rule. RetNet uses a fixed, non-data-dependent exponential decay. None of the three forgets based on retrieval error the way DeltaNet's delta rule does — they all forget on a schedule, not in response to what's actually stored.

What the memory bill looks like at this scale, in Session 08's own units

Before the accuracy numbers, price out the memory promise concretely, the way Session 08 always did, using the same per-token KV-cache formula Session 08 Chapter 1 derived: 2 × dmodel × dtype_bytes per token, per layer. Assume a representative 1.3B-scale architecture — hidden size 2,048, 24 layers, half precision — illustrative, since neither paper publishes the exact layer count, but the shape of the comparison doesn't depend on getting that number exactly right.

Transformer++'s KV cache, per token:

2 × 2,048 × 2 bytes × 24 layers = 196,608 bytes = 192 KiB per token, growing without bound

DeltaNet's state, total, fixed forever: a d×d matrix per layer, in half precision:

2,048 × 2,048 × 2 bytes × 24 layers = 201,326,592 bytes ≈ 192 MiB, at any sequence length

Divide the second number by the first to find the crossover token count — where does a Transformer's still-growing cache first reach what DeltaNet pays once, forever?

192 MiB = 196,608 KiB   ÷   192 KiB/token = 1,024 tokens

Past roughly one thousand tokens — well inside a single ordinary conversation turn — every additional token is pure additional cost for the Transformer and exactly zero additional cost for DeltaNet. This is Session 08's entire memory argument, replayed for a model that never needed a cache-eviction policy in the first place, because it never had a growing cache to evict from.

Model (1.3B / 100B tok)Wiki PPL ↓LAMBADA PPL ↓SWDESQUADFDA
Transformer++16.8513.4466.631.527.4
RetNet18.6417.2742.834.714.3
Mamba (w. conv)17.0613.8941.435.26.2
GLA (w. conv)17.2514.9252.437.422.3
DeltaNet (w. conv)16.8712.2149.537.417.2
DeltaNet + Global Attn (2 layers)16.5512.4071.043.029.8

Read this table the way you'd want a colleague to, not the way a marketing slide would. On raw WikiText perplexity, DeltaNet (16.87) is essentially tied with a full Transformer (16.85) — not better, statistically indistinguishable — and clearly ahead of every other linear-time baseline. On LAMBADA (a benchmark specifically built to require tracking a long-range dependency to predict the final word of a passage), DeltaNet actually beats the full Transformer, 12.21 vs 13.44 — the delta rule's error-correcting memory paying off exactly where you'd predict it would. But on real-world structured-extraction recall (SWDE), pure DeltaNet trails the full Transformer by a wide margin, 49.5 vs 66.6. The honest picture is mixed, not a clean win.

The rest of the 1.3B scoreboard: general commonsense reasoning

The table above showed perplexity and recall-specific benchmarks. The same 1.3B run is also evaluated on five standard zero-shot commonsense reasoning tasks — PIQA (physical commonsense), HellaSwag (sentence completion), WinoGrande (pronoun resolution), and ARC-easy/ARC-challenge (grade-school science questions) — which test something closer to general language competence than targeted recall:

Model (1.3B)LMB accPIQAHellaSwagWinoGrandeARC-eARC-cAvg
Transformer++48.970.849.653.656.026.550.9
RetNet43.370.047.352.554.825.648.9
Mamba (w. conv)46.272.240.154.159.028.250.0
GLA (w. conv)46.270.649.953.055.327.050.4
DeltaNet (w. conv)48.971.250.253.657.228.351.6

On this general-competence scoreboard, DeltaNet ties Transformer++ exactly on LAMBADA accuracy (48.9 both), posts the best HellaSwag score of any model shown, and edges out every linear-time baseline on the aggregate average (51.6, ahead of Transformer++'s own 50.9). Mamba, notably, wins PIQA and ARC by a clear margin but posts a strikingly weak HellaSwag score (40.1, nearly ten points below every other model) — a reminder that “which architecture is best” doesn't have a single answer even within one table; it depends which specific task's demands line up with a given architecture's strengths.

Why SWDE specifically is the hard case

It's worth being precise about why pure DeltaNet trails so much further on SWDE than on SQuAD or FDA, rather than treating it as unexplained noise. SWDE (Structured Web Data Extraction) asks a model to extract semi-structured fields from raw HTML — a task that often needs to hold and cross-reference many distinct fields simultaneously across a long, noisy document, closer to Chapter 7's MAD “Memorize” sub-task (where DeltaNet was also the weakest model measured) than to its “Recall” sub-tasks (where it was strongest). The distinction Chapter 4's honest-limits section drew still applies at full scale: a fixed-size state that corrects associations well is not automatically a fixed-size state that holds many simultaneous facts well. SQuAD and FDA lean more on locating and returning one specific answer given a clear cue — exactly DeltaNet's strength — which is why the gap on those two tasks is much narrower (37.4 vs 31.5 on SQuAD, DeltaNet actually ahead) than on SWDE's broader extraction demands.

This is a useful diagnostic habit worth generalizing beyond this one benchmark: whenever a model shows an uneven profile across a suite of tasks, ask what specific cognitive demand distinguishes the tasks it wins from the ones it loses, rather than averaging everything into a single number and calling the result “mostly good” or “mostly bad.” The single average-accuracy number in Chapter 8's earlier table would have hidden exactly this pattern; only breaking the benchmarks apart by what they actually measure reveals that DeltaNet's weakness is specific and mechanistically explicable, not a generic quality gap.

Does the picture hold at a smaller scale too?

The 1.3B table above is the headline, but the paper also runs the identical comparison at 340M parameters / 15B tokens — a useful check on whether the pattern is a large-scale artifact or shows up consistently:

Model (340M / 15B tok)Wiki PPL ↓LAMBADA PPL ↓PIQAHellaSwagWinoGrandeARC-e
Transformer++28.3942.6963.334.050.444.5
RetNet32.3349.1963.533.552.544.5
Mamba (w. conv)28.3939.6665.035.450.146.3
GLA (w. conv)29.4745.5365.133.851.644.4
DeltaNet (w. conv)28.2437.3764.834.352.245.8

The pattern holds: DeltaNet is competitive with or ahead of Mamba and GLA on WikiText perplexity, and clearly ahead of everything on LAMBADA (37.37 vs the next-best 39.66) at this smaller scale too — the same long-range-dependency advantage the 1.3B table showed, present already at a fifth of the parameters and roughly a seventh of the training tokens. This consistency across two very different scales is meaningfully more convincing than either single data point alone would be.

Why LAMBADA specifically rewards DeltaNet's mechanism

It's worth being mechanistic about why LAMBADA, of all the benchmarks in this chapter, shows DeltaNet's largest and most consistent edge. LAMBADA is constructed so that predicting the final word of each passage is only possible by using broad discourse context — a short local window is deliberately insufficient, by design of the benchmark. That's precisely the condition under which a decay-based memory (RetNet, GLA, Mamba) starts losing signal: information that entered the state many tokens ago has been progressively shrunk by repeated multiplicative decay, whether or not it was ever needed again. DeltaNet's delta rule only overwrites an association when a new key collides with it — it does not decay associations simply because time has passed. A distant fact that's never contradicted by a later, colliding key survives in DeltaNet's state essentially undamaged, however long ago it was written. LAMBADA is close to a purpose-built test of exactly that property, which is why the gap between DeltaNet and the decay-based baselines shows up there more clearly than almost anywhere else in this chapter's tables.

What the two hybrid variants trade off

Chapter 7 mentioned two hybrid designs; the results table above only shows one. The paper actually tries both: interleaving a sliding-window attention layer every other DeltaNet layer (bounded, cheap, local context — Session 08's window-attention idea, reused as a component rather than a full fix), versus replacing just two specific layers with full global attention. Both hybrids beat plain Transformer++ on perplexity; the global-attention variant is the one that also wins on every recall benchmark (SWDE, SQuAD, FDA), because unlike a sliding window, a full attention layer can reach arbitrarily far back in the sequence when it needs to. The sliding-window hybrid is the cheaper of the two (bounded window means bounded KV cache even in those two layers); the global-attention hybrid is the stronger one when real long-range recall is what the deployment actually needs. Neither is strictly better — they trade the same memory-versus- recall axis Session 08 spent an entire lesson on, just recomposed as a choice between two hybrid architectures instead of a choice of cache policy.

The finding that actually matters for deployment

Look at the last row. Take a DeltaNet model and replace just two of its layers — out of dozens — with ordinary full (global) attention, leaving every other layer linear. That hybrid beats the pure Transformer++ baseline on every single column in this table, including the recall-heavy ones (SWDE 71.0 vs 66.6, SQuAD 43.0 vs 31.5, FDA 29.8 vs 27.4) — while paying a full KV cache for only 2 layers out of the whole network instead of every layer. This is the paper's actual headline, and it's a more interesting claim than “linear attention wins”: almost-linear, with a couple of full-attention layers left in specifically to catch what linear attention still misses, beats both pure approaches.

Why DeltaNet specifically is stronger at recall than Mamba or GLA. Mamba, GLA, and RetNet all forget information on a schedule — a decay factor that shrinks old state regardless of whether that state still matters. DeltaNet's delta rule forgets conditionally: it only overwrites a stored association when a new key collides with an old one, and the size of the correction depends on how wrong the current retrieval already is. That is a mechanism built specifically for in-context retrieval, which is exactly why the improvements over Mamba are “particularly pronounced,” in the paper's own words, on the recall-intensive benchmarks (SWDE, SQuAD, FDA) rather than uniformly across the board.

What would have to be true for this to hold at frontier scale

Rather than just flagging “this is smaller than frontier scale” and moving on, it's worth naming what specifically would need to be checked before trusting these results at, say, 70B parameters and a trillion tokens. Three things in particular: whether the MAD-benchmark weakness on the Memorize task (Chapter 7) grows or shrinks as model capacity increases — a bigger model has more parameters to spend on memorization even with the same delta-rule mechanism, so this could go either way; whether the hybrid configuration's advantage (Chapter 8's actual winner) holds when the ratio of linear-to-full-attention layers is tuned differently at larger scale; and whether the SiLU/L2-normalization choices (Chapter 7's ablation) remain optimal, or whether a larger model's different training dynamics favor a different combination. None of these are reasons to doubt the results reported — they're the specific, falsifiable questions a careful reader should hold onto before assuming this chapter's conclusions transfer unchanged to a much bigger run.

What this does not claim

Be precise about the limits of this result, the way Session 08's Chapter 9 was precise about StreamingLLM's. This is a 1.3-billion-parameter, 100-billion-token comparison — genuinely larger than the original 2021 paper's 90M-parameter experiments, but still far short of frontier-scale training runs (hundreds of billions of parameters, trillions of tokens). Whether DeltaNet's advantages persist, shrink, or grow at that scale is not something this paper answers, and the authors don't claim it does. What it does establish, solidly: the chunkwise algorithm from Chapters 6–7 is what made training at this scale possible at all, and at this scale, the delta rule's specific recall advantage over decay-based alternatives (Mamba, GLA, RetNet) is real and measured, not theoretical.

What made this experiment affordable to even run

It's worth closing this chapter by naming the enabling condition explicitly, the same way Session 08 closed on StreamingLLM's accessibility angle. Training a 1.3B-parameter model on 100 billion tokens is a substantial but increasingly ordinary academic-scale compute budget — feasible on a modest GPU cluster in days, not the thousands of GPUs frontier labs use. That's only true for DeltaNet because of Chapter 7's chunkwise algorithm: the original 2021 DeltaNet, limited to a sequential training loop, would have made this exact experiment prohibitively slow to run at all, for the reasons Chapter 6 derived. The chunkwise speedup isn't just a convenience — it's the difference between a promising update rule that stays a 90M-parameter proof of concept forever, and one that a modest academic lab can actually test at a scale large enough to say something credible about. Cheaper, faster training methods don't just save money for whoever already has the compute; they lower the bar for who gets to run the experiment that finds out whether an idea holds up at all.

That accessibility argument isn't hypothetical here: the parallel DeltaNet kernel this session has spent two chapters deriving was released publicly as part of the FlashLinearAttention library, the same kind of open-source move Session 08's own StreamingLLM code release made. A worked derivation in a paper is one thing; a runnable, tested kernel anyone can install and train against is what actually lets an idea propagate past the lab that invented it — and it's a large part of why DeltaNet-style layers show up in follow-up work and hybrid architectures within the same year the parallelization paper was published, rather than sitting unused the way the original 2021 delta rule mostly did until this exact chunkwise fix arrived.

Why does the DeltaNet + 2 global-attention-layers hybrid outperform a pure Transformer++ on recall-heavy benchmarks like SWDE and SQuAD, according to the paper's own results?

Chapter 9: Limits & Connections

Pull every chapter's thread into one place, be honest about what's still unresolved, and connect this session to the rest of the site.

Look back at Chapter 0's opening question: could a mechanism exist with genuinely linear time and genuinely constant memory, without giving up the parallel trainability that made Transformers viable in the first place? Nine chapters later, the honest answer is: yes, mostly, with real caveats worth naming precisely rather than glossing over. That precision — not a triumphant “problem solved,” but a specific, evidenced account of what's actually been gained and what hasn't — is the note this closing chapter tries to end on.

The full arc, restated in one paragraph

Full attention costs O(L2) because every new token compares against every prior one (Chapter 0). Replacing softmax's exponential kernel with a factorizable one turns that comparison into a running matrix state that updates in constant time per token (Chapter 1) — and that state turns out to be mathematically identical to a 1991 idea called a fast weight programmer (Chapter 2). Because the state has fixed size, storing more associations than its dimension allows causes crosstalk, and the plain additive update has no way to correct for it (Chapter 3). The delta rule fixes this by retrieving the current value before writing, and writing only the correction (Chapter 4) — verified, at real scale, to keep an unbounded-length model coherent where the plain sum rule collapses (Chapter 5). But the delta rule's update is a matrix product, not a sum, which breaks the parallel training trick that made the plain version fast to train (Chapter 6). The WY representation restructures that product into two small matrices per chunk, recovering parallel training while producing the mathematically identical answer (Chapter 7) — which is what made training DeltaNet at real scale possible, and at that scale it roughly matches a full Transformer on perplexity, beats it on long-range LAMBADA, and a small hybrid (mostly DeltaNet, two full-attention layers) beats it outright on real-world recall (Chapter 8).

Notice the shape of that paragraph: every chapter fixed exactly one problem the previous chapter's fix introduced or left open. Chapter 1's speed came at the cost of Chapter 3's capacity ceiling. Chapter 4's fix for capacity came at the cost of Chapter 6's lost parallelism. Chapter 7's fix for parallelism came at the cost of nothing new — which is precisely why the session ends at Chapter 7, not Chapter 9: by the time the WY representation lands, every problem raised earlier in the session has a matching, verified answer. Chapter 8 isn't one more fix; it's the accounting, checking whether the whole chain of fixes was actually worth the engineering effort it took to build. The honest answer, laid out across this closing chapter, is: mostly yes, with the hybrid configuration as the strongest evidence for it.

The whole arc, as one small implementation

Every piece of that paragraph has appeared as working code somewhere in this session. Assembled in one place, here is a complete (if untrained) DeltaNet layer, recurrent form — the reader should be able to build this from the lesson alone, the standard this site holds every lesson to:

python
import torch, torch.nn as nn

class DeltaNetLayer(nn.Module):
    """Recurrent-form DeltaNet, single head, identity feature map (Ch 1)."""
    def __init__(self, d):
        super().__init__()
        self.Wq = nn.Linear(d, d, bias=False)
        self.Wk = nn.Linear(d, d, bias=False)
        self.Wv = nn.Linear(d, d, bias=False)
        self.Wbeta = nn.Linear(d, 1, bias=False)   # produces beta_i (Ch 4)
        self.d = d

    def forward(self, x):
        # x: (L, d) -- one sequence, processed token by token (Ch 1's recurrent form)
        L = x.shape[0]
        S = torch.zeros(self.d, self.d)          # fixed-size state, never grows (Ch 0's promise)
        for t in range(L):
            xt = x[t]
            q, k, v = self.Wq(xt), self.Wk(xt), self.Wv(xt)
            k = k / (k.norm() + 1e-6)              # sum/L2 normalization (Ch 5)
            beta = torch.sigmoid(self.Wbeta(xt))
            v_bar = S @ k                          # retrieve (Ch 4, step 1)
            S = S + beta * torch.outer(v - v_bar, k)  # delta rule write (Ch 4, step 3)
            o = S @ q
            yield o                              # Chapter 7's chunkwise form computes the same o's, differently

layer = DeltaNetLayer(d=8)
x = torch.randn(5, 8)
outputs = list(layer(x))
print(len(outputs), outputs[0].shape)   # 5 torch.Size([8]) -- one output per token, O(d^2) per step

Every line traces to a specific chapter: the fixed-size S is Chapter 0's promise; the token-by-token loop is Chapter 1's recurrent form; v_bar, beta, and the write line are Chapter 4's delta rule exactly; the L2 normalization is Chapter 5's ablation winner. What's not here is Chapter 7's chunkwise training path — a real implementation would replace this token-by-token Python loop with the WY chunkwise algorithm for training (parallel, fast) while keeping something close to this exact recurrent form for inference (cheap, constant-memory), the dual-path structure Chapter 1 first described and Chapter 6 explained the difficulty of achieving for this specific update rule.

What neither paper claims

Straight talk on scale. Every number in Chapter 8 came from a 1.3B-parameter, 100B-token run. Neither paper claims these results transfer unchanged to frontier scale, and this lesson won't claim it either. What is established: the capacity problem (Chapter 3) is real and reproducible at any scale, the delta rule fixes it (Chapter 4–5), and the WY chunking makes training the fix tractable (Chapter 7). Whether pure linear-time models close the remaining gap to full attention as scale grows, or whether hybrids (Chapter 8's actual winner) remain the practical answer indefinitely, is an open, actively-studied question — not something either 2021 or 2024 paper resolves.

There's a second limit worth naming precisely: even a perfectly-trained fixed-size state has an information- theoretic ceiling. A d×d matrix can only hold so many bits, no matter how cleverly the delta rule manages what goes in it. For tasks that genuinely require recalling many specific, disjoint facts from very far back — not just tracking a long-range pattern, which DeltaNet is demonstrably good at — a bounded state is a real architectural constraint, not an implementation detail to optimize away. This is exactly why Chapter 8's winning configuration keeps a couple of full-attention layers around: they're not vestigial, they're doing work no fixed-size state can.

Put a rough number on that ceiling, for intuition rather than precision. A d=128, half-precision state matrix holds 128×128×16 bits ≈ 262,144 bits of raw storage — a large number, but a fixed one, existing whether the sequence has produced 100 tokens or 100 million. Full attention's KV cache, by contrast, grows its storage by roughly 512 KiB (Session 08's own number) with every single new token, unboundedly. No matter how efficiently DeltaNet's delta rule packs information into its fixed budget, there exists some document long and fact-dense enough that a KV-cache-backed model can represent it exactly while a fixed-size state provably cannot — not because the delta rule is poorly designed, but because a fixed number of bits is a fixed number of bits, and no update rule changes how many of them there are to work with.

If you're deciding what to build today: a decision guide

Collapse this session's nine chapters into the sequence of questions an engineer actually has to answer, in order:

1 · How long are your sequences, really?
L well under d (Chapter 0's ~4,096 crossover for a 7B-class model)? Full attention is probably simpler and more accurate — don't reach for this session's machinery yet
↓ L comfortably exceeds d
2 · Is training compute or inference compute the bottleneck?
inference-bound, existing checkpoint → Session 08's StreamingLLM needs no retraining; training a new model from scratch → keep going
3 · Does the task need precise long-range recall?
yes (retrieval, structured extraction, QA over long context) → delta rule (Ch 4), not a decay-based linear model (RetNet/GLA/Mamba)
4 · Can you afford ANY full-attention layers?
yes, even 2 out of dozens → Chapter 8's hybrid is the strongest configuration measured; no, fully linear required → pure DeltaNet, accept the Chapter 8 recall gap
5 · Do you control pretraining infrastructure?
yes → use the WY chunkwise algorithm (Ch 7) from day one, chunk size 64–128; this is what makes any of the above trainable at scale at all

Every decision in that chain traces back to a specific chapter's derivation or measured result, not a rule of thumb handed down without justification — the same standard Session 08 tried to hold itself to.

How this compares to the alternatives, side by side

ApproachTime complexityState sizeForgetting mechanismBest at
Full attentionO(L²d)O(Ld), grows forevernever forgets (until evicted)accuracy, when you can afford it
StreamingLLM (Session 08)O(L²d), bounded Lbounded, fixed windowhard eviction past the windowkeeping an existing model running indefinitely, unmodified
RetNetO(Ld²)O(d²), constantfixed exponential decaysimplicity, hardware efficiency
Mamba / GLAO(Ld²)O(d²), constantlearned, data-dependent decaygeneral language modeling at constant memory
DeltaNetO(Ld²)O(d²), constanterror-corrected, retrieval-conditionedin-context recall at constant memory
Hybrid (DeltaNet + few full layers)mostly O(Ld²)mostly constant, small full-attention taxerror-corrected + exact recall in the full layersthe paper's actual best result

The bigger arc: three eras of sequence modeling, and where this session sits

Zoom out past this session's ten chapters to the field-level story they're one chapter of. Era one — RNNs, LSTMs, the pre-2017 default (Chapter 0): constant memory, linear time, genuinely elegant, but crippled at training time by a strictly sequential dependency chain that starved GPUs of the parallelism they're built for. Era two — the Transformer (2017 onward): trade away constant memory and linear time for a parallel training form that uses hardware efficiently, accepting O(L2) as the cost of that trade. It won, decisively, because training speed at scale mattered more than the asymptotic inference cost, for as long as sequences stayed short enough for the tradeoff to make sense.

Era three — this session — is the attempt to have both properties at once: RNN-style constant memory and linear time, Transformer-style parallel training. Chapters 1 through 7 are the specific sequence of ideas that gets there for one particular architecture family: remove softmax to expose the RNN hiding inside attention (Chapter 1), recognize it as a 1991-era memory model with a known, fixable capacity flaw (Chapters 2–4), and solve the resulting training-parallelism problem with a piece of 1980s numerical linear algebra repurposed for the job (Chapter 7). None of the three eras is simply “better” than the others in every dimension — Chapter 8's own results, where a small amount of era-two full attention folded back into an otherwise era-three model produced the strongest system measured, is the clearest evidence in this entire lesson that the honest answer, at least for now, is a blend rather than a clean succession.

Connections on this site

This session built linear attention up from the softmax formula itself. If any of the underlying attention mechanics felt shaky, or you want to see where this fits into the broader landscape of efficient attention, these are the lessons that connect directly:

  • Attention & Transformers — the QKTV mechanism Chapter 1's derivation started from.
  • The Transformer — the full architecture this session's attention layer lives inside.
  • Session 08: Attention Sinks & Streaming — the KV-cache memory problem this session's compute-side wall complements; two different diagnoses of “attention doesn't scale,” two different fixes.
  • State Space Models & Mamba — the selective-SSM branch of the same constant-memory family, reachable from a completely different derivation (control theory rather than fast weight programmers) that lands in a strikingly similar place.
  • Linear Attention & RWKV — a sibling lineage of linear attention, built around a different recurrence (WKV) than the fast-weight/delta-rule path this session took.
  • Attention Variants — the broader landscape of departures from vanilla softmax attention, for context on how this family fits among the others.
  • Session 09: Latent Attention — a different axis of attention efficiency (compressing the KV cache itself) rather than replacing the attention mechanism.
  • CS229S: FlashAttention — making full O(L²) attention faster at the kernel level, the complementary strategy to this session's O(L) restructuring.
  • CS229S: Transformer Efficiency — the broader landscape of serving-cost tricks (quantization, batching, speculative decoding) this session's complexity arithmetic belongs to.
  • RNNs & Sequences — the pre-Transformer recurrent models Chapter 0's historical detour compared this session's approach against.

Two of this session's own conceptual anchors are worth naming again by name, since they're easy to lose track of once the equations pile up: Session 08's KV cache is the memory problem (Chapter 0's opening contrast); FlashAttention is the kernel-efficiency problem (making the existing O(L2) computation run faster on real hardware, without changing its asymptotic shape). This session's fast-weight- programmer approach is a third, structurally different answer — not a faster O(L2), not a smarter cache policy, but a genuinely different O(L) computation underneath. All three are live, actively used strategies in real systems today, frequently combined rather than chosen between.

The numbers that carried this lesson, together in one place

QuantityValueFrom
Full vs. linear attention crossoverL = d = 4,096Chapter 0
Linear attention's speedup at L = 4,000,000≈977×Chapter 0
2D outer-product memory: exact retrievalW·k₁ = v₁, exactlyChapter 2
Overcapacity crosstalk, real ddot=64 caseerrors begin near S=60Chapter 3
Delta rule vs. sum rule overwrite, hand-worked(9.8,3.6) correct vs. (16,6) garbageChapter 4
Unbounded-context PPL, sum rule vs. delta rule>260 vs. 29.4, same 0.13M stateChapter 5
Chunkwise WY vs. sequential DeltaNet, hand-verifiedidentical state, to 3 decimalsChapter 7
Chunkwise training speedup, measuredup to 36.6× (dhead=256, L=16,384)Chapter 7
DeltaNet 1.3B: LAMBADA PPL vs. full Transformer12.21 vs. 13.44 — DeltaNet winsChapter 8
Hybrid (2 attention layers) vs. Transformer++, recall avgwins on SWDE, SQuAD, and FDAChapter 8
MAD benchmark: DeltaNet's Fuzzy Recall vs. next-best35.7 vs. 14.4 — more than doubleChapter 7
MAD benchmark: DeltaNet's Memorize score52.8 — worst of the models comparedChapter 7
Ablation: SiLU+L2 norm vs. original ELU+L1, 340M WikiText PPL28.24 vs. 31.12Chapter 7

What this session doesn't test, and honestly can't tell you

Follow the same discipline Session 08 closed with. Every language-modeling number in Chapters 5 and 8 comes from English-dominant text corpora (WikiText-103, SlimPajama), at parameter counts (up to 1.3B) far below frontier scale, using a specific pair of feature-map and normalization choices (Chapter 7's SiLU/L2). Neither paper tests multimodal sequences, heavily quantized inference, or scales beyond a few billion parameters. The underlying mathematics — the associativity trick (Chapter 1), the capacity argument (Chapter 3), the Householder/WY equivalence (Chapter 7) — are general and don't depend on any of those specifics. But “the math generalizes” and “the measured results generalize” are different claims, and this lesson has tried to keep that line visible rather than blur it, the same way it did with every number borrowed from Session 08.

A self-check: ten questions, one per chapter

Before the closing quiz, a harder test than any single multiple-choice question: can you answer each of these from memory, in one or two sentences, without scrolling back?

ChQuestion to answer from memory
0At what sequence length does linear attention start beating full attention, and why does that number scale with model size?
1What algebraic property turns the softmax-attention sum into a token-by-token recurrence?
2What is a fast weight programmer, and which two variables play the role of keys and values?
3Why can't a d-dimensional memory cleanly store more than d associations?
4What three steps does the delta rule run at every token, and why does step 1 matter?
5What real number shows the sum rule collapsing where the delta rule stays coherent?
6Why is the delta rule's update a matrix product rather than a sum, and why does that matter for training?
7What does the WY representation let you avoid materializing, and what replaces it?
8What configuration actually beat a full Transformer across every benchmark measured, and why?
9What's the one thing a fixed-size state can never do, no matter how good its update rule is?

If any of those ten feels shaky, that chapter's worked example is the fastest way back to solid ground — every one of them was derived from a hand-checkable calculation, not asserted.

What you should be able to do now

If this session worked, you should be able to derive the recurrent form of linear attention from the softmax formula using nothing but the associativity of matrix multiplication; explain, in one sentence, why the fast weight programmer framing is not just an analogy but a literal mathematical identity; hand-compute what happens when a fixed-size associative memory is asked to store more associations than its dimension allows; write down the delta rule and explain why subtracting the retrieved value before writing is the entire fix; and explain, without hand-waving, why a matrix product resists the same parallelization trick that a matrix sum gets for free — and how the WY representation gets around it. That last one is the real test: not recognizing the right term on a slide, but being able to rebuild, from Chapter 7's worked C=2 example, why chunking doesn't change the answer, only how it's computed.

The one idea to leave with. A mechanism invented in 1991 to let one neural network reprogram another, one outer product at a time, turns out to be exactly what's left of attention once you remove the function that makes it expensive. Understanding attention as a fast weight programmer isn't just historically interesting — it's what let researchers thirty years later import an entire pre-existing theory of memory capacity, diagnose why linear attention degrades, and fix it with an update rule (the delta rule) that is itself even older than the fast weight programmer framing. The newest-sounding ideas in efficient attention keep turning out to be old ideas, recognized.

“The purpose of computing is insight, not numbers.” — Richard Hamming

What is the honest, evidence-based summary of where pure linear-time attention (DeltaNet included) currently stands relative to full softmax attention, per this session's own numbers?