Circuits, explained · Part 4 of 7 · Covers Splitting Attention Head terms into Query-Key and Output-Value Circuits, Interpretation as Skip-Trigrams, Summarizing OV/QK Matrices, Do We Fully Understand One-Layer Models?
One-Layer Models: Skip-Trigrams, Copying and Their Bugs
What a one-layer attention-only model really learns, read from its weights: skip-trigrams, copying, heads that only look at position, the bugs of a factored form, and eigenvalues as a test for copying.
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 attention-only model looks like a black box, but the paper shows that it is a very large table of word patterns. We read that table out of the weights of a real model, and find both what it does well and the mistakes it cannot avoid.
Part 3 wrote the logits of a one-layer model as a sum of separate terms. One term is the direct path from Part 1: the bigram table WUWE. Then there is one term for each head. Here is the term of head h, in the paper's notation:
Ah⊗(WUWOVhWE),Ah=softmax∗(t⊤WE⊤WQKhWEt)
Read it from the inside. The vector t holds the tokens of the text, one one-hot vector per position. The matrix Ah is the attention pattern of the head: which position reads which. The bracket on the left is what reading a token does to the logits. So the term says: "read some earlier tokens with weights Ah, and let each token you read change the logits through one fixed table."
Part 2 met the two tables inside this formula. The paper now names them and makes them the main object of study.
The paper then adds one sentence that is easy to read past. It is the key to the bugs later in this part.
Part 3 also explains why we may study the two circuits apart: freeze the attention pattern, and the rest of the model is linear. Here we use the two circuits as tables and read them.
Part 1 showed that a model with zero layers can only learn bigrams: "after token A, token B is likely". A one-layer model can do more. The paper gives the new pattern a name.
Here is one skip-trigram at work in our model, attn-only-1l. The prompt ends with a quote mark and the letter "R". Without a space in front, the name "Ralph" is cut into two tokens, "R" and "alph". We come back to this later in the part.
The prompt "My friend Ralph called me today", a new line, a quote mark and "R". Head 0.0 stands on the last token "R" (the destination). Its QK side reads back to " Ralph" (the source) with weight 0.59. Its OV side then raises the logit of the piece "alph" (the out token) by 8.18.
How much does a source push an out token? Use the radio picture: the volume times the song. Write a for the source, b for the destination and c for the out token:
Δlogitc=Ab←ahMc,ah,Mh=WUWOVhWE
Read it left to right. Δlogitc is how much the head raises the logit of token c. Ab←ah is the attention weight from the destination to the source; it comes from the QK circuit through the softmax. Mc,ah is one entry of the OV circuit: row c, column a. If a head reads several earlier tokens, their pushes add up.
A tiny example. Say the head gives weight 0.5 to the source, and the OV entry is 4. The push on the out token is 0.5×4=2. If the head gives weight 0.1 to the same source, the push is 0.4. The volume changes, the song does not.
On the real model. On our prompt, head 0.0 at "R" gives 0.59 of its attention to " Ralph". The whole head raises the logit of "alph" by 8.18. The other seven heads add between −0.24 and +1.67. This is our script's output:
plain text
tokens: ['<|BOS|>', 'My', ' friend', ' Ralph', ' called', ' me', ' today', '.', '\n', '"', 'R'] each head's push on the logit of "alph" at the last token: 0.0: +8.18, 0.1: +1.60, 0.2: -0.18, 0.3: +0.16, 0.4: +0.27, 0.5: +0.60, 0.6: +1.67, 0.7: -0.24 head 0.0 at "R" puts 0.59 of its attention on " Ralph"
The paper then warns that "readable" does not mean "easy".
How big is the pile for our model? Our script counts it:
plain text
== 1. HOW BIG THE EXPANDED CIRCUITS ARE (attn-only-1l) vocabulary 48,262 tokens, 8 heads; one expanded circuit is 48,262 x 48,262 = 2.33 billion numbers QK + OV for all 8 heads: 37.3 billion numbers; the model stores 51.0 million weights
The model stores 51.0 million numbers. Written out as cards, the same model is 37.3 billion numbers, more than 700 times as many. Nothing new was added. The cards are a different way to write the same weights, and a much longer one.
With the cards in hand, the paper looks at the largest entries. The first thing it finds is copying.
The paper shows examples as a table. Each row fixes one source token. It lists the destinations that attend to it most (QK) and the out tokens it raises most (OV).
We can make this table for our own model. For each source token, our script takes one column of each circuit of a head and lists the top entries. As in Part 2, positions and biases are left out.
plain text
== 2. SKIP-TRIGRAMS READ FROM THE WEIGHTS: fix a source, top destinations (QK) and top out tokens (OV) head 0.0 source ' perfect' dest [' looks', 'Exper', ' look', ' equally', "'),"] out ['sembled', 'perfect', ' perfect', ' perfection', 'Perfect'] head 0.0 source ' Ralph' dest ['R', 'Van', ' Van', ' van', 'J'] out ['alph', ' MERCHANTABILITY', ' Ralph', ' deprec', ' deduction'] head 0.6 source ' couldn' dest [' they', ' we', ' technicians', ' They', ' We'] out [' couldn', ' hadn', ' wasn', ' didn', ' seemed'] head 0.7 source ' Paris' dest ['Sche', 'Pal', ' protector', '()`', ' Dou'] out ['Germ', ' Frankfurt', ' Amsterdam', ' Milan', ' Lyon']
Our version of the paper's table, for attn-only-1l. Head 0.0 reads " perfect" and raises "perfect"; it reads " Ralph" and raises "alph". Head 0.6 reads " couldn" from " they" or " we" and raises " couldn", " hadn", " wasn". Head 0.7 reads " Paris" and raises other cities, such as Frankfurt and Amsterdam.
Several rows look like the paper's. Head 0.0 reads " perfect" from the destination " looks" and raises " perfect": "a perfect ... looks perfect". It reads " large" and raises "large" and "small", the same pair as in the paper (that row of our output is in the full log). Head 0.6 raises " couldn" and its cousins " hadn", " wasn", " didn". Head 0.7 does not copy the word itself. It reads " Paris" and raises other European cities.
The destination columns are noisier. Some make sense (" they" and " we" before " couldn"), and some are pieces of code. This is the warning of the last box: scores of different destinations are not on one scale.
Does the copying show in the predictions of the whole model? We changed one word of a prompt and measured the probability of the next token:
plain text
== 3. SKIP-TRIGRAMS AT WORK IN THE WHOLE MODEL (probability of the next token) 'My friend Ralph called me today.\n"R' p('alph') = 0.8278 | 'My friend Bob called me today.\n"R' p('alph') = 0.0203 'We visited Ralph and his dog.\n"R' p('alph') = 0.7899 | 'We visited Tom and his dog.\n"R' p('alph') = 0.0154 'The perfect gift for her. It looks' p(' perfect') = 0.0492 | 'The nice gift for her. It looks' p(' perfect') = 0.0030
With "Ralph" earlier in the text, the model gives "alph" a probability of 0.83 after "R". With "Bob" instead, it gives 0.02. A single earlier word moved the guess by a factor of 40. With "perfect" earlier, " perfect" after "It looks" goes from 0.0030 to 0.0492, 16 times higher.
Copying is the most common pattern, but not the only one. The paper lists skip-trigrams for Python, HTML, LaTeX, URLs and English. Two of them:
Our model knows the idioms too. "You need to keep this in" gets " mind" with probability 0.64. "You need to keep the dogs at" gets " bay" with probability 0.32, and " bay" is its first choice.
So far each head chose its source by content. Some heads choose by distance.
We can find positional heads in our model without any text. The QK circuit has a part that comes from the position embeddings alone. Our script feeds the head only the position embeddings, with the query at position 200, and reads the attention it gives to each earlier position. We also measured the real pattern on 1,000 tokens of text, the start of Part 1 of this series.
plain text
attention from the position embeddings alone (query at position 200; weight on the same token, 1 back, 2 back): head 0.1: [0.913, 0.087, 0.0] head 0.2: [0.557, 0.334, 0.071] head 0.5: [0.936, 0.064, 0.0] head 0.7: [0.88, 0.119, 0.001]
Attention weight by distance for the eight heads. Left: from the position embeddings alone, heads 0.1, 0.5 and 0.7 put 0.91, 0.94 and 0.88 on the token itself and the rest on the token before; head 0.2 spreads over 0, 1 and 2 back. Right: on real text, the same four heads put the most weight one token back (head 0.5: 0.26), while heads 0.0, 0.3, 0.4 and 0.6 put about 0.02 or less on each near distance.
Four of the eight heads lean on position, and three of them match the paper's example: "the present token or the previous token". On real text the content also counts, and the start-of-text token takes some weight. Still, head 0.5 gives 0.16 to the token itself and 0.26 to the token before, on average. The copying heads 0.0, 0.3 and 0.4 give these near tokens less than 0.03.
What do the positional heads write? Their OV circuits look like the paper's table:
plain text
OV of head 0.5, source ' corresponding' -> [' to', 'rians', ' thereto', 'ements', 'ments'] OV of head 0.5, source ' couldn' -> ['t', 'yon', 'pute', 'itate', 'receive'] OV of head 0.1, source ' couldn' -> [' thereof', ' resist', ' guessed', ' blame', 'って']
Head 0.5 reads " corresponding" and raises " to", the same pair as in the paper. It reads " couldn" and raises "t", the next piece of "couldn't". Head 0.1 raises " resist" for " couldn", as in the paper, though its top entry " thereof" is noise.
The attention cancels. The ratio between the two pushes depends only on the source, never on the destination. That is the bug in one line.
A tiny example. Let the OV column of " keep" raise " mind" by 2 and " bay" by 6. After " in", the head reads " keep" with weight 0.5. After " at", it reads " keep" with weight 0.8. Then:
Left: what the text needs. After "keep ... in", mind should be high and bay low; after "keep ... at", the other way round. Right: what one head gives with toy numbers. After "in": mind 0.5 × 2 = 1.0 and bay 0.5 × 6 = 3.0. After "at": mind 1.6 and bay 4.8. Bay is three times mind in both rows.
The head can change how loud it is, but not which idiom it prefers. If it is tuned for "keep at bay", it also says "bay" after "keep in".
On the real model. Our head 0.2 has exactly the paper's " keep" row. Here is its OV column for " keep" and its QK column for " keep":
The OV column is a list of idiom endings: "in the forefront", "at bay", "on your toes", "in mind", "under wraps". The typical entry is about zero, and these sit far above it. The QK column likes " in", " at", " on" and " under", and even more " going" and " looking" ("keep going", "keep looking").
Now the bug. We ran two prompts and measured what head 0.2 adds through the " keep" position alone:
plain text
'You need to keep this in': head 0.2 puts 0.27 on " keep"; through " keep" it pushes mind +1.78, bay +5.28 (ratio bay/mind 2.96); whole head: mind +3.17, bay +4.76 whole model: p(mind) = 0.6416, p(bay) = 0.000014; top 3 [' mind', ' the', ' your']; head 0.7 pushes mind +4.45 'You need to keep the dogs at': head 0.2 puts 0.38 on " keep"; through " keep" it pushes mind +2.43, bay +7.19 (ratio bay/mind 2.96); whole head: mind +2.49, bay +7.49 whole model: p(mind) = 0.0000, p(bay) = 0.318783; top 3 [' bay', ' the', ' home']; head 0.7 pushes mind +2.03
Head 0.2's push through the source " keep". In "You need to keep this in", it reads keep with weight 0.27 and pushes mind by 1.78 and bay by 5.28. In "You need to keep the dogs at", it reads keep with weight 0.38 and pushes mind by 2.43 and bay by 7.19. The ratio bay / mind is 2.96 in both prompts.
This is the factored form, measured. The ratio of the two pushes is 2.96 in both prompts, to two decimals. The destination changed the volume (0.27 or 0.38), but not the song. After "keep this in", head 0.2 raises the wrong idiom: " bay" by 5.28 and " mind" by only 1.78.
Yet the whole model gets it right: " mind" has probability 0.64 and " bay" only 0.000014. Other paths fix the bug. The bigram table knows that "in mind" is common and "in bay" is rare. Head 0.7 also pushes " mind" by 4.45. This is the "fungibility" warning in action: to judge one head, you must look at the rest of the model too.
The paper calls these bugs an early example of "using interpretability to understand model failures". The mistake was predicted from the shape of the weights, before running any text. Our run adds one detail: the bug is real in the head, but the rest of the model can hide it.
The exact meaning of "comes back out" is an eigenvector.
A tiny example. Take a vocabulary of two tokens, " cat" and " Cat". A head reads one of them and raises it by 2, and the other one by 1. Its OV circuit is
M=[2112]
Try the vector v=(1,1), "both spellings together". Then Mv=(2+1,1+2)=(3,3)=3v. So v is an eigenvector with eigenvalue 3. The other eigenvector is (1,−1) with eigenvalue 1. Both eigenvalues are positive: this head copies.
Now a head that swaps: reading " cat" raises " cats", and reading " cats" raises " cat". Its matrix is [0110], with eigenvalues +1 and −1. The vector (1,1) is kept, but (1,−1) is flipped. This head does not copy; it moves between two tokens. A third head, [01−10], raises token 2 when it reads token 1 and lowers token 1 when it reads token 2. Its eigenvalues are +i and −i, complex, because the matrix turns vectors instead of stretching them.
The paper collapses all the eigenvalues of a head into one number. Add up the eigenvalues, and divide by the sum of their sizes:
copy score=∑i∣λi∣∑iλi
Read it this way. The bottom adds up the sizes of the eigenvalues. The top adds them with their signs, so positive and negative ones cancel, and complex pairs x±iy lose their imaginary parts. If every eigenvalue is positive, top and bottom are equal and the score is 1. If every one is negative, the score is −1. A random mix gives about 0.
For our three small heads: the copying head has eigenvalues 3 and 1, so the score is (3+1)/(3+1)=1. The swap head has +1 and −1: (1−1)/(1+1)=0. The rotating head has +i and −i: the top is 0, so the score is 0.
A real OV circuit is 48,262 × 48,262. Finding all its eigenvalues directly would take a very long time. But the circuit is a product of a tall and a wide matrix, and a fact of linear algebra makes it small:
M=48,262×64(WUWO)64×48,262(WVWE),non-zero eigenvalues of PQ=non-zero eigenvalues of QP
Here P=WUWO and Q=WVWE. The product QP is only 64 × 64. So the 48,262 × 48,262 circuit has 64 eigenvalues that matter, and all the others are zero.
A tiny example. Let P=(1,2,0) as a column and Q=(1,1,1) as a row. Then PQ is a 3 × 3 matrix with rows (1,1,1), (2,2,2) and (0,0,0). Its eigenvalues are 3, 0 and 0. And QP=1+2+0=3, a 1 × 1 matrix with the single eigenvalue 3. The same non-zero eigenvalue, from a much smaller matrix.
On the real model. Our script checks the trick on a piece of head 0.0 small enough to do both ways: 300 tokens.
plain text
check on a 300-token piece of head 0.0: 300 x 300 has 300 eigenvalues; the 64 largest match the 64 x 64 ones to 3.8e-13; the other 236 are at most 6.8e-14
The 64 large eigenvalues agree to 0.0000000000004, and the other 236 are zero up to rounding. So the 64 × 64 matrix gives the full answer.
We drew the same picture for our model, with one random head added for comparison:
The 64 non-zero eigenvalues of the OV circuit of each head of attn-only-1l, and of one random head. Dots on the right are positive. Heads 0.0, 0.3, 0.4, 0.6 and 0.7 have nearly all their dots on the right (copy scores +1.00, +1.00, +0.97, +0.91, +0.97); head 0.2 has most (+0.71); heads 0.1 and 0.5 lean left (−0.42, −0.37); the random head is spread evenly (about 0).
Here is our model, all eight heads:
plain text
head 0.0: copy score sum(l)/sum(|l|) = +0.998; 64/64 with positive real part (26 real and positive, 38 complex); trace 353,117 = sum of diagonal 353,117 head 0.1: copy score sum(l)/sum(|l|) = -0.418; 21/64 with positive real part (3 real and positive, 54 complex); trace -26,596 = sum of diagonal -26,596 head 0.2: copy score sum(l)/sum(|l|) = +0.707; 42/64 with positive real part (14 real and positive, 46 complex); trace 93,844 = sum of diagonal 93,844 head 0.3: copy score sum(l)/sum(|l|) = +1.000; 64/64 with positive real part (16 real and positive, 48 complex); trace 358,507 = sum of diagonal 358,507 head 0.4: copy score sum(l)/sum(|l|) = +0.974; 61/64 with positive real part (19 real and positive, 44 complex); trace 235,602 = sum of diagonal 235,602 head 0.5: copy score sum(l)/sum(|l|) = -0.365; 15/64 with positive real part (5 real and positive, 52 complex); trace -19,782 = sum of diagonal -19,782 head 0.6: copy score sum(l)/sum(|l|) = +0.915; 61/64 with positive real part (13 real and positive, 50 complex); trace 155,782 = sum of diagonal 155,782 head 0.7: copy score sum(l)/sum(|l|) = +0.970; 57/64 with positive real part (17 real and positive, 46 complex); trace 121,358 = sum of diagonal 121,358 20 random Gaussian heads of the same shapes: copy score from -0.035 to +0.070 (mean -0.002); positive real part 29 to 35 of 64; complex 54 to 60 of 64
The copy score of every head of attn-only-1l: 0.0 +1.00, 0.3 +1.00, 0.4 +0.97, 0.7 +0.97, 0.6 +0.91, 0.2 +0.71, 0.5 −0.37, 0.1 −0.42. The thin dashed band around zero shows 20 random heads of the same shapes, from −0.035 to +0.070.
Six of our eight heads score above 0.7, and four score 0.97 or more. Twenty random heads of the same shapes all score between −0.035 and +0.070. Training clearly pushed most heads towards copying, as in the paper's model.
The two heads with negative scores, 0.1 and 0.5, are two of the positional heads from earlier. They do not copy: they look one token back and predict what comes next, like "couldn" → "t". This matches the paper's picture, where most heads copy and a few do something else.
A tiny example. Take this OV circuit of two tokens:
M=[−1−164]
Its eigenvalues are 2 and 1, both positive, so its copy score is 1. You can check one: M(2,1)=(−2+6,−2+4)=(4,2)=2(2,1). But look at the first column: reading token 1 gives the logits (−1,−1). Token 1 lowers its own logit. The two eigenvectors, (2,1) and (3,1), point almost the same way: the angle between them is only 8.1 degrees. That narrow angle is what lets the matrix "copy on average" while one token does the opposite.
The fact is easy to check. For our copying toy, the diagonal is (2,2) with sum 4, and the eigenvalues are 3 and 1, also sum 4. For the pathological toy, −1+4=3=2+1. For the real heads, the printout above shows the trace of the 64 × 64 matrix. Next to it is the sum of all 48,262 diagonal entries of the full circuit. They agree in every head: for head 0.0, both are 353,117.
Our script also computed the other measures:
Head
Copy score
Diagonal > 0
Top-1 self
Top-5 self
0.0
+0.998
0.99
0.37
0.51
0.1
−0.418
0.37
0.00
0.00
0.2
+0.707
0.91
0.00
0.01
0.3
+1.000
0.98
0.34
0.43
0.4
+0.974
0.96
0.26
0.34
0.5
−0.365
0.38
0.00
0.00
0.6
+0.915
0.92
0.12
0.21
0.7
+0.970
0.96
0.00
0.02
"Diagonal > 0" is the share of all 48,262 tokens that raise their own logit. "Top-1 self" and "top-5 self" use tokens 1,000 to 2,999. They ask: is the token itself the first, or among the first five, tokens it raises?
Most measures agree, but look at head 0.7. Its copy score is 0.97, and 96% of tokens raise themselves. Yet a token is almost never its own top output: top-1 is 0.00. Earlier we saw why: on " Paris", head 0.7 raises Frankfurt, Amsterdam and Milan, not Paris. It copies the kind of token, "a city", not the token. This is exactly the paper's warning that copying "is actually a more complex concept than it might first appear".
The paper ends its study of one-layer models with an honest question.
Next, in Part 5: a second layer lets heads read what earlier heads wrote. The paper calls this composition and finds three kinds of it: Q-, K- and V-composition. Part 6 then shows the circuit that composition makes possible, the induction head, the stronger form of in-context learning promised in this part.
Run it yourself
The two scripts behind this part are circuits_part4.py (every real-model number: the size of the circuits, the skip-trigram tables, the prompts, the positional heads, the "keep" bug, the 64 × 64 trick, the eigenvalues and the other copying measures) and circuits_part4_toy.py (the hand-sized examples). They use TransformerLens 3.x and attn-only-1l on the CPU. The positional measurement reads 1,000 tokens of part1.md, so its numbers move a little if that page changes.