87 lines
3.2 KiB
Python
87 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Assemble the 10×5 training dynamics panel.
|
||
|
||
Layout: 3-row × 4-col gridspec with spanning
|
||
|
||
Top 2×2 (each cell spans 2 cols):
|
||
row 0, cols 0-1: binary early stopping sweep
|
||
row 0, cols 2-3: multiclass early stopping sweep
|
||
row 1, cols 0-1: binary cost of stopping early
|
||
row 1, cols 2-3: multiclass cost of stopping early
|
||
|
||
Bottom row of 4 (one col each):
|
||
row 2, col 0: binary holdout AUC by epoch
|
||
row 2, col 1: binary val−holdout gap (val-adjusted)
|
||
row 2, col 2: multiclass holdout AUC by epoch
|
||
row 2, col 3: multiclass val−holdout gap (val-adjusted)
|
||
|
||
Usage
|
||
-----
|
||
python scripts/output_analysis/visualizations/build_10x5_training_panel.py \
|
||
--run-dir analysis_data/pipeline_10x5 \
|
||
--out analysis_data/pipeline_10x5/aggregate/training_dynamics_panel.png
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
from pathlib import Path
|
||
|
||
import matplotlib
|
||
matplotlib.use("Agg")
|
||
import matplotlib.pyplot as plt
|
||
import matplotlib.image as mpimg
|
||
|
||
|
||
# (row, col_start, col_end, rel_path, label)
|
||
# col_end is exclusive slice — use None for single cell
|
||
CELLS = [
|
||
# ---- top 2×2: early stopping (each spans 2 cols) ----
|
||
(0, 0, 2, "binary/ensemble/plots/early_stopping_sweep_fused.png",
|
||
"Binary — Early stopping sweep"),
|
||
(0, 2, 4, "multiclass/ensemble/plots/early_stopping_sweep_fused.png",
|
||
"Multiclass — Early stopping sweep"),
|
||
(1, 0, 2, "binary/ensemble/plots/early_stopping_sweep_fused_inverted.png",
|
||
"Binary — Cost of stopping early"),
|
||
(1, 2, 4, "multiclass/ensemble/plots/early_stopping_sweep_fused_inverted_tol0.002.png",
|
||
"Multiclass — Cost of stopping early (CI tol=0.002)"),
|
||
# ---- bottom row of 4: holdout epoch curves (single col each) ----
|
||
(2, 0, 1, "binary/ensemble/plots/holdout_epoch_curves_fused.png",
|
||
"Binary — Holdout AUC by epoch"),
|
||
(2, 1, 2, "binary/ensemble/plots/holdout_epoch_curves_fused_delta_adj.png",
|
||
"Binary — Val−Holdout gap (val-adjusted)"),
|
||
(2, 2, 3, "multiclass/ensemble/plots/holdout_epoch_curves_fused.png",
|
||
"Multiclass — Holdout AUC by epoch"),
|
||
(2, 3, 4, "multiclass/ensemble/plots/holdout_epoch_curves_fused_delta_adj.png",
|
||
"Multiclass — Val−Holdout gap (val-adjusted)"),
|
||
]
|
||
|
||
|
||
def main():
|
||
ap = argparse.ArgumentParser()
|
||
ap.add_argument("--run-dir", default="analysis_data/pipeline_10x5")
|
||
ap.add_argument("--out", default=None)
|
||
args = ap.parse_args()
|
||
|
||
run_dir = Path(args.run_dir)
|
||
out = Path(args.out) if args.out else run_dir / "aggregate" / "training_dynamics_panel.png"
|
||
|
||
fig = plt.figure(figsize=(22, 16))
|
||
gs = fig.add_gridspec(3, 4, height_ratios=[1, 1, 0.75], hspace=0.08, wspace=0.04)
|
||
|
||
for row, col_start, col_end, rel, label in CELLS:
|
||
ax = fig.add_subplot(gs[row, col_start:col_end])
|
||
img = mpimg.imread(str(run_dir / rel))
|
||
ax.imshow(img)
|
||
ax.axis("off")
|
||
ax.set_title(label, fontsize=11, pad=5)
|
||
|
||
out.parent.mkdir(parents=True, exist_ok=True)
|
||
fig.savefig(out, dpi=150, bbox_inches="tight")
|
||
plt.close(fig)
|
||
print(f"Saved → {out}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|