anhtld commited on
Commit
ba8b348
·
verified ·
1 Parent(s): d45d1d2

ctt measured rollout full validation 2026-07-03T01:27:41Z

Browse files
workspace/scripts/build_ctt_rollout_comparison.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import math
7
+ import re
8
+ import subprocess
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ PROJECT_ROOT = Path(__file__).resolve().parents[1]
14
+ if str(PROJECT_ROOT) not in sys.path:
15
+ sys.path.insert(0, str(PROJECT_ROOT))
16
+
17
+ from scripts.eval_metrics import main as eval_metrics_main # noqa: E402
18
+
19
+
20
+ def main(argv: list[str] | None = None) -> int:
21
+ parser = argparse.ArgumentParser(description="Aggregate measured CTT rollout runs.")
22
+ parser.add_argument(
23
+ "--run-glob",
24
+ default="runs/ctt_residual_rollout_val69_seed*",
25
+ help="Glob for run directories containing measured_candidates.json.",
26
+ )
27
+ parser.add_argument("--out-dir", type=Path, default=Path("runs/ctt_val_rollout_comparison"))
28
+ parser.add_argument("--k", type=int, default=8)
29
+ parser.add_argument("--bootstrap-samples", type=int, default=1000)
30
+ args = parser.parse_args(argv)
31
+
32
+ run_dirs = [
33
+ path
34
+ for path in sorted(Path().glob(args.run_glob))
35
+ if (path / "measured_candidates.json").exists()
36
+ ]
37
+ if not run_dirs:
38
+ raise SystemExit(f"no measured rollout runs found for glob {args.run_glob!r}")
39
+
40
+ combined_rows: list[dict[str, Any]] = []
41
+ payloads = []
42
+ for run_dir in run_dirs:
43
+ payload = json.loads((run_dir / "measured_candidates.json").read_text())
44
+ payloads.append(payload)
45
+ train_seed = _seed_from_path(run_dir)
46
+ for row in payload.get("rows", []):
47
+ item = dict(row)
48
+ item["train_seed"] = train_seed
49
+ item["seed"] = f"{row.get('seed', 'unknown')}/train{train_seed}"
50
+ combined_rows.append(item)
51
+
52
+ out_dir = args.out_dir
53
+ out_dir.mkdir(parents=True, exist_ok=True)
54
+ combined = {
55
+ "report_type": "ctt_measured_rollout_comparison",
56
+ "schema_version": 1,
57
+ "k": args.k,
58
+ "run_dirs": [str(path) for path in run_dirs],
59
+ "train_seeds": [_seed_from_path(path) for path in run_dirs],
60
+ "num_rows": len(combined_rows),
61
+ "source_content_hash": _first(payloads, "source_content_hash"),
62
+ "target_content_hash": _first(payloads, "target_content_hash"),
63
+ "target_split_hash": _first(payloads, "target_split_hash"),
64
+ "rows": combined_rows,
65
+ }
66
+ combined_path = out_dir / "combined_measured_candidates.json"
67
+ combined_path.write_text(json.dumps(combined, indent=2, sort_keys=True) + "\n")
68
+ eval_metrics_main(
69
+ [
70
+ "--input",
71
+ str(combined_path),
72
+ "--out-dir",
73
+ str(out_dir / "measured_metrics"),
74
+ "--mode",
75
+ "measured",
76
+ "--k",
77
+ str(args.k),
78
+ "--bootstrap-samples",
79
+ str(args.bootstrap_samples),
80
+ ]
81
+ )
82
+ metrics = json.loads((out_dir / "measured_metrics" / "metrics.json").read_text())
83
+ (out_dir / "metrics.json").write_text(json.dumps(_summary_payload(combined, metrics), indent=2, sort_keys=True) + "\n")
84
+ (out_dir / "table.tex").write_text((out_dir / "measured_metrics" / "table.tex").read_text())
85
+ (out_dir / "report.md").write_text(_report(combined, metrics) + "\n")
86
+ (out_dir / "command.txt").write_text(
87
+ "python scripts/build_ctt_rollout_comparison.py " + " ".join(sys.argv[1:]) + "\n"
88
+ )
89
+ (out_dir / "git_hash.txt").write_text(_run(["git", "rev-parse", "HEAD"]) + "\n")
90
+ print(json.dumps({"out_dir": str(out_dir), "runs": len(run_dirs), "rows": len(combined_rows)}, indent=2))
91
+ return 0
92
+
93
+
94
+ def _summary_payload(combined: dict[str, Any], metrics: dict[str, Any]) -> dict[str, Any]:
95
+ return {
96
+ "report_type": "ctt_measured_rollout_comparison",
97
+ "k": combined["k"],
98
+ "run_dirs": combined["run_dirs"],
99
+ "train_seeds": combined["train_seeds"],
100
+ "num_rows": combined["num_rows"],
101
+ "summary": metrics.get("summary", {}),
102
+ "source_content_hash": combined.get("source_content_hash"),
103
+ "target_content_hash": combined.get("target_content_hash"),
104
+ "target_split_hash": combined.get("target_split_hash"),
105
+ }
106
+
107
+
108
+ def _report(combined: dict[str, Any], metrics: dict[str, Any]) -> str:
109
+ summary = metrics.get("summary", {})
110
+ lines = [
111
+ "# CTT Validation Measured Rollout Comparison",
112
+ "",
113
+ f"Runs: `{len(combined['run_dirs'])}`",
114
+ f"Rows: `{combined['num_rows']}`",
115
+ f"K: `{combined['k']}`",
116
+ "",
117
+ "| Metric | N | Micro mean | 95% CI |",
118
+ "| --- | ---: | ---: | ---: |",
119
+ ]
120
+ for name, payload in sorted(summary.items()):
121
+ micro = payload.get("micro", {})
122
+ lines.append(
123
+ f"| {name} | {micro.get('n', 0)} | {_fmt(micro.get('mean'))} | "
124
+ f"[{_fmt(micro.get('low'))}, {_fmt(micro.get('high'))}] |"
125
+ )
126
+ lines.append("")
127
+ lines.append("These are measured generated-candidate rollouts, not PPTC proxies.")
128
+ lines.append("")
129
+ lines.append("Run dirs:")
130
+ for run_dir in combined["run_dirs"]:
131
+ lines.append(f"- `{run_dir}`")
132
+ return "\n".join(lines)
133
+
134
+
135
+ def _seed_from_path(path: Path) -> str:
136
+ match = re.search(r"seed(\d+)", path.name)
137
+ return match.group(1) if match else path.name
138
+
139
+
140
+ def _first(payloads: list[dict[str, Any]], key: str) -> Any:
141
+ for payload in payloads:
142
+ value = payload.get(key)
143
+ if value:
144
+ return value
145
+ return None
146
+
147
+
148
+ def _fmt(value: Any) -> str:
149
+ if not isinstance(value, (int, float)) or not math.isfinite(float(value)):
150
+ return "n/a"
151
+ return f"{float(value):.4f}"
152
+
153
+
154
+ def _run(command: list[str]) -> str:
155
+ try:
156
+ return subprocess.check_output(command, cwd=PROJECT_ROOT, text=True).strip()
157
+ except (subprocess.CalledProcessError, FileNotFoundError):
158
+ return ""
159
+
160
+
161
+ if __name__ == "__main__":
162
+ raise SystemExit(main())