fix(detect): 推理尺寸与训练尺寸不一致时报警

用 640 整图训练的权重跑 160 切片推理,结果看起来完全正常——照常出框、照常有置信度,
但类别系统性地错:实测 mosquito 的 TP 归零,760 张里 599 张被判成 blackdot,
而同一权重整图推理时 mosquito 有 92% 判对。这种错误不会自己暴露。

从 checkpoint 里读训练时的 imgsz,与当前切片尺寸不符就警告。三个调用点都接上:
可视化、批量推送预测、ML backend 加载权重时。visualize 的 summary.json 也补上
tile_size / tile_overlap,免得事后分不清某次产出是怎么跑的。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Shaun Zhou
2026-09-24 15:33:41 +08:00
co-authored by Claude Opus 5
parent 6e746b7351
commit ddd77653b5
5 changed files with 106 additions and 3 deletions
+2 -1
View File
@@ -40,7 +40,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "ml_backend"))
from convert import to_prediction # noqa: E402
from engine.tiled_inference import load_image, predict_image # noqa: E402
from engine.tiled_inference import check_size_matches, load_image, predict_image # noqa: E402
from utils import setup_logging # noqa: E402
from utils.device import DeviceNotAvailableError, describe_device, resolve_device # noqa: E402
@@ -267,6 +267,7 @@ def main() -> int:
model = YOLO(str(weights))
classes = model.names
device_arg = device.index or 0 if device.type == "cuda" else device.type
check_size_matches(model, args.tile_size, logger)
if args.tile_size > 0:
logger.info("切片推理 %dx%d,重叠 %d(不缩放)", args.tile_size, args.tile_size, args.tile_overlap)
else:
+4 -1
View File
@@ -46,7 +46,7 @@ import yaml # noqa: E402
from datasets.yolo import IMAGE_SUFFIXES # noqa: E402
from engine.detection import Box, draw_boxes, match_boxes, parse_label # noqa: E402
from engine.tiled_inference import load_image, predict_image # noqa: E402
from engine.tiled_inference import check_size_matches, load_image, predict_image # noqa: E402
from utils import setup_logging # noqa: E402
from utils.device import DeviceNotAvailableError, describe_device, resolve_device # noqa: E402
@@ -180,6 +180,7 @@ def main() -> int:
from ultralytics import YOLO
model = YOLO(args.weights)
check_size_matches(model, args.tile_size, logger)
device_arg = device.index or 0 if device.type == "cuda" else device.type
outcomes: dict[str, Counter[str]] = {name: Counter() for name in splits}
@@ -246,6 +247,8 @@ def main() -> int:
"conf": args.conf,
"iou_threshold": args.iou_threshold,
"imgsz": args.imgsz,
"tile_size": args.tile_size,
"tile_overlap": args.tile_overlap,
"outcomes": {split: dict(counter) for split, counter in outcomes.items()},
"per_class": {
split: {