Instructions to use Lien-Feng/Lightweight-2-5D-LUNA16 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- ultralytics
How to use Lien-Feng/Lightweight-2-5D-LUNA16 with ultralytics:
# Couldn't find a valid YOLO version tag. # Replace XX with the correct version. from ultralytics import YOLOvXX model = YOLOvXX.from_pretrained("Lien-Feng/Lightweight-2-5D-LUNA16") source = 'http://images.cocodataset.org/val2017/000000039769.jpg' model.predict(source=source, save=True) - Notebooks
- Google Colab
- Kaggle
| """Run the official 10-fold training matrix. | |
| Every ``(configuration, fold)`` pair is trained with the same pinned schedule. | |
| A run counts as complete only when it wrote ``summary.json`` - Ultralytics | |
| emits ``weights/best.pt`` from the first epoch, so a checkpoint alone does not | |
| mean the schedule finished. The matrix is therefore resumable across | |
| invocations without silently keeping a half-trained fold. | |
| Usage | |
| ----- | |
| python scripts/03_train.py # everything still missing | |
| python scripts/03_train.py --groups main # one group | |
| python scripts/03_train.py --exp Exp4_2p5D_Strict --folds 0 | |
| python scripts/03_train.py --dry-run # just print the plan | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| import time | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) | |
| from luna_rev import config as cfg | |
| from luna_rev import splits | |
| from luna_rev.train import is_complete | |
| LOG_PATH = cfg.RESULTS_DIR / "training_log.jsonl" | |
| #: Seconds to idle after each run so Windows can reap the dataloader workers | |
| #: before the next run starts allocating. | |
| COOLDOWN_S = 8 | |
| def run_in_subprocess(exp, fold, force: bool = False, epochs: int | None = None) -> dict: | |
| """Train one pair in a fresh interpreter and return its record. | |
| Isolating each run guarantees that workers, pinned buffers and the CUDA | |
| context are released before the next one begins; sharing one interpreter | |
| across 120 runs exhausted page-locked host memory partway through. | |
| """ | |
| import subprocess | |
| cmd = [sys.executable, "-m", "luna_rev._train_worker", exp.name, str(fold.index)] | |
| if force: | |
| cmd.append("--force") | |
| if epochs: | |
| cmd += ["--epochs", str(epochs)] | |
| proc = subprocess.run(cmd, cwd=str(cfg.ROOT), capture_output=True, text=True, | |
| encoding="utf-8", errors="replace") | |
| time.sleep(COOLDOWN_S) | |
| for line in reversed((proc.stdout or "").splitlines()): | |
| if line.startswith("TRAIN_RECORD "): | |
| return json.loads(line[len("TRAIN_RECORD "):]) | |
| tail = "\n".join((proc.stderr or "").strip().splitlines()[-12:]) | |
| print(f" subprocess exit {proc.returncode}\n{tail}", flush=True) | |
| return {"exp": exp.name, "fold": fold.index, "status": "failed", | |
| "returncode": proc.returncode, "stderr_tail": tail} | |
| def build_plan(groups, exp_names, fold_filter) -> list[tuple]: | |
| """Expand the requested selection into ``(exp, fold)`` pairs.""" | |
| folds = {f.index: f for f in splits.get_folds("official")} | |
| experiments = cfg.experiments_for_group(*groups) if groups else list(cfg.ALL_EXPERIMENTS) | |
| if exp_names: | |
| wanted = set(exp_names) | |
| experiments = [e for e in experiments if e.name in wanted] | |
| unknown = wanted - {e.name for e in experiments} | |
| if unknown: | |
| raise SystemExit(f"Unknown experiment(s): {sorted(unknown)}") | |
| plan = [] | |
| for exp in experiments: | |
| for k in cfg.folds_for(exp): | |
| if fold_filter is not None and k not in fold_filter: | |
| continue | |
| plan.append((exp, folds[k])) | |
| return plan | |
| def main() -> int: | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--groups", nargs="*", default=None, | |
| help="main r_sweep w_sweep neg_ablation seeds yolo26") | |
| ap.add_argument("--exp", nargs="*", default=None, help="explicit configuration names") | |
| ap.add_argument("--folds", nargs="*", type=int, default=None) | |
| ap.add_argument("--force", action="store_true", help="retrain even if weights exist") | |
| ap.add_argument("--dry-run", action="store_true") | |
| ap.add_argument("--epochs", type=int, default=None, help="override the schedule (benchmarking)") | |
| args = ap.parse_args() | |
| plan = build_plan(args.groups, args.exp, set(args.folds) if args.folds else None) | |
| pending = [(e, f) for e, f in plan if args.force or not is_complete(e, f.index)] | |
| print(f"planned runs : {len(plan)}") | |
| print(f"already done : {len(plan) - len(pending)}") | |
| print(f"to train : {len(pending)}") | |
| if args.dry_run: | |
| for e, f in pending: | |
| print(f" {e.name:28s} fold{f.index} [{e.model}, r={e.r_sample}, " | |
| f"w_min={e.w_min_px}, neg={e.negatives}, seed={e.seed}]") | |
| return 0 | |
| if not pending: | |
| return 0 | |
| t_start = time.time() | |
| failures: list[tuple] = [] | |
| for i, (exp, fold) in enumerate(pending, 1): | |
| t0 = time.time() | |
| print(f"\n[{i}/{len(pending)}] {exp.name} fold{fold.index} " | |
| f"(elapsed {(time.time() - t_start) / 3600:.2f} h)", flush=True) | |
| rec = run_in_subprocess(exp, fold, force=args.force, epochs=args.epochs) | |
| rec["wall_seconds"] = round(time.time() - t0, 1) | |
| with LOG_PATH.open("a", encoding="utf-8") as fh: | |
| fh.write(json.dumps(rec) + "\n") | |
| if rec["status"] == "failed": | |
| failures.append((exp, fold)) | |
| print(f" -> FAILED after {rec['wall_seconds'] / 60:.1f} min " | |
| f"(continuing; will retry at the end)", flush=True) | |
| continue | |
| rate = (time.time() - t_start) / i | |
| print(f" -> {rec['status']} in {rec['wall_seconds'] / 60:.1f} min " | |
| f"| ETA {(len(pending) - i) * rate / 3600:.1f} h", flush=True) | |
| # One retry pass: the failures we have seen are transient resource | |
| # exhaustion, which a fresh process after an idle gap usually clears. | |
| if failures: | |
| print(f"\nRetrying {len(failures)} failed run(s) ...", flush=True) | |
| still_failing = [] | |
| for exp, fold in failures: | |
| time.sleep(30) | |
| rec = run_in_subprocess(exp, fold, force=True, epochs=args.epochs) | |
| with LOG_PATH.open("a", encoding="utf-8") as fh: | |
| fh.write(json.dumps({**rec, "retry": True}) + "\n") | |
| if rec["status"] == "failed": | |
| still_failing.append(f"{exp.name} fold{fold.index}") | |
| if still_failing: | |
| print("PERMANENT FAILURES: " + ", ".join(still_failing), flush=True) | |
| return 1 | |
| print(f"\nAll done in {(time.time() - t_start) / 3600:.2f} h") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |