# 逐文件使用说明 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 同目录,找不到才用打包进去的。不用重新打包。