Experiment: does lab 16's model really learn an induction circuit?¶
Date: 2026-09-26 · Code version: after the lab 16 fix (September 2026) · Hardware: any laptop CPU, about 5 s per run · Code: labs/16_interpretability/solution.py (train_induction, induction_scores, previous_token_scores, copy_loss)
Note
A worked example of the experiment template, written from a real investigation of this repo's lab 16. Every number below reproduces on a CPU in under a minute with the snippet at the end. Use it as the standard for your own entries: a prediction written first, one change at a time, several seeds, and the lesson stated plainly.
Question and hypothesis¶
Lab 16's test test_an_induction_head_forms_in_layer_2 passed. Does that mean the model implements the two-head induction circuit: a previous-token head in layer 0 whose output the layer-1 induction head reads (K-composition)?
Prediction, written before running. If the circuit is real, then:
- the model copies at a repeat period it never trained on (7) almost as well as at its training period (10);
- some layer-0 head attends to the previous token (score above 0.5);
- layer-0 heads do not score as induction heads, since one layer cannot compose.
If instead the model learned a positional shortcut, "attend 9 positions back", which is possible because the period was always 10 and positions are learned, then (1) fails badly and (2) fails.
Setup¶
- Model:
AttnOnly, 2 layers × 2 heads, \(d = 64\), learned absolute positions, vocabulary 16, loss on the second half only. - The one change: the repeat period during training.
- Fixed: period 10, 600 steps, the lab's original setting:
train_induction(steps=600, min_half=10, seed=s). - Variable: period drawn from 5–10 for each batch, 1,000 steps:
train_induction(seed=s), the lab's current default. - Seeds: 0, 1, 2 for each regime.
- Metrics, on fresh sequences: second-half copy loss in nats (256 sequences) at periods 10 and 7; the best head's induction score in each layer; the best layer-0 previous-token score (32 sequences). For scale: guessing costs \(\ln 16 = 2.77\) nats, and uniform attention scores about 0.07.
Results¶
Mean of three seeds, with the per-seed range in brackets.
| metric | fixed period | variable period |
|---|---|---|
| copy loss at period 10 (nats) | 0.00 | 0.10 [0.09–0.11] |
| copy loss at period 7 (nats) | 15.7 [15.0–16.3] | 0.10 [0.06–0.16] |
| best layer-1 induction score, period 10 | 0.90 [0.90–0.90] | 0.85 [0.82–0.87] |
| best layer-1 induction score, period 7 | 0.07 [0.02–0.14] | 0.88 [0.86–0.90] |
| best layer-0 induction score, period 10 | 0.89 [0.88–0.90] | 0.26 [0.20–0.30] |
| best layer-0 previous-token score | 0.11 [0.08–0.16] | 0.73 [0.70–0.76] |
Prediction vs result¶
For the fixed-period model all three predictions of a real circuit fail, on every seed. At period 7 its loss is 15.7 nats, over five times worse than guessing. No head attends to the previous token, and its layer-0 heads already score 0.89 as "induction heads", because they attend 9 positions back by position alone. The passing test measured attention at the training period, where the shortcut and the circuit look the same.
The variable-period model matches all three predictions: it copies equally well at periods 7 and 10, has a clear previous-token head in layer 0 (0.73), and its induction heads are in layer 1. For seed 0, zero-ablating the previous-token head raises the period-7 loss from 0.09 to 0.46, so layer 1 uses that head's output. The effect is causal, though only partial.
What surprised me: the shortcut model is better at the training period (0.00 against 0.10 nats). With 16 tokens, about 97% of 10-token first halves repeat some token, which makes content-based lookup ambiguous; the positional rule never is. A metric measured only on the training distribution cannot separate the two mechanisms, and it even rewards the wrong one. An input the shortcut cannot solve can.
Threats to validity¶
- Three seeds, one model size, one vocabulary. The shortcut's advantage depends on the vocabulary size and the range of periods.
- Attention scores show where heads look, not that the result is used. The ablation is the causal check, and I ran it for one seed only.
- The variable model trained for 1,000 steps and the fixed one for 600. The fixed model is already at 0.00 loss, so more steps are unlikely to add a circuit, but a run with matched steps would remove the doubt.
What changed as a result¶
- Lab 16 now trains with a variable period, and two new tests check exactly what the shortcut fails: copying at period 7, and a previous-token head in layer 0.
- The lab 16 handout now teaches the shortcut and shows how activation patching tells the two models apart.
Next¶
Train with periods 5–10 and a longer position table (max_len of 40), then evaluate at periods 12–20. Do learned absolute positions stop the circuit from working at periods longer than any it has seen?
Reproduce¶
import torch
from labs._impl import load
mi = load("labs/16_interpretability/x.py", "solution")
for name, kw in [("fixed", dict(steps=600, min_half=10)), ("variable", {})]:
model, _ = mi.train_induction(seed=0, **kw)
with torch.no_grad():
seq = mi.repeated_batch(256, 7, 16, torch.Generator().manual_seed(3))
print(name, "loss at period 7:", round(mi.copy_loss(model, seq, 7).item(), 2))