Support dense full-AOI building evaluation
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-27 05:53:14 +02:00
parent f07ff5d52c
commit 8a4a995659
4 changed files with 23 additions and 1 deletions
@@ -31,6 +31,8 @@ def test_training_command_is_cuda_deterministic_and_bound_to_frozen_inputs(tmp_p
assert "deterministic=True" in command
assert "seed=42" in command
assert "epochs=160" in command
assert "max_det=1000" in command
assert "imgsz=640" in command
assert f"data={tmp_path / 'dataset.yaml'}" in command
+1
View File
@@ -35,6 +35,7 @@ data. A new iteration adds independent training AOIs instead.
| Temporal truth | Unknown per-pixel dates remain unknown; acquisition dates may not masquerade as observation dates |
| Threshold selection | Calibration set only; maximise the worst regional F1 before aggregate F1 |
| Test aggregate | F1 at least 0.55 at the frozen footprint/detection match IoU 0.25 contract |
| Dense-tile capacity | Retain up to 1000 detections per tile; the library default of 300 is below observed Belgian urban label density |
| Regional test | Every region: F1 at least 0.45, precision at least 0.50 and recall at least 0.40 |
| Pure background | Zero detections on every pure-empty tile at the selected threshold |
| Production | Exact candidate checksum and fail-closed promotion report required |
@@ -82,6 +82,12 @@ def main() -> int:
parser.add_argument("--split", default="val")
parser.add_argument("--augment", action="store_true", help="Enable deterministic YOLO test-time augmentation.")
parser.add_argument("--imgsz", type=int, default=640)
parser.add_argument(
"--max-det",
type=int,
default=1000,
help="Maximum detections retained per tile; dense Belgian urban tiles exceed YOLO's default 300.",
)
args = parser.parse_args()
from ultralytics import YOLO
@@ -106,6 +112,7 @@ def main() -> int:
device=args.device,
augment=args.augment,
imgsz=args.imgsz,
max_det=args.max_det,
verbose=False,
)
observations: list[dict[str, Any]] = []
@@ -155,6 +162,7 @@ def main() -> int:
"match_iou": args.match_iou,
"test_time_augmentation": args.augment,
"inference_imgsz": args.imgsz,
"max_detections_per_tile": args.max_det,
"tile_count": len(tiles),
"sweeps": sweeps,
}
+12 -1
View File
@@ -40,6 +40,8 @@ def training_command(
seed: int,
batch: int,
workers: int,
max_det: int = 1000,
imgsz: int = 640,
) -> list[str]:
return [
yolo,
@@ -47,13 +49,14 @@ def training_command(
f"model={model}",
f"data={data}",
f"epochs={epochs}",
"imgsz=640",
f"imgsz={imgsz}",
f"batch={batch}",
"device=0",
f"workers={workers}",
"patience=35",
"cache=disk",
"close_mosaic=20",
f"max_det={max_det}",
f"seed={seed}",
"deterministic=True",
f"project={project}",
@@ -88,6 +91,8 @@ def main() -> int:
parser.add_argument("--epochs", type=int, default=160)
parser.add_argument("--batch", type=int, default=2)
parser.add_argument("--workers", type=int, default=4)
parser.add_argument("--max-det", type=int, default=1000)
parser.add_argument("--imgsz", type=int, default=640)
parser.add_argument("--seed", type=int, default=20260731)
parser.add_argument("--yolo", default="yolo")
parser.add_argument("--dry-run", action="store_true")
@@ -133,6 +138,8 @@ def main() -> int:
seed=args.seed + index,
batch=args.batch,
workers=args.workers,
max_det=args.max_det,
imgsz=args.imgsz,
)
if args.dry_run:
print(json.dumps({"training_command": command}, indent=2))
@@ -166,6 +173,10 @@ def main() -> int:
str(report),
"--device",
"cuda:0",
"--max-det",
str(args.max_det),
"--imgsz",
str(args.imgsz),
],
iteration_dir / f"{role}.log",
)