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