LLMModule 08 · Stage 09

Mechanistic interpretability

Open the model up, read what it's thinking layer by layer, and run controlled experiments to find which parts actually cause an answer.

PrerequisitesTransformer internals (attention, MLP, residual stream), PyTorch hooks, stage 01
Time25–40 h: ~10 h of ARENA exercises, 10–20 h on the project, the rest reading
HardwareLaptop CPU for GPT-2 small. A free Colab GPU for GPT-2 XL or 1–2B models
Key papersROME (Meng et al. 2022), IOI circuit (Wang et al. 2022), ACDC (Conmy et al. 2023), Geva et al. 2023

Source: Suraj Sharma, X post, stage 9

Start here (no maths needed)

Mechanistic interpretability is detective work inside an AI. You switch small parts of it on and off to find out which parts make it give a certain answer.

Think of it like this

Imagine a long line of people passing a note down a row, each one adding a few words. At the end, someone reads out "Paris". Which person added the important bit? You can peek at the note after each person (that's the "logit lens"). Or you can run the game twice, once about the Eiffel Tower and once about the Colosseum, then swap one person's words from the first game into the second. If the answer flips to "Paris", that person mattered (that's "activation patching").

How it works

  1. Pick a behaviour, like finishing "The Eiffel Tower is in the city of" with "Paris".
  2. Make a near-identical prompt that gives a different answer, like the Colosseum and "Rome".
  3. Record everything the model computes inside on the first prompt.
  4. Run the second prompt, but paste in one recorded piece from the first. Measure how far the answer moves back towards "Paris".
  5. Do this for every piece. The pieces that move the answer most are part of the "circuit".

Why robots care

Robot brains built from language and vision models can do the wrong thing for hidden reasons. A grasping model might be looking at the table's colour instead of the cup. The same switch-and-measure tricks can show what the model is really paying attention to before you trust it near people.

Try it

Words to know

Residual stream
The running "note" each layer reads from and adds to.
Logit lens
Reading the model's best guess early, from a middle layer.
Activation patching
Pasting a part's output from one run into another to test if it matters.
Attention head
A small unit that moves information between words.
Circuit
The small set of parts that work together to produce one behaviour.

Going deeper: everything below is the full version, with some maths. On a first read, skim the headings and the "Why it matters for robotics" box, and come back for the rest later.

1. What this stage covers

TL;DR

Interpretability tools let you test hypotheses about how a trained transformer computes something. The logit lens reads intermediate predictions. Activation patching gives causal evidence by swapping activations between a clean and a corrupted run. Circuit discovery scales patching up to find the subgraph responsible for a behaviour. The post's project, "find the exact attention head for the Eiffel Tower fact", needs one correction: the classic study of this exact example (ROME) found factual recall concentrated in mid-layer MLPs at the last subject token, with attention later moving the answer to the final position. Facts are spread over several components, not stored in one head.

2. Concepts, explained

The residual stream and the logit lens

In a decoder transformer, each layer reads the residual stream and adds its output back to it. The final layer norm and unembedding turn the last residual vector into logits. The logit lens (nostalgebraist, 2020, LessWrong post) applies that same final step to the residual stream after every layer, to see what the model "would say" if it stopped there:

h_0 = embed(x) + pos
h_l = h_{l-1} + attn_l(h_{l-1}) + mlp_l(…)      # each block adds to the stream
lens_l = W_U · LN_final(h_l)                     # logits "as of" layer l

It's cheap and often revealing in GPT-2: the right answer climbs the ranking over the later layers. It's also biased, because middle layers weren't trained to be read by the final unembedding. The tuned lens (Belrose et al., 2023) learns a small affine map per layer to fix this. Treat the logit lens as a quick look, not a proof. It's correlational: it shows what's present, not what's used.

Activation patching (causal tracing)

Patching is an intervention. You need two inputs that differ in one controlled way, a metric, and a list of components to test:

clean:     "The Eiffel Tower is located in the city of"  → Paris
corrupted: same template, different subject              → Rome (say)
metric:    LD = logit(Paris) − logit(Rome) at the last position

for each component c (layer × position, or head):
    run corrupted, but replace c's activation with its clean value
    recovered(c) = (LD_patched − LD_corr) / (LD_clean − LD_corr)

This direction (clean into corrupted) asks "is this component sufficient to restore the behaviour?". The reverse (corrupted into clean, sometimes called noising) asks "is it necessary?". The two can disagree when information is stored redundantly. ROME's causal tracing (Meng, Bau, Andonian & Belinkov, 2022) used a variant: it corrupts the subject's token embeddings with Gaussian noise rather than swapping subjects, then restores one hidden state at a time. Practical details that change results: the choice of metric (logit difference is more stable than probability), whether the two prompts tokenise to the same length, and whether you patch a single position or all of them.

What ROME found, and the correction to the post

The post frames the project as finding "the exact attention head" for "The Eiffel Tower is in…". The paper that studied exactly this kind of fact found something different. Meng et al. traced factual predictions in GPT-2 XL and found a distinct set of steps in middle-layer feed-forward (MLP) modules while the model processes the last token of the subject ("Tower"). They then edited one MLP weight matrix with a rank-one update (ROME) to rewrite a fact, and showed the edit generalised to paraphrases without spilling onto unrelated subjects.

Follow-up work filled in the picture. Geva, Bastings, Filippova & Globerson (2023) found a three-step flow: early MLPs enrich the last-subject-token representation with many attributes, the relation ("is located in") is moved to the final position, and then attention heads at the final position extract the attribute from the enriched subject. So heads do matter, but as readers and movers spread over several layers, not as a single head that "contains" the fact. A good project result is a map with an MLP hot spot at the subject and an attention hot spot at the end, not one glowing head.

One more caution. Hase, Bansal, Kim & Ghandeharioun (2023) found that where causal tracing localises a fact doesn't predict which layer is best to edit. Localisation and control are separate questions.

Circuits and circuit discovery

A circuit is a subgraph of the model (heads, MLPs and the edges between them) that implements one behaviour. The framing comes from Anthropic's "A Mathematical Framework for Transformer Circuits" (Elhage et al., 2021), which treats each attention head as reading from and writing to the residual stream independently.

The landmark manual study is the IOI circuit (Wang, Variengien, Conmy, Shlegeris & Steinhardt, 2022). In "When Mary and John went to the store, John gave a drink to", GPT-2 small predicts "Mary". They explained it with 26 attention heads in 7 classes, found with causal interventions, and checked the explanation for faithfulness, completeness and minimality. Even then they reported gaps.

ACDC (Conmy, Mavor-Parker, Lynch, Heimersheim & Garriga-Alonso, 2023) automates the search: it prunes edges of the computational graph whose removal barely changes the metric. On GPT-2 small's greater-than task it kept 68 of 32,000 edges and recovered all 5 component types that manual work had found. Path patching and attribution patching (a gradient-based linear approximation of patching) are cheaper ways to rank components before confirming with real patches.

circuit ⊂ graph(model)
faithful: running only the circuit (rest ablated) ≈ full model on the task
minimal:  removing any node hurts the metric

Tools: TransformerLens and Baukit

TransformerLens (Neel Nanda and contributors) reimplements GPT-style models with a named hook at every activation: blocks.5.hook_mlp_out, blocks.9.attn.hook_z and so on. run_with_cache stores them all, and run_with_hooks lets you edit any of them during a forward pass. That makes patching a few lines. Baukit (David Bau's lab, the ROME authors) wraps an ordinary PyTorch model and lets you trace or edit any named module's output. It's lighter and works on models TransformerLens doesn't support. Under the hood both use the forward hooks you met in stage 01. For larger models, nnsight follows the same idea. The newer direction of the field, sparse autoencoders that split activations into interpretable features, is covered in the Transformer Circuits Thread linked below.

3. The practice project

(a) Laptop version: patch a fact in GPT-2 small

  1. pip install transformer_lens. Load gpt2 (124M parameters, runs on CPU).
  2. Check the model knows the fact first. Print the top 5 next tokens for your prompt. If " Paris" isn't top-1, try a rephrase, a different fact, or gpt2-medium/gpt2-xl on Colab. Patching a fact the model doesn't know measures noise.
  3. Pick a corrupted subject with the same number of tokens (check with m.to_str_tokens) and a single-token answer.
  4. Logit lens: for each layer, project the last-position residual through ln_final and the unembedding. Record the rank of " Paris".
  5. Patching heatmaps: for resid_pre, mlp_out and attn_out, patch each (layer, position) from clean into corrupted and record the recovered fraction. Then patch individual heads (hook_z) at the final position.
  6. Repeat on 20–50 facts (city, language, company), averaged. One prompt is an anecdote.
from transformer_lens import HookedTransformer, utils
import torch

m = HookedTransformer.from_pretrained("gpt2")
clean = m.to_tokens("The Eiffel Tower is located in the city of")
corr = m.to_tokens("The Colosseum is located in the city of")  # check lengths match
assert clean.shape == corr.shape, m.to_str_tokens(corr)
P, R = m.to_single_token(" Paris"), m.to_single_token(" Rome")
ld = lambda lg: (lg[0, -1, P] - lg[0, -1, R]).item()

lg_c, cache = m.run_with_cache(clean)
base_c, base_x = ld(lg_c), ld(m(corr))

def patch(act, hook, pos):
    act[:, pos] = cache[hook.name][:, pos]
    return act

L, T = m.cfg.n_layers, clean.shape[1]
heat = torch.zeros(L, T)
for l in range(L):
    for p in range(T):
        h = (utils.get_act_name("mlp_out", l), lambda a, hook, p=p: patch(a, hook, p))
        heat[l, p] = (ld(m.run_with_hooks(corr, fwd_hooks=[h])) - base_x) / (base_c - base_x)

# logit lens at the last position
for l in range(L):
    r = cache["resid_post", l][:, -1:]
    print(l, m.to_string(m.unembed(m.ln_final(r))[0, -1].argmax()))

What to measure: the recovered-fraction heatmap for MLP, attention and residual; the layer where " Paris" first reaches top-1 in the lens; the top 5 heads at the final position. What to look for, based on ROME and Geva et al. rather than a run I can vouch for in GPT-2 small: a band of MLP effect around the subject's last token in early-to-middle layers, and attention effect at the final position in later layers. If your map differs, report it. That's a finding.

"Change the behaviour": patching from a corrupted run already flips the answer in that run. To make a persistent change, add a steering vector (for example, mean activation difference between Paris and Rome prompts at the hot layer) or try a ROME edit with the authors' code. Then test on paraphrases and unrelated facts so you can see side effects.

(b) Full version from the post

The same experiment on a 1–8B model (Pythia, Gemma 2 2B or Llama-class), over a few hundred facts, with head-level patching, path patching between the key heads, and an edit or steering intervention evaluated for generalisation and specificity. A 2B model fits a free Colab T4 in half precision. An 8B model with caching of all activations needs a 24–48 GB GPU, about $0.50–2 per hour rented; head-level sweeps over hundreds of prompts take a few GPU-hours. Use attribution patching to shortlist components so you don't run every patch.

(c) Pitfalls

Interactive: logit lens and patching in a toy model

A hand-built 6-layer toy, not a real model. Subject MLPs (layers 1–3) write a city into the subject token. Attention at layers 4–5 copies it to the last token. Left: logit lens at the last token, one row per layer, darker = more probable. Right: how much of the clean answer each single patch recovers (clean → corrupted). The highlighted bar is your current patch.

Run
–
Top token
–
P(Paris) / P(Rome)
–
Recovered
–

Try this: on the corrupted run, layer 2's subject MLP alone recovers 50%, the layer 4 head 67%, and only the whole subject state after layer 3 recovers 100%. The lens shows "the" until layer 3 and the city from layer 4: the fact is computed at the subject, then moved. The patch slider only applies to the corrupted run.

4. Reality check

The claim: you can't fix hallucinations or bias without knowing which circuit generates them.

Still worth learning: patching and the lens are the debugger of model internals, and the habit of forming a hypothesis and testing it with an intervention transfers to everything else on this roadmap.

5. In this course

6. Why it matters for robotics

7. Self-check

What does the logit lens compute, and why can it mislead?

It applies the final layer norm and unembedding to an intermediate residual stream. Middle layers weren't trained to be read that way, so the representation can be in a different basis. The lens shows what's decodable, not what's causally used. The tuned lens adds a learned per-layer map to reduce the bias.

Patching a component from clean into corrupted recovers 90% of the logit difference. Is it necessary for the behaviour?

Not necessarily. That shows sufficiency. To test necessity, patch corrupted into clean or ablate it, and check whether backup components take over.

Where did ROME localise factual recall, and what did it edit?

In middle-layer MLP (feed-forward) modules while processing the last subject token. It edited one MLP weight matrix with a rank-one update.

Why would a single attention head rarely be "the" location of a fact?

Facts are enriched into the subject representation by MLPs. Attention heads at the final position read and move that information, and several heads across layers share the job. Geva et al. (2023) describe this enrich-then-extract flow.

Why use logit difference rather than probability as the patching metric?

Probability saturates near 0 and 1, so big internal changes can look small. Logit difference is roughly linear in the residual stream and compares the right answer to a specific alternative.

What does ACDC automate?

Finding the circuit: it iteratively removes edges of the computational graph whose removal barely changes the task metric, leaving a small subgraph.

8. Courses & content

ResourceTypeUse it for
ARENA · Transformer interpretability chapterCourse (free materials)Hands-on exercises: logit lens, attribution, activation patching, circuits
ARENA 3.0 materials (GitHub)Exercises (free)The notebooks themselves
Neel Nanda · Getting started in mechanistic interpretabilityGuideReading list and learning path
TransformerLens docsLibrary + docsHooking and patching activations
Transformer Circuits ThreadResearch publicationsThe primary research on circuits and features

Start with ARENA's transformer interpretability chapter: it walks you through the logit lens, patching and the IOI circuit in TransformerLens, which is exactly this stage's project.

Full course map for all 12 stages →

9. Papers & docs