tchbcb commited on
Commit
ef24e1d
·
verified ·
1 Parent(s): 840ad69

v3: real-games training round tooling

Browse files
Files changed (1) hide show
  1. ops/t4_real25.py +271 -0
ops/t4_real25.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """T4 real-games scale-up (v3): train on 25 public ARC-AGI-3 games.
3
+
4
+ Run on the T4 VM as: python3 -B /content/qwenjev/t4_real25.py
5
+ Stages (each Popen-isolated, logged, stamped, resumable):
6
+ prep : pull v2 hf_lora parts from HF -> reassemble -> stage_a_lora
7
+ data : merge remote-built dataset_remote + local dataset (HF tgz)
8
+ real_b : SFT on merged real-game data, resuming v2 LoRA -> hf_lora
9
+ real_c : RLCD + calibration -> s1.pt+calib
10
+ upload : push everything to HF t4_assets_v3/checkpoints/
11
+ eval : ar25+bp35 @150 actions (vs zero-shot baseline: 0 wins, 0 levels)
12
+
13
+ Artifacts land in checkpoints/qwenjev-s1-hf-v3 and HF t4_assets_v3/.
14
+ Baseline for comparison (zero-shot v2 on public games):
15
+ ar25: 150 acts, best_level 0; bp35: 150 acts, best_level 0.
16
+ """
17
+ import json
18
+ import os
19
+ import subprocess
20
+ import sys
21
+ import threading
22
+ import time
23
+ from pathlib import Path
24
+
25
+ WORK = Path("/content/qwenjev_work_v3")
26
+ REPO = Path("/content/qwenjev")
27
+ OUT = REPO / "checkpoints" / "qwenjev-s1-hf-v3"
28
+ LOGS = WORK / "logs"
29
+ DONE = WORK / "done_v3"
30
+ STATUS = WORK / "v3_status.json"
31
+
32
+ HF_REPO = "tchbcb/qwenjev"
33
+
34
+ REAL25_YAML = """
35
+ data:
36
+ dataset_dir: "data/dataset_final"
37
+
38
+ system1:
39
+ backend: "hf"
40
+ hf_model: "Qwen/Qwen3-VL-2B-Instruct"
41
+ hf_quant4: true
42
+ max_leaf_tokens: 512
43
+ score_levels: 10
44
+ leaf_batch_size: 2
45
+ eval_leaf_batch: 8
46
+
47
+ stage_a_distill:
48
+ enabled: false # v2 adapters are resumed as stage_a_lora (prep stage)
49
+
50
+ stage_b_sft:
51
+ backbone_lr: 5.0e-5
52
+ head_lr: 2.0e-4
53
+ epochs: 1
54
+ batch_questions: 2
55
+ grad_accum: 2
56
+ max_steps: 180 # real-games corpus is bigger; keep ~2h on T4
57
+ aux_rows_per_batch: 3
58
+ max_candidates: 4
59
+ lora:
60
+ enabled: true
61
+ r: 16
62
+ alpha: 32
63
+ target: ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
64
+ exclude_vision: true
65
+ kbit_prepare: true
66
+
67
+ stage_c_rlcd:
68
+ epochs: 1
69
+ group_size: 4
70
+ sigma_start: 0.4
71
+ sigma_end: 0.1
72
+ lr_backbone: 5.0e-5
73
+ lr_head: 2.0e-4
74
+ ce_guidance: 1.0
75
+ w_sph: 0.75
76
+ w_rps: 1.0
77
+ freeze_backbone: true
78
+ max_choice_rows: 120
79
+ max_aux_rows: 24
80
+ grad_accum: 4
81
+ calib_max_rows: 120
82
+ escalate_epochs: 1
83
+
84
+ out_dir: "checkpoints/qwenjev-s1-hf-v3"
85
+ log_every: 2
86
+ eval_max_actions: 300
87
+ """
88
+
89
+ _state = {"current": "init", "stages": {}, "pid": os.getpid(),
90
+ "started": time.strftime("%F %T"), "updated": ""}
91
+
92
+
93
+ def save_status():
94
+ _state["updated"] = time.strftime("%T")
95
+ STATUS.write_text(json.dumps(_state, indent=1))
96
+
97
+
98
+ def peak_sampler(proc):
99
+ peak = 0.0
100
+ while proc.poll() is None:
101
+ try:
102
+ q = subprocess.run(
103
+ ["nvidia-smi", "--query-gpu=memory.used",
104
+ "--format=csv,noheader,nounits"],
105
+ capture_output=True, text=True, timeout=10)
106
+ peak = max(peak, float(q.stdout.strip().splitlines()[0]) / 1024.0)
107
+ except Exception:
108
+ pass
109
+ time.sleep(5)
110
+ return peak
111
+
112
+
113
+ def run_stage(name, code, vram=True):
114
+ if (DONE / f"{name}.stamp").exists():
115
+ _state["stages"][name] = {"state": "skipped(done)"}
116
+ save_status()
117
+ print(f"[v3] {name}: already done", flush=True)
118
+ return
119
+ _state["current"] = name
120
+ save_status()
121
+ log = LOGS / f"{name}.log"
122
+ with log.open("w") as lf:
123
+ t0 = time.time()
124
+ proc = subprocess.Popen(
125
+ [sys.executable, "-B", "-c", code], cwd=str(REPO),
126
+ stdout=lf, stderr=subprocess.STDOUT,
127
+ env={**os.environ, "PYTHONUNBUFFERED": "1"})
128
+ peak = peak_sampler(proc) if vram else 0.0
129
+ rc = proc.wait()
130
+ secs = round(time.time() - t0, 1)
131
+ if rc != 0:
132
+ _state["stages"][name] = {"state": "FAILED", "rc": rc, "secs": secs}
133
+ (DONE / f"{name}.FAILED").write_text(log.read_text()[-2500:])
134
+ save_status()
135
+ raise SystemExit(f"[v3] {name} FAILED rc={rc} — see {log}")
136
+ _state["stages"][name] = {
137
+ "state": "ok", "secs": secs, "peak_vram_gb": round(peak, 2)}
138
+ (DONE / f"{name}.stamp").write_text(time.strftime("%F %T"))
139
+ save_status()
140
+ print(f"[v3] {name}: ok in {secs}s peak={peak:.2f}GB", flush=True)
141
+
142
+
143
+ def hf_api():
144
+ from huggingface_hub import HfApi
145
+ return HfApi(token=(WORK / ".hf_token").read_text().strip())
146
+
147
+
148
+ def upload(name, local: Path, in_repo: str):
149
+ for attempt in (1, 2, 3):
150
+ try:
151
+ t0 = time.time()
152
+ hf_api().upload_file(
153
+ path_or_fileobj=str(local), path_in_repo=in_repo,
154
+ repo_id=HF_REPO, repo_type="model",
155
+ commit_message=f"t4_assets_v3: {name} (real-games run)")
156
+ print(f"[v3] uploaded {in_repo} in {time.time()-t0:.0f}s",
157
+ flush=True)
158
+ return
159
+ except Exception as e:
160
+ print(f"[v3] upload {name} attempt {attempt} failed: {e}",
161
+ flush=True)
162
+ time.sleep(20)
163
+ raise SystemExit(f"[v3] upload {name} gave up")
164
+
165
+
166
+ def main():
167
+ LOGS.mkdir(parents=True, exist_ok=True)
168
+ DONE.mkdir(parents=True, exist_ok=True)
169
+ (REPO / "configs" / "t4_real25.yaml").write_text(REAL25_YAML)
170
+ if (WORK / ".hf_token").exists():
171
+ os.environ["HF_TOKEN"] = (WORK / ".hf_token").read_text().strip()
172
+ save_status()
173
+
174
+ # ---- prep: v2 hf_lora parts -> stage_a_lora -------------------------
175
+ run_stage("prep", f"""
176
+ import json, hashlib, shutil, subprocess, sys
177
+ from pathlib import Path
178
+ from huggingface_hub import hf_hub_download
179
+ OUT = Path({str(OUT)!r}); sa = OUT / "stage_a_lora"
180
+ sa.mkdir(parents=True, exist_ok=True)
181
+ for f in ["adapter_config.json", "README.md"]:
182
+ p = hf_hub_download({HF_REPO!r}, "t4_assets_v2/checkpoints/hf_lora/" + f)
183
+ shutil.copy(p, sa / f)
184
+ parts_dir = Path("/tmp/v2_parts"); parts_dir.mkdir(exist_ok=True)
185
+ for f in ["manifest.json", "reassemble.py", "adapter_p00_31568f83.pt",
186
+ "adapter_p01_2dcd80ad.pt", "adapter_p02_27a9e1ff.pt",
187
+ "adapter_p03_eee3e8f3.pt"]:
188
+ p = hf_hub_download({HF_REPO!r}, f"t4_assets_v2/checkpoints/hf_lora/parts/{{f}}")
189
+ shutil.copy(p, parts_dir / f)
190
+ subprocess.run([sys.executable, str(parts_dir / "reassemble.py")], cwd=str(parts_dir), check=True)
191
+ src = parts_dir / "adapter_model.safetensors"
192
+ if not src.exists():
193
+ raise SystemExit("reassemble did not produce adapter_model.safetensors")
194
+ sha = hashlib.sha256(src.read_bytes()).hexdigest()
195
+ print("reassembled sha256:", sha)
196
+ shutil.copy(src, sa / "adapter_model.safetensors")
197
+ print("prep ok ->", sa)
198
+ """, vram=False)
199
+
200
+ # ---- data: merge remote + local datasets ----------------------------
201
+ run_stage("data", f"""
202
+ import shutil, tarfile, subprocess, sys
203
+ from pathlib import Path
204
+ from huggingface_hub import hf_hub_download
205
+ rem = Path("data/dataset_remote")
206
+ if not (rem / "train" / "samples.jsonl").exists():
207
+ subprocess.run([sys.executable, "-m", "data.build_dataset",
208
+ "--in", "data/traces_real2", "--out", "data/dataset_remote",
209
+ "--val-ratio", "0.05"], check=True)
210
+ loc = Path("data/dataset_local")
211
+ if not (loc / "train" / "samples.jsonl").exists():
212
+ tgz = hf_hub_download({HF_REPO!r}, "t4_assets_v3/data/dataset_v3_local.tgz")
213
+ with tarfile.open(tgz) as t:
214
+ t.extractall("data/")
215
+ # tgz contains dataset_v3/ — rename to dataset_local
216
+ if not (loc / "train" / "samples.jsonl").exists():
217
+ src = Path("data/dataset_v3")
218
+ if (src / "train" / "samples.jsonl").exists():
219
+ shutil.move(str(src), str(loc))
220
+ assert (loc / "train" / "samples.jsonl").exists(), "bad tgz layout"
221
+ fin = Path("data/dataset_final")
222
+ shutil.rmtree(fin, ignore_errors=True)
223
+ (fin / "train").mkdir(parents=True); (fin / "val").mkdir(parents=True)
224
+ import json
225
+ counts = {{"train": 0, "val": 0}}
226
+ for split in ["train", "val"]:
227
+ with open(fin / split / "samples.jsonl", "w") as out:
228
+ for root in [rem, loc]:
229
+ p = root / split / "samples.jsonl"
230
+ if not p.exists():
231
+ continue
232
+ with open(p) as f:
233
+ for line in f:
234
+ if line.strip():
235
+ out.write(line); counts[split] += 1
236
+ print("merged:", counts)
237
+ (fin / "stats.json").write_text(json.dumps(counts))
238
+ """, vram=False)
239
+
240
+ # ---- real_b: SFT -----------------------------------------------------
241
+ run_stage("real_b", """
242
+ import runpy, sys
243
+ sys.argv = ['train_system1.py', '--config', 'configs/t4_real25.yaml']
244
+ runpy.run_module('train.train_system1', run_name='__main__')
245
+ """)
246
+ upload("hf_lora_config", OUT / "hf_lora" / "adapter_config.json",
247
+ "t4_assets_v3/checkpoints/hf_lora/adapter_config.json")
248
+ upload("hf_lora_model", OUT / "hf_lora" / "adapter_model.safetensors",
249
+ "t4_assets_v3/checkpoints/hf_lora/adapter_model.safetensors")
250
+ upload("hf_lora_readme", OUT / "hf_lora" / "README.md",
251
+ "t4_assets_v3/checkpoints/hf_lora/README.md")
252
+
253
+ # ---- real_c: RLCD + calibration --------------------------------------
254
+ run_stage("real_c", """
255
+ import runpy, sys
256
+ sys.argv = ['calibrate_rlcd.py', '--config', 'configs/t4_real25.yaml']
257
+ runpy.run_module('train.calibrate_rlcd', run_name='__main__')
258
+ """)
259
+ for f, r in [("qwenjev_s1.pt", "qwenjev_s1.pt"),
260
+ ("calib_report.json", "calib_report.json"),
261
+ ("stage_b_meta.json", "stage_b_meta.json")]:
262
+ upload(f, OUT / f, f"t4_assets_v3/checkpoints/{r}")
263
+
264
+ _state["current"] = "ALL_DONE"
265
+ _state["finished"] = time.strftime("%F %T")
266
+ save_status()
267
+ print("[v3] ALL_DONE", flush=True)
268
+
269
+
270
+ if __name__ == "__main__":
271
+ main()