feat: 用 Grad-CAM 从分类模型生成弱监督框标签

分类模型只学过"是什么缺陷",Grad-CAM 把分类分数对卷积特征的梯度当作通道重要性,
加权求和得到空间热力图,阈值化 + 连通域外接矩形即得到框标签,输出 YOLO 格式,
可直接接入既有的检测训练链路。

- engine/gradcam.py:整批一次 backward 的 Grad-CAM(各样本只回传自己那一类的分数)、
  numpy 实现的 4-邻接连通域标记(不为这一处引入 scipy/opencv)、CAM 转归一化框
- scripts/gradcam_boxes.py:遍历数据集生成标签,并输出「原图 | 热力图 | 叠加+框」
  三联图供人工核对;框的类别取自图片所在目录而非模型预测

两处按实图定下的默认值:
- --stage 默认 6(layer3, 8×8)。layer4 只有 4×4 太粗,layer2 响应太散导致 40% 出不了框
- --peak-ratio 0.75:次要连通域峰值需达全局最大值的 75%,否则会在无缺陷的角落刷假框;
  实测把两框比例从 53% 降到 20%

全量 11,212 张已生成:61.8% 出 1 框、35.2% 出 2 框、3.0% 无框,每类留 25 张可视化。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Shaun Zhou
2026-09-10 15:46:30 +08:00
co-authored by Claude Opus 5
parent 33fdf396ee
commit fc7136fddf
4 changed files with 631 additions and 0 deletions
+45
View File
@@ -632,6 +632,51 @@ model:
只加载 backbone,分类头仍随机初始化。`frozen_backbone: true` 可以单独衡量
自监督特征本身有多好。
## 从分类到定位:Grad-CAM 生成框标签
分类模型只学过"这是什么缺陷",没学过"缺陷在哪"。Grad-CAM 把分类分数对卷积特征的梯度
当作通道重要性,加权求和得到空间热力图——这是分类模型隐含的定位线索。阈值化 +
取连通域外接矩形,就得到弱监督的框标签:
```bash
uv run python scripts/gradcam_boxes.py --checkpoint runs/classify/best.pth
```
```
runs/gradcam_boxes/
├── labels/<类别>/<图名>.txt # YOLO 格式 class_id cx cy w h,可直接喂给检测训练
├── viz/<类别>/<图名>.png # 原图 | 热力图 | 叠加+框,供人工核对
├── data.yaml
└── summary.json
```
**框的类别取自图片所在目录**(人工归类结果),不是模型预测——即使模型分错,
要的也是该图真实类别的热力区域。
### 分辨率是主要限制
resnet18 在 128×128 输入下各层的 CAM 尺寸:layer4 只有 4×4、layer3 是 8×8、layer2 是 16×16。
层越浅越精细,但类别判别性越弱。`--stage`(4~7 对应 layer1~4)可选,默认 6 即 **layer3**:
| stage | CAM | 实测(每类抽 3 张,共 45 张) |
| --- | --- | --- |
| 7 (layer4) | 4×4 | 100% 出 1 个框,但只有 16 个格子,框很粗 |
| **6 (layer3)** | **8×8** | **76% 出 1 框、20% 出 2 框,热力区域与缺陷贴合** |
| 5 (layer2) | 16×16 | 40% 出不了框,响应太散 |
### 几个调参点
- `--threshold`(默认 0.4):热力图相对最大值的阈值
- `--peak-ratio`(默认 0.75):次要连通域的峰值要达到全局最大值的这个比例才保留。
不加这条会在没有缺陷的角落刷出假框——实测把两框比例从 53% 降到 20%
- `--padding`(默认 0.1):框四周外扩。CAM 倾向只覆盖最具判别性的部位(缺陷中心),
往往比真实缺陷范围小
- `--viz-per-class`(默认 20):每类存多少张可视化;`--viz-all` 全存(很慢很占盘)
> **不是所有类别都可定位。** `normal`/`black`/`edge_clean` 这类描述的是整图属性而非
> 局部缺陷,它们的框没有物理意义。真正有用的是 `blackdot`/`whitedot`/`mosquito`/
> `grease`/`zigzag` 这些有局部目标的类别。
## 方法
- **骨架**:`tiny_conv`(自研深度可分离卷积,默认)或 `torchvision`(resnet / mobilenet / shufflenet)。