Circuits, explained · Part 5 of 7 · Covers Two-Layer Attention-Only Transformers, Three Kinds of Composition, Path Expansion of Logits and of Attention Scores, Analyzing a Two-Layer Model, Term Importance Analysis, Virtual Attention Heads
Two-Layer Models: How Heads Compose
With a second layer, one head can read what an earlier head wrote. We follow the paper's three kinds of composition, expand the logits and the attention scores into paths, and measure which paths matter in a real two-layer model.
A Mathematical Framework for Transformer Circuits. Nelson Elhage, Neel Nanda, Catherine Olsson, Tom Henighan, Nicholas Joseph, Ben Mann, Amanda Askell, Yuntao Bai, Anna Chen, Tom Conerly, Nova DasSarma, Dawn Drain, Deep Ganguli, Zac Hatfield-Dodds, Danny Hernandez, Andy Jones, Jackson Kernion, Liane Lovitt, Kamal Ndousse, Dario Amodei, Tom Brown, Jack Clark, Jared Kaplan, Sam McCandlish, Chris Olah. Transformer Circuits Thread, 2021.
A one-layer model can only look up tables of token pairs and skip-trigrams. Give it a second layer, and a head can read what another head wrote a moment before. That small change is where the paper's most interesting results begin.
The real model here is attn-only-2l: two layers of attention, eight heads per layer, and nothing else. We run it on 3,072 tokens of ordinary English text from WikiText.
Every head reads the stream through three matrices and writes back through one. So there are three places where a later head can pick up an earlier head's work.
Three kinds of composition. A layer-0 head writes into the stream. A layer-1 head reads the stream through W_Q, W_K and W_V. If its W_Q reads the layer-0 head's output, that is Q-composition; if its W_K does, K-composition; if its W_V does, V-composition.
The expansion is the same algebra as (1+a)(1+b)=1+a+b+ab, with matrices. Write H1 for the heads of the first layer and H2 for the second. From Part 2, one head is Ah⊗WOVh: its attention pattern Ah mixes tokens, and WOVh=WOhWVh acts inside each token. A layer adds its heads to the stream, so a layer is Id+∑hAh⊗WOVh.
Multiply the two middle brackets out. You get four groups: Id⋅Id, layer-2 heads times Id, Id times layer-1 heads, and layer-2 heads times layer-1 heads. The mixed-product rule from Part 2, (A⊗B)(C⊗D)=(AC)⊗(BD), turns each product of two heads into one term:
Read it group by group. The first line is the direct path: embed a token and unembed it at once, which is the bigram table of Part 1. The second line has one term per head, in either layer, that reads the embeddings directly. These are the same skip-trigram terms as in the one-layer model. The last two lines hold one term for every pair of a layer-2 head and a layer-1 head. In each, the layer-1 head moves information, and then the layer-2 head moves the result again.
A toy example. Take a stream of one number per token and three tokens, x0=(1,2,3). Each layer has one head. In layer 1, each token reads the token before it (token 1 reads itself), and the OV "matrix" is the number w1=0.5. In layer 2, token 2 reads tokens 1 and 2 half each, the others read themselves, and w2=2.
The toy path expansion. The direct path is (1, 2, 3). Head 1 alone adds (0.5, 0.5, 1). Head 2 alone, reading the embeddings, adds (2, 3, 6). The virtual head, head 2 reading what head 1 wrote, adds (1, 1, 2). The four rows add up to (4.5, 6.5, 12), exactly the two-layer output.
Run the model the normal way first. After layer 1, x1=x0+w1A1x0=(1.5,2.5,4). After layer 2, x2=x1+w2A2x1=(4.5,6.5,12). Now add the four paths. Token 3 gets 3+1+6+2=12. The sum matches at every token, so no route was missed and none was counted twice.
On the real model. Our script does the same on attn-only-2l, with 3,072 tokens. It keeps the attention patterns and the layer-norm scales from a normal run, and holds them fixed. Then every step is a matrix product, so the expansion is exact. It builds the direct path, the 16 single-head terms and the 64 virtual-head terms, adds them, and compares with the model:
python
o0 = [A0[:, h] @ (x0n @ W_V[0, h]) @ W_O[0, h] for h in range(8)] # what each layer-0 head writesvirt[h2][h1] = A1[:, h2] @ (cen(o0[h1]) / s1 @ W_V[1, h2]) @ W_O[1, h2] # layer-1 head h2 moving what h1 wrotefull = direct + sum(o0) + sum(o1_single) + sum(all virt) # then centre, scale and unembed
plain text
== 2. PATH EXPANSION OF THE LOGITS ON REAL TEXT (patterns and layer-norm scales frozen) stream before layer 1 rebuilt from the terms: max |diff| = 5.7e-06 logits rebuilt as direct + 16 head terms + 64 virtual heads: max |diff| from the model = 4.2e-05 (logits range -18.2 .. 25.8)
The 81 terms rebuild every logit to within 0.00005, on logits that run from -18.2 to 25.8. So the expansion is not an approximation. It is the model, written another way.
We test the claim about virtual heads later, in the term importance section. Our model gives a more mixed answer than the paper's.
Write the stream at position i before layer 2 as a sum of paths. It is the embedding, plus what each layer-1 head h moved into position i:
x1,i=WEti+h∈H1∑i′∑Aii′hWOVhWEti′
Here ti is the one-hot vector of token i, as in Part 2. The score of a layer-2 head h for destination i and source j is sij=x1,i⊤WQKhx1,j. Put the sum in on both sides. Each side has a "direct" part and a "moved by a head" part, so the product has four groups. Write di=WEti for the direct part and mi for the part that layer-1 heads moved in:
The first term is the no composition group: both sides are plain embeddings, as in a one-layer model. The second is the Q-composition group: a layer-1 head prepared the query: it changes what the destination is looking for. The third is the K-composition group: a layer-1 head prepared the key, which changes what each source offers. In the last group, layer 1 prepared both.
A toy example. Let the query be q=qd+qh with a direct part qd=(1,0) and a moved part qh=(0,1). Let the key be k=kd+kh with kd=(2,1) and kh=(1,3). The four terms are qd⋅kd=2, qh⋅kd=1, qd⋅kh=1 and qh⋅kh=3. They add up to 7, and indeed (1,1)⋅(3,4)=7.
On the real model. Our script splits the scores of all eight layer-1 heads into these four groups, with the layer-0 patterns frozen. The groups add up to the model's scores to within 0.00007. Then it asks a functional question. If we delete one group, how much does the attention pattern change? We measure the change with the total variation distance.
plain text
no-composition + Q + K + Q-and-K terms vs the model's scores: max |diff| = 6.5e-05 head 1.6: without K terms 0.75, without Q terms 0.20, without Q-and-K 0.23; no-composition term alone 0.58 head 1.7: without K terms 0.45, without Q terms 0.24, without Q-and-K 0.15; no-composition term alone 0.38
For each layer-1 head, how much its attention pattern changes when the K-composition terms are removed (orange) and when the Q-composition terms are removed (blue), on 3,072 tokens of WikiText. Head 1.6 changes by 0.75 without its K terms and only 0.20 without its Q terms.
Head 1.6 stands out. Remove its K-composition terms, and its pattern changes by 0.75, which means it now looks almost somewhere else. Remove its Q-composition terms, and it changes by only 0.20. So this head mostly relies on keys that layer 1 prepared. Part 6 shows that head 1.6 is an induction head. On repeated random tokens its induction score is 0.71, and no other head is above 0.04.
Heads 1.0 to 1.5 change a lot without either kind of term, between 0.44 and 0.81. So almost every layer-1 head uses Q- or K-composition in some way. This measure cannot say whether that use is useful for the predictions, only that the pattern depends on it.
Read it left to right. On top is the size of the combined map "head h1 writes, then head h2 reads the result as a key". Below are the sizes of the two maps on their own. The ratio can never be more than 1, because the norm of a product is at most the product of the norms.
A toy example. First the norm. For the matrix [1220], the norm is 1+4+4+0=3. Now take a stream of three numbers. Head 1 reads dimension 1 and writes it, doubled, into dimension 3. So its WOV has a single entry 2, in row 3 and column 1. Head 2's query reads dimension 1 and its key reads dimension 3: WQK has a single 1, in row 1 and column 3.
The product WQKWOV has a single entry 2, so its norm is 2. The norms of the two factors are 1 and 2. The K-composition score is 2/(1⋅2)=1: perfect alignment. The Q-composition score uses WQK⊤, whose query side reads dimension 3. The product is zero, so the score is 0. A third head whose key reads dimension 2 also scores 0, because head 1 never writes there.
On the real model. Our script computes all three scores for all 64 pairs of heads. For the random baseline, it averages the score of 200 pairs of random 512 × 512 matrices of rank 64:
plain text
== 4. COMPOSITION SCORES ||W_A W_B||_F / (||W_A||_F ||W_B||_F) (paper shapes, layer-0 head h1 -> layer-1 head h2) random baseline (two random rank-64 512 x 512 matrices, mean of 200): 0.0442 Q-composition: max 0.084 (head 1.3 reads head 0.4), mean 0.042, pairs above baseline + 0.05: 0 of 64 K-composition: max 0.124 (head 1.6 reads head 0.3), mean 0.040, pairs above baseline + 0.05: 1 of 64 V-composition: max 0.055 (head 1.1 reads head 0.5), mean 0.029, pairs above baseline + 0.05: 0 of 64 layer-0 heads, mean attention to the previous token: 0.0 0.19, 0.1 0.06, 0.2 0.04, 0.3 0.49, 0.4 0.02, 0.5 0.15, 0.6 0.08, 0.7 0.01
Q-, K- and V-composition scores between every layer-0 head (columns) and every layer-1 head (rows) of attn-only-2l, minus the random baseline of 0.044. The brightest cell is in the K-composition map: head 1.6 reading head 0.3, with a score of 0.124. The column of head 0.4 is a little above the baseline in the Q and K maps.
The picture repeats the paper's story on a different model. Most scores sit near the random baseline of 0.044. Only one pair stands clearly above it: head 1.6 reading head 0.3, with K-composition 0.124. Head 0.3 is the head that attends most to the previous token, with a mean weight of 0.49. Head 1.6 is the induction head we met above. So the weights alone point to a previous-token head feeding the keys of an induction head.
There is a second, weaker signal in the maps: the column of head 0.4. Averaged over the eight layer-1 heads, its Q-composition is 0.069 and its K-composition 0.068. Every other layer-0 head stays at or below 0.046. No single cell of that column passes our threshold, but the whole column sits above the baseline.
The functional test of the previous section agrees. Remove the K term of head 0.4 alone, and the patterns of six layer-1 heads change by 0.35 to 0.46. Head 0.4 puts 65% of its weight on the start token, so much of what it writes is the same at every position. Many layer-1 heads seem to use that vector to tune their scores. Our tests show that they depend on it, not what they use it for.
plain text
Q-composition, mean over the 8 layer-1 heads, per layer-0 head: 0.0 0.029, 0.1 0.041, 0.2 0.040, 0.3 0.029, 0.4 0.069, 0.5 0.046, 0.6 0.040, 0.7 0.044 K-composition, mean over the 8 layer-1 heads, per layer-0 head: 0.0 0.027, 0.1 0.035, 0.2 0.034, 0.3 0.035, 0.4 0.068, 0.5 0.037, 0.6 0.038, 0.7 0.041
The paper chose to ignore the virtual heads because it saw no V-composition. It then checks that choice with an experiment.
Our script does the same thing in a different way. It already has every term from the path expansion, so it just adds up the orders it wants, and unembeds the sum. With frozen patterns and frozen layer-norm scales, this gives the same partial models.
The marginal effect of order n is a simple difference:
Δn=L≤n−L≤n−1
Here L≤n is the loss when all terms up to order n are kept. For order 0, the paper compares with the uniform guess. A negative Δn means order n helps. The paper also divides by the number of terms, to get the help per term.
On the real model. Here is the same table for attn-only-2l on our 3,072 tokens:
plain text
== 3. TERM IMPORTANCE: LOSS WHEN ONLY SOME TERMS ARE KEPT (nats per token, lower is better) uniform guess over 48,262 tokens: ln(48262) = 10.78 order 0 (direct path only): 10.11 marginal -0.68 (-0.68 per term, 1 term) order <= 1 (+ 16 single-head terms): 6.06 marginal -4.04 (-0.253 per term) order <= 2 (+ 64 virtual heads): 4.93 marginal -1.13 (-0.0177 per term) the model itself: 4.93
The loss drop from each order of terms. Top, the paper's model: direct path 1.8 nats, single heads 5.2, virtual heads 0.3. Bottom, our run of attn-only-2l on 3,072 WikiText tokens: direct path 0.68, single heads 4.04, virtual heads 1.13.
The broad shape is the same: the single-head terms do most of the work, with 4.04 of the 5.85 nats. The full sum gives 4.93 nats, exactly the model's own loss, which is one more check on the expansion.
Two numbers differ from the paper. The direct path helps less, only 0.68 nats. One reason is our method: the final layer-norm scale is frozen at the full model's value. A partial sum is smaller than the full stream, so its logits come out flatter. The paper does not say how it handled layer norm here.
The second difference matters more. In our model the virtual heads give 1.13 nats, not 0.3. That is nearly four times the paper's number, from fewer virtual heads (64 instead of 144). So the claim "virtual heads do not matter" does not carry over to this model as it stands. The next measurements show where this comes from.
plain text
largest virtual heads (h2 o h1): 1.7 o 0.7 +0.723, 1.7 o 0.3 +0.334, 1.7 o 0.4 +0.298, 1.7 o 0.5 +0.288, 1.7 o 0.2 +0.200 virtual heads: 51 of 64 above +0.01; median +0.0364 virtual heads split by what the layer-0 head read (check 1.0e-05): from the start token -0.90, from real tokens -0.07 (each added alone to order <= 1)
Removing one virtual head at a time, the largest is 1.7 ∘ 0.7: without it, the loss rises by 0.72. All five largest run through head 1.7. Now split every layer-0 head's output into two parts: what it copied from the start token, and what it copied from real tokens. The virtual heads that start from the start token give 0.90 nats. The virtual heads that start from real tokens give only 0.07.
Adding all 64 virtual heads to the direct path and single heads lowers the loss by 1.13 nats. The part that a layer-0 head copied from the start token gives 0.90 nats. The part copied from real tokens gives only 0.07 nats.
So the large number has a plain cause. Many layer-0 heads park their attention on the start token: head 0.7 puts 60% of its weight there. The start token is the same in every sequence, so such a head writes a nearly fixed vector. Head 1.7 then moves that vector again. This acts like a learned bias delivered through two heads, not like a rule about the text. Virtual heads that carry real token information give 0.07 nats, which is close to the paper's small number.
Our model shows the same split:
plain text
head terms split by layer: layer-0 heads: -0.26 vs direct path only, -0.26 vs direct path + layer-1 heads layer-1 heads: -3.78 vs direct path only, -3.78 vs direct path + layer-0 heads
The layer-1 heads give 3.78 nats and the layer-0 heads only 0.26. In our model, the two numbers of each row happen to be almost the same. In the paper's model they differ a lot for the first layer, so there the two layers overlap in what they do.
The paper ends the section with a closer look at the term it decided to skip.
Is Ah2Ah1 really an attention pattern? Each row of Ah1 adds up to 1, and so does each row of Ah2. A row of the product is a weighted average of rows of Ah1, with weights that add up to 1. So it also adds up to 1. And if neither head looks into the future, neither does the product. On the real model, the rows of 1.7 ∘ 0.7 add up to 1 to within 0.0000005, and its future entries are 0.
Two previous-token patterns on four tokens, and their product. In each of the first two, every token reads the one before it (token 1 reads itself). In the product, token 3 reads token 1 and token 4 reads token 2: the virtual head looks two tokens back. Every row still adds up to 1.
In our model the guess is only partly true. Head 0.0 itself puts 0.23 of its weight two tokens back, so this model does have a head that looks two back. The virtual head that looks two back the most is 1.4 ∘ 0.0, with only 0.12, and removing it costs just 0.02 nats.
plain text
most important: 1.7 o 0.7: pattern [0.76, 0.0, 0.0, 0.0]; rows add up to 1 (max |sum - 1| 4.8e-07); future entries 0; V-composition score 0.036; removing it: loss +0.723 most two-back: 1.4 o 0.0: pattern [0.35, 0.03, 0.09, 0.12]; rows add up to 1 (max |sum - 1| 7.2e-07); future entries 0; V-composition score 0.015; removing it: loss +0.021
Each pattern list gives the mean weight on the start token, on the token itself, one back and two back. The most important virtual head, 1.7 ∘ 0.7, puts 0.76 on the start token and almost nothing anywhere else. That is the start-token path we found above, now seen as a single virtual head.
The paper's next section takes up the one kind of composition that really matters in its two-layer model: the K-composition behind induction heads. That is Part 6.
Run it yourself
The two scripts behind this part are circuits_part5.py (the path expansions, term importance, composition scores and virtual heads on attn-only-2l) and circuits_part5_toy.py (the hand-sized examples). The first one uses the WikiText-2 test set from Hugging Face and runs on the CPU in a few minutes.