学习初稿 · 第 14 课。 本篇已开放网页阅读,用于先建立完整认识。文中手算值和示意记录不代表真实训练成绩;真实数据训练、预训练权重和板端测试以本篇具体说明为准,本篇已附代码练习,实际验证范围见文末与包内记录。
一个准确率数字隐藏了什么
假设某数据中 95% 都是汽车,程序永远回答汽车就能达到 95% 准确率,却完全不会识别飞机。指标必须结合类别分布与任务代价解释。
本课把汽车视为正类、飞机视为负类。正负只是一种统计约定,不表示好坏。换了正类,精确率和召回率的含义也跟着变,因此报告时必须写清楚。
从四种结果建立混淆矩阵
| 真实与预测 | 预测汽车 | 预测飞机 |
|---|---|---|
| 真实汽车 | TP:正确找出汽车 | FN:漏掉汽车 |
| 真实飞机 | FP:误报汽车 | TN:正确排除汽车 |
准确率为 (TP+TN)/总数,精确率为 TP/(TP+FP),召回率为 TP/(TP+FN)。精确率问“报出来的有多少是真的”,召回率问“真实存在的找出了多少”。
下面是一组固定标签的独立手算检查:
|
本例精确率为 2/3、召回率为 1/2、准确率为 1/2。如果分母为零,指标并没有天然合理的数值,应注明采用未定义、零值或工具规定的处理方式。
阈值会改变错误的分布
二分类按汽车概率超过阈值报警时,降低阈值通常会接收更多样本,可能减少漏报、增加误报。这个方向不是对任意一小批数据都保证严格变化,但能帮助理解取舍。
阈值应使用验证集和业务代价确定,随后冻结,再在测试集上测试。不能先在测试集尝试几十个阈值,再把最好的结果当作独立验证。
多类别也要看每类指标。宏平均给各类别同等权重,按样本量加权的平均则更受大类影响。比较两个报告前先核对类别集合、平均方式和样本来源。
先看错误图,再改模型
为错误样本保存文件 ID、真实标签、预测标签、分数和来源组;按小目标、遮挡、逆光、背景相似、标签错误等条件归类。几张高分错误图往往能暴露 RGB 顺序、裁剪和标签映射问题。
如果错误主要发生在某个拍摄来源,先检查分布变化;如果模型总是混淆两类的细节,检查分辨率和标注定义;如果所有类别突然反转,先检查类别表。直接换更深网络可能把错误隐藏得更难定位。
错误分析需要避免反复利用测试集做开发。开发阶段用训练与验证错误建立假设,最终测试用于检验已冻结方案;若已经依据测试错误做了修改,需要新的独立评估来源或明确承认测试被用于开发。
测试报告应能回答的四个问题
用了哪些样本,采用哪份模型和预处理,按什么决策规则计算,结果在哪些条件下失效。还应记录样本数,避免把 10 张测试图上的 100% 与几千张独立图片的成绩相提并论。
相关样本数量很多也不等于独立证据很多。一段视频的 10000 个相邻帧不能简单当作 10000 次独立试验;泛化范围取决于来源覆盖和相关性。
本草稿的固定数组可以验证指标计算,但没有真实模型测试成绩。发布前应导出验证错误清单,在配置冻结后执行独立测试,并保存可追溯报告。
练习
- TP=8、FP=2、FN=4,精确率与召回率分别是多少?
- 只对模型最有把握的一半样本计算准确率,报告还缺什么?
- 为什么“测试集不反向传播”仍不保证测试独立?
参考答案
精确率 0.8,召回率 8/12。还缺接收率、拒绝样本分布及全量表现,否则只说明筛选后子集。若用测试成绩选择超参数、轮数或阈值,测试已经参与开发决策,即使未计算梯度也会造成选择偏差。
下一课把选好的模型保存下来,并验证重新加载后是否产生同样输出。
综合实践
想把这些知识接成一套可运行的代码,可以进入实现 MLP:从损失计算、训练到 ONNX 推理。实践包含自生成数据、训练与验证、模型保存、独立推理、ONNX 导出和数值对照;遇到问题可按实践中的索引返回对应理论章节。
配套代码练习
实验:混淆矩阵与阈值比较
查看完整代码:main.py
"""混淆矩阵与阈值比较。运行方式与练习见 README.md。"""
labels = [0,0,0,1,1,1]
scores = [0.1,0.4,0.8,0.3,0.7,0.9] # 第 1 类分数,构造样本
for threshold in [0.5,0.75]:
matrix = [[0,0],[0,0]]
for truth,score in zip(labels,scores): matrix[truth][int(score>=threshold)] += 1
tn,fp = matrix[0]; fn,tp = matrix[1]
precision = tp/(tp+fp) if tp+fp else None
recall = tp/(tp+fn) if tp+fn else None
print('threshold:',threshold,'matrix:',matrix,'precision:',precision,'recall:',recall)
assert sum(map(sum,matrix)) == len(labels)
if threshold == 0.5: assert matrix == [[2,1],[1,2]]
在项目根目录执行;下载包解压后,在解压目录执行 python3 main.py。
cd 'content/posts/AI-深度学习/从零搭建视觉模型/14-验证测试与错误分析/code' && python3 main.py
本篇已附独立实验包,在代码卡片中查看源码、下载或复制运行命令。解压后执行 python3 main.py;依赖安装见包内 README.md。
观察目标: 0.5 时精确率与召回率都为 2/3;0.75 时分别为 1/2 和 1/3。
动手修改: 加入 100 个容易识别的负样本,比较准确率和正类召回率;说明为什么只看准确率会误导。
所有输入均在本篇代码中构造;运行范围和验证记录见包内 verification.md。
