Lien-Feng's picture
Upload 211 files
c3f98a1 verified
Raw
History Blame Contribute Delete
3.99 kB
"""Pre-flight checks: official assets, fold hygiene, evaluator verification.
Run this before anything else. It fails loudly if the official LUNA16 protocol
cannot be reproduced on this machine.
Usage
-----
python scripts/01_prepare.py
"""
from __future__ import annotations
import json
import sys
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 evaluate, splits
from luna_rev.io_luna import all_uids, load_annotations, load_excluded, verify_scans_present
def main() -> int:
ok = True
report: dict = {}
print("=" * 72)
print("1) Official assets")
print("=" * 72)
for label, path in [("annotations.csv", cfg.ANNOTATIONS_CSV),
("annotations_excluded.csv", cfg.ANNOTATIONS_EXCLUDED_CSV),
("seriesuids.csv", cfg.SERIESUIDS_CSV)]:
exists = path.exists()
ok &= exists
print(f" {'OK ' if exists else 'FAIL'} {label:26s} {path}")
ann, excl = load_annotations(), load_excluded()
uids = all_uids()
report["n_scans"] = len(uids)
report["n_nodules"] = len(ann)
report["n_excluded_findings"] = len(excl)
# Evaluation needs only the CSVs, but the pipeline as a whole needs pixels.
verify_scans_present()
print(f" scans={len(uids)} reference nodules={len(ann)} irrelevant findings={len(excl)}")
ok &= (len(uids) == 888 and len(ann) == 1186)
print("\n" + "=" * 72)
print("2) Official subset0-9 folds")
print("=" * 72)
subsets = splits.official_subsets()
print(" subset sizes: " + ", ".join(f"{k}:{len(v)}" for k, v in sorted(subsets.items())))
audit = splits.audit("official")
report["fold_audit"] = audit
ok &= audit["clean"]
print(f" covers all scans : {audit['test_partition_covers_all_scans']}")
print(f" test partitions disjoint: {audit['test_partitions_disjoint']}")
print(f" train/val/test overlaps : "
f"{max(audit['train_test_overlap'].values())}, "
f"{max(audit['val_test_overlap'].values())}, "
f"{max(audit['train_val_overlap'].values())} (all must be 0)")
f0 = splits.official_folds()[0]
print(f" fold0 -> train {len(f0.train)}, val {len(f0.val)}, test {len(f0.test)}")
splits.export(protocol="official")
splits.export(protocol="legacy")
print("\n" + "=" * 72)
print("3) Evaluator verification against the official reference output")
print("=" * 72)
ref = evaluate.validate_against_reference("legacy_abs")
report["evaluator_verification"] = ref
ok &= ref["matches_reference"]
print(f" reproduces CADAnalysis.txt exactly: {ref['matches_reference']}")
for k, v in ref["counters"].items():
if isinstance(v, int):
print(f" {k:28s} {v}")
if ref["mismatches"]:
print(f" MISMATCHES: {ref['mismatches']}")
print("\n" + "=" * 72)
print("4) Experiment matrix")
print("=" * 72)
total_runs = 0
for group in dict.fromkeys(e.group for e in cfg.ALL_EXPERIMENTS):
exps = cfg.experiments_for_group(group)
n = sum(len(cfg.folds_for(e)) for e in exps)
total_runs += n
print(f" {group:14s} {len(exps):2d} configs x folds -> {n:3d} training runs")
report["total_training_runs"] = total_runs
print(f" {'TOTAL':14s} {len(cfg.ALL_EXPERIMENTS):2d} configs -> {total_runs:3d} training runs")
print(f" imgsz={cfg.IMG_SIZE} epochs={cfg.SCHEDULE.epochs} batch={cfg.HW.train_batch} "
f"negatives/scan={cfg.NEG.per_scan}")
out = cfg.RESULTS_DIR / "prepare_report.json"
out.write_text(json.dumps(report, indent=1, default=str), encoding="utf-8")
print(f"\nWrote {out}")
print("\nRESULT:", "PASS" if ok else "FAIL")
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())