File size: 14,939 Bytes
0e9808f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# 逐文件使用说明

41 个脚本、11 张结果表、5 份 HTML、51 个训练产物目录。
所有脚本的工作目录都假定是 `/workspace/CV_Task/seg3/newdata`,解释器 `/workspace/venvs/seg/bin/python`。

---

## 目录结构

```
newdata/
├── raw/                     解包后的原始数据(3728 PNG + 1977 JSON,11 个包目录)
├── splits/
│   ├── splitA/              全混合 8:1:1
│   │   ├── meta.csv         3611 行,切分表
│   │   ├── dropped.csv      117 行,剔除清单带原因
│   │   ├── best.pt          分割模型(按 val 选)
│   │   ├── eval_report.json 分割评估
│   │   ├── curve_{val,test}.csv   阈值 → 逐类检出曲线
│   │   ├── geom_{val,test}.csv    逐图几何量测
│   │   ├── viz_{val,test}/  分割可视化 PNG
│   │   ├── aug_{none,weak,mid,strong}/   增广消融
│   │   ├── cls/             第一阶段分类模型(13 个)
│   │   └── cls2/            第二阶段多轴消融(19 个)
│   └── splitB/              跨批次,结构同上
├── release/                 部署包(predict.py + onnx + 文档)
├── docs/                    本文档集
└── *.py *.sh *.csv *.json *.html
```

---

## 脚本:数据准备

### `fetch_and_unpack.sh` (25 行)`样本7_27.zip` 并调 `unpack.py`。一键起步。
```bash
bash fetch_and_unpack.sh
```

### `unpack.py` (87 行)
递归解包嵌套 zip。**两个关键行为**- **GBK 文件名修复**:zip 内中文名以 cp437 存储,按 `cp437 → gbk → gb18030 → big5` 回退解码。不修则目录名全乱码,`pkg` 字段失效。
- **zip-slip 防护**:拒绝解到 `raw/` 之外。
```bash
python unpack.py <zip路径> <输出目录>
```
输出:`raw/` 下 11 个包目录。

### `inventory.py` (175 行)
全量清点,解析批次 / 机器 / 会话 / 采集配置 / 标注精细度。
```bash
python inventory.py            # -> inventory.csv (3728 行, 854 KB)
```
**关键约束**:无批次号或无机器号的包,字段**留空**,不做默认填充。11 个包中 5 个无机器号。

### `metrics_fix.py` (164 行)
逐图几何指标。修了原版 `metrics.py` 的两个 bug:circle 类型 shape 漏计(展开为 24 边形)、
`tightness` 指标无物理意义(换成 `in_body_ratio` + `vert_density`)。
```bash
python metrics_fix.py          # -> metrics_fixed.csv (1948 行, 313 KB)
```

### `build_splits.py` (219 行)
生成两套切分。分组单位 `group = pkg‖sess`,同组不跨 split;剔除 rare_class / zero_shapes / no_json。
```bash
python build_splits.py         # -> splits/split{A,B}/{meta,dropped}.csv
```

### `build_dataset_v2.py` (207 行)
**历史脚本**,一代数据的构建流程。保留用于对照,新流程不用它。

### `make_samples.py` (206 行)
每个包抽 N 张交给 subagent 目检。产出 `agent_tasks.json`(抽样清单)与 `qa_results.json`(核查结论,440 KB)。

---

## 脚本:分割路线

### `train_v3.py` (181 行) ★ 主训练脚本
SMP U-Net + resnet34,三类分割(划痕 / 麻点 / 崩边)。
```bash
python train_v3.py <split目录> <epochs> <size> <batch> <workers>
python train_v3.py splits/splitA 200 384 32 10
```
相对初版的改动:全量 RAM 预载、batch 32 + channels_last + bf16、`gwz` 归一化、固定种子 42、
**test 泄漏断言**。产出 `best.pt` / `last.pt` / `train_summary.json` / `trainlog.csv`。

### `train_v2.py` (158 行)
上一代训练脚本,保留对照。

### `train_aug.py` (188 行)
增广消融专用(`none` / `weak` / `mid` / `strong`)。
```bash
python train_aug.py <split目录> <aug> <epochs> <size> <batch> <workers>
```

### `run_aug_ablation.sh` (35 行)
4 档 × 2 切分 = 8 个作业,每波 4 并发。
```bash
bash run_aug_ablation.sh 200
```

### `eval_v3.py` (206 行) ★ 主评估脚本
val + test 一次性评估,**不做阈值标定**(标定在 `optimize_rules.py`)。
```bash
python eval_v3.py splits/splitA
```
产出 `eval_report.json`(逐类 Dice/IoU + 三种 AUROC)、`eval_{val,test}.csv`(逐图分数)、
`curve_{val,test}.csv`(阈值扫描 → 逐类检出)。

### `eval_v2.py` (151 行) / `eval_aug_models.py` (185 行)
上一代评估 / 增广消融批量评估(产出 `aug_compare.json`)。

### `measure_geom.py` (180 行)
从分割预测图里量 7 个几何量:划痕长度/宽度/面积/条数、麻点颗数/最大颗/面积/直径、崩边面积/弧长/过角。
```bash
python measure_geom.py splits/splitA    # -> geom_{val,test}.csv
```

### `optimize_rules.py` (153 行)
搜几何判据阈值,**同时输出 oracle(test 上调,作弊)与 honest(val 上调)两套**,供对比过拟合幅度。
```bash
python optimize_rules.py    # -> rules_maxfp3.json, rules3_maxfp3.json
```

---

## 脚本:分类路线

### `cls_train.py` (185 行) ★
timm 骨干,二分类或四分类。
```bash
python cls_train.py <split目录> <backbone> <aug> <task> <epochs> <size> <batch>
python cls_train.py splits/splitA efficientnet_b0.ra_in1k mid bin 60 320 32
```
产出 `best.pt` / `summary.json` / `train.log` 到 `splits/<sp>/cls/{task}_{bb}_{aug}_{size}/`。

### `cls_train2.py` (233 行) ★ 多轴版
6 个正交轴,命名带全部超参。
```bash
python cls_train2.py splits/splitA --ep 60 \
  --bb efficientnet_b0.ra_in1k --sz 320 --aug mid --norm gwz --loss ce --task bin
```
| 参数 | 取值 |
|---|---|
| `--sz` | 224 / 256 / 320 / 384 / 448 |
| `--norm` | `gwz`(灰世界+z-score)/ `imnet` / `zscore` |
| `--aug` | `none` `geo` `photo` `weak` `mid` `strong` `mixup` `cutmix` |
| `--loss` | `ce` / `ce_nols`(无标签平滑)/ `focal` |
| `--task` | `bin` / `cls4` |

### `run_cls_sweep.sh` (60 行) / `run_cls2.sh` (30 行) / `run_cls_aug.sh` (17 行)
批量调度。`run_cls_sweep.sh` 分两阶段(`bash run_cls_sweep.sh 1|2`),`run_cls2.sh` 跑 19 个多轴作业。
并发数由 `CONC` 控制(4090 上 2–3 合适)。

### `cls_eval.py` (152 行)
单模型评估,产出 `eval.json`(含混淆矩阵与 `fp_budget` 表)。
> `confusion` 的方向是 **外层 = 预测类,内层 = 真实类**。读反会得到完全不同的数字。

### `cls_eval_all.py` (157 行) ★ 统一评测
把所有分类模型(各用自己的 size/norm)评到 test。
```bash
python cls_eval_all.py                       # 默认 splitA + splitB
python cls_eval_all.py splits/splitA         # 只评一套
```
产出 `cls_test_all.csv`(42 行)。脚本**主动声明选择偏差**:在 test 上横比 N 个模型再挑最好是有偏的,
所以它额外打印「按 val 预先选定」的那个模型——**只有那个数字是无偏的**。

### `cls_tta_ens.py` (92 行)
TTA×8(4 旋转 × 2 翻转)与模型集成。
```bash
python cls_tta_ens.py splits/splitA          # -> tta_ens.json
```

### `cls_collect.py` (34 行)
把散落的 `summary.json` 汇成 `cls_all_results.csv`。

---

## 脚本:诚实性审计 ★★

这四个脚本是本项目最重要的部分。原理见 [`AUDIT.md`](AUDIT.md)。

### `cls_probe.py` (188 行)
三合一审计:
1. **批次指纹**——用模型特征预测「这张图来自哪个包」,准确率越高说明可作弊空间越大
2. **未见类「碎」硬正样本**——这一类整类未参与训练,检出率 `sui` 应接近 1
3. **eta²**——包身份能解释多少良品分数方差,越低越好
```bash
python cls_probe.py            # -> cls_probe_all.csv
```

### `cls_frozen_probe.py` (80 行) ★ 决定性审计
冻结骨干特征,对比两种探针:
- **有标签线性探针**(用了良/不良标签)
- **只见良品的 kNN**(完全没用标签)

**如果存在可解锁的捷径,有标签的必然显著胜出。实测 kNN 0.9634 > 探针 0.9461——它没有。**
```bash
python cls_frozen_probe.py splits/splitA resnet34 320
# -> frozen_probe_resnet34_320.json
```

### `cls_hardprobe.py` (59 行)
硬正样本专项检验。

---

## 脚本:报告与指标

### `precision_table.py` (118 行)
产线不良率 × 不良桶良品率上限 → 可达性表。
```bash
python precision_table.py      # -> precision_table_{A,B}.csv (各 36 行)
```

### `gen_p10_report.py` (287 行)
产线不良率固定 10% 的专项报告。**阈值在 val 上定、test 只评一次**,
产出 `p10_honest.json` + HTML。

> 这个脚本存在的原因:我此前有两次**在 test 上选阈值**(precision 表、以及早期只用 train/val 的切分),
> 得到 A 套不良桶良品率 0% 的假象。诚实口径重算是 **17.89%**。这个脚本就是修正版。

### `gen_precision_report.py` (182 行) / `gen_report.py` (613 行) / `gen_showcase.py` (323 行) / `gen_nonzero_goods.py` (178 行)
HTML 报告生成器,见下方 HTML 清单。

### `make_granularity_figs.py` (220 行) / `make_risk1_figs.py` (185 行)
标注精细度对比图、风险 1(良品漏标)实例图。

### `bench_*.py` (39/170/49/124 行)
CPU 核数 / 内存 / 吞吐基准,产出 `cpu_bench.json` `cpu_cores.json` `throughput.json`。

### `export_onnx.py` (46 行)
导出 ONNX。**`dynamo=False` 不能删**——新导出器会把权重拆成 `.onnx.data`,破坏单文件分发。

---

## 结果表字段说明

### `cls_test_all.csv`(42 行,主榜单)

| 列 | 含义 |
|---|---|
| `split` | `A` / `B` |
| `task` | `bin` / `cls4` |
| `backbone` `size` `aug` `norm` `loss` `param` | 超参与参数量(M) |
| `val_auroc` `test_auroc` | AUROC |
| `test_se` | AUROC 的 Hanley–McNeil 标准误,**约 0.005,是噪声地板** |
| `rec@K` | 在 val 上取阈值使 val 误报 ≤K,搬到 test 后的**检出率** |
| `fp@K` | 该阈值下 **test 上实际发生的误报数**(不受 K 约束,可能超) |
| `imp_test` | test 口径不良桶良品率 = FP/(TP+FP) |
| `imp_line10` | 换算到**产线不良率 10%** 的不良桶良品率 |
| `sui` | 未见类「碎」的检出率(诚实性指标,越高越好) |
| `dir` | 模型目录 |

### `cls_probe_all.csv`(6 行,审计)

| 列 | 含义 |
|---|---|
| `tau` | 阈值 |
| `test_rec` `test_fp` | test 检出 / 误报 |
| `sui` | 未见类「碎」检出率 |
| `dirty` | 良品中疑似脏标的检出率 |
| `nolabel` | 无标注缺陷图的检出率 |
| `eta2` | **包身份解释良品分数方差的比例,越低越好**(A 套 0.05–0.06,B 套 0.00) |
| `good_med` `def_med` | 良品 / 缺陷分数中位数 |

### `precision_table_{A,B}.csv`(各 36 行)
列名已是中文:`产线不良率` `不良桶中良品率上限` `可达` `tau` `检出率` `漏检率` `误报率`
`实际不良桶良品率` `放行桶良品纯度` `放行桶混入不良` `每万片漏检` `每万片冤杀`
`误报率CP95上界` `最坏不良桶良品率` `最坏放行桶纯度` `TP` `FP` `split`

> `误报率CP95上界` 是 Clopper–Pearson 精确上界。**185 张良品下,即使 0 误报,
> 95% 置信上界也有 1.61%**——这是样本量的硬限制,不是模型的问题。

### `curve_{val,test}.csv`(9 行)
`tau` `良品误报率` `误报数` `总检出率` `检出数` `划痕` `麻点` `崩边`。
分割的阈值扫描曲线,**麻点那一列是本项目最重要的一张表**(见 [`INDEX.md`](INDEX.md))。

### 其他

| 文件 | 内容 |
|---|---|
| `inventory.csv` | 3728 行全量清点 |
| `metrics_fixed.csv` | 1948 行几何指标 |
| `qa_results.json` | subagent 目检结论(440 KB) |
| `agent_tasks.json` | 抽样清单 |
| `rules_maxfp3.json` | 单阈值 + 7 维几何,oracle vs honest |
| `rules3_maxfp3.json` | 3 自由度几何,按误报预算 0–5 逐档 |
| `p10_honest.json` | 产线 10% 的诚实口径结果 |
| `cls_all_results.csv` | 25 行,训练期汇总(含 `best_ep` `tr_loss` `va_loss`) |
| `cpu_bench.json` `cpu_cores.json` `throughput.json` | 性能基准 |

---

## HTML 报告(5 份)

| 文件 | 大小 | 内容 |
|---|---|---|
| `样本7_27_数据核查报告.html` | 2.1 MB | **数据核查主报告**。11 个包逐包核查:批次/机器识别、标注精细度分档、缺陷类型分布、抽样目检结论、6 类数据质量风险 |
| `分割效果展示.html` | 6.4 MB | 每种缺陷类型 / 良品 / 误判良品各 10 张,A、B 两套都有,原图 + 预测掩码叠加 |
| `被模型报警的良品.html` | 2.6 MB | 185 张 test 良品里被打非零分的 16 张。**169 张分数恰好为 0** |
| `不良率10%_判废纯度报告.html` | 0.7 MB | 产线不良率 10% 下的判废纯度,诚实口径 |
| `判废纯度对照表.html` | — | 不良率 × 纯度上限的可达性矩阵 |

---

## 训练产物目录(51 个)

每个目录里:`best.pt`(按 val 选)、`summary.json`(超参 + val 指标)、`train.log`、
部分有 `eval.json`(含混淆矩阵)。

| 位置 | 数量 | 内容 |
|---|---|---|
| `splits/splitA/cls/` | 13 | 第一阶段:bin × {convnext_nano, efficientnet_b0, mobilenetv3, resnet34, vit_small} + cls4 × {mobilenetv3, resnet34} |
| `splits/splitA/cls2/` | 19 | 第二阶段多轴消融 |
| `splits/splitB/cls/` | 9 | B 套对照 |
| `splits/split{A,B}/aug_*/` | 8 | 分割增广消融 |
| `splits/split{A,B}/best.pt` | 2 | 分割主模型 |

命名规则:
- `cls/` → `{task}_{backbone}_{aug}_{size}`
- `cls2/` → `{task}_{backbone}_{aug}_{norm}_{loss}_{size}`

---

## `release/` 部署包

| 文件 | 说明 |
|---|---|
| `predict.py` | 单文件推理,只依赖 onnxruntime + numpy + pillow(+ openpyxl 可选) |
| `filter_binary.onnx` (16.8 MB) | 判废模型,mobilenetv3_large_100 |
| `filter_binary.json` | 元信息 + **阈值标定表** |
| `filter_4class.onnx` (85.1 MB) | 类型模型,resnet34 |
| `filter_4class.json` | 元信息 |
| `requirements.txt` | 4 个 pip 包 |
| `README.md` | 技术说明 |
| `操作指南.md` | 傻瓜版:下载 + 一条命令 |
| `数据集下载指南.md` | 图片仓库下载方式 |
| `sample_images/` | 22 张样例(来自锁箱 test) |

### `predict.py` 用法

```bash
python predict.py -i 图片.png                              # 单张
python predict.py -i 图片目录/ -o 结果.xlsx                 # 批量(递归)
python predict.py -i 图片目录/ -o 结果.xlsx --with-type     # 附缺陷类型
python predict.py -i 图片目录/ -o 结果.csv                  # 不装 openpyxl
python predict.py -i 图片目录/ -o r.xlsx --threshold 0.787  # 调松紧
```

**预处理必须与训练逐字节一致**:等比缩放 → 居中补零 → 逐图灰世界白平衡 → 逐图 z-score。
改动其中任何一步,标定好的阈值全部失效。

**换自己重训的模型**:把新的 `filter_binary.onnx` + `.json` 放在可执行文件**旁边**即可,
`find_model()` 优先找 exe 同目录,找不到才用打包进去的。不用重新打包。