Circuits, explained · Part 6 of 7 · Covers Analyzing a Two-Layer Model, Induction Heads (function, how they work, checking the theory), the correction to the composition diagram, Pointer Arithmetic with Positional Embeddings

Induction Heads

How a two-layer model learns to continue a pattern it saw earlier in the text. We find the two heads that do it in a real model, test them on random tokens, and break them on purpose.

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.

Read "Mrs Brindlewick" once, and you can finish "Mrs Br..." the next time it appears. A two-layer transformer learns to do the same with two attention heads that work as a team. This part takes that team apart, in the paper's order and on a real model.

The paper calls the second head of the team an induction head. It is the best-known result of the paper, and later work found the same kind of head in large models. We use the open two-layer model attn-only-2l from the earlier parts. Each piece below shows the exact lines of the paper, then explains them.

The model we study

The paper's two-layer model has 12 heads per layer and no position vectors in the stream. We cannot get those weights. Our model is the open attn-only-2l: two attention layers, eight heads per layer, and no MLP layers. It has one difference that matters later: it adds a learned position vector to the stream of each token.

plain text
== 1. THE MODEL: attn-only-2l (TransformerLens)
  layers 2, heads per layer 8, d_model 512, d_head 64, vocabulary 48,262, context 1024
  position: standard (learned position vectors added to the stream; the paper's model has none in the stream)
  the paper's two-layer model has 12 heads per layer; ours has 8, so head numbers differ

We name heads as in Part 2: head 0.3 is head 3 of layer 0, and head 1.6 is head 6 of layer 1. Head numbers in our model have no link to the numbers in the paper's model.

A narrow mystery

Part 5 showed three ways in which a layer-1 head can read what a layer-0 head wrote. The paper now looks at one real two-layer model and asks which of those ways it uses.

The paper shows the connections as a diagram. Each dot is a head, and a line joins two heads when a layer-1 head reads strongly from a layer-0 head.

We measured the same score on our model, from the previous-token head into the head we will call the induction head. Part 5 defines it. Here it is enough to know that random matrices of the same shapes score 0.044:

plain text
== 7. K-COMPOSITION SCORE ||W_QK^h W_OV^p||_F / (||W_QK^h||_F ||W_OV^p||_F), INTO THE INDUCTION HEAD
  into 1.6: 0.0 0.081, 0.1 0.067, 0.2 0.038, 0.3 0.124, 0.4 0.058, 0.5 0.044, 0.6 0.040, 0.7 0.045
  the same measure for random rank-64 matrices of these shapes: 0.044 (average of 20)

Head 0.3 scores 0.124, almost three times the random level. Head 0.0 is second with 0.081. The other layer-0 heads are near the random level. So our model has the same shape as the paper's: one main layer-0 head feeds the key of head 1.6.

Looking at the patterns

The paper then invites the reader to look at the attention patterns on the first paragraph of a well-known novel. It shows a special kind of pattern there.

What an induction head does

An induction head on "... Mrs Brindlewick ... Mrs Br" (two steps, two layers)MrsBrindlewick...MrsBr?Layer 0, previous-token head: each token gets a note "the token before me was ...".note: "Br"query: "Br"Layer 1, induction head: the query "Br" matches the note "Br", so it attends to "indle"and its OV circuit copies "indle": the prediction for "?" is "indle".
The two heads of an induction circuit on a made-up name. In layer 0, a previous-token head gives each position a note that says which token came before it; the position of "indle" gets the note "Br". In layer 1, the induction head stands on the second "Br". Its query looks for the note "Br", finds it at "indle", attends there, and copies "indle" as the prediction.

Why this beats a copying head

Our sentence with a made-up name shows the difference. The tokenizer splits " Brindlewick" into " Br", "indle" and "wick". We gave both models the text "The new teacher was Mrs Brindlewick. On Monday the class met Mrs Brindlewick", and looked at the second copy of the name:

plain text
== 10. A MADE-UP NAME IN A SENTENCE
  attn-only-1l: at ' Mrs' (pos 15) the next token ' Br' gets probability 0.01
  attn-only-1l: at ' Br' (pos 16) the next token 'indle' gets probability 0.82
  attn-only-1l: at 'indle' (pos 17) the next token 'wick' gets probability 0.00
  attn-only-2l: at ' Mrs' (pos 15) the next token ' Br' gets probability 0.63; head 1.6 reads '<|BOS|>' (pos 0) with 0.55, adds +4.84 to the logit of ' Br'
  attn-only-2l: at ' Br' (pos 16) the next token 'indle' gets probability 0.93; head 1.6 reads 'indle' (pos 7) with 0.93, adds +11.95 to the logit of 'indle'
  attn-only-2l: at 'indle' (pos 17) the next token 'wick' gets probability 0.98; head 1.6 reads 'wick' (pos 8) with 0.75, adds +13.82 to the logit of 'wick'

The two-layer model gets all three pieces: 0.63, 0.93 and 0.98. On "indle", head 1.6 reads the earlier "wick" with weight 0.75 and adds +13.82 to the logit of "wick". That is the rule from the paper, exactly.

The one-layer model gets "indle" right (0.82), which is a skip-trigram at work: "indle" appeared earlier, so a copying head raised it. But it gives "wick" a probability of 0.00. Its copying heads cannot tell which earlier token is the one to repeat.

The first row of the two-layer model is less clean. On " Mrs", head 1.6's largest single weight (0.55) is on the first token, its parking spot. Yet it still adds +4.84 to " Br". Our log does not show where the rest of its weight went, so we only report what it shows.

The paper's examples

The hardest test: random tokens

Text from a novel has a weakness as evidence. A name like "Dursley" is also a common pattern in training text, so other heads may help. The paper removes that help.

Our run

We built 100 sequences. Each is the first token, then 50 random tokens, then the same 50 tokens again. We measure the loss of each model on each copy.

The loss of one token is

ℓ=−ln⁡pright\ell=-\ln p_{\text{right}}

Here prightp_{\text{right}} is the probability the model gave to the token that really came next. Take three small cases by hand. If p=0.5p=0.5, the loss is ln⁡2≈0.69\ln 2\approx0.69. If p=0.05p=0.05, it is about 3.0. A model that guesses evenly over our 48,262 tokens gives each one p=1/48,262p=1/48{,}262, so its loss is ln⁡48,262≈10.78\ln 48{,}262\approx10.78.

python
logits = model(toks)                                        # toks: [100, 101] = BOS + 50 random + the same 50
loss = -logits.log_softmax(-1)[:, :-1].gather(-1, toks[:, 1:, None])[..., 0]
first, second = loss[:, 1:50].mean(), loss[:, 51:].mean()    # first copy, second copy
plain text
== 2. REPEATED RANDOM TOKENS: [BOS] t1 ... t50 t1 ... t50, 100 sequences, tokens drawn uniformly from the vocabulary
  a model that guesses uniformly over 48,262 tokens has loss ln(48262) = 10.78 nats on every token
  attn-only-1l: loss on the first copy 15.52, on the second copy 14.83 nats; top-1 right: first 0.0%, second 0.0%
  attn-only-2l: loss on the first copy 15.04, on the second copy 3.41 nats; top-1 right: first 0.0%, second 55.9%
  probability the 2-layer model gives the right token on the second copy, on average: exp(-3.41) = 0.03

On the first copy both models do worse than an even guess: about 15 nats against 10.78. They are sure of next tokens that real text would have, and random text punishes that. On the second copy the one-layer model barely moves, from 15.52 to 14.83. The two-layer model drops to 3.41 nats, and its top guess is right 55.9% of the time.

Read 3.41 back as a probability: e−3.41≈0.03e^{-3.41}\approx0.03. That number is an average of logarithms, so it is pulled down by the hard tokens. More than half of the tokens are the top guess, which shows that most predictions are much better than 0.03.

051015201255175100one layer, 1: 14.6one layer, 4: 15.4one layer, 7: 15.6one layer, 10: 15.6one layer, 13: 15.7one layer, 16: 15.6one layer, 19: 15.6one layer, 22: 15.7one layer, 25: 15.6one layer, 28: 15.5one layer, 31: 15.6one layer, 34: 15.4one layer, 37: 15.4one layer, 40: 15.2one layer, 43: 15.5one layer, 46: 15.4one layer, 49: 15.1one layer, 52: 14.6one layer, 55: 14.5one layer, 58: 14.7one layer, 61: 14.6one layer, 64: 14.8one layer, 67: 14.7one layer, 70: 14.8one layer, 73: 15.1one layer, 76: 14.9one layer, 79: 14.9one layer, 82: 14.8one layer, 85: 14.9one layer, 88: 14.8one layer, 91: 14.9one layer, 94: 14.9one layer, 97: 14.9one layer, 100: 14.8two layers, 1: 14.9two layers, 4: 15.8two layers, 7: 15.7two layers, 10: 15.4two layers, 13: 15.4two layers, 16: 15.3two layers, 19: 15.2two layers, 22: 15.3two layers, 25: 15.1two layers, 28: 14.9two layers, 31: 14.8two layers, 34: 14.8two layers, 37: 14.5two layers, 40: 14.5two layers, 43: 14.7two layers, 46: 14.6two layers, 49: 14.3two layers, 52: 9.1two layers, 55: 4.0two layers, 58: 3.6two layers, 61: 3.2two layers, 64: 3.4two layers, 67: 3.5two layers, 70: 3.6two layers, 73: 3.4two layers, 76: 3.3two layers, 79: 3.1two layers, 82: 3.1two layers, 85: 3.1two layers, 88: 2.9two layers, 91: 3.0two layers, 94: 3.2two layers, 97: 2.9two layers, 100: 2.9one layer: 14.8two layers: 2.9position in the sequence (the second copy starts at 51)loss (nats)
Loss at each position, averaged over 100 sequences. The one-layer model stays near 15 nats on both copies. The two-layer model falls from about 15 to about 3 nats right where the second copy starts, at position 51.

The drop is sharp and happens at once. The model does not slowly get used to the string. One token into the repeat, it can already use the rule. A one-layer model cannot, which matches the paper's claim that induction heads need two layers.

Finding the two heads

Which heads do this? We give every head two scores on the repeated sequences. The previous-token score is the average weight a head puts on the position just before. The induction score is the average weight, on the second copy, on the token right after the earlier copy of the present token.

plain text
== 3. WHICH HEADS DO WHAT, MEASURED ON THE REPEATED SEQUENCES
  previous-token score (average attention to the token just before):
    layer 0: 0.0 0.20, 0.1 0.05, 0.2 0.03, 0.3 0.52, 0.4 0.02, 0.5 0.05, 0.6 0.08, 0.7 0.01
    layer 1: 1.0 0.07, 1.1 0.10, 1.2 0.10, 1.3 0.09, 1.4 0.08, 1.5 0.04, 1.6 0.02, 1.7 0.03
  induction score (on the second copy, average attention to the token after the earlier copy of the present token):
    layer 0: 0.0 0.00, 0.1 0.00, 0.2 0.00, 0.3 0.00, 0.4 0.00, 0.5 0.00, 0.6 0.01, 0.7 0.00
    layer 1: 1.0 0.00, 1.1 0.00, 1.2 0.00, 1.3 0.00, 1.4 0.00, 1.5 0.00, 1.6 0.66, 1.7 0.03
  previous-token head: 0.3; induction head: 1.6 (the next best layer-1 head is 1.7 with 0.03)
  a head that spread its weight evenly over the 51 to 100 earlier tokens would score about 0.013

The answer is clear. Head 0.3 puts 0.52 of its weight on the previous token, and head 0.0 is a weaker second with 0.20. Head 1.6 puts 0.66 of its weight on the exact right source, out of 51 to 100 choices. An even spread would score about 0.013, so 0.66 is about fifty times more. No layer-0 head scores above 0.01, which is expected: one layer cannot do this.

This is the same pair as the composition scores showed: head 0.3 feeds head 1.6. Here is what the two patterns look like on a short string of 8 random tokens, repeated:

head 0.3 (previous token)BOSBOS → BOS: 1.00t1t1 → BOS: 0.88t1 → t1: 0.12t2t2 → BOS: 0.24t2 → t1: 0.71t2 → t2: 0.05t3t3 → BOS: 0.17t3 → t1: 0.10t3 → t2: 0.65t3 → t3: 0.08t4t4 → BOS: 0.53t4 → t1: 0.00t4 → t2: 0.05t4 → t3: 0.30t4 → t4: 0.11t5t5 → BOS: 0.51t5 → t1: 0.00t5 → t2: 0.00t5 → t3: 0.04t5 → t4: 0.36t5 → t5: 0.09t6t6 → BOS: 0.44t6 → t1: 0.00t6 → t2: 0.00t6 → t3: 0.00t6 → t4: 0.05t6 → t5: 0.47t6 → t6: 0.04t7t7 → BOS: 0.22t7 → t1: 0.00t7 → t2: 0.00t7 → t3: 0.00t7 → t4: 0.00t7 → t5: 0.10t7 → t6: 0.60t7 → t7: 0.07t8t8 → BOS: 0.25t8 → t1: 0.00t8 → t2: 0.00t8 → t3: 0.00t8 → t4: 0.00t8 → t5: 0.01t8 → t6: 0.05t8 → t7: 0.65t8 → t8: 0.04t1t1 → BOS: 0.43t1 → t1: 0.00t1 → t2: 0.00t1 → t3: 0.00t1 → t4: 0.00t1 → t5: 0.00t1 → t6: 0.01t1 → t7: 0.05t1 → t8: 0.44t1 → t1: 0.07t2t2 → BOS: 0.20t2 → t1: 0.00t2 → t2: 0.00t2 → t3: 0.00t2 → t4: 0.00t2 → t5: 0.00t2 → t6: 0.00t2 → t7: 0.00t2 → t8: 0.07t2 → t1: 0.68t2 → t2: 0.04t3t3 → BOS: 0.15t3 → t1: 0.00t3 → t2: 0.00t3 → t3: 0.00t3 → t4: 0.00t3 → t5: 0.00t3 → t6: 0.00t3 → t7: 0.00t3 → t8: 0.00t3 → t1: 0.09t3 → t2: 0.66t3 → t3: 0.08t4t4 → BOS: 0.54t4 → t1: 0.00t4 → t2: 0.00t4 → t3: 0.00t4 → t4: 0.01t4 → t5: 0.00t4 → t6: 0.00t4 → t7: 0.01t4 → t8: 0.00t4 → t1: 0.00t4 → t2: 0.05t4 → t3: 0.29t4 → t4: 0.09t5t5 → BOS: 0.52t5 → t1: 0.00t5 → t2: 0.00t5 → t3: 0.00t5 → t4: 0.00t5 → t5: 0.01t5 → t6: 0.00t5 → t7: 0.00t5 → t8: 0.00t5 → t1: 0.00t5 → t2: 0.00t5 → t3: 0.03t5 → t4: 0.35t5 → t5: 0.09t6t6 → BOS: 0.44t6 → t1: 0.00t6 → t2: 0.00t6 → t3: 0.00t6 → t4: 0.00t6 → t5: 0.00t6 → t6: 0.00t6 → t7: 0.00t6 → t8: 0.00t6 → t1: 0.00t6 → t2: 0.00t6 → t3: 0.00t6 → t4: 0.04t6 → t5: 0.47t6 → t6: 0.04t7t7 → BOS: 0.20t7 → t1: 0.00t7 → t2: 0.00t7 → t3: 0.00t7 → t4: 0.00t7 → t5: 0.00t7 → t6: 0.00t7 → t7: 0.00t7 → t8: 0.00t7 → t1: 0.00t7 → t2: 0.00t7 → t3: 0.00t7 → t4: 0.00t7 → t5: 0.09t7 → t6: 0.62t7 → t7: 0.06t8t8 → BOS: 0.24t8 → t1: 0.00t8 → t2: 0.00t8 → t3: 0.00t8 → t4: 0.00t8 → t5: 0.00t8 → t6: 0.00t8 → t7: 0.00t8 → t8: 0.00t8 → t1: 0.00t8 → t2: 0.00t8 → t3: 0.00t8 → t4: 0.00t8 → t5: 0.00t8 → t6: 0.05t8 → t7: 0.65t8 → t8: 0.03BOSt1t2t3t4t5t6t7t8t1t2t3t4t5t6t7t8head 1.6 (induction)BOSBOS → BOS: 1.00t1t1 → BOS: 0.98t1 → t1: 0.02t2t2 → BOS: 0.98t2 → t1: 0.00t2 → t2: 0.02t3t3 → BOS: 0.99t3 → t1: 0.00t3 → t2: 0.00t3 → t3: 0.01t4t4 → BOS: 0.87t4 → t1: 0.01t4 → t2: 0.03t4 → t3: 0.05t4 → t4: 0.04t5t5 → BOS: 0.96t5 → t1: 0.01t5 → t2: 0.00t5 → t3: 0.00t5 → t4: 0.02t5 → t5: 0.01t6t6 → BOS: 0.94t6 → t1: 0.01t6 → t2: 0.01t6 → t3: 0.00t6 → t4: 0.01t6 → t5: 0.00t6 → t6: 0.03t7t7 → BOS: 0.96t7 → t1: 0.01t7 → t2: 0.00t7 → t3: 0.00t7 → t4: 0.01t7 → t5: 0.01t7 → t6: 0.00t7 → t7: 0.01t8t8 → BOS: 0.95t8 → t1: 0.01t8 → t2: 0.01t8 → t3: 0.00t8 → t4: 0.00t8 → t5: 0.00t8 → t6: 0.00t8 → t7: 0.00t8 → t8: 0.01t1t1 → BOS: 0.42t1 → t1: 0.01t1 → t2: 0.51t1 → t3: 0.01t1 → t4: 0.00t1 → t5: 0.01t1 → t6: 0.01t1 → t7: 0.01t1 → t8: 0.00t1 → t1: 0.04t2t2 → BOS: 0.61t2 → t1: 0.00t2 → t2: 0.01t2 → t3: 0.28t2 → t4: 0.05t2 → t5: 0.01t2 → t6: 0.01t2 → t7: 0.01t2 → t8: 0.00t2 → t1: 0.01t2 → t2: 0.01t3t3 → BOS: 0.72t3 → t1: 0.00t3 → t2: 0.00t3 → t3: 0.01t3 → t4: 0.23t3 → t5: 0.01t3 → t6: 0.01t3 → t7: 0.01t3 → t8: 0.00t3 → t1: 0.00t3 → t2: 0.00t3 → t3: 0.00t4t4 → BOS: 0.41t4 → t1: 0.01t4 → t2: 0.01t4 → t3: 0.03t4 → t4: 0.02t4 → t5: 0.31t4 → t6: 0.02t4 → t7: 0.01t4 → t8: 0.08t4 → t1: 0.04t4 → t2: 0.01t4 → t3: 0.02t4 → t4: 0.02t5t5 → BOS: 0.61t5 → t1: 0.00t5 → t2: 0.00t5 → t3: 0.00t5 → t4: 0.01t5 → t5: 0.01t5 → t6: 0.27t5 → t7: 0.05t5 → t8: 0.00t5 → t1: 0.01t5 → t2: 0.00t5 → t3: 0.00t5 → t4: 0.02t5 → t5: 0.01t6t6 → BOS: 0.24t6 → t1: 0.00t6 → t2: 0.00t6 → t3: 0.00t6 → t4: 0.00t6 → t5: 0.00t6 → t6: 0.01t6 → t7: 0.70t6 → t8: 0.00t6 → t1: 0.01t6 → t2: 0.00t6 → t3: 0.00t6 → t4: 0.00t6 → t5: 0.00t6 → t6: 0.01t7t7 → BOS: 0.19t7 → t1: 0.00t7 → t2: 0.00t7 → t3: 0.00t7 → t4: 0.00t7 → t5: 0.00t7 → t6: 0.00t7 → t7: 0.00t7 → t8: 0.77t7 → t1: 0.02t7 → t2: 0.00t7 → t3: 0.00t7 → t4: 0.01t7 → t5: 0.00t7 → t6: 0.00t7 → t7: 0.00t8t8 → BOS: 0.59t8 → t1: 0.01t8 → t2: 0.01t8 → t3: 0.00t8 → t4: 0.00t8 → t5: 0.00t8 → t6: 0.00t8 → t7: 0.00t8 → t8: 0.01t8 → t1: 0.31t8 → t2: 0.04t8 → t3: 0.00t8 → t4: 0.00t8 → t5: 0.00t8 → t6: 0.01t8 → t7: 0.00t8 → t8: 0.01BOSt1t2t3t4t5t6t7t8t1t2t3t4t5t6t7t8
Two attention patterns on BOS and 8 random tokens t1 to t8, repeated. Head 0.3 (left) puts weight just left of the diagonal, on the previous token. Head 1.6 (right) puts nearly all weight on BOS during the first copy, and a stripe of weight seven places back during the second copy: on the token after the earlier copy.
plain text
  head 1.6 on the first copy (nothing to find): weight on BOS, averaged over positions 2..8: 0.95
  head 1.6 on the second copy: weight on the token after the earlier copy, averaged: 0.41

During the first copy head 1.6 parks 0.95 of its weight on the first token, as the paper said. During the second copy it moves to the stripe. On this short string the stripe holds 0.41 on average, less than the 0.66 of the long strings. Both are far above the even spread.

How the two heads work together

We now know what head 1.6 does. The question is how a head can find "the token after an earlier copy". A head only scores pairs of positions with its query and key. Neither of them, alone, knows about the next token.

The K-composition term, step by step

The paper writes the trick as a term of the QK circuit. We build it from the pieces of Part 2 and Part 5. Leave out positions and layer norm for now, as the paper does.

The stream at a source position jj, as layer 1 reads it, holds the token's embedding plus what layer 0 wrote. Keep only the previous-token head pp, and say that it reads the position just before with weight 1:

xj=WE tj+WOVp WE tj−1x_j=W_{\mathrm{E}}\,t_j+W_{\mathrm{OV}}^{p}\,W_{\mathrm{E}}\,t_{j-1}

Read it left to right. tjt_j is the one-hot vector of the token at jj, and WEtjW_{\mathrm{E}}t_j is its embedding. The second term is head pp's output: the embedding of the token before jj, passed through its OV matrix. The destination ii has only its own embedding, xi=WEtix_i=W_{\mathrm{E}}t_i, in this simple view.

The attention score of the layer-1 head hh is sij=xi⊤WQKh xjs_{ij}=x_i^\top W_{\mathrm{QK}}^{h}\,x_j. Put the two streams in and multiply out:

sij=ti⊤(WE⊤WQKhWE) tj+ti⊤(WE⊤WQKhWOVpWE) tj−1\begin{aligned} s_{ij}={}&t_i^\top\big({W_{\mathrm{E}}}^{\top}W_{\mathrm{QK}}^{h}W_{\mathrm{E}}\big)\,t_j\\ &+t_i^\top\big({W_{\mathrm{E}}}^{\top}W_{\mathrm{QK}}^{h}W_{\mathrm{OV}}^{p}W_{\mathrm{E}}\big)\,t_{j-1} \end{aligned}

The first line is the plain QK circuit of Part 2: present token against source token. The second line is new. It compares the present token tit_i with the token before the source, tj−1t_{j-1}. Call its matrix WW:

W=WE⊤WQKhWOVpWEW={W_{\mathrm{E}}}^{\top}W_{\mathrm{QK}}^{h}W_{\mathrm{OV}}^{p}W_{\mathrm{E}}

WW is a token-by-token table, like the circuits of Part 2. Its entry in row aa, column bb says how much a destination token aa likes a source whose previous token is bb. Now suppose WW is large on its diagonal and small elsewhere. Then the score is large exactly when tj−1=tit_{j-1}=t_i: the token before the source equals the present token. That is the induction rule, written as a matrix.

A tiny example by hand. Our toy script builds the whole circuit with three tokens, a, b and c. The stream has six numbers. Three say "this token is a, b or c". Three say "the token before me was a, b or c". The sequence is a b c a. We want the last "a" to predict "b".

Layer 0 is a previous-token head. It copies each token's "this token" numbers into the "token before" numbers of the next position. Layer 1 is the induction head, with WQKW_{\mathrm{QK}} that multiplies "this token" at the destination with "token before" at the source, times 4:

plain text
== A. AN INDUCTION HEAD BUILT BY HAND (tokens a, b, c; sequence a b c a)
  stream after layer 0 (first 3 = this token, last 3 = previous token): [[1.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0, 1.0, 0.0], [1.0, 0.0, 0.0, 0.0, 0.0, 1.0]]
  scores from the last position (token a) to positions 1..4: [0.0, 4.0, 0.0, 0.0]
  softmax: [0.017, 0.948, 0.017, 0.017]   (e^4 / (e^4 + 3) = 0.948)
  induction head output read as logits for a, b, c: [0.035, 0.948, 0.017]  -> predicts 'b'

Check it by hand. Position 2 holds "b", with the note "the token before me was a", and the last position holds "a". Only position 2 has a note that matches "a", so only it scores 4. The softmax gives it e4/(e4+3)≈0.948e^4/(e^4+3)\approx0.948. The head's OV circuit copies what is at position 2, which is "b". So "b" gets 0.948, the largest logit.

The WW table of this toy is 44 times the identity, a pure "same matching" table:

plain text
== B. THE K-COMPOSITION TERM W_E^T W_QK W_OV^prev W_E (3 x 3, one row and column per token)
  term = [[4.0, 0.0, 0.0], [0.0, 4.0, 0.0], [0.0, 0.0, 4.0]]; eigenvalues [4.0, 4.0, 4.0]; positivity 1.00

Where the real key comes from

Does head 1.6 really get its match from head 0.3? We can check it on the real model, with positions, biases and layer norm included. The stream that layer 1 reads is a sum: embedding plus position, plus each layer-0 head's output, plus a bias. Layer norm only divides the sum by one number per position. So the key, and the score, also split into one part per piece.

python
parts = {'embed+pos': cache['hook_embed'] + cache['hook_pos_embed']}
for p in range(8):
    parts[f'0.{p}'] = cache['blocks.0.attn.hook_z'][:, :, p] @ model.W_O[0, p]   # what head 0.p wrote
keys = {k: (centre(v) / scale) @ model.W_K[1, 6] for k, v in parts.items()}       # the key, one part at a time

For each part we compare its share of the score at the right source with its share at an average allowed source. The difference is the lift: how much that part helps the right source win.

plain text
== 8. WHERE THE INDUCTION HEAD'S KEY COMES FROM (head 1.6, the repeated sequences)
  share of the attention score, at the induction target vs averaged over all allowed sources:
    from       0.3: target +6.30, average source +2.12, lift +4.18
    from       0.0: target +3.01, average source +0.77, lift +2.24
    from embed+pos: target +3.34, average source +3.09, lift +0.25
    from       0.1: target +0.63, average source +0.39, lift +0.24
    from       0.6: target +1.43, average source +1.33, lift +0.10
    from       0.2: target +1.23, average source +1.16, lift +0.07
    from       0.5: target -0.94, average source -0.96, lift +0.02
    from      bias: target +9.76, average source +9.78, lift -0.02
    from       0.4: target +1.41, average source +1.41, lift +0.00
    from       0.7: target -0.74, average source -0.75, lift +0.00
    key bias part: +5.49 (the same for every source, so the softmax ignores it)
  sum of the parts at the target 30.93; the model's own score 30.93
How much each part of the stream lifts the score of the right source (head 1.6)head 0.3head 0.3: +4.18+4.18head 0.0head 0.0: +2.24+2.24embed + posembed + pos: +0.25+0.25head 0.1head 0.1: +0.24+0.24head 0.6head 0.6: +0.10+0.10head 0.2head 0.2: +0.07+0.07head 0.5head 0.5: +0.02+0.02head 0.7head 0.7: +0.01+0.01head 0.4head 0.4: +0.00+0.00
How much each part of the stream lifts head 1.6's score at the right source, above an average source. Head 0.3 gives +4.18 and head 0.0 gives +2.24. The token and position embeddings give only +0.25, and every other layer-0 head gives less than 0.3.

The parts add up to the model's own score, 30.93, so the split is exact. Almost all of the lift comes from what heads 0.3 and 0.0 wrote: +4.18 and +2.24. The source token's own embedding gives only +0.25. So head 1.6 does not choose its source by what the source is. It chooses it by the note that the previous-token heads left there. That is K-composition, measured.

Checking the theory from the weights

The tests so far ran the model on text. The paper also checks the theory from the weights alone, with no text at all.

The statistic is

positivity(M)=∑kλk∑k∣λk∣\text{positivity}(M)=\frac{\sum_k \lambda_k}{\sum_k \lvert\lambda_k\rvert}

The top sums the eigenvalues λk\lambda_k of MM, and the bottom sums their sizes. If every eigenvalue is positive, top and bottom are equal and the score is 1. If every eigenvalue is negative, the score is −1-1. A copying matrix sends each token back toward itself, so its eigenvalues are mostly positive and its score is near 1.

Three 2 × 2 matrices show the range:

plain text
== C. EIGENVALUE POSITIVITY sum(lambda) / sum(|lambda|)
  copy : M = [[2.0, 0.0], [0.0, 1.0]], eigenvalues [2.0, 1.0], positivity 1.00
  swap : M = [[0.0, 1.0], [1.0, 0.0]], eigenvalues [1.0, -1.0], positivity 0.00
  mixed: M = [[2.0, 0.0], [0.0, -1.0]], eigenvalues [2.0, -1.0], positivity 0.33

The "copy" matrix keeps each token, so it scores 1. The "swap" matrix turns a into b and b into a, so it copies nothing and scores 0. The "mixed" one keeps one token and flips the other: (2−1)/(2+1)=0.33(2-1)/(2+1)=0.33.

The real tables are 48,262 × 48,262, too big for eigenvalues. But a product ABAB and the product in the other order, BABA, have the same non-zero eigenvalues. So the eigenvalues of WUWOVWEW_{\mathrm{U}}W_{\mathrm{OV}}W_{\mathrm{E}} are those of the 512 × 512 matrix WOVWEWUW_{\mathrm{OV}}W_{\mathrm{E}}W_{\mathrm{U}}. The same trick works for the K-composition term.

plain text
== 5. COPYING: EIGENVALUE POSITIVITY OF THE OV CIRCUIT W_U W_OV W_E (1 = all eigenvalues positive, -1 = all negative)
  layer 1 positivity: 1.0 +0.72, 1.1 +0.11, 1.2 +0.94, 1.3 +0.57, 1.4 +0.03, 1.5 +0.71, 1.6 +1.00, 1.7 +1.00
  layer 1 share of tokens 1000..2999 whose top output is the token itself: 1.0 0.01, 1.1 0.00, 1.2 0.01, 1.3 0.01, 1.4 0.00, 1.5 0.00, 1.6 0.99, 1.7 0.26
  OV of 1.6: attend to ' Paris' -> raises [' Paris', 'Paris', ' Geneva', ' São', ' Madrid']

== 6. SAME MATCHING: EIGENVALUE POSITIVITY OF THE K-COMPOSITION TERM W_E^T W_QK^h W_OV^p W_E (p = a layer-0 head)
  with the previous-token head 0.3: 1.0 +0.10, 1.1 -0.08, 1.2 -0.24, 1.3 +0.85, 1.4 -0.36, 1.5 -0.06, 1.6 +1.00, 1.7 +0.98

Head 1.6 scores +1.00 on both. Its OV circuit is a near-perfect copier: for 99% of tokens 1,000 to 2,999, the top output is the token itself. Its K-composition table with head 0.3 is a near-perfect matcher. These two numbers come from the weights only, with no text.

Here is the same plot for our model:

-1.0-1.0-0.5-0.500+0.5+0.5+1.0+1.0OV circuit positivity (copying)QK term with head 0.3 (same matching)head 1.0: OV +0.72, QK +0.10, induction score 0.00head 1.1: OV +0.11, QK -0.08, induction score 0.00head 1.2: OV +0.94, QK -0.24, induction score 0.00head 1.3: OV +0.57, QK +0.85, induction score 0.00head 1.4: OV +0.03, QK -0.36, induction score 0.00head 1.5: OV +0.71, QK -0.06, induction score 0.00head 1.6: OV +1.00, QK +1.00, induction score 0.66head 1.7: OV +1.00, QK +0.98, induction score 0.031.01.11.21.31.41.51.61.7induction score above 0.02the other layer-1 heads1.6: OV +1.00, QK +1.00induction score 0.661.7: OV +1.00, QK +0.98induction score 0.03
The eight layer-1 heads of attn-only-2l. Across: OV circuit positivity. Up: positivity of the K-composition term with head 0.3. Heads 1.6 and 1.7 sit in the top right corner, near (1, 1). The other six heads are spread out.

Two heads land in the corner: 1.6, and 1.7 at (+1.00, +0.98). Head 1.7 is a surprise, because its induction score on our sequences was only 0.03. Its weights have the recipe, but on this data it rarely acts on it. When we remove it (next section), the loss on the second copy rises from 3.41 to 3.77, so it helps a little. These numbers do not say why it attends so weakly. Its plain QK circuit, the first line of our derivation, may pull against the match.

Our run adds a control. The same positivity for head 1.6, but with the other layer-0 heads in place of 0.3:

plain text
  head 1.6 with each other layer-0 head: 0.0 +0.64, 0.1 +0.01, 0.2 -0.02, 0.4 -0.59, 0.5 +0.57, 0.6 +0.13, 0.7 -0.12
  direct term W_E^T W_QK^h W_E (no layer-0 head): 1.0 +0.69, 1.1 +0.68, 1.2 +0.75, 1.3 +0.52, 1.4 +0.87, 1.5 +0.46, 1.6 -0.64, 1.7 -0.24

With other heads the score falls to between -0.59 and +0.64. The next best is head 0.0, the second previous-token head. And head 1.6's direct term, present token against source token, is negative (-0.64). On its own, head 1.6 slightly avoids a source that equals the present token. It only matches through the shifted note.

Breaking the circuit on purpose

The weights tell a clean story. A last test is to remove a part and see what breaks. This is our own test; the paper's term-importance section, in Part 5, uses a related idea.

python
mean = cache['blocks.0.attn.hook_z'][:, :, 3].mean((0, 1))    # head 0.3's average result vector
def hook(z, hook):
    z[:, :, 3] = mean                                         # same vector at every position
    return z
logits = model.run_with_hooks(toks, fwd_hooks=[('blocks.0.attn.hook_z', hook)])
plain text
== 9. ABLATIONS: REPLACE ONE HEAD'S RESULT VECTORS WITH THEIR AVERAGE OVER ALL POSITIONS AND SEQUENCES
  no ablation:            loss first 15.04, second 3.41; induction score of 1.6 0.66
  ablate 0.3 (the previous-token head): loss first 13.68, second 11.85; induction score of 1.6 0.06
  ablate 0.0 (the second most previous-token head): loss first 14.27, second 6.33; induction score of 1.6 0.37
  ablate 0.7 (the least previous-token layer-0 head): loss first 17.97, second 4.15; induction score of 1.6 0.64
  ablate 1.6 (the induction head): loss first 14.25, second 13.04; induction score of 1.6 0.66
  ablate 1.7 (the second induction-like head): loss first 15.04, second 3.77; induction score of 1.6 0.66
  ablate 1.1 (the least induction-like layer-1 head): loss first 14.86, second 3.33; induction score of 1.6 0.64
Loss on the second copy (nats) after replacing one head with its average outputno ablationno ablation: 3.413.410.3 (previous-token head)0.3 (previous-token head): 11.8511.850.0 (second previous-token head)0.0 (second previous-token head): 6.336.330.70.7: 4.154.151.6 (induction head)1.6 (induction head): 13.0413.041.71.7: 3.773.771.11.1: 3.333.33
Loss on the second copy after replacing one head with its average output. With no ablation it is 3.41 nats. Replacing head 0.3 raises it to 11.85, and replacing head 1.6 raises it to 13.04. Replacing head 0.0 raises it to 6.33. The other heads change it by less than 0.8.

Remove head 0.3, and head 1.6 loses its way: its induction score falls from 0.66 to 0.06. The loss on the second copy jumps from 3.41 to 11.85. Head 1.6 itself is untouched, yet it no longer finds the right source. The note it searched for is gone.

Remove head 1.6, and its pattern is unchanged (0.66), because nothing before it changed. But its output is gone, and the loss on the second copy rises to 13.04. So the two heads are both needed: one to find the place, one to copy.

The controls behave as expected. Head 0.0 is a partial helper (6.33). Heads 1.7, 1.1 and 0.7 change the second-copy loss by less than 0.8. Head 0.7 has one odd effect: the first-copy loss rises to 17.97. That copy is random text, so the change says little about induction, and we do not explain it here.

What these tests cannot show: mean ablation is only one way to remove a head. Other ways, such as setting it to zero, can give different numbers. And random tokens are not real text. They isolate induction, but they say nothing about how much the model uses it on ordinary prose.

The correction to the diagram

After publication, the authors found a bug that changed one diagram: the composition diagram at the start of this part.

What attention patterns hide

Induction heads also teach a lesson about reading attention patterns. The paper makes it in its related-work section.

Another way to build one

The paper's appendix notes that the K-composition circuit is not the only way to build an induction head. Our model has position vectors in its stream, so this note applies to it.

Next, in Part 7: what the paper left out, MLP layers above all. Then what came after: induction heads in large models, superposition, dictionary learning and circuit tracing. Part 7 also ties the whole series together.

Run it yourself

The script circuits_part6.py runs every real-model test of this part: the repeated random tokens, the head scores, the eigenvalue positivity, the composition scores, the split of the key and the ablations. circuits_part6_toy.py builds the three-token induction circuit by hand. Both run on the CPU; the first one took a few minutes while other jobs ran on the same machine.

bash
pip install torch "transformer_lens<4"
python circuits_part6.py        # writes results/part6.json and results/part6_stdout.txt
python circuits_part6_toy.py    # writes results/part6_toy.json
Terminal output of circuits_part6.py: the model, the loss on both copies of the repeated random tokens, the previous-token and induction scores of every head, the two patterns on a short sequence, the eigenvalue positivity of the OV circuits and of the K-composition terms, and the composition scores
The first part of the output of circuits_part6.py.
Terminal output of circuits_part6_toy.py: the hand-built induction circuit on the sequence a b c a, its K-composition term, and eigenvalue positivity on three 2 by 2 matrices
The output of circuits_part6_toy.py.

The paper and the code

A Mathematical Framework for Transformer Circuits (2021) · our scripts for this series