From 32e56cb2307bfafb78e772d012b28c6655c00a18 Mon Sep 17 00:00:00 2001 From: Shaun Zhou Date: Tue, 15 Sep 2026 20:31:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(boxes):=20generate=5Fboxes.py=20=E7=9A=84?= =?UTF-8?q?=20--target=20=E9=BB=98=E8=AE=A4=E6=94=B9=E4=B8=BA=20label?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 框的类别应该来自人工归类(图片所在目录),分类模型只负责定位。原先默认 predicted, 框的类别取模型 top1 预测,模型分错的图会把错误类别写进标签:boxes_shap 那次预测与 目录一致率 0.9915,约 95 张图的类别因此是错的。 两种取值只在预测与目录不一致的图上有区别,其余图解释的是同一个 logit、框完全相同。 --target predicted 仍保留。 Co-Authored-By: Claude Opus 5 --- README.md | 4 +++- scripts/generate_boxes.py | 8 +++++--- tests/test_generate_boxes.py | 8 ++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4c0bfbb..f362344 100644 --- a/README.md +++ b/README.md @@ -557,7 +557,9 @@ runs// `images/` 与 `labels/` 的对应关系符合本仓库的 YOLO 约定,所以 `FilmDefectDataset` 能直接读——这一步把分类的产出接回了检测训练链路。 -热力图默认针对模型的 **top1 预测类别**;`--target label` 改用图片所在目录的标注类别。 +热力图与框的类别默认取**图片所在目录的人工标注**(`--target label`)。类别的真值是人工归类, +分类模型只用来定位;改用 `--target predicted`(模型 top1 预测)时,模型分错的图框的类别也会跟着错。 +两者只在预测与目录不一致的图上有区别——`boxes_shap` 那次是 0.85%,约 95 张。 `summary.json` 里的 `prediction_matches_folder_label` 记录两者的一致率(实测 0.987)。 ### 两种方法的取舍 diff --git a/scripts/generate_boxes.py b/scripts/generate_boxes.py index 0da49f7..6b4070e 100644 --- a/scripts/generate_boxes.py +++ b/scripts/generate_boxes.py @@ -30,7 +30,9 @@ images/ 与 labels/ 的对应关系符合本仓库的 YOLO 约定(datasets/yol 所以 `FilmDefectDataset` 能直接读。 默认对**全量**数据生成;`--limit` / `--per-class` 只用于快速试参数。 -热力图默认针对模型的 **top1 预测类别**;`--target label` 可改用图片所在目录的标注类别。 +热力图与框的类别默认取**图片所在目录的人工标注类别**(`--target label`):类别的真值是人工归类, +分类模型只负责回答"为什么是这个类",也就是框在哪里。`--target predicted` 改用模型的 top1 预测, +这时模型分错的图,框的类别也会跟着错。 """ from __future__ import annotations @@ -132,8 +134,8 @@ def parse_args() -> argparse.Namespace: parser.add_argument("--set", nargs="*", default=[], metavar="KEY=VALUE") parser.add_argument("--device", default=None) parser.add_argument( - "--target", choices=("predicted", "label"), default="predicted", - help="热力图针对哪个类别:predicted=模型 top1 预测(默认),label=图片所在目录", + "--target", choices=("predicted", "label"), default="label", + help="热力图与框针对哪个类别:label=图片所在目录的人工标注(默认),predicted=模型 top1 预测", ) parser.add_argument("--threshold", type=float, default=0.4, help="热力图阈值(相对最大值)") diff --git a/tests/test_generate_boxes.py b/tests/test_generate_boxes.py index 2e4d057..e5db281 100644 --- a/tests/test_generate_boxes.py +++ b/tests/test_generate_boxes.py @@ -162,6 +162,14 @@ def test_full_dataset_by_default_and_subsampling_is_opt_in(project, tmp_path, mo assert json.loads((per_class / "summary.json").read_text("utf-8"))["num_images"] == 2 +def test_target_defaults_to_folder_label(project, tmp_path, monkeypatch): + """框的类别默认用人工标注,不让分类模型的错误预测混进标签。""" + out = tmp_path / "out_default" + assert run(base_argv(project, out, "gradcam"), monkeypatch) == 0 + summary = json.loads((out / "summary.json").read_text(encoding="utf-8")) + assert summary["target"] == "label" + + def test_target_can_be_prediction_or_folder_label(project, tmp_path, monkeypatch): for target in ("predicted", "label"): out = tmp_path / f"out_{target}"