rogermt commited on
Commit
e0b6ea4
Β·
verified Β·
1 Parent(s): 492f908

Add --log_every arg to control epoch print frequency (default 100)

Browse files
Files changed (1) hide show
  1. trm_solver/trm_nano/train_task.py +12 -20
trm_solver/trm_nano/train_task.py CHANGED
@@ -8,10 +8,11 @@ via manual builder (weight reuse), logs everything.
8
  Usage (Kaggle):
9
  python train_task.py --data_dir /kaggle/input/competitions/neurogolf-2026/ \
10
  --output_dir /kaggle/working/trm_onnx/ \
11
- --skip_solved /kaggle/working/solved_by_analytical.json
 
12
 
13
  Single task mode:
14
- python train_task.py --task_file /path/to/task001.json --output_dir onnx_out/
15
  """
16
 
17
  import os
@@ -140,7 +141,7 @@ class ARCTaskDataset(Dataset):
140
  # ─── Training ─────────────────────────────────────────────────────────────────
141
 
142
  def train_on_task(task_data, config, epochs=500, lr=1e-3, batch_size=64,
143
- num_aug=1000, device="cuda"):
144
  model = TRMNano(config).to(device)
145
  dataset = ARCTaskDataset(task_data, num_augmentations=num_aug)
146
  loader = DataLoader(dataset, batch_size=batch_size, shuffle=True,
@@ -182,7 +183,7 @@ def train_on_task(task_data, config, epochs=500, lr=1e-3, batch_size=64,
182
  best_loss = avg_loss
183
  best_state = {n: p.clone() for n, p in ema_params.items()}
184
 
185
- if (epoch + 1) % 100 == 0:
186
  logging.info(f" epoch {epoch+1}/{epochs} loss={avg_loss:.4f} best={best_loss:.4f}")
187
 
188
  if best_state:
@@ -212,7 +213,6 @@ def verify_with_onnx(onnx_path, task_data):
212
  inp_grid = np.array(example["input"], dtype=np.int64)
213
  out_grid = np.array(example["output"], dtype=np.int64)
214
 
215
- # Convert input to one-hot [1, 10, 30, 30]
216
  h, w = inp_grid.shape
217
  if h > 30 or w > 30:
218
  fails += 1
@@ -222,11 +222,9 @@ def verify_with_onnx(onnx_path, task_data):
222
  for c in range(w):
223
  inp_onehot[0, inp_grid[r, c], r, c] = 1.0
224
 
225
- # Run inference
226
  result = sess.run(None, {"input": inp_onehot})
227
  pred_onehot = (result[0] > 0.0).astype(np.float32)
228
 
229
- # Convert expected output to one-hot
230
  oh, ow = out_grid.shape
231
  exp_onehot = np.zeros((1, 10, 30, 30), dtype=np.float32)
232
  for r in range(oh):
@@ -271,6 +269,8 @@ def main():
271
  parser.add_argument("--hidden_size", type=int, default=96)
272
  parser.add_argument("--H_cycles", type=int, default=3)
273
  parser.add_argument("--L_cycles", type=int, default=4)
 
 
274
  # Limits
275
  parser.add_argument("--max_tasks", type=int, default=None)
276
  parser.add_argument("--start_task", type=int, default=1)
@@ -293,6 +293,7 @@ def main():
293
  logging.info(f"Params: {count_params(TRMNano(config)):,}")
294
  logging.info(f"Device: {args.device}")
295
  logging.info(f"Epochs: {args.epochs}, LR: {args.lr}, Batch: {args.batch_size}, Aug: {args.num_aug}")
 
296
  logging.info(f"Output: {args.output_dir}")
297
 
298
  # ─── Single task mode ─────────────────────────────────────────────────────
@@ -305,7 +306,7 @@ def main():
305
  t0 = time.time()
306
  model, best_loss = train_on_task(task_data, config, epochs=args.epochs, lr=args.lr,
307
  batch_size=args.batch_size, num_aug=args.num_aug,
308
- device=args.device)
309
  elapsed = time.time() - t0
310
  logging.info(f" Trained in {elapsed:.1f}s, best_loss={best_loss:.4f}")
311
 
@@ -322,7 +323,6 @@ def main():
322
  return
323
 
324
  # ─── Batch mode ───────────────────────────────────────────────────────────
325
- # Load skip list
326
  skip_tasks = set()
327
  if args.skip_solved and os.path.exists(args.skip_solved):
328
  with open(args.skip_solved) as f:
@@ -333,26 +333,23 @@ def main():
333
  skip_tasks = set(skip_data.keys())
334
  logging.info(f"Skipping {len(skip_tasks)} already-solved tasks")
335
 
336
- # Find task files
337
  task_files = sorted(glob.glob(os.path.join(args.data_dir, "task*.json")))
338
  if not task_files:
339
  task_files = sorted(glob.glob(os.path.join(args.data_dir, "*.json")))
340
  logging.info(f"Found {len(task_files)} task files in {args.data_dir}")
341
 
342
- # Filter
343
  tasks_to_run = []
344
  for f in task_files:
345
  task_id = os.path.basename(f).replace(".json", "")
346
- # Extract task number if possible
347
  task_num = None
348
  try:
349
  task_num = int(task_id.replace("task", ""))
350
  except ValueError:
351
  pass
352
-
353
- # Skip if in solved list (check both task_id and task_num)
354
  if task_id in skip_tasks or str(task_num) in skip_tasks:
355
  continue
 
 
356
  if task_num is not None and task_num < args.start_task:
357
  continue
358
  tasks_to_run.append((task_id, task_num, f))
@@ -384,14 +381,12 @@ def main():
384
  model, best_loss = train_on_task(
385
  task_data, config, epochs=args.epochs, lr=args.lr,
386
  batch_size=args.batch_size, num_aug=args.num_aug,
387
- device=args.device)
388
  train_time = time.time() - t0
389
 
390
- # Export ONNX
391
  onnx_path = os.path.join(args.output_dir, f"{task_id}.onnx")
392
  size_bytes = export_onnx_manual(model, config, onnx_path)
393
 
394
- # Verify
395
  passes, fails = verify_with_onnx(onnx_path, task_data)
396
  solved = (fails == 0 and passes > 0)
397
  if solved:
@@ -411,7 +406,6 @@ def main():
411
  "train_time_s": train_time,
412
  })
413
 
414
- # Remove ONNX if not solved (save disk)
415
  if not solved and os.path.exists(onnx_path):
416
  os.remove(onnx_path)
417
 
@@ -424,7 +418,6 @@ def main():
424
  "error": str(e),
425
  })
426
 
427
- # Save progress after each task
428
  progress_path = os.path.join(args.output_dir, "progress.json")
429
  with open(progress_path, "w") as f:
430
  json.dump({
@@ -445,7 +438,6 @@ def main():
445
  logging.info(f" Output: {args.output_dir}")
446
  logging.info(f" Log: {log_path}")
447
 
448
- # List solved ONNX files
449
  onnx_files = sorted(glob.glob(os.path.join(args.output_dir, "*.onnx")))
450
  total_kb = sum(os.path.getsize(f) for f in onnx_files) / 1024
451
  logging.info(f" ONNX files: {len(onnx_files)} ({total_kb:.1f} KB total)")
 
8
  Usage (Kaggle):
9
  python train_task.py --data_dir /kaggle/input/competitions/neurogolf-2026/ \
10
  --output_dir /kaggle/working/trm_onnx/ \
11
+ --skip_solved /kaggle/working/solved_by_analytical.json \
12
+ --log_every 50
13
 
14
  Single task mode:
15
+ python train_task.py --task_file /path/to/task001.json --output_dir onnx_out/ --log_every 25
16
  """
17
 
18
  import os
 
141
  # ─── Training ─────────────────────────────────────────────────────────────────
142
 
143
  def train_on_task(task_data, config, epochs=500, lr=1e-3, batch_size=64,
144
+ num_aug=1000, device="cuda", log_every=100):
145
  model = TRMNano(config).to(device)
146
  dataset = ARCTaskDataset(task_data, num_augmentations=num_aug)
147
  loader = DataLoader(dataset, batch_size=batch_size, shuffle=True,
 
183
  best_loss = avg_loss
184
  best_state = {n: p.clone() for n, p in ema_params.items()}
185
 
186
+ if (epoch + 1) % log_every == 0:
187
  logging.info(f" epoch {epoch+1}/{epochs} loss={avg_loss:.4f} best={best_loss:.4f}")
188
 
189
  if best_state:
 
213
  inp_grid = np.array(example["input"], dtype=np.int64)
214
  out_grid = np.array(example["output"], dtype=np.int64)
215
 
 
216
  h, w = inp_grid.shape
217
  if h > 30 or w > 30:
218
  fails += 1
 
222
  for c in range(w):
223
  inp_onehot[0, inp_grid[r, c], r, c] = 1.0
224
 
 
225
  result = sess.run(None, {"input": inp_onehot})
226
  pred_onehot = (result[0] > 0.0).astype(np.float32)
227
 
 
228
  oh, ow = out_grid.shape
229
  exp_onehot = np.zeros((1, 10, 30, 30), dtype=np.float32)
230
  for r in range(oh):
 
269
  parser.add_argument("--hidden_size", type=int, default=96)
270
  parser.add_argument("--H_cycles", type=int, default=3)
271
  parser.add_argument("--L_cycles", type=int, default=4)
272
+ parser.add_argument("--log_every", type=int, default=100,
273
+ help="Print training loss every N epochs (default: 100)")
274
  # Limits
275
  parser.add_argument("--max_tasks", type=int, default=None)
276
  parser.add_argument("--start_task", type=int, default=1)
 
293
  logging.info(f"Params: {count_params(TRMNano(config)):,}")
294
  logging.info(f"Device: {args.device}")
295
  logging.info(f"Epochs: {args.epochs}, LR: {args.lr}, Batch: {args.batch_size}, Aug: {args.num_aug}")
296
+ logging.info(f"Log every: {args.log_every} epochs")
297
  logging.info(f"Output: {args.output_dir}")
298
 
299
  # ─── Single task mode ─────────────────────────────────────────────────────
 
306
  t0 = time.time()
307
  model, best_loss = train_on_task(task_data, config, epochs=args.epochs, lr=args.lr,
308
  batch_size=args.batch_size, num_aug=args.num_aug,
309
+ device=args.device, log_every=args.log_every)
310
  elapsed = time.time() - t0
311
  logging.info(f" Trained in {elapsed:.1f}s, best_loss={best_loss:.4f}")
312
 
 
323
  return
324
 
325
  # ─── Batch mode ───────────────────────────────────────────────────────────
 
326
  skip_tasks = set()
327
  if args.skip_solved and os.path.exists(args.skip_solved):
328
  with open(args.skip_solved) as f:
 
333
  skip_tasks = set(skip_data.keys())
334
  logging.info(f"Skipping {len(skip_tasks)} already-solved tasks")
335
 
 
336
  task_files = sorted(glob.glob(os.path.join(args.data_dir, "task*.json")))
337
  if not task_files:
338
  task_files = sorted(glob.glob(os.path.join(args.data_dir, "*.json")))
339
  logging.info(f"Found {len(task_files)} task files in {args.data_dir}")
340
 
 
341
  tasks_to_run = []
342
  for f in task_files:
343
  task_id = os.path.basename(f).replace(".json", "")
 
344
  task_num = None
345
  try:
346
  task_num = int(task_id.replace("task", ""))
347
  except ValueError:
348
  pass
 
 
349
  if task_id in skip_tasks or str(task_num) in skip_tasks:
350
  continue
351
+ if task_num is not None and task_num in skip_tasks:
352
+ continue
353
  if task_num is not None and task_num < args.start_task:
354
  continue
355
  tasks_to_run.append((task_id, task_num, f))
 
381
  model, best_loss = train_on_task(
382
  task_data, config, epochs=args.epochs, lr=args.lr,
383
  batch_size=args.batch_size, num_aug=args.num_aug,
384
+ device=args.device, log_every=args.log_every)
385
  train_time = time.time() - t0
386
 
 
387
  onnx_path = os.path.join(args.output_dir, f"{task_id}.onnx")
388
  size_bytes = export_onnx_manual(model, config, onnx_path)
389
 
 
390
  passes, fails = verify_with_onnx(onnx_path, task_data)
391
  solved = (fails == 0 and passes > 0)
392
  if solved:
 
406
  "train_time_s": train_time,
407
  })
408
 
 
409
  if not solved and os.path.exists(onnx_path):
410
  os.remove(onnx_path)
411
 
 
418
  "error": str(e),
419
  })
420
 
 
421
  progress_path = os.path.join(args.output_dir, "progress.json")
422
  with open(progress_path, "w") as f:
423
  json.dump({
 
438
  logging.info(f" Output: {args.output_dir}")
439
  logging.info(f" Log: {log_path}")
440
 
 
441
  onnx_files = sorted(glob.glob(os.path.join(args.output_dir, "*.onnx")))
442
  total_kb = sum(os.path.getsize(f) for f in onnx_files) / 1024
443
  logging.info(f" ONNX files: {len(onnx_files)} ({total_kb:.1f} KB total)")