From 1bdf1fc6b15d8c7971db3282c68f4d5ddc76b2fa Mon Sep 17 00:00:00 2001 From: GotthardG <51994228+GotthardG@users.noreply.github.com> Date: Mon, 28 Sep 2026 21:01:31 +0200 Subject: [PATCH] train_run: --set KEY=VALUE overrides for Ultralytics train arguments, recorded in the ledger Co-Authored-By: Claude Fable 5.1 --- tools/train_run.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tools/train_run.py b/tools/train_run.py index 146cda2..46b3070 100644 --- a/tools/train_run.py +++ b/tools/train_run.py @@ -171,6 +171,8 @@ def main(argv=None) -> int: ap.add_argument("--baseline", default=None, help="gold report to gate against (default: the active model's)") ap.add_argument("--no-promote", action="store_true", help="gate, but leave production.json alone") ap.add_argument("--skip-train", action="store_true", help="reuse runs//ultralytics/train/weights/best.pt") + ap.add_argument("--set", nargs="*", default=[], metavar="KEY=VALUE", + help="override any Ultralytics train argument, e.g. fliplr=0 mask_ratio=1 overlap_mask=true; recorded in the ledger") args = ap.parse_args(argv) run = ledger.load(args.run_id) @@ -179,8 +181,17 @@ def main(argv=None) -> int: data_yaml = resolved_yaml(run_dir, ds) names = [v for _, v in sorted(json.loads((ds / "source.json").read_text())["classes"].items(), key=lambda kv: int(kv[0]))] \ if (ds / "source.json").exists() else ["loop_all", "pin", "crystal", "loop_face", "ice", "needle"] + overrides: Dict[str, Any] = {} + for kv in args.set or []: + if "=" not in kv: + raise SystemExit(f"--set expects KEY=VALUE, got {kv!r}") + k, v = kv.split("=", 1) + try: + overrides[k] = json.loads(v.lower() if v.lower() in ("true", "false") else v) + except json.JSONDecodeError: + overrides[k] = v hyper = {"epochs": args.epochs, "patience": args.patience, "imgsz": args.imgsz, "batch": args.batch, - "device": args.device, "overlap_mask": False, **AUGMENT} + "device": args.device, "overlap_mask": False, **AUGMENT, **overrides} ledger.set_status(args.run_id, "training", note="train_run.py", kv={ "hyperparams": hyper, "host": socket.gethostname(), "started": datetime.now().isoformat(timespec="seconds")}) @@ -188,9 +199,10 @@ def main(argv=None) -> int: if not args.skip_train: from ultralytics import YOLO model = YOLO(base_weights_path(run["base_weights"])) - model.train(data=str(data_yaml), epochs=args.epochs, patience=args.patience, imgsz=args.imgsz, - batch=args.batch, device=args.device, workers=args.workers, overlap_mask=False, - project=str(run_dir / "ultralytics"), name="train", exist_ok=True, amp=False, **AUGMENT) + train_kwargs = dict(epochs=args.epochs, patience=args.patience, imgsz=args.imgsz, batch=args.batch, + device=args.device, workers=args.workers, overlap_mask=False, amp=False, **AUGMENT) + train_kwargs.update(overrides) # --set wins over the recipe + model.train(data=str(data_yaml), project=str(run_dir / "ultralytics"), name="train", exist_ok=True, **train_kwargs) if not best.exists(): ledger.set_status(args.run_id, "aborted", note=f"no best.pt at {best}") return 1