dropout-decay / scripts /generate_paper_assets.py
Mandeep Sidhu
Update v1.1 preprint citation and DOI
e55f912
Raw
History Blame Contribute Delete
35.6 kB
#!/usr/bin/env python3
"""
Derived from Andrej Karpathy's nanochat project.
MIT License
Copyright (c) 2025 Andrej Karpathy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"""
from __future__ import annotations
import csv
import json
import math
from pathlib import Path
import random
import statistics
ROOT = Path(__file__).resolve().parents[1]
PAPER_DIR = ROOT / "paper"
FIG_DIR = PAPER_DIR / "figures"
TABLE_DIR = PAPER_DIR / "tables"
REGIMES = {
"openweb": {
"label": "OpenWebText10K",
"short": "OWT10K",
"coeff": ROOT
/ "runs/coefficient_calibration/cross_regime_backtest/openwebtext10k_main_interaction/coefficients.json",
"cells": ROOT
/ "runs/coefficient_calibration/cross_regime_backtest/openwebtext10k_main_interaction/calibration_cells.csv",
"curve_run": ROOT / "runs/screen_static/20260525-133008",
"curve_model": "L12_H8_D320",
"curve_prefix": 1_000_000,
"curve_title": "OpenWebText10K, L12, 1M",
"small_stream": ROOT
/ "runs/openwebtext10k_l16_updated_formula_clean_5seed/locked_stream/20260530-174525",
"small_condition": "openwebtext10k_interaction",
"small_model": "L16_H8_D384",
"small_protocol": "4M final prefix / 5 seeds",
"l20_stream": ROOT
/ "runs/openwebtext10k_l20_8m_extrapolation_3seed/locked_stream/20260601-191150",
"l20_condition": "openwebtext10k_l20_interaction_8m",
},
"tinystories": {
"label": "TinyStories",
"short": "TinyStories",
"coeff": ROOT
/ "runs/coefficient_calibration/tinystories_combined_plus_all_holdouts_interaction/coefficients.json",
"cells": ROOT
/ "runs/coefficient_calibration/tinystories_combined_plus_all_holdouts_interaction/calibration_cells.csv",
"curve_run": ROOT
/ "runs/regime_calibration_tinystories_coarse/screen_static/20260529-161726",
"curve_model": "L12_H8_D320",
"curve_prefix": 1_000_000,
"curve_title": "TinyStories, L12, 1M",
"small_stream": [
ROOT
/ "runs/streaming_tinystories_interaction_schedule_l12/locked_stream/20260530-053831",
ROOT
/ "runs/streaming_tinystories_multiseed_validation_l12/locked_stream/20260530-111523",
ROOT
/ "runs/streaming_tinystories_multiseed_validation_l12/locked_stream/20260530-141335",
],
"small_condition": "interaction",
"small_model": "L12_H8_D320",
"small_protocol": "4M final prefix / 5 seeds",
"l20_stream": ROOT
/ "runs/tinystories_l20_8m_extrapolation_3seed/locked_stream/20260602-112249",
"l20_condition": "tinystories_l20_interaction_8m",
},
"wikitext": {
"label": "WikiText-103",
"short": "WikiText",
"coeff": ROOT / "runs/coefficient_calibration/wikitext103_interaction/coefficients.json",
"cells": ROOT / "runs/coefficient_calibration/wikitext103_interaction/calibration_cells.csv",
"curve_run": ROOT
/ "runs/wikitext103_interaction_static_screen/screen_static/20260603-153314",
"curve_model": "L12_H8_D320",
"curve_prefix": 250_000,
"curve_title": "WikiText-103, L12, 250k",
"small_stream": ROOT
/ "runs/wikitext103_l12_streaming_validation_5seed/locked_stream/20260531-093525",
"small_condition": "wikitext103_formula_l12",
"small_model": "L12_H8_D320",
"small_protocol": "4M final prefix / 5 seeds",
"l20_stream": ROOT
/ "runs/wikitext103_l20_8m_extrapolation_3seed/locked_stream/20260603-231952",
"l20_condition": "wikitext103_l20_interaction_8m",
},
}
def read_json(path: Path) -> dict | list:
return json.loads(path.read_text(encoding="utf-8"))
def read_jsonl(path: Path) -> list[dict]:
rows = []
for line in path.read_text(encoding="utf-8").splitlines():
if line.strip():
rows.append(json.loads(line))
return rows
def read_csv(path: Path) -> list[dict]:
with path.open(newline="", encoding="utf-8") as handle:
return list(csv.DictReader(handle))
def mean_std(values: list[float]) -> tuple[float, float]:
if not values:
return float("nan"), float("nan")
mean = statistics.fmean(values)
std = statistics.stdev(values) if len(values) > 1 else 0.0
return mean, std
def bootstrap_ci(
values: list[float], *, confidence: float = 0.95, iterations: int = 20_000, seed: int = 1337
) -> tuple[float, float]:
if not values:
return float("nan"), float("nan")
if len(values) == 1:
return values[0], values[0]
rng = random.Random(seed)
n = len(values)
means = []
for _ in range(iterations):
sample = [values[rng.randrange(n)] for _ in range(n)]
means.append(statistics.fmean(sample))
means.sort()
lower_q = (1.0 - confidence) / 2.0
upper_q = 1.0 - lower_q
lower_idx = min(iterations - 1, max(0, int(lower_q * iterations)))
upper_idx = min(iterations - 1, max(0, int(upper_q * iterations)))
return means[lower_idx], means[upper_idx]
def fmt(x: float, digits: int = 4) -> str:
return f"{x:.{digits}f}"
def tex_escape(value: object) -> str:
text = str(value)
return (
text.replace("\\", "\\textbackslash{}")
.replace("_", "\\_")
.replace("&", "\\&")
.replace("%", "\\%")
.replace("#", "\\#")
)
def model_label(model_name: str) -> str:
return tex_escape(model_name.replace("_", "-"))
def token_label(value: int) -> str:
if value >= 1_000_000:
whole = value / 1_000_000
return f"{whole:g}M"
return f"{value // 1000}k"
def protocol_budget(condition_rows: list[dict], stages: list[int]) -> dict:
rows_by_seed: dict[int, list[dict]] = {}
for row in condition_rows:
rows_by_seed.setdefault(int(row["seed"]), []).append(row)
seed = sorted(rows_by_seed)[0]
seed_rows = sorted(rows_by_seed[seed], key=lambda item: int(item["stage"]))
sampled_deltas = []
previous_tokens_seen = 0
for row in seed_rows:
tokens_seen = int(row["tokens_seen"])
sampled_deltas.append(tokens_seen - previous_tokens_seen)
previous_tokens_seen = tokens_seen
first = seed_rows[0]
steps_per_stage = sorted({int(row["steps"]) for row in seed_rows})
block_size = int(first["model_config"]["block_size"])
sampled_per_stage = sorted(set(sampled_deltas))
batch_sizes = sorted(
{
round(delta / (int(row["steps"]) * block_size))
for delta, row in zip(sampled_deltas, seed_rows, strict=True)
}
)
return {
"steps_per_stage": steps_per_stage,
"batch_sizes": batch_sizes,
"block_size": block_size,
"sampled_per_stage": sampled_per_stage,
"final_sampled_tokens": int(seed_rows[-1]["tokens_seen"]),
"prefix_path": [int(row["token_limit"]) for row in seed_rows],
"stage_count": len(stages),
}
def metric_stage_summary(run_dir: Path | list[Path], condition_name: str) -> dict:
run_dirs = run_dir if isinstance(run_dir, list) else [run_dir]
rows = []
for path in run_dirs:
rows.extend(read_jsonl(path / "metrics.jsonl"))
stages = sorted({int(row["stage"]) for row in rows if row.get("stage") is not None})
final_stage = max(stages)
by_condition_stage: dict[tuple[str, int], list[dict]] = {}
for row in rows:
if row.get("stage") is None:
continue
key = (row["condition"], int(row["stage"]))
by_condition_stage.setdefault(key, []).append(row)
condition_rows_by_stage = [
row
for (condition, _stage), group in by_condition_stage.items()
if condition == condition_name
for row in group
]
condition_rows = by_condition_stage[(condition_name, final_stage)]
final_vals = [float(row["val_eval_loss"]) for row in condition_rows]
condition_final_mean, condition_final_std = mean_std(final_vals)
final_static_by_condition: dict[str, list[dict]] = {}
for row in rows:
if int(row.get("stage", -1)) != final_stage:
continue
if row["condition_kind"] == "static":
final_static_by_condition.setdefault(row["condition"], []).append(row)
static_means = {
condition: statistics.fmean(float(row["val_eval_loss"]) for row in group)
for condition, group in final_static_by_condition.items()
}
best_static_condition = min(static_means, key=static_means.get)
best_static_rows = final_static_by_condition[best_static_condition]
best_static_vals = [float(row["val_eval_loss"]) for row in best_static_rows]
best_static_mean, best_static_std = mean_std(best_static_vals)
final_static_by_seed: dict[int, tuple[str, float]] = {}
for row in rows:
if int(row.get("stage", -1)) != final_stage or row["condition_kind"] != "static":
continue
seed = int(row["seed"])
value = float(row["val_eval_loss"])
if seed not in final_static_by_seed or value < final_static_by_seed[seed][1]:
final_static_by_seed[seed] = (row["condition"], value)
paired_deltas = []
paired_records = []
for row in sorted(condition_rows, key=lambda item: int(item["seed"])):
seed = int(row["seed"])
decay_loss = float(row["val_eval_loss"])
static_condition, static_loss = final_static_by_seed[seed]
delta = decay_loss - static_loss
paired_deltas.append(delta)
paired_records.append(
{
"seed": seed,
"decay_loss": decay_loss,
"best_static_condition": static_condition,
"best_static_loss": static_loss,
"delta": delta,
"gain": -delta,
}
)
paired_mean, paired_std = mean_std(paired_deltas)
paired_gains = [-delta for delta in paired_deltas]
paired_gain_mean, paired_gain_std = mean_std(paired_gains)
paired_gain_ci_low, paired_gain_ci_high = bootstrap_ci(paired_gains)
stage_deltas = []
for stage in stages:
condition_group = by_condition_stage[(condition_name, stage)]
condition_mean = statistics.fmean(
float(row["val_eval_loss"]) for row in condition_group
)
static_candidates = []
for (condition, candidate_stage), group in by_condition_stage.items():
if candidate_stage != stage:
continue
if group[0]["condition_kind"] != "static":
continue
static_candidates.append(
(
condition,
statistics.fmean(float(row["val_eval_loss"]) for row in group),
statistics.stdev(float(row["val_eval_loss"]) for row in group)
if len(group) > 1
else 0.0,
)
)
best_static_stage = min(static_candidates, key=lambda item: item[1])
first = condition_group[0]
stage_deltas.append(
{
"stage": stage,
"prefix": int(first["token_limit"]),
"condition_dropout": statistics.fmean(
float(row["dropout_active_final"]) for row in condition_group
),
"condition_mean": condition_mean,
"condition_std": statistics.stdev(
float(row["val_eval_loss"]) for row in condition_group
)
if len(condition_group) > 1
else 0.0,
"best_static_condition": best_static_stage[0],
"best_static_mean": best_static_stage[1],
"best_static_std": best_static_stage[2],
"delta": condition_mean - best_static_stage[1],
}
)
return {
"n": len(condition_rows),
"final_stage": final_stage,
"final_prefix": int(condition_rows[0]["token_limit"]),
"condition_final_mean": condition_final_mean,
"condition_final_std": condition_final_std,
"best_static_condition": best_static_condition,
"best_static_mean": best_static_mean,
"best_static_std": best_static_std,
"improvement": best_static_mean - condition_final_mean,
"paired_deltas": paired_deltas,
"paired_records": paired_records,
"paired_wins": sum(1 for delta in paired_deltas if delta < 0),
"paired_mean_delta": paired_mean,
"paired_std_delta": paired_std,
"paired_gain_mean": paired_gain_mean,
"paired_gain_std": paired_gain_std,
"paired_gain_ci_low": paired_gain_ci_low,
"paired_gain_ci_high": paired_gain_ci_high,
"budget": protocol_budget(condition_rows_by_stage, stages),
"stage_deltas": stage_deltas,
}
def static_curve_points(run_dir: Path, model: str, prefix: int) -> list[tuple[float, float]]:
rows = read_json(run_dir / "summary.json")
curve = []
for row in rows:
if row["run_mode"] != "screen_static":
continue
if row["condition_kind"] != "static":
continue
if row["model_name"] != model:
continue
if int(row["token_limit"]) != prefix:
continue
curve.append((float(row["dropout_initial"]), float(row["mean_val_eval_loss"])))
return sorted(curve)
def calibration_points(cells_path: Path) -> dict[str, list[tuple[int, float]]]:
rows = read_csv(cells_path)
grouped: dict[str, dict[int, float]] = {}
for row in rows:
sampled = int(float(row["sampled_tokens"]))
unique = int(float(row["unique_tokens"]))
source = row["source"]
if "sample_pressure_low" in source:
continue
if "heldout_model" in source:
continue
model = row["model_name"]
target = max(0.0, float(row["target_dropout"]))
grouped.setdefault(model, {})[unique] = target
return {
model: sorted(points.items())
for model, points in sorted(grouped.items())
if len(points) >= 2
}
def coeff_summary(regime_key: str, paths: dict) -> dict:
coeff = read_json(paths["coeff"])
return {
"regime": paths["label"],
"cells": int(coeff["metrics"]["n"]),
"A": float(coeff["coefficients"]["A"]),
"B": float(coeff["coefficients"]["B"]),
"D": float(coeff["coefficients"]["D"]),
"C0": float(coeff["coefficients"]["C0"]),
"mae": float(coeff["metrics"]["mae"]),
"rmse": float(coeff["metrics"]["rmse"]),
"leave_model_mae": float(coeff["cv"]["leave_model"]["mae"]),
"leave_prefix_mae": float(coeff["cv"]["leave_prefix"]["mae"]),
"formula": coeff["formula"],
}
def tikz_coordinates(points: list[tuple[float, float]], digits: int = 4) -> str:
return " ".join(f"({x:.4g},{y:.{digits}f})" for x, y in points)
def write_calibration_curve_figure() -> None:
axes = []
for idx, (key, paths) in enumerate(REGIMES.items()):
raw_curve = static_curve_points(
paths["curve_run"], paths["curve_model"], paths["curve_prefix"]
)
minimum = min(loss for _, loss in raw_curve)
points = [(dropout, loss - minimum) for dropout, loss in raw_curve]
best = min(raw_curve, key=lambda item: item[1])
axis_options = [
f" title={{{paths['curve_title']}}},",
" xlabel={Dropout},",
]
if idx == 0:
axis_options.append(" ylabel={Validation loss above best},")
axis_options.extend(
[
" ymin=0,",
" grid=both,",
]
)
axes.append(
rf"""
\nextgroupplot[
{chr(10).join(axis_options)}
]
\addplot+[thick, mark=*] coordinates {{{tikz_coordinates(points, digits=5)}}};
\addlegendentry{{static sweep}}
\addplot+[only marks, mark=star, mark size=3pt] coordinates {{({best[0]:.4g},0)}};
\addlegendentry{{target $p^\star={best[0]:.2f}$}}
"""
)
content = (
"\\begin{tikzpicture}\n"
"\\begin{groupplot}[\n"
" group style={group size=3 by 1, horizontal sep=1.0cm},\n"
" width=0.32\\textwidth,\n"
" height=0.26\\textwidth,\n"
" legend style={font=\\scriptsize},\n"
" tick label style={font=\\scriptsize},\n"
" label style={font=\\scriptsize},\n"
" title style={font=\\scriptsize},\n"
"]\n"
+ "\n".join(axes)
+ "\n\\end{groupplot}\n\\end{tikzpicture}\n"
)
(FIG_DIR / "calibration_cell_curves.tex").write_text(content, encoding="utf-8")
def write_static_optima_figure() -> None:
axes = []
for key, paths in REGIMES.items():
grouped = calibration_points(paths["cells"])
labels = sorted({prefix for points in grouped.values() for prefix, _ in points})
label_to_x = {prefix: idx for idx, prefix in enumerate(labels)}
plots = []
for model, points in grouped.items():
coords = [
(label_to_x[prefix], target)
for prefix, target in points
if prefix in label_to_x
]
plots.append(
"\\addplot+[thick, mark=*] coordinates {"
+ tikz_coordinates(coords)
+ "};\n"
+ f"\\addlegendentry{{{model_label(model)}}}"
)
xticks = ",".join(str(i) for i in range(len(labels)))
xticklabels = ",".join(token_label(prefix) for prefix in labels)
axes.append(
rf"""
\nextgroupplot[
title={{{paths["label"]}}},
xlabel={{Unique prefix $U$}},
ylabel={{Fitted target dropout}},
xtick={{{xticks}}},
xticklabels={{{xticklabels}}},
ymin=0,
ymax=0.65,
grid=both,
]
{chr(10).join(plots)}
"""
)
content = (
"\\begin{tikzpicture}\n"
"\\begin{groupplot}[\n"
" group style={group size=3 by 1, horizontal sep=1.0cm},\n"
" width=0.32\\textwidth,\n"
" height=0.28\\textwidth,\n"
" legend style={font=\\tiny},\n"
" tick label style={font=\\scriptsize},\n"
" label style={font=\\scriptsize},\n"
" title style={font=\\scriptsize},\n"
"]\n"
+ "\n".join(axes)
+ "\n\\end{groupplot}\n\\end{tikzpicture}\n"
)
(FIG_DIR / "static_optima_by_regime.tex").write_text(content, encoding="utf-8")
def write_l20_stage_delta_figure(summaries: dict[str, dict]) -> None:
axes = []
for key, paths in REGIMES.items():
stage_rows = summaries[key]["l20"]["stage_deltas"]
labels = [token_label(row["prefix"]) for row in stage_rows]
coords = [(idx, row["delta"]) for idx, row in enumerate(stage_rows)]
zero_coords = [(0, 0.0), (len(stage_rows) - 1, 0.0)]
xticks = ",".join(str(i) for i in range(len(labels)))
xticklabels = ",".join(labels)
axes.append(
rf"""
\nextgroupplot[
title={{{paths["label"]}}},
xlabel={{Prefix}},
ylabel={{Decay $-$ best static}},
xtick={{{xticks}}},
xticklabels={{{xticklabels}}},
grid=both,
]
\addplot+[thick, mark=*] coordinates {{{tikz_coordinates(coords, digits=5)}}};
\addplot+[black, dashed, mark=none] coordinates {{{tikz_coordinates(zero_coords, digits=5)}}};
"""
)
content = (
"\\begin{tikzpicture}\n"
"\\begin{groupplot}[\n"
" group style={group size=3 by 1, horizontal sep=1.0cm},\n"
" width=0.32\\textwidth,\n"
" height=0.27\\textwidth,\n"
" tick label style={font=\\scriptsize},\n"
" label style={font=\\scriptsize},\n"
" title style={font=\\scriptsize},\n"
"]\n"
+ "\n".join(axes)
+ "\n\\end{groupplot}\n\\end{tikzpicture}\n"
)
(FIG_DIR / "l20_stage_deltas.tex").write_text(content, encoding="utf-8")
def write_final_improvement_figure(summaries: dict[str, dict]) -> None:
labels = []
coords_small = []
coords_l20 = []
for idx, (key, paths) in enumerate(REGIMES.items()):
labels.append(paths["short"])
coords_small.append((idx, summaries[key]["small"]["improvement"]))
coords_l20.append((idx, summaries[key]["l20"]["improvement"]))
xticks = ",".join(str(i) for i in range(len(labels)))
xticklabels = ",".join(labels)
content = rf"""
\begin{{tikzpicture}}
\begin{{axis}}[
ybar,
bar width=8pt,
width=0.72\textwidth,
height=0.34\textwidth,
ymin=0,
ylabel={{Best static final loss $-$ decay final loss}},
symbolic x coords={{{xticklabels}}},
xtick=data,
xtick={{{xticklabels}}},
grid=both,
legend style={{font=\scriptsize, at={{(0.5,1.04)}}, anchor=south, legend columns=2}},
tick label style={{font=\scriptsize}},
label style={{font=\scriptsize}},
]
\addplot coordinates {{{" ".join(f"({label},{y:.5f})" for label, (_, y) in zip(labels, coords_small))}}};
\addlegendentry{{4M validation}}
\addplot coordinates {{{" ".join(f"({label},{y:.5f})" for label, (_, y) in zip(labels, coords_l20))}}};
\addlegendentry{{L20 8M extrapolation}}
\end{{axis}}
\end{{tikzpicture}}
"""
(FIG_DIR / "final_improvements.tex").write_text(content, encoding="utf-8")
def write_protocol_schematic() -> None:
content = r"""
\begin{tikzpicture}[font=\scriptsize]
\tikzstyle{box}=[
draw,
rounded corners=2pt,
thick,
align=center,
text width=0.19\textwidth,
minimum height=1.5cm,
inner sep=5pt
]
\node[box, fill=blue!7] at (0,0) (sweep) {Static calibration cells\\
\footnotesize model $P$, prefix $U$, sampled tokens $C$, dropout grid};
\node[box, fill=green!8] at (3.7,0) (target) {Extract $p^\star$\\
\footnotesize grid best or bracketed quadratic minimum};
\node[box, fill=yellow!12] at (7.4,0) (fit) {Fit pressure law\\
\footnotesize $p^\star \approx A x + B y + Dxy + C_0$};
\node[box, fill=red!7] at (11.1,0) (stream) {Frozen streaming test\\
\footnotesize compare decay path to best static dropout grid};
\draw[->, thick] (sweep.east) -- (target.west);
\draw[->, thick] (target.east) -- (fit.west);
\draw[->, thick] (fit.east) -- (stream.west);
\node[align=center, text width=0.9\textwidth] at (5.55,-1.45) {
Inputs to the fitted law are absolute quantities available before or during a run:
$x=\log_{10}(P/U)$ measures capacity pressure and
$y=\log_{10}(C/U)$ measures repeated-sampling pressure.
};
\end{tikzpicture}
"""
(FIG_DIR / "calibration_to_streaming_protocol.tex").write_text(
content.strip() + "\n", encoding="utf-8"
)
def write_tables(summaries: dict[str, dict], coeffs: dict[str, dict]) -> None:
symbol_lines = [
"\\begin{tabular}{ll}",
"\\toprule",
"Symbol & Meaning \\\\",
"\\midrule",
"$P$ & Trainable parameter count \\\\",
"$U_t$ & Unique-token prefix currently revealed by stream stage $t$ \\\\",
"$C_t$ & Cumulative sampled training tokens consumed by the optimizer \\\\",
"$x_t$ & $\\log_{10}(P/U_t)$, capacity pressure \\\\",
"$y_t$ & $\\log_{10}(C_t/U_t)$, repeated-sampling pressure \\\\",
"$p_t$ & Dropout rate used at stream stage $t$ \\\\",
"$p^\\star$ & Static-sweep target dropout for one calibration cell \\\\",
"$A,B,D,C_0$ & Regime-specific fitted pressure-law coefficients \\\\",
"$p_{\\min},p_{\\max}$ & Clamp bounds applied to generated dropout schedules \\\\",
"\\bottomrule",
"\\end{tabular}",
]
(TABLE_DIR / "symbol_table.tex").write_text(
"\n".join(symbol_lines) + "\n", encoding="utf-8"
)
regime_lines = [
"\\begin{tabular}{llll}",
"\\toprule",
"Regime & Corpus source & Tokenizer / vocab & Streaming prefixes \\\\",
"\\midrule",
(
"OpenWebText10K & Cached OpenWebText-style subset & BPE / 4096 & "
"250k, 500k, 1M, 2M, 4M; plus 8M L20 tier \\\\"
),
(
"TinyStories & Cached TinyStories subset & BPE / 4096 & "
"500k, 1M, 2M, 4M; plus 8M L20 tier \\\\"
),
(
"WikiText-103 & Cached WikiText-103 subset & BPE / 4096 & "
"250k, 500k, 1M, 2M, 4M; plus 8M L20 tier \\\\"
),
"\\bottomrule",
"\\end{tabular}",
]
(TABLE_DIR / "regime_setup_table.tex").write_text(
"\n".join(regime_lines) + "\n", encoding="utf-8"
)
repro_lines = [
"\\begin{tabular}{ll}",
"\\toprule",
"Item & Value \\\\",
"\\midrule",
"Repository & \\url{https://huggingface.co/cuber12/dropout-decay} \\\\",
"Archived snapshot DOI & \\url{https://doi.org/10.5281/zenodo.20616633} \\\\",
"Repository snapshot & Git tag \\texttt{v1.1-preprint} \\\\",
"License & MIT-derived source with nanochat attribution retained \\\\",
"Python & 3.11.15 \\\\",
"PyTorch & 2.12.0 \\\\",
"Operating system & macOS 26.4 arm64 \\\\",
"Training backend & Apple Metal Performance Shaders (MPS); runner refuses CPU/CUDA \\\\",
"\\bottomrule",
"\\end{tabular}",
]
(TABLE_DIR / "reproducibility_table.tex").write_text(
"\n".join(repro_lines) + "\n", encoding="utf-8"
)
weight_lines = [
"\\begin{tabular}{lll}",
"\\toprule",
"Cell property & Weight multiplier & Reason \\\\",
"\\midrule",
"Best grid point on dropout boundary & $0.30$ & Optimum may lie outside tested grid \\\\",
"Quadratic minimum not bracketed & $0.50$ & Local parabola is less reliable \\\\",
"Loss span below $0.02$ nats & $0.50$ & Very flat curve weakly identifies $p^\\star$ \\\\",
(
"Best-loss std. $\\sigma > 0$ & $1/(1+20\\sigma)$ & "
"Noisier minima receive less influence \\\\"
),
"\\bottomrule",
"\\end{tabular}",
]
(TABLE_DIR / "calibration_weight_table.tex").write_text(
"\n".join(weight_lines) + "\n", encoding="utf-8"
)
protocol_lines = [
"\\begin{tabular}{llll}",
"\\toprule",
"Experiment layer & What is run & Question answered & Statistical unit \\\\",
"\\midrule",
(
"Static calibration & Per-regime dropout grid at fixed $(P,U,C)$ cells & "
"Where does validation loss plateau over dropout? & 15--16 cells per regime \\\\"
),
(
"Coefficient fit & Per-regime regression from cell targets $p^\\star$ & "
"Can pressure variables predict useful dropout? & MAE / leave-out MAE \\\\"
),
(
"4M final-prefix locked stream & Per-regime continuing model over growing prefixes & "
"Does decay beat the best tested fixed dropout? & 5 paired seeds per regime \\\\"
),
(
"L20 8M extrapolation & Per-regime 51.84M-param model with frozen coefficients & "
"Does the law extend to larger model/data scale? & 3 paired seeds per regime \\\\"
),
(
"Stage-wise audit & Per-regime decay vs best static at every prefix & "
"Is the schedule final-loss or per-prefix optimal? & Prefix-level deltas \\\\"
),
"\\bottomrule",
"\\end{tabular}",
]
(TABLE_DIR / "experiment_protocol_table.tex").write_text(
"\n".join(protocol_lines) + "\n", encoding="utf-8"
)
budget_lines = [
"\\begin{tabular}{llllrr}",
"\\toprule",
"Regime & Protocol & Prefix path & Sampled/stage & Batch & Block \\\\",
"\\midrule",
]
for key, paths in REGIMES.items():
for protocol_key, protocol_label in [
("small", paths["small_protocol"]),
("l20", "8M final prefix / 3 seeds"),
]:
budget = summaries[key][protocol_key]["budget"]
prefix_path = " $\\rightarrow$ ".join(token_label(prefix) for prefix in budget["prefix_path"])
sampled = ", ".join(token_label(value) for value in budget["sampled_per_stage"])
batch = "/".join(str(value) for value in budget["batch_sizes"])
block = str(budget["block_size"])
budget_lines.append(
f"{tex_escape(paths['label'])} & {tex_escape(protocol_label)} & "
f"{prefix_path} & {sampled} & {batch} & {block} \\\\"
)
budget_lines.extend(["\\bottomrule", "\\end{tabular}"])
(TABLE_DIR / "protocol_budget_table.tex").write_text(
"\n".join(budget_lines) + "\n", encoding="utf-8"
)
coeff_lines = [
"\\begin{tabular}{lrrrrrrrr}",
"\\toprule",
"Regime & Cells & A & B & D & C0 & Dropout MAE (abs. $p$) & L-model MAE & L-prefix MAE \\\\",
"\\midrule",
]
for key in REGIMES:
row = coeffs[key]
coeff_lines.append(
f"{tex_escape(row['regime'])} & {row['cells']} & "
f"{row['A']:.4f} & {row['B']:.4f} & {row['D']:.4f} & {row['C0']:.4f} & "
f"{row['mae']:.4f} & {row['leave_model_mae']:.4f} & {row['leave_prefix_mae']:.4f} \\\\"
)
coeff_lines.extend(["\\bottomrule", "\\end{tabular}"])
(TABLE_DIR / "coefficient_table.tex").write_text(
"\n".join(coeff_lines) + "\n", encoding="utf-8"
)
evidence_lines = [
"\\begin{tabular}{lrrrrr}",
"\\toprule",
"Regime & Calib. dropout MAE & 4M gain (nats) & 4M wins & 8M gain (nats) & 8M wins \\\\",
"\\midrule",
]
for key, paths in REGIMES.items():
small = summaries[key]["small"]
l20 = summaries[key]["l20"]
evidence_lines.append(
f"{tex_escape(paths['label'])} & {coeffs[key]['mae']:.4f} & "
f"{small['improvement']:.4f} & {small['paired_wins']}/{small['n']} & "
f"{l20['improvement']:.4f} & {l20['paired_wins']}/{l20['n']} \\\\"
)
evidence_lines.extend(["\\bottomrule", "\\end{tabular}"])
(TABLE_DIR / "evidence_ladder_table.tex").write_text(
"\n".join(evidence_lines) + "\n", encoding="utf-8"
)
val_lines = [
"\\begin{tabular}{llrrrrr}",
"\\toprule",
"Regime & Protocol & Decay final (nats) & Best static final (nats) & Gain (nats) & Seeds & Paired wins \\\\",
"\\midrule",
]
for key, paths in REGIMES.items():
for protocol_key, protocol_label in [
("small", paths["small_protocol"]),
("l20", "8M final prefix / 3 seeds"),
]:
row = summaries[key][protocol_key]
val_lines.append(
f"{tex_escape(paths['label'])} & {tex_escape(protocol_label)} & "
f"{row['condition_final_mean']:.4f} $\\pm$ {row['condition_final_std']:.4f} & "
f"{row['best_static_mean']:.4f} $\\pm$ {row['best_static_std']:.4f} & "
f"{row['improvement']:.4f} & {row['n']} & {row['paired_wins']}/{row['n']} \\\\"
)
val_lines.extend(["\\bottomrule", "\\end{tabular}"])
(TABLE_DIR / "streaming_validation_table.tex").write_text(
"\n".join(val_lines) + "\n", encoding="utf-8"
)
paired_lines = [
"\\begin{tabular}{llrrrr}",
"\\toprule",
"Regime & Protocol & Paired gain mean & 95\\% bootstrap CI & Seeds & Wins \\\\",
"\\midrule",
]
for key, paths in REGIMES.items():
for protocol_key, protocol_label in [
("small", paths["small_protocol"]),
("l20", "8M final prefix / 3 seeds"),
]:
row = summaries[key][protocol_key]
paired_lines.append(
f"{tex_escape(paths['label'])} & {tex_escape(protocol_label)} & "
f"{row['paired_gain_mean']:.4f} & "
f"[{row['paired_gain_ci_low']:.4f}, {row['paired_gain_ci_high']:.4f}] & "
f"{row['n']} & {row['paired_wins']}/{row['n']} \\\\"
)
paired_lines.extend(["\\bottomrule", "\\end{tabular}"])
(TABLE_DIR / "paired_gain_ci_table.tex").write_text(
"\n".join(paired_lines) + "\n", encoding="utf-8"
)
path_lines = [
"\\begin{tabular}{llll}",
"\\toprule",
"Regime & Locked-stream prefix path & Frozen decay path & Final best static \\\\",
"\\midrule",
]
for key, paths in REGIMES.items():
stage_rows = summaries[key]["l20"]["stage_deltas"]
prefix_path = " $\\rightarrow$ ".join(token_label(row["prefix"]) for row in stage_rows)
dropout_path = " $\\rightarrow$ ".join(
f"{row['condition_dropout']:.3f}" for row in stage_rows
)
final_static = summaries[key]["l20"]["best_static_condition"].replace(
"static_dropout_", "p="
)
path_lines.append(
f"{tex_escape(paths['label'])} & {prefix_path} & {dropout_path} & "
f"{tex_escape(final_static)} \\\\"
)
path_lines.extend(["\\bottomrule", "\\end{tabular}"])
(TABLE_DIR / "l20_schedule_path_table.tex").write_text(
"\n".join(path_lines) + "\n", encoding="utf-8"
)
l20_lines = [
"\\begin{tabular}{lrrrrr}",
"\\toprule",
"Regime & Prefix & Decay dropout & Decay val (nats) & Best static val (nats) & Delta (nats) \\\\",
"\\midrule",
]
for key, paths in REGIMES.items():
for row in summaries[key]["l20"]["stage_deltas"]:
l20_lines.append(
f"{tex_escape(paths['label'])} & {token_label(row['prefix'])} & "
f"{row['condition_dropout']:.3f} & {row['condition_mean']:.4f} & "
f"{row['best_static_mean']:.4f} & {row['delta']:+.4f} \\\\"
)
l20_lines.extend(["\\bottomrule", "\\end{tabular}"])
(TABLE_DIR / "l20_stage_table.tex").write_text(
"\n".join(l20_lines) + "\n", encoding="utf-8"
)
summary = {
key: {
"coefficients": coeffs[key],
"small_stream": summaries[key]["small"],
"l20_stream": summaries[key]["l20"],
}
for key in REGIMES
}
(PAPER_DIR / "paper_results_summary.json").write_text(
json.dumps(summary, indent=2), encoding="utf-8"
)
def main() -> None:
FIG_DIR.mkdir(parents=True, exist_ok=True)
TABLE_DIR.mkdir(parents=True, exist_ok=True)
summaries = {}
coeffs = {}
for key, paths in REGIMES.items():
coeffs[key] = coeff_summary(key, paths)
summaries[key] = {
"small": metric_stage_summary(paths["small_stream"], paths["small_condition"]),
"l20": metric_stage_summary(paths["l20_stream"], paths["l20_condition"]),
}
write_calibration_curve_figure()
write_static_optima_figure()
write_protocol_schematic()
write_l20_stage_delta_figure(summaries)
write_final_improvement_figure(summaries)
write_tables(summaries, coeffs)
print(json.dumps({"figures": str(FIG_DIR), "tables": str(TABLE_DIR)}, indent=2))
if __name__ == "__main__":
main()