div18 commited on
Commit
aaad9f1
Β·
1 Parent(s): 8425a53

final commit

Browse files
training/config.yaml CHANGED
@@ -80,15 +80,16 @@ generation_do_sample: true
80
 
81
  # ---- Evaluation ----
82
  eval_interval: 50 # Evaluate every N iterations
83
- eval_episodes: 3 # Episodes per task during eval
84
- eval_max_steps: 60
85
 
86
  # ---- Checkpointing ----
87
  checkpoint_interval: 5 # Save checkpoint every 5 iters (every ~15 min)
88
  save_total_limit: 5 # Max checkpoints on Hub
89
 
90
- # ---- Metrics ----
91
- push_interval: 10 # Push metrics to Hub every N iterations
 
92
 
93
  # ---- Output ----
94
  output_dir: "/workspace/antiatropos_checkpoints" # Base dir β€” run_id subfolder auto-created
@@ -100,7 +101,8 @@ plot_dpi: 150 # Resolution
100
 
101
  # ---- Hugging Face Hub ----
102
  hub_model_repo: "" # e.g. "pranavkk/antiatropos-qlora-qwen3.5-4b"
103
- hub_metrics_dataset: "" # e.g. "pranavkk/antiatropos-training-metrics"
 
104
  push_to_hub: true # Push adapter + metrics after training
105
 
106
  # ---- Reproducibility ----
 
80
 
81
  # ---- Evaluation ----
82
  eval_interval: 50 # Evaluate every N iterations
83
+ eval_episodes: 2 # Episodes per task during eval
84
+ eval_max_steps: 30 # 30 steps Γ— 2 episodes per task = 60 total
85
 
86
  # ---- Checkpointing ----
87
  checkpoint_interval: 5 # Save checkpoint every 5 iters (every ~15 min)
88
  save_total_limit: 5 # Max checkpoints on Hub
89
 
90
+ # ---- Metrics & Logging ----
91
+ # Metrics, logs, eval results, and plots are pushed to hub_model_repo/<run_id>/
92
+ # alongside checkpoints. No separate dataset repo needed.
93
 
94
  # ---- Output ----
95
  output_dir: "/workspace/antiatropos_checkpoints" # Base dir β€” run_id subfolder auto-created
 
101
 
102
  # ---- Hugging Face Hub ----
103
  hub_model_repo: "" # e.g. "pranavkk/antiatropos-qlora-qwen3.5-4b"
104
+ # All metrics, logs, eval results, and plots are pushed to this repo under <run_id>/
105
+ # along with checkpoints.
106
  push_to_hub: true # Push adapter + metrics after training
107
 
108
  # ---- Reproducibility ----
training/eval.py CHANGED
@@ -5,7 +5,8 @@ Runs episodes with:
5
  1. The fine-tuned model (current LoRA adapter)
6
  2. The heuristic baseline
7
 
8
- Compares average rewards across tasks. Pushes results to Hub metrics dataset.
 
9
  """
10
 
11
  from __future__ import annotations
@@ -128,70 +129,12 @@ def evaluate(
128
  # Save eval results
129
  import os
130
  os.makedirs(output_dir, exist_ok=True)
131
- with open(f"{output_dir}/eval_results.json", "w") as f:
 
132
  json.dump(summary, f, indent=2)
 
133
 
134
  return summary
135
 
136
 
137
- def push_eval_results(
138
- results: Dict[str, Any],
139
- hub_dataset: str,
140
- run_id: str,
141
- iteration: int,
142
- ) -> None:
143
- """Push eval results as a row to the HF metrics dataset."""
144
- if not hub_dataset:
145
- return
146
-
147
- row = {
148
- "run_id": run_id,
149
- "step": iteration,
150
- "type": "eval",
151
- **{f"eval_{k}": v for k, v in results.items() if not isinstance(v, dict)},
152
- }
153
- # Flatten per-task results
154
- for task_id, task_results in results.get("per_task", {}).items():
155
- for metric, value in task_results.items():
156
- row[f"eval_{task_id}_{metric}"] = value
157
-
158
- _append_to_dataset(row, hub_dataset)
159
-
160
 
161
- def _append_to_dataset(row: Dict[str, Any], hub_dataset: str) -> None:
162
- """Append a row to a JSONL file on Hub (creates if not exists)."""
163
- try:
164
- from huggingface_hub import HfApi
165
- api = HfApi()
166
-
167
- # Download existing data or start fresh
168
- import tempfile, os
169
- tmp_dir = tempfile.mkdtemp()
170
- jsonl_path = os.path.join(tmp_dir, "metrics.jsonl")
171
-
172
- try:
173
- api.hf_hub_download(
174
- repo_id=hub_dataset,
175
- filename="metrics.jsonl",
176
- repo_type="dataset",
177
- local_dir=tmp_dir,
178
- )
179
- except Exception:
180
- pass # File doesn't exist yet β€” that's fine
181
-
182
- # Append row
183
- with open(jsonl_path, "a") as f:
184
- f.write(json.dumps(row) + "\n")
185
-
186
- # Upload back
187
- api.upload_file(
188
- path_or_fileobj=jsonl_path,
189
- path_in_repo="metrics.jsonl",
190
- repo_id=hub_dataset,
191
- repo_type="dataset",
192
- commit_message=f"AntiAtropos metrics β€” {row.get('run_id', 'unknown')} step {row.get('step', '?')}",
193
- )
194
- print(f"[eval] Metrics pushed to {hub_dataset}")
195
-
196
- except Exception as e:
197
- print(f"[eval] Failed to push metrics: {e}")
 
5
  1. The fine-tuned model (current LoRA adapter)
6
  2. The heuristic baseline
7
 
8
+ Compares average rewards across tasks. Results are saved locally and pushed
9
+ via push_run_files_to_hub (in train.py) under hub_model_repo/<run_id>/eval_results.json.
10
  """
11
 
12
  from __future__ import annotations
 
129
  # Save eval results
130
  import os
131
  os.makedirs(output_dir, exist_ok=True)
132
+ eval_path = f"{output_dir}/eval_results.json"
133
+ with open(eval_path, "w") as f:
134
  json.dump(summary, f, indent=2)
135
+ print(f" [eval] Saved results β†’ {eval_path}")
136
 
137
  return summary
138
 
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
training/launch_train.py CHANGED
@@ -2,7 +2,7 @@
2
  """
3
  launch_train.py β€” Launch full AntiAtropos training on Hugging Face Jobs.
4
 
5
- Pushes model checkpoints, metrics dataset, and plots to HF Hub.
6
  The local server is co-located for zero-latency environment interaction.
7
  Supports automatic resume from latest Hub checkpoint.
8
 
@@ -10,56 +10,38 @@ Prerequisites:
10
  1. pip install "huggingface_hub>=0.25.0"
11
  2. huggingface-cli login (or set HF_TOKEN env var)
12
  3. HF Pro/Team account (required for GPU jobs)
13
- 4. The Hub model and dataset repos are auto-created if they don't exist.
14
- Alternatively create them manually:
15
  hf repo create <hub-model-repo> --type model
16
- hf repo create <hub-metrics-dataset> --type dataset
17
 
18
  Lifecycle:
19
- β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
20
- β”‚ HF Job (A10G, ~4h) β”‚
21
- β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
22
- β”‚ β”‚ uvicorn :8000 ←──→ train.py β”‚ β”‚
23
- β”‚ β”‚ (simulator) (GPU model) β”‚ β”‚
24
- β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
25
- β”‚ β”‚ push adapter + plots β”‚
26
- β”‚ β–Ό β”‚
27
- β”‚ HF Hub Model Repo β”‚
28
- β”‚ (checkpoint-25, checkpoint-50, ...) β”‚
29
- β”‚ β”‚ push metrics.jsonl β”‚
30
- β”‚ β–Ό β”‚
31
- β”‚ HF Hub Metrics Dataset β”‚
32
- β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
33
 
34
  Usage:
35
- # Quick test (∼10 min):
36
  python training/launch_train.py \
37
  --hub-model-repo Keshav051/antiatropos-qlora \
38
- --hub-metrics-dataset Keshav051/antiatropos-training-metrics \
39
  --num-iterations 20 --num-episodes 4
40
 
41
- # Full training (a10g-large β‰ˆ $0.34/hr, ∼2h):
42
  python training/launch_train.py \
43
- --hub-model-repo Keshav051/antiatropos-qlora \
44
- --hub-metrics-dataset Keshav051/antiatropos-training-metrics
45
 
46
  # Custom flavor / longer timeout:
47
  python training/launch_train.py \
48
  --hub-model-repo Keshav051/antiatropos-qlora \
49
- --hub-metrics-dataset Keshav051/antiatropos-training-metrics \
50
  --flavor a10g-xlarge --timeout 12h \
51
  --num-iterations 2000 --num-episodes 24
52
 
53
  # Resume from latest Hub checkpoint:
54
  python training/launch_train.py \
55
  --hub-model-repo Keshav051/antiatropos-qlora \
56
- --hub-metrics-dataset Keshav051/antiatropos-training-metrics \
57
  --run-id exp_002
58
 
59
  # Dry run (prints job command without launching):
60
  python training/launch_train.py \
61
  --hub-model-repo Keshav051/antiatropos-qlora \
62
- --hub-metrics-dataset Keshav051/antiatropos-training-metrics \
63
  --dry-run
64
  """
65
 
@@ -76,12 +58,12 @@ TRAINING_DIR = Path(__file__).resolve().parent
76
 
77
  DOCKER_IMAGE = "pytorch/pytorch:2.10.0-cuda12.6-cudnn9-devel"
78
 
79
- DEFAULT_NUM_ITERATIONS = 500
80
- DEFAULT_NUM_EPISODES = 4
81
  DEFAULT_MAX_STEPS = 20
82
  DEFAULT_EVAL_INTERVAL = 50
83
- DEFAULT_CHECKPOINT_INTERVAL = 25
84
- DEFAULT_PLOT_INTERVAL = 25
85
 
86
 
87
  def build_job_command() -> str:
@@ -123,7 +105,6 @@ def build_job_command() -> str:
123
  "echo '[bootstrap] Launching training (local server, Hub persistence)...'\n"
124
  "export PYTORCH_ALLOC_CONF='expandable_segments:True' # required by Qwen3.5 to avoid OOM fragmentation\n"
125
  "ANTIATROPOS_HUB_MODEL_REPO=$HUB_MODEL_REPO "
126
- "ANTIATROPOS_HUB_METRICS_DATASET=$HUB_METRICS_DATASET "
127
  "ANTIATROPOS_ENV_URL=http://localhost:8000 "
128
  "python training/train.py "
129
  "--run-id $RUN_ID "
@@ -146,48 +127,38 @@ def build_job_command() -> str:
146
 
147
  def ensure_hub_repos(
148
  hub_model_repo: str,
149
- hub_metrics_dataset: str,
150
  hf_token: Optional[str],
151
  ) -> None:
152
- """Check if Hub repos exist; create them automatically if not."""
153
  if not hf_token:
154
  print(" [hub] No HF_TOKEN available, skipping repo check")
155
  return
156
 
 
 
 
157
  try:
158
  from huggingface_hub import HfApi
159
 
160
  api = HfApi()
161
 
162
- for repo_id, repo_type in [
163
- (hub_model_repo, "model"),
164
- (hub_metrics_dataset, "dataset"),
165
- ]:
166
- base_url = (
167
- "https://huggingface.co"
168
- if repo_type == "model"
169
- else "https://huggingface.co/datasets"
 
 
170
  )
171
- try:
172
- info = api.repo_info(repo_id=repo_id, repo_type=repo_type)
173
- print(f" [hub] Repo OK: {base_url}/{repo_id}")
174
- except Exception:
175
- print(f" [hub] Creating repo: {repo_id} ({repo_type})...")
176
- api.create_repo(
177
- repo_id=repo_id,
178
- repo_type=repo_type,
179
- private=True,
180
- exist_ok=True,
181
- )
182
- print(f" [hub] Created: {base_url}/{repo_id}")
183
  except Exception as e:
184
- print(f"\n [hub] WARNING: Could not verify/create Hub repos: {e}")
185
- print(" [hub] Create them manually:")
186
  print(f" hf repo create {hub_model_repo} --type model")
187
- print(f" hf repo create {hub_metrics_dataset} --type dataset")
188
- print(f" Then visit:")
189
- print(f" https://huggingface.co/{hub_model_repo}")
190
- print(f" https://huggingface.co/datasets/{hub_metrics_dataset}")
191
 
192
 
193
  def main() -> None:
@@ -212,14 +183,8 @@ def main() -> None:
212
  parser.add_argument(
213
  "--hub-model-repo",
214
  required=True,
215
- help="HF Hub model repo for checkpoints, final adapter, and plots "
216
- "(e.g. Keshav051/antiatropos-qlora)",
217
- )
218
- parser.add_argument(
219
- "--hub-metrics-dataset",
220
- required=True,
221
- help="HF Hub dataset repo for training metrics.jsonl "
222
- "(e.g. Keshav051/antiatropos-training-metrics)",
223
  )
224
  parser.add_argument(
225
  "--run-id",
@@ -300,7 +265,6 @@ def main() -> None:
300
  print(f" Timeout: {args.timeout}")
301
  print(f" Code repo: {args.repo}")
302
  print(f" Hub model repo: {args.hub_model_repo}")
303
- print(f" Hub metrics dataset: {args.hub_metrics_dataset}")
304
  print(f" Run ID: {run_id}")
305
  print(f" Loss type: {args.loss_type}")
306
  if args.loss_type == "grpo":
@@ -361,7 +325,7 @@ def main() -> None:
361
  # ---- Ensure Hub repos exist ----
362
  if not args.no_create_repos and hf_token:
363
  ensure_hub_repos(
364
- args.hub_model_repo, args.hub_metrics_dataset, hf_token
365
  )
366
 
367
  # ---- Launch via run_job ----
@@ -385,7 +349,6 @@ def main() -> None:
385
  "REPO": args.repo,
386
  "RUN_ID": run_id,
387
  "HUB_MODEL_REPO": args.hub_model_repo,
388
- "HUB_METRICS_DATASET": args.hub_metrics_dataset,
389
  "NUM_ITERATIONS": str(args.num_iterations),
390
  "NUM_EPISODES": str(args.num_episodes),
391
  "MAX_STEPS": str(args.max_steps),
 
2
  """
3
  launch_train.py β€” Launch full AntiAtropos training on Hugging Face Jobs.
4
 
5
+ Pushes model checkpoints, metrics, logs, and plots to HF Hub model repo.
6
  The local server is co-located for zero-latency environment interaction.
7
  Supports automatic resume from latest Hub checkpoint.
8
 
 
10
  1. pip install "huggingface_hub>=0.25.0"
11
  2. huggingface-cli login (or set HF_TOKEN env var)
12
  3. HF Pro/Team account (required for GPU jobs)
13
+ 4. The Hub model repo is auto-created if it doesn't exist.
14
+ Alternatively create it manually:
15
  hf repo create <hub-model-repo> --type model
 
16
 
17
  Lifecycle:
18
+ All run artifacts (checkpoints, metrics, logs, eval results, plots)
19
+ are pushed to <hub-model-repo>/<run_id>/ on the Hub.
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  Usage:
22
+ # Quick test (~10 min):
23
  python training/launch_train.py \
24
  --hub-model-repo Keshav051/antiatropos-qlora \
 
25
  --num-iterations 20 --num-episodes 4
26
 
27
+ # Full training (a10g-large ~ $0.34/hr, ~2h):
28
  python training/launch_train.py \
29
+ --hub-model-repo Keshav051/antiatropos-qlora
 
30
 
31
  # Custom flavor / longer timeout:
32
  python training/launch_train.py \
33
  --hub-model-repo Keshav051/antiatropos-qlora \
 
34
  --flavor a10g-xlarge --timeout 12h \
35
  --num-iterations 2000 --num-episodes 24
36
 
37
  # Resume from latest Hub checkpoint:
38
  python training/launch_train.py \
39
  --hub-model-repo Keshav051/antiatropos-qlora \
 
40
  --run-id exp_002
41
 
42
  # Dry run (prints job command without launching):
43
  python training/launch_train.py \
44
  --hub-model-repo Keshav051/antiatropos-qlora \
 
45
  --dry-run
46
  """
47
 
 
58
 
59
  DOCKER_IMAGE = "pytorch/pytorch:2.10.0-cuda12.6-cudnn9-devel"
60
 
61
+ DEFAULT_NUM_ITERATIONS = 15
62
+ DEFAULT_NUM_EPISODES = 6
63
  DEFAULT_MAX_STEPS = 20
64
  DEFAULT_EVAL_INTERVAL = 50
65
+ DEFAULT_CHECKPOINT_INTERVAL = 5
66
+ DEFAULT_PLOT_INTERVAL = 10
67
 
68
 
69
  def build_job_command() -> str:
 
105
  "echo '[bootstrap] Launching training (local server, Hub persistence)...'\n"
106
  "export PYTORCH_ALLOC_CONF='expandable_segments:True' # required by Qwen3.5 to avoid OOM fragmentation\n"
107
  "ANTIATROPOS_HUB_MODEL_REPO=$HUB_MODEL_REPO "
 
108
  "ANTIATROPOS_ENV_URL=http://localhost:8000 "
109
  "python training/train.py "
110
  "--run-id $RUN_ID "
 
127
 
128
  def ensure_hub_repos(
129
  hub_model_repo: str,
 
130
  hf_token: Optional[str],
131
  ) -> None:
132
+ """Check if the Hub model repo exists; create it automatically if not."""
133
  if not hf_token:
134
  print(" [hub] No HF_TOKEN available, skipping repo check")
135
  return
136
 
137
+ if not hub_model_repo:
138
+ return
139
+
140
  try:
141
  from huggingface_hub import HfApi
142
 
143
  api = HfApi()
144
 
145
+ try:
146
+ info = api.repo_info(repo_id=hub_model_repo, repo_type="model")
147
+ print(f" [hub] Repo OK: https://huggingface.co/{hub_model_repo}")
148
+ except Exception:
149
+ print(f" [hub] Creating repo: {hub_model_repo} (model)...")
150
+ api.create_repo(
151
+ repo_id=hub_model_repo,
152
+ repo_type="model",
153
+ private=True,
154
+ exist_ok=True,
155
  )
156
+ print(f" [hub] Created: https://huggingface.co/{hub_model_repo}")
 
 
 
 
 
 
 
 
 
 
 
157
  except Exception as e:
158
+ print(f"\n [hub] WARNING: Could not verify/create Hub repo: {e}")
159
+ print(" [hub] Create it manually:")
160
  print(f" hf repo create {hub_model_repo} --type model")
161
+ print(f" Then visit: https://huggingface.co/{hub_model_repo}")
 
 
 
162
 
163
 
164
  def main() -> None:
 
183
  parser.add_argument(
184
  "--hub-model-repo",
185
  required=True,
186
+ help="HF Hub model repo for checkpoints, metrics, logs, and plots "
187
+ "(e.g. Keshav051/antiatropos-qlora). All run artifacts go under <run_id>/.",
 
 
 
 
 
 
188
  )
189
  parser.add_argument(
190
  "--run-id",
 
265
  print(f" Timeout: {args.timeout}")
266
  print(f" Code repo: {args.repo}")
267
  print(f" Hub model repo: {args.hub_model_repo}")
 
268
  print(f" Run ID: {run_id}")
269
  print(f" Loss type: {args.loss_type}")
270
  if args.loss_type == "grpo":
 
325
  # ---- Ensure Hub repos exist ----
326
  if not args.no_create_repos and hf_token:
327
  ensure_hub_repos(
328
+ args.hub_model_repo, hf_token
329
  )
330
 
331
  # ---- Launch via run_job ----
 
349
  "REPO": args.repo,
350
  "RUN_ID": run_id,
351
  "HUB_MODEL_REPO": args.hub_model_repo,
 
352
  "NUM_ITERATIONS": str(args.num_iterations),
353
  "NUM_EPISODES": str(args.num_episodes),
354
  "MAX_STEPS": str(args.max_steps),
training/plotting.py CHANGED
@@ -520,6 +520,7 @@ def push_plots_to_hub(
520
  plot_paths: List[str],
521
  hub_repo: str,
522
  iteration: int,
 
523
  ) -> None:
524
  if not hub_repo or not plot_paths:
525
  return
@@ -528,14 +529,15 @@ def push_plots_to_hub(
528
  api = HfApi()
529
  for path in plot_paths:
530
  filename = Path(path).name
 
531
  api.upload_file(
532
  path_or_fileobj=path,
533
- path_in_repo=f"plots/iter_{iteration}/{filename}",
534
  repo_id=hub_repo,
535
  repo_type="model",
536
- commit_message=f"Training plots - iteration {iteration}",
537
  )
538
- print(f"[plotting] Pushed {len(plot_paths)} plots to {hub_repo}")
539
  except Exception as e:
540
  print(f"[plotting] Push failed: {e}")
541
 
 
520
  plot_paths: List[str],
521
  hub_repo: str,
522
  iteration: int,
523
+ run_id: str = "",
524
  ) -> None:
525
  if not hub_repo or not plot_paths:
526
  return
 
529
  api = HfApi()
530
  for path in plot_paths:
531
  filename = Path(path).name
532
+ prefix = f"{run_id}/" if run_id else ""
533
  api.upload_file(
534
  path_or_fileobj=path,
535
+ path_in_repo=f"{prefix}plots/{filename}",
536
  repo_id=hub_repo,
537
  repo_type="model",
538
+ commit_message=f"[{run_id}] Training plots - iteration {iteration}",
539
  )
540
+ print(f"[plotting] Pushed {len(plot_paths)} plots to {hub_repo}/{prefix}plots/")
541
  except Exception as e:
542
  print(f"[plotting] Push failed: {e}")
543
 
training/train.py CHANGED
@@ -66,7 +66,7 @@ from openenv_loop import (
66
  rollout_episode,
67
  rollout_heuristic_episode,
68
  )
69
- from eval import evaluate, push_eval_results
70
  from plotting import (
71
  generate_all_plots,
72
  push_plots_to_hub,
@@ -516,63 +516,22 @@ def grpo_loss_fn(
516
 
517
 
518
  # ────────────────────────────────────────────────────────────
519
- # Metrics Push
520
  # ────────────────────────────────────────────────────────────
521
 
522
- def push_train_metrics(
523
- metrics: Dict[str, Any],
524
- hub_dataset: str,
525
- ) -> None:
526
- """Push training metrics row to the Hub dataset."""
527
- if not hub_dataset:
528
- return
529
-
530
- try:
531
- from huggingface_hub import HfApi
532
- api = HfApi()
533
- import tempfile
534
-
535
- tmp_dir = tempfile.mkdtemp()
536
- jsonl_path = os.path.join(tmp_dir, "metrics.jsonl")
537
-
538
- try:
539
- api.hf_hub_download(
540
- repo_id=hub_dataset,
541
- filename="metrics.jsonl",
542
- repo_type="dataset",
543
- local_dir=tmp_dir,
544
- )
545
- except Exception:
546
- pass
547
-
548
- with open(jsonl_path, "a") as f:
549
- f.write(json.dumps(metrics) + "\n")
550
-
551
- api.upload_file(
552
- path_or_fileobj=jsonl_path,
553
- path_in_repo="metrics.jsonl",
554
- repo_id=hub_dataset,
555
- repo_type="dataset",
556
- commit_message=f"train metrics β€” {metrics.get('run_id')} iter {metrics.get('iteration')}",
557
- )
558
- except Exception as e:
559
- print(f"[train] Metrics push failed: {e}")
560
-
561
 
562
  def push_run_files_to_hub(
563
  run_id: str,
564
  output_dir: Path,
565
- hub_dataset: str,
566
  iteration: int,
567
  ) -> None:
568
- """Upload step_metrics.jsonl, iter_metrics.jsonl, and training.log to HF Hub.
569
 
570
- Files are uploaded under <run_id>/ prefix in the metrics dataset so each
571
- run's data is isolated and you can diff runs on the Hub viewer.
572
-
573
- Called every push_interval iterations and at the end of training.
574
  """
575
- if not hub_dataset:
576
  return
577
 
578
  files_to_push = [
@@ -582,6 +541,14 @@ def push_run_files_to_hub(
582
  ("run_info.json", f"{run_id}/run_info.json"),
583
  ]
584
 
 
 
 
 
 
 
 
 
585
  try:
586
  from huggingface_hub import HfApi
587
  api = HfApi()
@@ -594,15 +561,15 @@ def push_run_files_to_hub(
594
  api.upload_file(
595
  path_or_fileobj=str(local_path),
596
  path_in_repo=hub_path,
597
- repo_id=hub_dataset,
598
- repo_type="dataset",
599
  commit_message=f"[{run_id}] iter {iteration}: {local_name}",
600
  )
601
  pushed.append(local_name)
602
  except Exception as e:
603
  print(f" [push] Failed to push {local_name}: {e}")
604
  if pushed:
605
- print(f" [push] β†’ HF dataset {hub_dataset}/{run_id}/: {', '.join(pushed)}",
606
  flush=True)
607
  except Exception as e:
608
  print(f"[train] Hub file push failed: {e}")
@@ -802,7 +769,6 @@ def train(cfg: Dict[str, Any]) -> None:
802
  print(f"[train] Run manifest: {run_info_path}")
803
 
804
  hub_model_repo = cfg.get("hub_model_repo", "")
805
- hub_metrics_dataset = cfg.get("hub_metrics_dataset", "")
806
  push_to_hub_flag = cfg.get("push_to_hub", True)
807
 
808
  # ---- Verify environment ----
@@ -863,7 +829,6 @@ def train(cfg: Dict[str, Any]) -> None:
863
  max_grad_norm = cfg.get("max_grad_norm", 1.0)
864
  checkpoint_interval = cfg.get("checkpoint_interval", 10) # default: every 10 iters
865
  eval_interval = cfg.get("eval_interval", 50)
866
- push_interval = cfg.get("push_interval", 10)
867
  plot_interval = cfg.get("plot_interval", 25)
868
 
869
  # ---- Training loop ----
@@ -878,7 +843,6 @@ def train(cfg: Dict[str, Any]) -> None:
878
  print(f" Max steps: {max_steps}")
879
  print(f" Learning rate: {lr}")
880
  print(f" Hub model: {hub_model_repo or '(not configured)'}")
881
- print(f" Hub metrics: {hub_metrics_dataset or '(not configured)'}")
882
  print(f" Output dir: {output_dir}")
883
  print(f"{'='*70}\n")
884
 
@@ -1017,12 +981,7 @@ def train(cfg: Dict[str, Any]) -> None:
1017
  if len(recent_episodes_data) > 200: # Keep last ~200 episodes
1018
  recent_episodes_data = recent_episodes_data[-200:]
1019
 
1020
- # ---- Push metrics + run files to Hub ----
1021
- if (iteration + 1) % push_interval == 0 and hub_metrics_dataset:
1022
- # Push step_metrics.jsonl, iter_metrics.jsonl, training.log, run_info.json
1023
- push_run_files_to_hub(run_id, output_dir, hub_metrics_dataset, iteration + 1)
1024
-
1025
- # ---- Checkpoint ----
1026
  if (iteration + 1) % checkpoint_interval == 0:
1027
  # Pad iteration number so ls sorts correctly: checkpoint-0010, checkpoint-0050, ...
1028
  ckpt_name = f"checkpoint-{iteration + 1:04d}"
@@ -1041,7 +1000,7 @@ def train(cfg: Dict[str, Any]) -> None:
1041
  (ckpt_dir / "checkpoint_meta.json").write_text(
1042
  _json.dumps(ckpt_meta, indent=2)
1043
  )
1044
- print(f" [ckpt] Saved β†’ {ckpt_dir} "
1045
  f"(reward={avg_reward:.4f} loss={loss.item():.4f})", flush=True)
1046
  if push_to_hub_flag and hub_model_repo:
1047
  push_to_hub(
@@ -1050,6 +1009,8 @@ def train(cfg: Dict[str, Any]) -> None:
1050
  commit_message=f"[{run_id}] {ckpt_name}",
1051
  path_in_repo=f"{run_id}/{ckpt_name}",
1052
  )
 
 
1053
 
1054
  # ---- Evaluation ----
1055
  if (iteration + 1) % eval_interval == 0:
@@ -1071,10 +1032,6 @@ def train(cfg: Dict[str, Any]) -> None:
1071
  eval_row[f"eval_{tid}_{mk}"] = mv
1072
  eval_metrics_history.append(eval_row)
1073
 
1074
- if hub_metrics_dataset:
1075
- push_eval_results(
1076
- eval_results, hub_metrics_dataset, run_id, iteration
1077
- )
1078
  # Re-enable training mode
1079
  model.train()
1080
 
@@ -1089,7 +1046,7 @@ def train(cfg: Dict[str, Any]) -> None:
1089
  cfg=cfg,
1090
  )
1091
  if push_to_hub_flag and hub_model_repo:
1092
- push_plots_to_hub(plot_paths, hub_model_repo, iteration)
1093
  except Exception as e:
1094
  print(f" [iter {iteration}] Plotting failed: {e}")
1095
 
@@ -1123,12 +1080,6 @@ def train(cfg: Dict[str, Any]) -> None:
1123
  client, model, tokenizer, cfg,
1124
  output_dir=str(output_dir / "final_eval"),
1125
  )
1126
-
1127
- if hub_metrics_dataset:
1128
- push_eval_results(
1129
- final_eval, hub_metrics_dataset, run_id, num_iterations
1130
- )
1131
-
1132
  # Final plots (full training history)
1133
  try:
1134
  final_eval_row = {
@@ -1152,19 +1103,17 @@ def train(cfg: Dict[str, Any]) -> None:
1152
  cfg=cfg,
1153
  )
1154
  if push_to_hub_flag and hub_model_repo:
1155
- push_plots_to_hub(plot_paths, hub_model_repo, num_iterations)
1156
  except Exception as e:
1157
  print(f"[train] Final plotting failed: {e}")
1158
 
1159
  print(f"\n[train] All done. Final adapter: {final_dir}")
1160
  if hub_model_repo:
1161
  print(f"[train] Hub repo: https://huggingface.co/{hub_model_repo}")
1162
- if hub_metrics_dataset:
1163
- print(f"[train] Metrics: https://huggingface.co/datasets/{hub_metrics_dataset}/tree/main/{run_id}")
1164
-
1165
- # ── Final push of all run files (captures last iterations even if push_interval missed them)
1166
- if hub_metrics_dataset:
1167
- push_run_files_to_hub(run_id, output_dir, hub_metrics_dataset, num_iterations)
1168
 
1169
  # ── Flush and close the TeeLogger ────────────────────────────────────────
1170
  # Restore original stdout/stderr so any code after train() works normally.
@@ -1265,7 +1214,6 @@ def main():
1265
  if args.no_push:
1266
  cfg["push_to_hub"] = False
1267
  cfg["hub_model_repo"] = ""
1268
- cfg["hub_metrics_dataset"] = ""
1269
 
1270
  # Allow HF_TOKEN from env
1271
  hf_token = os.getenv("HF_TOKEN") or os.getenv("HUGGING_FACE_HUB_TOKEN")
 
66
  rollout_episode,
67
  rollout_heuristic_episode,
68
  )
69
+ from eval import evaluate
70
  from plotting import (
71
  generate_all_plots,
72
  push_plots_to_hub,
 
516
 
517
 
518
  # ────────────────────────────────────────────────────────────
519
+ # Run Files Push (to hub_model_repo/<run_id>/)
520
  # ────────────────────────────────────────────────────────────
521
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
 
523
  def push_run_files_to_hub(
524
  run_id: str,
525
  output_dir: Path,
526
+ hub_model_repo: str,
527
  iteration: int,
528
  ) -> None:
529
+ """Upload step_metrics.jsonl, iter_metrics.jsonl, training.log, and eval results.
530
 
531
+ Files are uploaded under <run_id>/ in the model repo alongside checkpoints.
532
+ Called every checkpoint_interval iterations and at the end of training.
 
 
533
  """
534
+ if not hub_model_repo:
535
  return
536
 
537
  files_to_push = [
 
541
  ("run_info.json", f"{run_id}/run_info.json"),
542
  ]
543
 
544
+ # Also push eval results if they exist
545
+ eval_path = output_dir / "eval" / "eval_results.json"
546
+ if eval_path.exists():
547
+ files_to_push.append(("eval/eval_results.json", f"{run_id}/eval_results.json"))
548
+ final_eval_path = output_dir / "final_eval" / "eval_results.json"
549
+ if final_eval_path.exists():
550
+ files_to_push.append(("final_eval/eval_results.json", f"{run_id}/final_eval_results.json"))
551
+
552
  try:
553
  from huggingface_hub import HfApi
554
  api = HfApi()
 
561
  api.upload_file(
562
  path_or_fileobj=str(local_path),
563
  path_in_repo=hub_path,
564
+ repo_id=hub_model_repo,
565
+ repo_type="model",
566
  commit_message=f"[{run_id}] iter {iteration}: {local_name}",
567
  )
568
  pushed.append(local_name)
569
  except Exception as e:
570
  print(f" [push] Failed to push {local_name}: {e}")
571
  if pushed:
572
+ print(f" [push] \u2192 HF model {hub_model_repo}/{run_id}/: {', '.join(pushed)}",
573
  flush=True)
574
  except Exception as e:
575
  print(f"[train] Hub file push failed: {e}")
 
769
  print(f"[train] Run manifest: {run_info_path}")
770
 
771
  hub_model_repo = cfg.get("hub_model_repo", "")
 
772
  push_to_hub_flag = cfg.get("push_to_hub", True)
773
 
774
  # ---- Verify environment ----
 
829
  max_grad_norm = cfg.get("max_grad_norm", 1.0)
830
  checkpoint_interval = cfg.get("checkpoint_interval", 10) # default: every 10 iters
831
  eval_interval = cfg.get("eval_interval", 50)
 
832
  plot_interval = cfg.get("plot_interval", 25)
833
 
834
  # ---- Training loop ----
 
843
  print(f" Max steps: {max_steps}")
844
  print(f" Learning rate: {lr}")
845
  print(f" Hub model: {hub_model_repo or '(not configured)'}")
 
846
  print(f" Output dir: {output_dir}")
847
  print(f"{'='*70}\n")
848
 
 
981
  if len(recent_episodes_data) > 200: # Keep last ~200 episodes
982
  recent_episodes_data = recent_episodes_data[-200:]
983
 
984
+ # ---- Checkpoint + push run files ----
 
 
 
 
 
985
  if (iteration + 1) % checkpoint_interval == 0:
986
  # Pad iteration number so ls sorts correctly: checkpoint-0010, checkpoint-0050, ...
987
  ckpt_name = f"checkpoint-{iteration + 1:04d}"
 
1000
  (ckpt_dir / "checkpoint_meta.json").write_text(
1001
  _json.dumps(ckpt_meta, indent=2)
1002
  )
1003
+ print(f" [ckpt] Saved \u2192 {ckpt_dir} "
1004
  f"(reward={avg_reward:.4f} loss={loss.item():.4f})", flush=True)
1005
  if push_to_hub_flag and hub_model_repo:
1006
  push_to_hub(
 
1009
  commit_message=f"[{run_id}] {ckpt_name}",
1010
  path_in_repo=f"{run_id}/{ckpt_name}",
1011
  )
1012
+ # Push run files (metrics, logs) alongside checkpoint
1013
+ push_run_files_to_hub(run_id, output_dir, hub_model_repo, iteration + 1)
1014
 
1015
  # ---- Evaluation ----
1016
  if (iteration + 1) % eval_interval == 0:
 
1032
  eval_row[f"eval_{tid}_{mk}"] = mv
1033
  eval_metrics_history.append(eval_row)
1034
 
 
 
 
 
1035
  # Re-enable training mode
1036
  model.train()
1037
 
 
1046
  cfg=cfg,
1047
  )
1048
  if push_to_hub_flag and hub_model_repo:
1049
+ push_plots_to_hub(plot_paths, hub_model_repo, iteration, run_id=run_id)
1050
  except Exception as e:
1051
  print(f" [iter {iteration}] Plotting failed: {e}")
1052
 
 
1080
  client, model, tokenizer, cfg,
1081
  output_dir=str(output_dir / "final_eval"),
1082
  )
 
 
 
 
 
 
1083
  # Final plots (full training history)
1084
  try:
1085
  final_eval_row = {
 
1103
  cfg=cfg,
1104
  )
1105
  if push_to_hub_flag and hub_model_repo:
1106
+ push_plots_to_hub(plot_paths, hub_model_repo, num_iterations, run_id=run_id)
1107
  except Exception as e:
1108
  print(f"[train] Final plotting failed: {e}")
1109
 
1110
  print(f"\n[train] All done. Final adapter: {final_dir}")
1111
  if hub_model_repo:
1112
  print(f"[train] Hub repo: https://huggingface.co/{hub_model_repo}")
1113
+
1114
+ # \u2500\u2500 Final push of all run files
1115
+ if hub_model_repo:
1116
+ push_run_files_to_hub(run_id, output_dir, hub_model_repo, num_iterations)
 
 
1117
 
1118
  # ── Flush and close the TeeLogger ────────────────────────────────────────
1119
  # Restore original stdout/stderr so any code after train() works normally.
 
1214
  if args.no_push:
1215
  cfg["push_to_hub"] = False
1216
  cfg["hub_model_repo"] = ""
 
1217
 
1218
  # Allow HF_TOKEN from env
1219
  hf_token = os.getenv("HF_TOKEN") or os.getenv("HUGGING_FACE_HUB_TOKEN")