Download code/scripts/plot_runs.py from thegovind/weight-space-programming: direct link, hf CLI and curl.
- Browser
- Download file 8.91 kB
-
https://huggingface.co/spaces/thegovind/weight-space-programming/resolve/main/code/scripts/plot_runs.py
- Command line
-
hf download hf://spaces/thegovind/weight-space-programming/code/scripts/plot_runs.py
-
curl -L -o plot_runs.py https://huggingface.co/spaces/thegovind/weight-space-programming/resolve/main/code/scripts/plot_runs.py
8.91 kB
| #!/usr/bin/env python | |
| """Draw the figures for Labs 2, 4 and 5 from the finished runs. | |
| python scripts/summarize.py results/runs # first: writes results/summary.json | |
| uv run --with matplotlib python scripts/plot_runs.py # any machine, a few seconds | |
| Reads results/summary.json, the answer lengths in results/runs/**/merged.jsonl and Lab 7's | |
| results/lab7/side_effects.json, and writes results/figures/lab2_*.png, lab4_*.png and lab5_*.png. | |
| Runs that have not finished yet are left out. | |
| """ | |
| import json | |
| import os | |
| import matplotlib | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt # noqa: E402 | |
| ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| RES = os.path.join(ROOT, "results") | |
| FIG = os.path.join(RES, "figures") | |
| os.makedirs(FIG, exist_ok=True) | |
| S = {r["name"]: r["summary"] for r in json.load(open(os.path.join(RES, "summary.json")))} | |
| SIDE = json.load(open(os.path.join(RES, "lab7", "side_effects.json")))["models"] | |
| # the blog's think35 numbers: accuracy %, cut at the cap %, mean tokens | |
| PAPER = {"math500": {"base": (7.9, 93.0, 30868), "p50c": (48.1, 33.05, 22037)}, | |
| "gsm8k": {"base": (13.84, 87.5, 29289), "p50c": (67.1, 7.24, 11993)}, | |
| "ifeval": {"base": (21.0, 74.9, 25296), "p50c": (62.3, 2.2, 4241)}} | |
| GRAY, GREEN, RED, PURPLE, BLUE = "#8c959f", "#1a7f37", "#cf222e", "#8250df", "#0969da" | |
| METRICS = [("acc", "accuracy %", 100), ("cut_share", "answers cut at the token cap %", 100), | |
| ("mean_tokens", "mean answer length (thousand tokens)", 1e-3)] | |
| def val(s, k, scale): | |
| return None if s is None or s.get(k) is None else s[k] * scale | |
| def err(s, k): | |
| return 100 * s["acc_se"] if k == "acc" and s and s.get("acc_se") is not None else 0 | |
| def lengths(name): | |
| p = os.path.join(RES, "runs", name, "merged.jsonl") | |
| if not os.path.exists(p): | |
| return None | |
| return [s["tokens"] for line in open(p) for s in json.loads(line)["samples"]] | |
| made = [] | |
| # Lab 2: the paper's benchmarks, ours next to the blog's | |
| bench = [b for b in PAPER if f"bench/base/{b}" in S or f"bench/p50c/{b}" in S] | |
| if bench: | |
| fig, ax = plt.subplots(1, 3, figsize=(15, 4.2)) | |
| w = 0.2 | |
| for i, (k, lab, scale) in enumerate(METRICS): | |
| for j, b in enumerate(bench): | |
| for o, (model, src, col, hatch) in enumerate([("base", "ours", GRAY, None), ("base", "paper", GRAY, "//"), | |
| ("p50c", "ours", GREEN, None), | |
| ("p50c", "paper", GREEN, "//")]): | |
| if src == "ours": | |
| s = S.get(f"bench/{model}/{b}") | |
| v, e = val(s, k, scale), err(s, k) | |
| else: | |
| v, e = PAPER[b][model][i] * (1e-3 if k == "mean_tokens" else 1), 0 | |
| if v is None: | |
| continue | |
| ax[i].bar(j + (o - 1.5) * w, v, w, yerr=e or None, color=col, hatch=hatch, edgecolor="white", | |
| label=f"{'unedited' if model == 'base' else 'edited'}, {src}" if j == 0 else None) | |
| ax[i].set_xticks(range(len(bench)), [b.upper().replace("MATH500", "MATH-500") for b in bench]) | |
| ax[i].set_title(lab) | |
| ax[0].legend(fontsize=8) | |
| fig.suptitle("The paper's three benchmarks: our runs (solid) next to the blog's numbers (hatched)") | |
| fig.tight_layout() | |
| fig.savefig(os.path.join(FIG, "lab2_benchmarks.png"), dpi=130) | |
| made.append("lab2_benchmarks.png") | |
| pairs = [(n, c) for n, c in (("dev/base", GRAY), ("dev/p50c", GREEN)) if lengths(n)] | |
| if pairs: | |
| fig, ax = plt.subplots(figsize=(10, 3.8)) | |
| for n, c in pairs: | |
| ax.hist(lengths(n), bins=64, range=(0, 32768), color=c, alpha=0.6, | |
| label=f"{'unedited' if 'base' in n else 'edited'} ({n})") | |
| ax.set_yscale("log") | |
| ax.set_xlabel("answer length in tokens (the cap is 32,768)") | |
| ax.set_ylabel("answers") | |
| ax.set_title("Where the answers end: the tall bar at the right edge is answers that never stopped") | |
| ax.legend() | |
| fig.tight_layout() | |
| fig.savefig(os.path.join(FIG, "lab2_lengths.png"), dpi=130) | |
| made.append("lab2_lengths.png") | |
| # Lab 4: turn the knob, then the controls | |
| DOSE = [(-4, "a-4"), (0, "base"), (1, "a1"), (2, "a2"), (4, "a4"), (6, "p50c"), (8, "a8"), (12, "a12")] | |
| dose = [(x, S[f"dev/{n}"], SIDE.get(n)) for x, n in DOSE if f"dev/{n}" in S] | |
| if len(dose) >= 3: | |
| fig, ax = plt.subplots(1, 4, figsize=(17, 4)) | |
| xs = [d[0] for d in dose] | |
| for i, (k, lab, scale) in enumerate(METRICS): | |
| ax[i].errorbar(xs, [val(d[1], k, scale) for d in dose], yerr=[err(d[1], k) for d in dose], fmt="o-", | |
| color=BLUE, capsize=3, label="released u and 64 neurons") | |
| for n, dx, mk, col, lab2 in (("rebuilt", 0.35, "s", RED, "your Lab 3 rebuild"), | |
| ("capA-a6", 0.7, "^", PURPLE, "capstone, recipe A"), | |
| ("capB-a6", 1.05, "v", PURPLE, "capstone, recipe B")): | |
| if f"dev/{n}" in S: | |
| ax[i].errorbar([6 + dx], [val(S[f"dev/{n}"], k, scale)], yerr=[err(S[f"dev/{n}"], k)], fmt=mk, | |
| color=col, mfc="none", capsize=3, label=f"{lab2} (alpha 6, drawn to the right)") | |
| ax[i].set_title(lab) | |
| sx = [d[0] for d in dose if d[2]] | |
| ax[3].plot(sx, [d[2]["ppl_change_pct"] for d in dose if d[2]], "o-", color=RED) | |
| if SIDE.get("rebuilt"): | |
| ax[3].plot([6.35], [SIDE["rebuilt"]["ppl_change_pct"]], "s", color=RED, mfc="none") | |
| ax[3].axhline(0, color="black", lw=0.5) | |
| ax[3].set_title("side effect: perplexity change % (Lab 7)") | |
| for a_ in ax: | |
| a_.set_xlabel("alpha (0 = no edit, 1 = erase, 2 = mirror, 6 = released)") | |
| a_.set_xticks([-4, 0, 1, 2, 4, 6, 8, 12]) | |
| h, lb = ax[0].get_legend_handles_labels() | |
| fig.legend(h, lb, loc="lower center", ncol=len(lb), fontsize=9, frameon=False) | |
| fig.suptitle("Turning the knob: same direction and neurons, different alpha (256 unseen practice problems)") | |
| fig.tight_layout(rect=(0, 0.07, 1, 1)) | |
| fig.savefig(os.path.join(FIG, "lab4_dose.png"), dpi=130) | |
| made.append("lab4_dose.png") | |
| CTRL = [("base", "no edit", GRAY), ("p50c", "released edit", GREEN), ("rebuilt", "your Lab 3 rebuild", GREEN), | |
| ("capA-a6", "capstone A: own loops, just before", PURPLE), ("capB-a6", "capstone B: own loops, well before", | |
| PURPLE), | |
| ("l15only", "layer 15 only", BLUE), ("l19only", "layer 19 only", BLUE), | |
| ("all1", "erase u from all 3,584 neurons", BLUE), ("all2", "mirror u in all 3,584 neurons", BLUE), | |
| ("randdir", "control: random direction", RED), ("randcols", "control: 64 random neurons", RED)] | |
| ctrl = [(lab, S[f"dev/{n}"], c) for n, lab, c in CTRL if f"dev/{n}" in S] | |
| if len(ctrl) >= 3: | |
| fig, ax = plt.subplots(1, 3, figsize=(17, 0.45 * len(ctrl) + 1.5), sharey=True) | |
| ys = list(range(len(ctrl)))[::-1] | |
| for i, (k, lab, scale) in enumerate(METRICS): | |
| ax[i].barh(ys, [val(s, k, scale) for _, s, _ in ctrl], | |
| xerr=[err(s, k) for _, s, _ in ctrl] if k == "acc" else None, | |
| color=[c for *_, c in ctrl], capsize=2) | |
| ax[i].set_title(lab) | |
| ax[0].set_yticks(ys, [lab for lab, *_ in ctrl]) | |
| fig.suptitle("Which parts of the edit matter? Same 256 unseen practice problems, 2 answers each") | |
| fig.tight_layout() | |
| fig.savefig(os.path.join(FIG, "lab4_controls.png"), dpi=130) | |
| made.append("lab4_controls.png") | |
| # Lab 5: decoding fixes on the unedited model vs the weight edit | |
| DEC = [("base", "unedited, paper settings", GRAY), ("base+temp1.0", "temperature 1.0 (from 0.6)", BLUE), | |
| ("base+pp0", "presence penalty 0 (from 1.5)", BLUE), ("base+pp2", "presence penalty 2.0", BLUE), | |
| ("base+rep1.1", "repetition penalty 1.1", BLUE), ("base+max4k", "cap at 4,096 tokens", BLUE), | |
| ("base+nothink", "thinking off (cap 8,192)", BLUE), ("p50c", "edited, paper settings", GREEN), | |
| ("p50c+nothink", "edited, thinking off", GREEN)] | |
| dec = [(lab, S[f"dev/{n}"], c) for n, lab, c in DEC if f"dev/{n}" in S] | |
| if len(dec) >= 3: | |
| fig, ax = plt.subplots(1, 3, figsize=(17, 0.45 * len(dec) + 1.5), sharey=True) | |
| ys = list(range(len(dec)))[::-1] | |
| for i, (k, lab, scale) in enumerate(METRICS): | |
| ax[i].barh(ys, [val(s, k, scale) for _, s, _ in dec], | |
| xerr=[err(s, k) for _, s, _ in dec] if k == "acc" else None, | |
| color=[c for *_, c in dec], capsize=2) | |
| ax[i].set_title(lab) | |
| ax[0].set_yticks(ys, [lab for lab, *_ in dec]) | |
| fig.suptitle("Can decoding settings do what the weight edit does? Same 256 unseen practice problems") | |
| fig.tight_layout() | |
| fig.savefig(os.path.join(FIG, "lab5_decoding.png"), dpi=130) | |
| made.append("lab5_decoding.png") | |
| print("wrote", ", ".join(made) if made else "nothing yet (no finished runs)") | |