# 逐文件使用说明
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 <输出目录>
```
输出:`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
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
```
### `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
python cls_train.py splits/splitA efficientnet_b0.ra_in1k mid bin 60 320 32
```
产出 `best.pt` / `summary.json` / `train.log` 到 `splits//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 同目录,找不到才用打包进去的。不用重新打包。