from __future__ import annotations import argparse import shutil from pathlib import Path from ultralytics import YOLO def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Train a lightweight steel-surface detector and install it into the project." ) parser.add_argument( "--data", default="training/data/surface_gate_detector/yolo_dataset/dataset.yaml", help="Path to the detector dataset YAML file.", ) parser.add_argument( "--model", default="yolo11n.pt", help="Pretrained Ultralytics detector checkpoint to finetune.", ) parser.add_argument("--epochs", type=int, default=18) parser.add_argument("--imgsz", type=int, default=512) parser.add_argument("--batch", type=float, default=16) parser.add_argument("--device", default="") parser.add_argument("--workers", type=int, default=4) parser.add_argument("--project", default="training/runs") parser.add_argument("--name", default="surface_gate_detector") parser.add_argument( "--target", default="models/steel_surface_detector.pt", help="Where to copy the best trained checkpoint for app inference.", ) return parser.parse_args() def main() -> None: args = parse_args() data_path = Path(args.data).resolve() if not data_path.exists(): raise FileNotFoundError(f"Dataset YAML not found: {data_path}") batch = int(args.batch) if float(args.batch).is_integer() else float(args.batch) model = YOLO(args.model) results = model.train( data=str(data_path), epochs=args.epochs, imgsz=args.imgsz, batch=batch, device=args.device or None, workers=args.workers, project=args.project, name=args.name, optimizer="AdamW", patience=5, cos_lr=True, close_mosaic=4, hsv_h=0.01, hsv_s=0.15, hsv_v=0.15, translate=0.05, scale=0.15, fliplr=0.5, mosaic=0.3, mixup=0.0, cache=True, pretrained=True, single_cls=True, save=True, verbose=True, ) best_path = Path(results.save_dir) / "weights" / "best.pt" if not best_path.exists(): raise FileNotFoundError(f"Best checkpoint was not produced at {best_path}") target_path = Path(args.target).resolve() target_path.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(best_path, target_path) print(f"Training run saved to: {results.save_dir}") print(f"Best detector copied to: {target_path}") print("Next step: restart the FastAPI app so the steel ROI detector is used in the inspection pipeline.") if __name__ == "__main__": main()