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.
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.
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.
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:
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.
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.
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:
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 L | Ratio L ÷ d | What it means |
|---|---|---|
| 512 (short chat turn) | 0.125 | full attention is 8× cheaper here |
| 4,096 (= d, the crossover) | 1.0 | exactly tied |
| 16,384 (a long document) | 4.0 | linear attention is 4× cheaper |
| 4,000,000 (Session 08's headline length) | ≈977 | linear 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.
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.
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.
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:
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.
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.
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.
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:
| Question | Full attention | Linear 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, forever | O(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.
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.
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.
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.
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.
Start from the attention every prior session has used. For output position i, with query qi, and keys/values k1…ki, v1…vi from every token processed so far:
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 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:
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:
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:
and the whole layer collapses to:
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:
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.
φ 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:
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.
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.
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:
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.
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.
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.
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.
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.
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.
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:
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 a→v, b→k, x→q. 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.
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.
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.
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.
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:
Build the fast weight matrix by summing the two outer products, exactly as Chapter 1's recurrence would after two steps. Recall v ⊗ k = vkT is a dvalue × dkey matrix — here 2×2 — built by multiplying each entry of v against each entry of k:
Now retrieve. Query with k1 itself — o = Wk1 = W·(1,0), which picks out exactly W's first column:
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):
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.
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 {k1…kn} that are orthonormal (each unit length, all mutually perpendicular), querying the memory W = ∑j vj⊗kj with any stored key ki gives:
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 j≠i — which is exactly the crosstalk term Chapter 3 computes by hand.
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.
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.
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 = ∑ v⊗k 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.
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 = ∑ vj⊗kj 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 v1⊗k1 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.
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?
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.
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):
Query with k3 itself, hoping for v3 = (9,1) back:
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:
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):
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.
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.
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.
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
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:
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.
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:
| Model | ddot=64, test BLEU | ddot=256, test BLEU | ddot=512, test BLEU |
|---|---|---|---|
| Standard Transformer (softmax) | 27.7 | — | — |
| Linear Transformer (φ=ELU+1) | 26.8 | — | — |
| Performer (FAVOR+) | 24.4 | 25.3 | 27.7 |
| DPFP (this paper's φ) | — | 26.9 | 27.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.
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.
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 k1–k3 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.
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.
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.
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.
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.
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:
β — 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.
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.
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.
Substitute step 2 into step 3 and the write and remove terms combine (Appendix A.1 of the paper works this out formally):
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.
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:
Step 2 — interpolate:
Step 3 — write the correction:
Verify by re-querying. With k1:
And with k2, which was never touched — because it's orthogonal to k1, the update to k1's slot should leave it entirely alone:
A clean, selective, verified overwrite: the target association updated exactly as intended, the unrelated one left completely alone.
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:
Query with k1:
(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 rule | Result at k1 | What 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 |
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
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:
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:
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.
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 + βv⊗k. 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:
Query k2: W′(0,1) = (9.4, 4.6) = 0.2v2+0.8v3, the correct blend. But query k1, which was never touched:
The delta rule, same scenario: retrieve v̄ = Wk2 = (3,7) = v2 exactly (orthogonal to k1, so retrieval is clean); write the correction:
Query k2: W′(0,1) = (9.4, 4.6) — same correct blend. Query k1:
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.
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.
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.
Sum normalization means dividing a projected key vector by the sum of its own components before using it anywhere:
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.
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 encoding | Extra attn. normalization | Valid PPL | Test PPL |
|---|---|---|---|
| Yes | Yes | 30.4 | 32.1 |
| No | Yes | 29.2 | 31.2 |
| Yes | No | 29.7 | 31.5 |
| No | No | 28.1 | 31.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.
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 rule | Small, test PPL | Medium, test PPL |
|---|---|---|
| Standard Transformer (softmax) | 34.1 | 29.6 |
| Linear Transformer, sum rule | 38.3 | 33.0 |
| Delta Network, delta rule | 35.5 | 31.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.
“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:
The medium configuration: D=256, H=8, so ddot=32, trained at L=384:
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.
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.
| Model | Parameters | State size (constant, in M) | Test PPL |
|---|---|---|---|
| Linear Transformer (sum rule) | 89.8M | 0.13M | > 260 |
| Delta Network | 89.9M | 0.13M | 29.4 |
| Transformer-XL (small memory) | 90.9M | 1.05M | 30.1 |
| Transformer-XL (large memory) | 90.9M | 6.29M | 25.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.
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.
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.
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:
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.
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.
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:
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.
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.
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 = (QKT ⋅ M)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.
Write the delta rule's update (Chapter 4) in a slightly different, equivalent form. Starting from Wi = Wi−1 + βi(vi − Wi−1ki) ⊗ ki, distribute the outer product across the subtraction inside the parentheses (v̄i = Wi−1ki, Chapter 4's retrieval step, substituted back in):
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:
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:
Compare that to the plain sum rule's St = ∑ivi⊗ ki: 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.
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 I − kkT:
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
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 = I − k1k1T = [ [0,0],[0,1] ], and H2 = I − k2k2T = [ [1,0],[0,1] ] − [ [0.36,0.48],[0.48,0.64] ] = [ [0.64,−0.48],[−0.48,0.36] ].
Different matrices — H1H2 ≠ H2H1, 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.
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.
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:
| Method | Sequential steps | Total work | Parallel-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.
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.
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:
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 = ∑vi⊗ki = [ [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.
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.
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.
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.
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.
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.
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:
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.
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:
WY construction, row by row. Row 1 has no earlier rows to correct for:
Row 2 corrects for row 1, using k1·k2 = 0.6:
Chunk-end state. Since S[t]=0, the whole chunk's contribution is just ∑ ui⊗ki:
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:
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.
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.
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 L | head dim=64 | head dim=128 | head dim=256 |
|---|---|---|---|
| 512 | 4.0× | 7.1× | 10.2× |
| 1,024 | 4.5× | 7.4× | 10.2× |
| 2,048 | 5.8× | 7.2× | 10.2× |
| 4,096 | 9.5× | 10.5× | 17.5× |
| 8,192 | 15.6× | 15.8× | 28.7× |
| 16,384 | 23.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.
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:
| Model | Compress | Fuzzy Recall | In-Context Recall | Memorize | Noisy Recall | Selective Copy | Average |
|---|---|---|---|---|---|---|---|
| Transformer | 51.6 | 29.8 | 94.1 | 85.2 | 86.8 | 99.6 | 74.5 |
| Mamba | 52.7 | 6.7 | 90.4 | 89.5 | 90.1 | 86.3 | 69.3 |
| GLA | 38.8 | 6.9 | 80.8 | 63.3 | 81.6 | 88.6 | 60.0 |
| DeltaNet | 42.2 | 35.7 | 100 | 52.8 | 100 | 100 | 71.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.
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 / normalization | Wiki PPL ↓ | LAMBADA PPL ↓ | Downstream avg |
|---|---|---|---|
| ELU+1, L1-norm (original DeltaNet) | 31.12 | 55.96 | 40.1 |
| ELU+1, L2-norm | 28.03 | 37.62 | 42.1 |
| ReLU, L2-norm | 28.75 | 43.53 | 40.9 |
| SiLU, L2-norm (this paper's choice) | 28.24 | 37.37 | 42.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 (I − kkT) 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.
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.
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 C | Sequential steps L/C | Extra FLOPs term LCd |
|---|---|---|
| 1 (pure recurrent) | 8,192 | 1,048,576 |
| 16 | 512 | 16,777,216 |
| 64 | 128 | 67,108,864 |
| 128 | 64 | 134,217,728 |
| 512 | 16 | 536,870,912 |
| 8,192 (fully parallel) | 1 | 8,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.
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.
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.
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.
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:
DeltaNet's state, total, fixed forever: a d×d matrix per layer, in half precision:
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?
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 ↓ | SWDE | SQUAD | FDA |
|---|---|---|---|---|---|
| Transformer++ | 16.85 | 13.44 | 66.6 | 31.5 | 27.4 |
| RetNet | 18.64 | 17.27 | 42.8 | 34.7 | 14.3 |
| Mamba (w. conv) | 17.06 | 13.89 | 41.4 | 35.2 | 6.2 |
| GLA (w. conv) | 17.25 | 14.92 | 52.4 | 37.4 | 22.3 |
| DeltaNet (w. conv) | 16.87 | 12.21 | 49.5 | 37.4 | 17.2 |
| DeltaNet + Global Attn (2 layers) | 16.55 | 12.40 | 71.0 | 43.0 | 29.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 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 acc | PIQA | HellaSwag | WinoGrande | ARC-e | ARC-c | Avg |
|---|---|---|---|---|---|---|---|
| Transformer++ | 48.9 | 70.8 | 49.6 | 53.6 | 56.0 | 26.5 | 50.9 |
| RetNet | 43.3 | 70.0 | 47.3 | 52.5 | 54.8 | 25.6 | 48.9 |
| Mamba (w. conv) | 46.2 | 72.2 | 40.1 | 54.1 | 59.0 | 28.2 | 50.0 |
| GLA (w. conv) | 46.2 | 70.6 | 49.9 | 53.0 | 55.3 | 27.0 | 50.4 |
| DeltaNet (w. conv) | 48.9 | 71.2 | 50.2 | 53.6 | 57.2 | 28.3 | 51.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.
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.
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 ↓ | PIQA | HellaSwag | WinoGrande | ARC-e |
|---|---|---|---|---|---|---|
| Transformer++ | 28.39 | 42.69 | 63.3 | 34.0 | 50.4 | 44.5 |
| RetNet | 32.33 | 49.19 | 63.5 | 33.5 | 52.5 | 44.5 |
| Mamba (w. conv) | 28.39 | 39.66 | 65.0 | 35.4 | 50.1 | 46.3 |
| GLA (w. conv) | 29.47 | 45.53 | 65.1 | 33.8 | 51.6 | 44.4 |
| DeltaNet (w. conv) | 28.24 | 37.37 | 64.8 | 34.3 | 52.2 | 45.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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Collapse this session's nine chapters into the sequence of questions an engineer actually has to answer, in order:
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.
| Approach | Time complexity | State size | Forgetting mechanism | Best at |
|---|---|---|---|---|
| Full attention | O(L²d) | O(Ld), grows forever | never forgets (until evicted) | accuracy, when you can afford it |
| StreamingLLM (Session 08) | O(L²d), bounded L | bounded, fixed window | hard eviction past the window | keeping an existing model running indefinitely, unmodified |
| RetNet | O(Ld²) | O(d²), constant | fixed exponential decay | simplicity, hardware efficiency |
| Mamba / GLA | O(Ld²) | O(d²), constant | learned, data-dependent decay | general language modeling at constant memory |
| DeltaNet | O(Ld²) | O(d²), constant | error-corrected, retrieval-conditioned | in-context recall at constant memory |
| Hybrid (DeltaNet + few full layers) | mostly O(Ld²) | mostly constant, small full-attention tax | error-corrected + exact recall in the full layers | the paper's actual best result |
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.
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:
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.
| Quantity | Value | From |
|---|---|---|
| Full vs. linear attention crossover | L = d = 4,096 | Chapter 0 |
| Linear attention's speedup at L = 4,000,000 | ≈977× | Chapter 0 |
| 2D outer-product memory: exact retrieval | W·k₁ = v₁, exactly | Chapter 2 |
| Overcapacity crosstalk, real ddot=64 case | errors begin near S=60 | Chapter 3 |
| Delta rule vs. sum rule overwrite, hand-worked | (9.8,3.6) correct vs. (16,6) garbage | Chapter 4 |
| Unbounded-context PPL, sum rule vs. delta rule | >260 vs. 29.4, same 0.13M state | Chapter 5 |
| Chunkwise WY vs. sequential DeltaNet, hand-verified | identical state, to 3 decimals | Chapter 7 |
| Chunkwise training speedup, measured | up to 36.6× (dhead=256, L=16,384) | Chapter 7 |
| DeltaNet 1.3B: LAMBADA PPL vs. full Transformer | 12.21 vs. 13.44 — DeltaNet wins | Chapter 8 |
| Hybrid (2 attention layers) vs. Transformer++, recall avg | wins on SWDE, SQuAD, and FDA | Chapter 8 |
| MAD benchmark: DeltaNet's Fuzzy Recall vs. next-best | 35.7 vs. 14.4 — more than double | Chapter 7 |
| MAD benchmark: DeltaNet's Memorize score | 52.8 — worst of the models compared | Chapter 7 |
| Ablation: SiLU+L2 norm vs. original ELU+L1, 340M WikiText PPL | 28.24 vs. 31.12 | Chapter 7 |
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.
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?
| Ch | Question to answer from memory |
|---|---|
| 0 | At what sequence length does linear attention start beating full attention, and why does that number scale with model size? |
| 1 | What algebraic property turns the softmax-attention sum into a token-by-token recurrence? |
| 2 | What is a fast weight programmer, and which two variables play the role of keys and values? |
| 3 | Why can't a d-dimensional memory cleanly store more than d associations? |
| 4 | What three steps does the delta rule run at every token, and why does step 1 matter? |
| 5 | What real number shows the sum rule collapsing where the delta rule stays coherent? |
| 6 | Why is the delta rule's update a matrix product rather than a sum, and why does that matter for training? |
| 7 | What does the WY representation let you avoid materializing, and what replaces it? |
| 8 | What configuration actually beat a full Transformer across every benchmark measured, and why? |
| 9 | What'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.
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 purpose of computing is insight, not numbers.” — Richard Hamming