Download tables/scripts/paper_numbers.py from sae-anon/sae-null-step: direct link, hf CLI and curl.
- Browser
- Download file 23.4 kB
-
https://huggingface.co/sae-anon/sae-null-step/resolve/main/tables/scripts/paper_numbers.py
- Command line
-
hf download hf://sae-anon/sae-null-step/tables/scripts/paper_numbers.py
-
curl -L -o paper_numbers.py https://huggingface.co/sae-anon/sae-null-step/resolve/main/tables/scripts/paper_numbers.py
23.4 kB
| """Main manuscript tables and headline values, recomputed from the artifact. | |
| Run this and diff against the draft. Validation-checkpoint epochs and epoch-matched | |
| shares are emitted by the companion ``checkpoint_selection_audit.py``. | |
| python scripts/paper_numbers.py # human-readable | |
| python scripts/paper_numbers.py --json # machine-readable | |
| Sources (all under ../drift_run unless noted): | |
| drift_out/drift_by_stage.csv per (chain, layer, stage) drift, 3 inner products | |
| drift_out/drift_per_feature.csv per-latent drift, density, persistence | |
| drift_out/base_feature_z.csv per-latent logit-lens peakedness at the base SAE | |
| drift_out/null_replicates.csv null-step replicates + real first transitions | |
| drift_out/null_trajectory_l18.csv the originally reported 7-step null trajectory | |
| drift_out/null_trajectory_l18_seeds.csv the same, 4 seeds (supersedes the above) | |
| null_chain_all/layer{L}_seed{S}/ per-layer null trajectories, 3 seeds each | |
| null_budget/ epoch-budget sweep + budget-matched trajectories | |
| arch_null/arch_null_trajectory.csv TopK / BatchTopK / JumpReLU at matched recipe | |
| saes_local/, null_ctrl/, null_seeds/ SAE weights (for cosines and hashes) | |
| ../sae_rl/results/sae_checkpoint_metrics.csv reconstruction fidelity | |
| ../sae_rl/results/task_performance.csv GSM8K accuracy | |
| ../sae_rl/data/activations*/ activation-set sizes | |
| Sections needing SAE weights (direction, budget, trajectories, alignment, persistent | |
| null) run on CPU by default and are skipped with a warning if the weights are absent, | |
| so the script still works from the CSVs alone. | |
| """ | |
| import argparse | |
| import glob | |
| import json | |
| import os | |
| import pathlib | |
| import re | |
| import pandas as pd | |
| from scipy.stats import spearmanr | |
| DEV = "cpu" | |
| HERE = pathlib.Path(__file__).resolve().parent | |
| def _find(*parts): | |
| """Locate a data directory whether this paper tree sits inside sae_rl/ or beside it. | |
| The paper moved out of sae_rl/paper/ on 2026-08-20, so the data root is one | |
| level further away than it used to be. Try both rather than pinning either. | |
| """ | |
| for base in (HERE.parent.parent, HERE.parent.parent / "sae_rl"): | |
| cand = base.joinpath(*parts) | |
| if cand.exists(): | |
| return cand | |
| return HERE.parent.parent.joinpath(*parts) | |
| DRIFT = _find("drift_run", "drift_out") | |
| RESULTS = _find("sae_rl", "results") | |
| ACC = "val-core/openai/gsm8k/acc/mean@1" | |
| # Chains sharing a bit-identical base SAE, so cross-chain latent indices are | |
| # comparable. Verified by hashing W_dec; see base_alignment(). | |
| ALIGNED = ["sae_sft", "sae_flexible", "sae_strict/k64"] | |
| def _round(o, n=4): | |
| if isinstance(o, dict): | |
| return {k: _round(v, n) for k, v in o.items()} | |
| if isinstance(o, list): | |
| return [_round(v, n) for v in o] | |
| return round(o, n) if isinstance(o, float) else o | |
| def artifact(drift): | |
| """Size of the artifact: SAEs, chains, layers.""" | |
| return { | |
| "n_saes": int(len(drift)), | |
| "n_chains": int(drift.chain.nunique()), | |
| "layers": sorted(drift.layer.unique().tolist()), | |
| "chain_n_checkpoints": {c: int(g.stage.nunique()) | |
| for c, g in drift.groupby("chain")}, | |
| "d_sae": int(drift.n_features.iloc[0]), | |
| } | |
| def identity_vs_fidelity(drift, metrics): | |
| """Inherited-index alignment turns over while reconstruction stays flat.""" | |
| out = {} | |
| fin = drift[(drift.chain == "sae_sft") & (drift.step == 348)] | |
| for _, r in fin.iterrows(): | |
| out[f"L{int(r.layer)}"] = { | |
| "dec_cos_geom": r.dec_geom_all_cos_matched_mean, | |
| "dec_cos_readout": r.dec_readout_all_cos_matched_mean, | |
| "dec_cos_lens": r.dec_lens_all_cos_matched_mean, | |
| "tok_jaccard": r.tok_jaccard_matched_all, | |
| "frac_top1_identical": r.frac_identical_top1_all, | |
| "frac_top1_changed": 1 - r.frac_identical_top1_all, | |
| } | |
| f = metrics[(metrics.chain == "sft") & (metrics.layer == 18)] | |
| out["L18_fidelity_over_sft"] = { | |
| "nmse_min": f.nmse_recomputed.min(), "nmse_max": f.nmse_recomputed.max(), | |
| "frac_rec_min": f.frac_rec.min(), "frac_rec_max": f.frac_rec.max(), | |
| "dead_pct_max": f.dead_latents_pct.max(), | |
| } | |
| out["dead_pct_max_all_chains"] = metrics.dead_latents_pct.max() | |
| return out | |
| def metric_agreement(drift): | |
| """The three inner products must agree, else the geometry story is metric-dependent.""" | |
| cols = ["dec_geom_all_drift_norm", "dec_readout_all_drift_norm", | |
| "dec_lens_all_drift_norm"] | |
| d = drift[drift.step > 0].copy() | |
| d["spread"] = d[cols].max(axis=1) - d[cols].min(axis=1) | |
| per_cell = d.groupby(["chain", "layer"]).spread.max() | |
| return { | |
| "n_cells": int(len(per_cell)), | |
| "max_spread": per_cell.max(), | |
| "median_spread": per_cell.median(), | |
| "argmax_cell": "/".join(str(x) for x in per_cell.idxmax()), | |
| } | |
| def null_step(reps): | |
| """How much of a real first transition a warm-started retraining reproduces.""" | |
| nulls, out = {}, {} | |
| for L, g in reps[reps.kind == "nullstep"].groupby("layer"): | |
| nulls[L] = {"n": int(len(g)), "drift_mean": g.drift.mean(), | |
| "drift_min": g.drift.min(), "drift_max": g.drift.max(), | |
| "tokJ_mean": g.tok_jaccard.mean()} | |
| shares = [] | |
| for _, r in reps[reps.kind == "real"].iterrows(): | |
| n = nulls[r.layer] | |
| shares.append({ | |
| "layer": int(r.layer), "chain": r.label, | |
| "real_drift": r.drift, "null_drift_mean": n["drift_mean"], | |
| "pct_mean": 100 * n["drift_mean"] / r.drift, | |
| "pct_lo": 100 * n["drift_min"] / r.drift, | |
| "pct_hi": 100 * n["drift_max"] / r.drift, | |
| # top-token turnover share: (1 - J_null) / (1 - J_real) | |
| "tok_pct": 100 * (1 - n["tokJ_mean"]) / (1 - r.tok_jaccard), | |
| }) | |
| out["null_replicates"] = {f"L{k}": v for k, v in nulls.items()} | |
| out["share_of_real"] = shares | |
| # Headline range, excluding L12 (null replicates there are unstable: see | |
| # drift_min/drift_max) and excluding cells where the real transition drifts | |
| # *less* than the null step (pct > 100). | |
| core = [s for s in shares if s["layer"] != 12 and s["pct_mean"] <= 110] | |
| out["headline_pct_range"] = [min(s["pct_mean"] for s in core), | |
| max(s["pct_mean"] for s in core)] | |
| out["headline_tok_pct_range"] = [min(s["tok_pct"] for s in core), | |
| max(s["tok_pct"] for s in core)] | |
| out["exceeds_100"] = [f"{s['chain']}@L{s['layer']}={s['pct_mean']:.0f}%" | |
| for s in shares if s["pct_mean"] > 110] | |
| return out | |
| def null_trajectory(traj, drift): | |
| """A whole trajectory of retrainings, with the model held fixed throughout.""" | |
| real = (drift[(drift.chain == "sae_sft") & (drift.layer == 18)] | |
| .sort_values("step").reset_index(drop=True)) | |
| rows = [] | |
| for i, r in traj.sort_values("null_step").iterrows(): | |
| rr = real.iloc[i] if i < len(real) else None | |
| rows.append({ | |
| "step_index": int(r.null_step), | |
| "null_dec_cos": r.dec_cos, "null_tok_jaccard": r.tok_jaccard, | |
| "real_step": int(rr.step) if rr is not None else None, | |
| "real_dec_cos": rr.dec_geom_all_cos_matched_mean if rr is not None else None, | |
| "real_tok_jaccard": rr.tok_jaccard_matched_all if rr is not None else None, | |
| }) | |
| end = rows[-1] | |
| return { | |
| "per_step": rows, | |
| "final_null_cos": end["null_dec_cos"], "final_real_cos": end["real_dec_cos"], | |
| "final_null_tokJ": end["null_tok_jaccard"], | |
| "final_real_tokJ": end["real_tok_jaccard"], | |
| # Fraction of the real end-to-end drift reproduced with the model frozen. | |
| "pct_drift_reproduced": 100 * (1 - end["null_dec_cos"]) / (1 - end["real_dec_cos"]), | |
| "pct_tok_turnover_reproduced": | |
| 100 * (1 - end["null_tok_jaccard"]) / (1 - end["real_tok_jaccard"]), | |
| } | |
| def shuffled_control(drift, task): | |
| """A reward carrying no task information, at a matched optimiser step.""" | |
| out = {"accuracy": {}} | |
| for c, g in task.groupby("chain"): | |
| g = g.sort_values("step") | |
| out["accuracy"][c] = {"start": g[ACC].iloc[0], "final": g[ACC].iloc[-1], | |
| "max": g[ACC].max()} | |
| for step in (10, 30): | |
| p = (drift[drift.step == step] | |
| .pivot_table(index="chain", columns="layer", | |
| values="dec_geom_all_drift_norm")) | |
| cell = {} | |
| for ref in ("sae_flexible", "sae_strict/k64"): | |
| ratios = (p.loc["sae_shuffled"] / p.loc[ref]) | |
| cell[ref] = {f"L{int(k)}": v for k, v in ratios.items()} | |
| out[f"step{step}_shuffled_over_informative"] = cell | |
| out[f"step{step}_drift_norm"] = { | |
| ch: {f"L{int(k)}": v for k, v in row.items()} for ch, row in p.iterrows()} | |
| return out | |
| def heterogeneity(per_feat, z): | |
| """Stability is not uniform, and our selection heuristic rides the tail.""" | |
| d = per_feat.merge(z, on=["chain", "layer", "feature"]) | |
| out = {"spearman_final_sft": {}, "selection_bias_final_sft": {}} | |
| for L in (6, 12, 18, 23): | |
| s = d[(d.chain == "sae_sft") & (d.layer == L) & (d.step == 348)] | |
| out["spearman_final_sft"][f"L{L}"] = { | |
| "cos_vs_density": spearmanr(s.dec_geom_cos, s.density_base).statistic, | |
| "cos_vs_peakedness": spearmanr(s.dec_geom_cos, s.logit_z).statistic, | |
| "tokJ_vs_density": spearmanr(s.tok_jaccard, s.density_base).statistic, | |
| "tokJ_vs_peakedness": spearmanr(s.tok_jaccard, s.logit_z).statistic, | |
| } | |
| # The sweep's own heuristic: frequency band, then top-n by peakedness. | |
| pool = s[(s.density_base > 0.05) & (s.density_base < 0.5)] | |
| sel = pool.nlargest(12, "logit_z") | |
| out["selection_bias_final_sft"][f"L{L}"] = { | |
| "n_pool": int(len(pool)), | |
| "sel_tokJ": sel.tok_jaccard.mean(), "pop_tokJ": s.tok_jaccard.mean(), | |
| "tokJ_ratio": sel.tok_jaccard.mean() / s.tok_jaccard.mean(), | |
| "sel_cos": sel.dec_geom_cos.mean(), "pop_cos": s.dec_geom_cos.mean(), | |
| } | |
| r = [v["tokJ_ratio"] for v in out["selection_bias_final_sft"].values()] | |
| out["tokJ_ratio_range"] = [min(r), max(r)] | |
| return out | |
| def _torch(): | |
| import torch | |
| return torch | |
| def _wdec(path): | |
| """Decoder directions [d_sae, d_model] from any of the on-disk SAE formats.""" | |
| import sys | |
| sys.path.insert(0, str(_find("sae_tuned_lens").parent)) | |
| from sae_tuned_lens.sae_io import load_sae_full | |
| return load_sae_full(str(path))["W_dec"].float() | |
| def _cos(A, B): | |
| return _torch().nn.functional.cosine_similarity(A, B, dim=-1) | |
| ROOT = _find("drift_run") | |
| SFT_SAES = _find("sae_rl", "checkpoints", "saes_sft") | |
| def direction_of_drift(): | |
| """Does the null step move the basis the same WAY, or only as far? (§4.5) | |
| A share of drift is a magnitude claim. If retraining and fine-tuning rotated | |
| latents by similar amounts into unrelated subspaces, the shares would hold while | |
| retraining explained none of the real chain's particular movement. | |
| """ | |
| torch = _torch() | |
| base = _wdec(ROOT / "saes_local/sae_sft/sae_instruct_base_layer18.pt") | |
| out = {} | |
| cases = [ | |
| ("one_retraining", ROOT / "null_ctrl/sae_instruct_base_layer18_nullstep.pt", | |
| ROOT / "saes_local/sae_sft/sae_sft_step29_layer18.pt"), | |
| ("seven_retrainings", ROOT / "null_chain/sae_nullchain7_layer18.pt", | |
| ROOT / "saes_local/sae_sft/sae_sft_step348_layer18.pt"), | |
| ] | |
| for label, npath, rpath in cases: | |
| if not (npath.exists() and rpath.exists()): | |
| out[label] = "weights absent" | |
| continue | |
| nul, real = _wdec(npath), _wdec(rpath) | |
| c_bn = _cos(base, nul).mean().item() | |
| c_br = _cos(base, real).mean().item() | |
| c_nr = _cos(nul, real).mean().item() | |
| n = base.shape[0] | |
| g = torch.Generator().manual_seed(0) | |
| perm = torch.randperm(n, generator=g) | |
| idx = torch.arange(n) | |
| perm[perm == idx] = (perm[perm == idx] + 1) % n | |
| out[label] = { | |
| "cos_base_null": c_bn, "cos_base_real": c_br, | |
| "cos_null_real_observed": c_nr, | |
| "cos_null_real_if_independent": c_bn * c_br, | |
| "random_pairing_floor": _cos(nul, real[perm]).mean().item(), | |
| "excess_over_independent": c_nr - c_bn * c_br, | |
| } | |
| return out | |
| def activation_budgets(): | |
| """Optimisation budget of the null step vs each chain's own transitions (§3.3).""" | |
| torch = _torch() | |
| act = _find("sae_rl", "data") | |
| out = {} | |
| for sub, stem in [("activations", "instruct_base_layer18_train.pt"), | |
| ("activations", "ppo_step10_layer18_train.pt"), | |
| ("activations_sft", "sft_step29_layer18_train.pt"), | |
| ("activations_shuffled", "ppo_step10_layer18_train.pt"), | |
| ("activations_kl0p025", "ppo_step10_layer18_train.pt")]: | |
| p = act / sub / stem | |
| if not p.exists(): | |
| continue | |
| n = torch.load(p, map_location="cpu", mmap=True).shape[0] | |
| out[f"{sub}/{stem}"] = {"tokens": int(n), "batches_at_512": n // 512, | |
| "adam_steps_20_epochs": (n // 512) * 20} | |
| base = out.get("activations/instruct_base_layer18_train.pt") | |
| sft = out.get("activations_sft/sft_step29_layer18_train.pt") | |
| if base and sft: | |
| out["null_step_shortfall_vs_sft_pct"] = 100 * ( | |
| 1 - base["adam_steps_20_epochs"] / sft["adam_steps_20_epochs"]) | |
| out["epochs_to_match_sft"] = 20 * sft["batches_at_512"] / base["batches_at_512"] | |
| return out | |
| def budget_sweep(drift): | |
| """Nominal budget sweep, interpreted as LR-horizon sensitivity (§4.4).""" | |
| real = drift[(drift.chain == "sae_sft") & (drift.layer == 18)].sort_values("step") | |
| real_drift = 1 - real.dec_geom_all_cos_matched_mean.iloc[1] | |
| base = ROOT / "saes_local/sae_sft/sae_instruct_base_layer18.pt" | |
| if not base.exists(): | |
| return "weights absent" | |
| W0 = _wdec(base) | |
| rows = {} | |
| for p in sorted(glob.glob(str(ROOT / "null_budget/sae_instruct_base_layer18_e*_s*.pt"))): | |
| m = re.search(r"_e(\d+)_s(\d+)", p) | |
| rows.setdefault(int(m.group(1)), []).append(1 - _cos(W0, _wdec(p)).mean().item()) | |
| out = {"real_first_transition_drift": real_drift, "by_epochs": {}} | |
| for E in sorted(rows): | |
| v = rows[E] | |
| out["by_epochs"][E] = { | |
| "adam_steps": E * 696, "n_seeds": len(v), | |
| "drift_mean": sum(v) / len(v), "drift_min": min(v), "drift_max": max(v), | |
| "pct_of_real": 100 * (sum(v) / len(v)) / real_drift, | |
| } | |
| return out | |
| def trajectories_per_layer(drift): | |
| """Null trajectories against the real SFT chain, per layer, over seeds (§4.4).""" | |
| out = {} | |
| for L in (6, 12, 18, 23): | |
| base = ROOT / f"saes_local/sae_sft/sae_instruct_base_layer{L}.pt" | |
| if not base.exists(): | |
| continue | |
| dirs = [d for d in sorted(glob.glob(str(ROOT / f"null_chain_all/layer{L}_seed*"))) | |
| if os.path.exists(os.path.join(d, "DONE"))] | |
| if L == 18: # the originally reported chain belongs to this population | |
| if (ROOT / "null_chain").exists(): | |
| dirs = [str(ROOT / "null_chain")] + dirs | |
| if not dirs: | |
| continue | |
| W0 = _wdec(base) | |
| real = (drift[(drift.chain == "sae_sft") & (drift.layer == L)] | |
| .sort_values("step").reset_index(drop=True)) | |
| rf = real.dec_geom_all_cos_matched_mean.iloc[-1] | |
| ends = [] | |
| for d in dirs: | |
| ch = sorted(glob.glob(f"{d}/sae_nullchain*_layer{L}.pt"), | |
| key=lambda q: int(re.search(r"nullchain(\d+)", q).group(1))) | |
| if len(ch) < len(real): | |
| continue | |
| ends.append(_cos(W0, _wdec(ch[len(real) - 1])).mean().item()) | |
| if not ends: | |
| continue | |
| pct = [100 * (1 - e) / (1 - rf) for e in ends] | |
| out[f"L{L}"] = { | |
| "n_chains": len(ends), "real_end_cos": rf, | |
| "null_end_cos_mean": sum(ends) / len(ends), | |
| "null_end_cos_min": min(ends), "null_end_cos_max": max(ends), | |
| "pct_reproduced_mean": sum(pct) / len(pct), | |
| "pct_reproduced_min": min(pct), "pct_reproduced_max": max(pct), | |
| } | |
| return out | |
| def persistent_null(drift, per_feat): | |
| """The null step restricted to evaluation-persistent latents (§4.3). | |
| The mask is based on firing density over shared frozen-base evaluation | |
| activations; it does not record whether a latent was resampled in training. | |
| Without this, a persistent-subset real drift gets divided by an all-latent null and | |
| the share is wrong. | |
| """ | |
| torch = _torch() | |
| first = {"sae_sft": 29, "sae_flexible": 10, "sae_strict/k64": 10} | |
| out = {} | |
| for L in (6, 12, 18, 23): | |
| base = ROOT / f"saes_local/sae_sft/sae_instruct_base_layer{L}.pt" | |
| if not base.exists(): | |
| continue | |
| W0 = _wdec(base) | |
| # Every seed on disk, not a fixed subset: the seed count is what makes the | |
| # published shares reproducible, and s10/s11 sort after s9 only numerically. | |
| paths = [ROOT / f"null_ctrl/sae_instruct_base_layer{L}_nullstep.pt"] | |
| paths += [pathlib.Path(p) for p in | |
| sorted(glob.glob(str(ROOT / f"null_seeds/sae_instruct_base_layer{L}_null_s*.pt")), | |
| key=lambda q: int(re.search(r"_null_s(\d+)", q).group(1)))] | |
| cs = [_cos(W0, _wdec(p)) for p in paths if p.exists()] | |
| if not cs: | |
| continue | |
| for ch, st in first.items(): | |
| row = drift[(drift.chain == ch) & (drift.layer == L) & (drift.step == st)] | |
| m = per_feat[(per_feat.chain == ch) & (per_feat.layer == L) & (per_feat.step == st)] | |
| if not len(row) or not len(m): | |
| continue | |
| row = row.iloc[0] | |
| idx = torch.tensor(m.sort_values("feature").persistent.values.astype(bool)) | |
| na = sum(1 - c.mean().item() for c in cs) / len(cs) | |
| npr = sum(1 - c[idx].mean().item() for c in cs) / len(cs) | |
| ra = 1 - row.dec_geom_all_cos_matched_mean | |
| rp = 1 - row.dec_geom_persist_cos_matched_mean | |
| out[f"L{L}/{ch}"] = { | |
| "n_persist": int(idx.sum()), "null_all": na, "null_persist": npr, | |
| "real_all": ra, "real_persist": rp, | |
| "pct_all": 100 * na / ra, "pct_persist": 100 * npr / rp, | |
| } | |
| return out | |
| def architectures(): | |
| """Does the confound depend on the SAE parameterisation? (Jacob's grid, L12.)""" | |
| p = ROOT / "arch_null/arch_null_trajectory.csv" | |
| if not p.exists(): | |
| return "not run" | |
| a = pd.read_csv(p) | |
| last = a.null_step.max() | |
| return {r.arm: {"arch": r.arch, "final_dec_cos": r.dec_cos, | |
| "final_tok_jaccard": r.tok_jaccard, "mean_l0": r.mean_l0, | |
| "val_mse": r.val_mse, "steps": int(last)} | |
| for _, r in a[a.null_step == last].iterrows()} | |
| def fidelity_detail(metrics, drift): | |
| """The Table 1 / §4.1 numbers the earlier version of this script did not emit.""" | |
| fin = metrics[(metrics.chain == "sft")].copy() | |
| out = {"sft_final_nmse_by_layer": {}, "sft_mse_base_to_final": {}, | |
| "persist_counts": {}, "layer23_nmse_all_runs": {}} | |
| for L in (6, 12, 18, 23): | |
| s = fin[fin.layer == L].sort_values("step") | |
| if not len(s): | |
| continue | |
| out["sft_final_nmse_by_layer"][f"L{L}"] = s.nmse_recomputed.iloc[-1] | |
| out["sft_mse_base_to_final"][f"L{L}"] = [s.mse.iloc[0], s.mse.iloc[-1]] | |
| n = drift.n_persist | |
| out["persist_counts"] = {"min": int(n.min()), "max": int(n.max()), | |
| "d_sae": int(drift.n_features.iloc[0])} | |
| l23 = metrics[metrics.layer == 23] | |
| out["layer23_nmse_all_runs"] = {"min": l23.nmse_recomputed.min(), | |
| "max": l23.nmse_recomputed.max()} | |
| return out | |
| def base_alignment(): | |
| """Cross-chain latent indices are only comparable if the base SAE is shared.""" | |
| import hashlib | |
| import sys | |
| sys.path.insert(0, str(_find("sae_tuned_lens").parent)) | |
| from sae_tuned_lens.sae_io import load_sae_full | |
| root = _find("drift_run", "saes_local") | |
| chains = ["sae_sft", "sae_flexible", "sae_strict/k64", "sae_shuffled", "sae_kl0p025"] | |
| out = {} | |
| for L in (6, 12, 18, 23): | |
| hashes = {} | |
| for c in chains: | |
| p = root / c / f"sae_instruct_base_layer{L}.pt" | |
| if not p.exists(): | |
| hashes[c] = None | |
| continue | |
| W = load_sae_full(str(p))["W_dec"].float().cpu().numpy() | |
| hashes[c] = hashlib.md5(W.tobytes()).hexdigest()[:12] | |
| ref = hashes["sae_sft"] | |
| out[f"L{L}"] = {"hashes": hashes, | |
| "shares_base": [c for c, h in hashes.items() if h == ref], | |
| "own_init": [c for c, h in hashes.items() | |
| if h is not None and h != ref]} | |
| always_own = [c for c in chains | |
| if all(c in out[f"L{L}"]["own_init"] for L in (6, 12, 18, 23))] | |
| out["own_init_at_every_layer"] = always_own | |
| out["n_chains_own_init"] = len(always_own) | |
| return out | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--json", action="store_true") | |
| ap.add_argument("--skip-weights", "--skip-alignment", dest="skip_weights", | |
| action="store_true", | |
| help="skip every section that loads SAE weights (hashes, drift " | |
| "direction, budget sweep, per-layer trajectories, persistent null)") | |
| args = ap.parse_args() | |
| drift = pd.read_csv(DRIFT / "drift_by_stage.csv") | |
| per_feat = pd.read_csv(DRIFT / "drift_per_feature.csv") | |
| z = pd.read_csv(DRIFT / "base_feature_z.csv") | |
| reps = pd.read_csv(DRIFT / "null_replicates.csv") | |
| traj = pd.read_csv(DRIFT / "null_trajectory_l18.csv") | |
| metrics = pd.read_csv(RESULTS / "sae_checkpoint_metrics.csv") | |
| task = pd.read_csv(RESULTS / "task_performance.csv") | |
| report = { | |
| "artifact": artifact(drift), | |
| "identity_vs_fidelity": identity_vs_fidelity(drift, metrics), | |
| "fidelity_detail": fidelity_detail(metrics, drift), | |
| "metric_agreement": metric_agreement(drift), | |
| "null_step": null_step(reps), | |
| "null_trajectory_l18": null_trajectory(traj, drift), | |
| "shuffled_control": shuffled_control(drift, task), | |
| "heterogeneity": heterogeneity(per_feat, z), | |
| "activation_budgets": activation_budgets(), | |
| "architectures": architectures(), | |
| } | |
| if not args.skip_weights: | |
| report["direction_of_drift"] = direction_of_drift() | |
| report["budget_sweep"] = budget_sweep(drift) | |
| report["trajectories_per_layer"] = trajectories_per_layer(drift) | |
| report["persistent_null"] = persistent_null(drift, per_feat) | |
| report["base_alignment"] = base_alignment() | |
| report = _round(report) | |
| if args.json: | |
| print(json.dumps(report, indent=2, default=str)) | |
| else: | |
| for section, body in report.items(): | |
| print(f"\n{'=' * 70}\n{section}\n{'=' * 70}") | |
| print(json.dumps(body, indent=2, default=str)) | |
| if __name__ == "__main__": | |
| main() | |