SPB Git

spb/modelmap Public License

Internal cartography of local LLMs on Apple Silicon — registered, gated, negative-first. Public atlas at modelmap.io.

Python 66.3% JavaScript 24.5% CSS 8.1% Shell 0.7%
8.3 KB · 187 lines python
Raw Blame History
1#!/usr/bin/env python32# =============================================================================3#  Project   : modelmap4#  File      : experiments/micro/expA_probe_reliability/implementation/benchmark_v2.py5#  Purpose   : expA run #2 — structure-borne, token-balanced properties6#  Author    : Simon-Pierre Boucher7#  Contact   : contact@spboucher.ai8#  Website   : https://modelmap.io9#  Created   : 2026-08-1210#  Modified  : 2026-08-1211#  Platform  : macOS / Apple Silicon (arm64) — MLX / Metal12#  License   : All rights reserved (research code)13# =============================================================================14"""expA run #2 (hypothesis block registered in hypothesis.md before this run).1516v2 promptsets (word_order / agreement / arith_valid, token-balanced classes),17BOTH mean-pooled and last-token representations, random-init twin on A sets,18plus the v1 lang_id_A positive control (harness must still hit ceiling there).19"""2021from __future__ import annotations2223import json24import subprocess25import sys26import time27from pathlib import Path2829import numpy as np3031ROOT = Path(__file__).resolve().parents[4]32sys.path.insert(0, str(ROOT / "src"))33sys.path.insert(0, str(ROOT / "benchmarks"))34from hardware_manifest import manifest3536from modelmap.capture.mlx_capture import capture_pooled, install_taps, random_init_twin37from modelmap.probes.linear import probe_with_control38from modelmap.stats.replication import bh_fdr, bootstrap_ci, replication_rate3940MODEL = "mlx-community/Qwen3-0.6B-4bit"41PROPERTIES = ("word_order", "agreement", "arith_valid")42SETS = ("A", "B")43POOLINGS = ("mean", "last")44SEEDS = (0, 1, 2, 3, 4)45TOP_K = 546FDR_Q = 0.0547PROMPTS = ROOT / "benchmarks" / "promptsets"484950def load_set(name: str):51    items = [json.loads(l) for l in (PROMPTS / f"{name}.jsonl").read_text().splitlines()]52    return [it["text"] for it in items], np.array([it["label"] for it in items])535455def probe_grid(reps, labels):56    out = []57    for layer in range(reps.shape[1]):58        for seed in SEEDS:59            r = probe_with_control(reps[:, layer, :], labels, seed=seed)60            out.append({"layer": layer, "seed": seed, "task_acc": r.task_accuracy,61                        "control_acc": r.control_accuracy, "selectivity": r.selectivity})62    return out636465def summarize(cells, n_layers):66    rng = np.random.default_rng(0)67    layers, pvals = [], []68    for layer in range(n_layers):69        sel = np.array([c["selectivity"] for c in cells if c["layer"] == layer])70        acc = np.array([c["task_acc"] for c in cells if c["layer"] == layer])71        point, lo, hi = bootstrap_ci(sel, rng=rng)72        boots = rng.choice(sel, size=(10_000, sel.size)).mean(axis=1)73        p = float(max((boots <= 0).mean(), 1e-4))74        pvals.append(p)75        layers.append({"layer": layer, "task_acc_mean": float(acc.mean()),76                       "task_acc_seed_sd": float(acc.std(ddof=1)),77                       "selectivity_mean": point, "selectivity_ci": [lo, hi], "p_boot": p})78    disc = bh_fdr(np.array(pvals), q=FDR_Q)79    for row, d in zip(layers, disc):80        row["fdr_significant"] = bool(d)81    tops = []82    for seed in SEEDS:83        by = [(c["layer"], c["task_acc"]) for c in cells if c["seed"] == seed]84        tops.append({l for l, _ in sorted(by, key=lambda t: -t[1])[:TOP_K]})85    rp, rl, rh = replication_rate(tops)86    return {"layers": layers, "replication_rate_topk": {"k": TOP_K, "point": rp, "ci": [rl, rh]},87            "n_fdr_significant": int(disc.sum())}888990def main() -> int:91    import mlx.core as mx92    from mlx_lm import load93    from mlx_lm.utils import hf_repo_to_path9495    t0 = time.time()96    mx.random.seed(0)97    model, tokenizer = load(MODEL)98    taps = install_taps(model)99    twin = random_init_twin(hf_repo_to_path(MODEL))100    twin_taps = install_taps(twin)101102    results = {"real": {}, "twin": {}, "positive_control": {}}103104    # ---- positive control: v1 lang_id_A must still hit ceiling (mean pooling)105    texts, labels = load_set("lang_id_A")106    toks = [tokenizer.encode(t) for t in texts]107    reps = capture_pooled(model, taps, toks)108    pc = summarize(probe_grid(reps["mean"], labels), reps["mean"].shape[1])109    results["positive_control"]["lang_id_A_mean"] = {110        "max_task_acc": float(max(r["task_acc_mean"] for r in pc["layers"]))}111    print(f"positive control lang_id_A: maxAcc={results['positive_control']['lang_id_A_mean']['max_task_acc']:.3f}",112          flush=True)113114    grids = {"real": {}, "twin": {}}115    for prop in PROPERTIES:116        for s in SETS:117            name = f"{prop}_{s}"118            texts, labels = load_set(name)119            toks = [tokenizer.encode(t) for t in texts]120            print(f"capture real  {name}…", flush=True)121            reps = capture_pooled(model, taps, toks)122            for pool in POOLINGS:123                grids["real"][f"{name}_{pool}"] = (probe_grid(reps[pool], labels),124                                                   reps[pool].shape[1])125            if s == "A":126                print(f"capture twin  {name}…", flush=True)127                reps_t = capture_pooled(twin, twin_taps, toks)128                for pool in POOLINGS:129                    grids["twin"][f"{name}_{pool}"] = (probe_grid(reps_t[pool], labels),130                                                       reps_t[pool].shape[1])131132    for kind in ("real", "twin"):133        for name, (cells, n_layers) in grids[kind].items():134            results[kind][name] = summarize(cells, n_layers)135            results[kind][name]["cells"] = cells136137    # ---- headline138    summary = {}139    for prop in PROPERTIES:140        for pool in POOLINGS:141            a = results["real"][f"{prop}_A_{pool}"]["layers"]142            b = results["real"][f"{prop}_B_{pool}"]["layers"]143            tw = results["twin"][f"{prop}_A_{pool}"]["layers"]144            seed_sd = float(np.mean([r["task_acc_seed_sd"] for r in a + b]))145            shifts = [abs(x["task_acc_mean"] - y["task_acc_mean"]) for x, y in zip(a, b)]146            diff = [x["selectivity_mean"] - t["selectivity_mean"] for x, t in zip(a, tw)]147            n_signal = int(sum(1 for x, t, d in148                               zip(a, tw, diff) if d > 0.10 and x["fdr_significant"]))149            summary[f"{prop}_{pool}"] = {150                "max_task_acc_A": float(max(r["task_acc_mean"] for r in a)),151                "max_task_acc_B": float(max(r["task_acc_mean"] for r in b)),152                "twin_max_selectivity": float(max(r["selectivity_mean"] for r in tw)),153                "twin_max_acc": float(max(r["task_acc_mean"] for r in tw)),154                "mean_seed_sd": seed_sd,155                "mean_dataset_shift": float(np.mean(shifts)),156                "layers_shift_gt_seed_sd": int(sum(s > max(seed_sd, 1e-9) for s in shifts)),157                "layers_real_minus_twin_gt_0.10": n_signal,158                "max_real_minus_twin_sel": float(max(diff)),159                "replication_topk_A": results["real"][f"{prop}_A_{pool}"]["replication_rate_topk"]["point"],160            }161            s = summary[f"{prop}_{pool}"]162            print(f"{prop:12s} {pool:4s} accA={s['max_task_acc_A']:.3f} twinAcc={s['twin_max_acc']:.3f} "163                  f"twinSel={s['twin_max_selectivity']:.3f} signalLayers={s['layers_real_minus_twin_gt_0.10']} "164                  f"maxΔsel={s['max_real_minus_twin_sel']:+.3f} shift>{'sd'}={s['layers_shift_gt_seed_sd']}/28",165                  flush=True)166167    commit = subprocess.run(["git", "rev-parse", "HEAD"], cwd=ROOT,168                            capture_output=True, text=True, check=False).stdout.strip()169    ts = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())170    outdir = ROOT / "results" / "expA_probe_reliability" / ts171    outdir.mkdir(parents=True)172    doc = {"experiment": "expA_probe_reliability", "run": 2,173           "scope": "v2 structure-borne token-balanced properties, mean+last pooling, twin null",174           "commit": commit,175           "config": {"model": MODEL, "seeds": list(SEEDS), "top_k": TOP_K, "fdr_q": FDR_Q,176                      "poolings": list(POOLINGS),177                      "promptsets": json.loads((PROMPTS / "manifest_v2.json").read_text())},178           "manifest": manifest(), "summary": summary, "results": results,179           "wall_seconds": round(time.time() - t0, 1)}180    (outdir / "results.json").write_text(json.dumps(doc, indent=2) + "\n")181    print(f"results -> {outdir / 'results.json'}  ({doc['wall_seconds']} s)")182    return 0183184185if __name__ == "__main__":186    sys.exit(main())187