metrics

Implement Metrics (SOAR I-6)

Grades candidate parent→child edges between the nested blocks of a Matryoshka SAE on google/gemma-2-2b (residual stream, layers 1–24). A family of competing metrics decides which “edges” are real hierarchy and which are frequency, splitting or co-occurrence artifacts.

The SAE has D_SAE = 32768 features in 5 nested blocks with prefix lengths [128, 512, 2048, 8192, 32768]B0=[0,128) B1=[128,512) B2=[512,2048) B3=[2048,8192) B4=[8192,32768). Cross-block edges are computed between adjacent blocks only.

Live site: soar-eleuther-i6-hierarchy.github.io/metrics

Where to go What is there
metrics/ every metric: formula, threshold, what it catches, what it is blind to
outputs/ all results — dashboards, reports, per-layer pages, validation tiers
validation/ the two calibration tiers scored against a known tree (synthetic toy, trained toy) plus the qualitative Tier 3 and the lateral control

Install and run

Run everything from the metrics/ directory.

pip install torch sae_lens datasets plotly numpy matplotlib scipy   # scipy: Hungarian feature-matching (scoring.core.recovery.linear_sum_assignment)

python3 run_pipeline.py         # every stage in order, refusing any whose inputs are missing
python3 run_pipeline.py --list  # show the order and what is already satisfied
python3 run_pipeline.py --from 02   # resume after the slow one

What each metric catches

The validation table: metrics (rows) × properties (columns). A candidate pair can look like an edge for seven different reasons. Only one of them is real hierarchy; the rest are the pathologies the metrics have to separate out. Each metric is a partial detector, so the question for every metric is not “is it correct” but which column does it add.

Metric Parent→child Absorption Splitting Superparent Siblings Frequency coincidence Concept co-occurrence
1a Coverage — reverse R
1b Coverage — forward F
1c Joint-child R_supp / R_mass / energy share
2a Reconstruction ablation (contribution filter)
2b S_res probe, rank-scored
3 Sibling redundancy
4 Out-degree / superparent
5 Token-frequency control
6 Independence null (PMI / Dev)
7 In-block directed coverage

✅ detects this property · ◐ partial — necessary but not sufficient, or only in some regimes · ✗ blind

The properties. Parent→child: the child is a genuine refinement of the parent. Absorption: the child has absorbed a case from the parent, so the parent goes silent exactly where the child fires. Splitting: the “children” are near-copies of one another. Superparent: one parent fans out over most of the next block and fires on a huge share of tokens. Siblings: the two features are co-level (co-hyponyms or co-extensive duplicates), not parent and child. Frequency coincidence: the co-firing is base rate, carried by high-frequency tokens. Concept co-occurrence: both features are specific and genuinely unrelated (enzyme, CT scan) but share a latent topic (biology), so they co-fire in the same documents.

Two columns are still open — and are now demonstrated rather than argued. validation/synthetic_toy_world.py carries a scored negative control for each: an absorbed child (the true edge has R = 0.00 and never enters the candidate set) and a shared-topic pair (a non-edge that clears coverage, reconstruction, the frequency control and PMI). Both rows pass when the battery does nothing, so a regression turns a stated limitation into a visible failure.

Cells are read off each metric’s construction (see the module docstrings) together with the calibrations below — they state what a metric is able to separate, not a measured accuracy on the real SAE. Every cell is now exercised by a Tier-1 row: the calibration covers 21/21 metric functions, which it did not until 7 August.

One cell needs its own caveat. 2b’s ability to disfavour a superparent is bounded by dictionary size. The rank rule is a geometry test, so an unrelated parent passes whenever chance puts it in the top k of Dk/D, which is 0.015% on gemma’s 32768 latents but 0.28% on a 1792-latent PCFG dictionary and 11.9% on the toy. It ranks a superparent far down (median rank 24 of 42 against the true parent’s 1), and the top-k cutoff is what lets chance through. An S_res pass rate is only comparable between dictionaries of similar size.

Why trust the table: three tiers

Each tier gives up one guarantee and gains one dose of reality; a metric we trust has to hold across all three. (“Tier”, not “layer”, to avoid confusion with the model’s residual-stream layers.)

Tier What it is Ground truth? What it proves
1. Synthetic toy a known 5-parent tree plus six injected structures, reduced to cached statsand to the per-token view the probes need yes, by construction the maths is right —14/14 rows, covering all 21 metric functions, each pathology caught by its intended metric
2. Trained toy a Matryoshka SAE actually trained on the known tree; metrics run on thelearned features yes, the tree is known the metrics survive real training — precision 1.00, recall 1.00, 0 false positives, all 20 features learned
3. Released SAE the publishedgemma-2-2b Matryoshka SAE, read against Neuronpedia labels none — human reading 48 survivors read against autointerp labels

Tier 1 is certain but artificial; Tier 3 is realistic but has no ground truth and is a checkpoint we did not train, which is what released names — the rungs below it are real SAEs too. Tier 2 is the only rung with both a trained SAE and a known answer.

Tier 2 does not isolate the variable it is named for. It runs on a clean 20-feature tree while Tier 1 grades a larger pathology-injected world, so it changes the toy as well as the statistics. What it does isolate is blame — a missed edge counts against a metric only if the SAE learned both endpoints — and that is what it is for.

The PCFG SAE is a control, not a rung. A tier earns its place by scoring the battery against a known answer, and the PCFG run has none: its grammar is known, but nothing maps a latent to a grammar symbol, so it reports the same battery outputs as Tier 3 rather than a recovery score. The helper a mapping would be built from exists — pcfg_bridge.grammar.vocab.role_of(token_id) in the PCFG repo — and until it is consumed, the run answers a different question: what corpus complexity does to the same battery, between a hand-built world and natural language. Its numbers are reported beside gemma in outputs/README.md, not on the ladder.

Scope. Tier 1 scores 14/14 rows, covering 21/21 metric functions — including S_res and in-block directed coverage, which until 7 August were graded by nothing. The page used to say the probe functions were “calibrated in Tier 2”; Tier 2 imports five functions and none of them is one of those. Two rows are negative controls that pass when the battery does not act, so absorption and shared-topic co-occurrence are demonstrated limitations rather than asserted ones. Full detail, per-metric scorecards and how to run each tier: outputs/README.md.