1. 这个实验到底在折腾什么
先说结论:这个项目的核心想法特别朴素——让模型自己去找数据里的错。不是拿模型去跑分刷榜,而是反过来,把模型当成一个"质检员",用它来判断训练数据里哪些标签可能标错了、哪些样本可能是脏数据。
我做这个实验的起因很实际。手上有一个图像分类的数据集,大概两万张图,十几个类别,标注是外包团队做的。训练完模型之后发现验证集准确率卡在某个值上不去,混淆矩阵里总有几个类别互相"串门"。一开始以为是模型容量不够,换了 backbone、调了学习率、加了数据增强,折腾一圈发现提升有限。后来把训练集里预测置信度极高但和标签不一致的样本捞出来一看,好家伙,相当一部分是标注错了——猫标成狗、同一张图重复标注成两个类、还有几张图压根就是损坏的。
这件事让我意识到一个被很多人忽略的点:数据标注的质量问题,往往比模型结构问题更致命。你花大力气调模型,可能还不如花半天时间把标注错误清理一遍。于是就有了这个实验:用模型给数据挑错,从图像分类一路做到目标检测,看看这套思路在不同任务上到底好不好使。
这篇文章适合谁看?如果你正在自己标注数据集、或者外包标注后不放心质量、又或者训练指标怎么都上不去怀疑数据有问题,那这套方法你可以直接抄。我会把分类和目标检测两条线的具体做法、参数选择、踩过的坑都写清楚,代码级别的细节也会给到。
核心逻辑其实就一句话:模型在正确标注的样本上会表现出"自信且正确",在错误标注的样本上往往表现出"自信但错误"或者"极度不自信"。我们要做的就是设计一套筛选规则,把这两类异常样本捞出来。
2. 整体思路与方案选型
2.1 为什么选"模型挑错"而不是纯人工复查
两万张图纯人工复查,一个人一天看两千张,也要十天,而且看久了会疲劳,漏检率直线上升。用模型先筛一遍,把可疑样本从两万张压缩到几百张,人工只需要复核这几百张,效率提升几十倍。
这里有个关键认知:模型挑错不是要替代人工,而是要缩小人工的复查范围。它允许有误报(把对的标成可疑),但不能有太多漏报(把错的放过)。所以筛选阈值要偏"宽松"一点,宁可多捞一些可疑样本出来。
2.2 分类任务和目标检测任务的差异
这两个任务的挑错逻辑不完全一样,我列个表对比一下:
| 维度 | 图像分类 | 目标检测 |
|---|---|---|
| 标注内容 | 整图一个类别 | 多个框+类别 |
| 错误类型 | 类别错、重复、脏图 | 框位置偏、类别错、漏标、多标 |
| 模型输出 | 类别概率 | 框坐标+类别+置信度 |
| 挑错信号 | 预测与标签不一致 | IoU异常、置信度异常、类别混淆 |
| 复核成本 | 低(看一眼就知道) | 高(要判断框准不准) |
分类任务的挑错相对简单,因为一张图就一个标签,模型预测和标签一对比就有信号。目标检测复杂得多,因为一张图可能有十几个框,每个框都可能有问题,而且"框位置偏多少算错"本身就没有绝对标准。
2.3 整体流程设计
不管哪个任务,整体流程都是这四步:
- 训练一个基线模型:不需要调到最优,能收敛、指标合理就行。这个模型的作用是提供"预测信号",不是最终交付物。
- 对训练集做全量推理:注意是对训练集本身推理,不是验证集。这一步会得到每个样本的预测结果。
- 设计筛选规则捞可疑样本:根据预测和标注的差异,用阈值把可疑样本筛出来。
- 人工复核+修正:对筛出来的样本逐个确认,改标签或删除。
这里有个容易踩的坑:基线模型不能训得太差,也不能训得太好。训得太差,预测全是噪声,筛出来的可疑样本没有参考价值;训得太好(比如过拟合了),模型会把错误标签也"记住",反而不容易发现错误。我的经验是训练到验证集准确率稳定、但还没开始过拟合的那个 checkpoint 最合适。
3. 图像分类的挑错实操
3.1 基线模型训练的关键参数
我用的是 ResNet50 预训练模型做迁移学习,这是分类任务里最稳妥的选择。为什么不用更新的 ViT?因为 ViT 在小数据集上对数据量和增强策略更敏感,而我们的目的只是拿一个"够用"的模型来挑错,ResNet50 的稳定性和速度更合适。
关键训练配置:
# 核心训练参数 batch_size = 64 epochs = 30 lr = 1e-3 # 主干用预训练权重,学习率可以稍大 weight_decay = 1e-4 optimizer = "AdamW" scheduler = "CosineAnnealingLR" label_smoothing = 0.1 # 注意这个参数这里重点说label_smoothing。很多人训练分类模型不加这个,但在挑错场景下它很关键。加了标签平滑之后,模型对"错误标签"的拟合会弱一些,预测分布更平滑,反而更容易暴露标注错误。我实测下来,label_smoothing=0.1 时,错误样本的预测置信度分布和正确样本区分度更好。
训练过程中要记录每个 epoch 的验证集准确率,选验证准确率最高那个 checkpoint,不要选最后一个。因为最后一个可能已经过拟合了。
3.2 全量推理与信号提取
训练完之后,对训练集本身做一次推理,拿到每张图的预测类别和置信度。核心代码逻辑:
model.eval() all_probs = [] with torch.no_grad(): for imgs, labels in train_loader: imgs = imgs.cuda() logits = model(imgs) probs = torch.softmax(logits, dim=1) all_probs.append(probs.cpu()) all_probs = torch.cat(all_probs, dim=0) # [N, num_classes] pred_labels = all_probs.argmax(dim=1) pred_conf = all_probs.max(dim=1).values拿到这些之后,我们就有三个关键信号:
- 预测类别 pred_labels
- 预测置信度 pred_conf
- 真实标签 true_labels
3.3 三类可疑样本的筛选规则
基于这三个信号,我设计了三条筛选规则,分别对应不同类型的标注问题:
规则一:高置信度但预测错误
mask_confident_wrong = (pred_labels != true_labels) & (pred_conf > 0.9)这类样本模型非常自信地认为是另一个类别,大概率是标签标错了。这是最值得优先复核的一批。
规则二:低置信度样本
mask_low_conf = pred_conf < 0.5模型对这张图"拿不准",可能是图片本身模糊、有歧义,也可能是标注有问题。这类样本数量通常比较多,需要结合其他信号进一步过滤。
规则三:标签分布异常
统计每个类别的样本数,如果某个类别样本数远少于其他类别,或者某张图在多个类别上概率接近(比如 top2 概率差小于 0.1),都值得关注。
top2_probs = all_probs.topk(2, dim=1).values mask_ambiguous = (top2_probs[:, 0] - top2_probs[:, 1]) < 0.13.4 阈值怎么定:一个实操中的计算方法
阈值不是拍脑袋定的。我的做法是:先人工抽查一小批样本,统计错误率,再反推阈值。
具体操作:从"高置信度但预测错误"的样本里随机抽 100 张,人工看一遍,统计其中真正标错的比例。如果这个比例超过 70%,说明这条规则很准,可以放心用;如果只有 30%,说明模型本身有问题,或者阈值需要调整。
我实测下来,pred_conf > 0.9这个阈值下,真正标错的比例大概在 65%~80% 之间,取决于数据集质量。如果数据集本身标注质量就差,这个比例会更高。
注意:不要一次性把所有可疑样本都丢给人工复核。先按"可疑程度"排序,从最可疑的开始复核,复核到一定数量后如果发现错误率下降明显,就可以停了。因为后面的样本错误率低,复核性价比不高。
3.5 一个真实的复核案例
我筛出来的第一批可疑样本里,有一张图标签是"哈士奇",模型预测"阿拉斯加"置信度 0.97。人工一看,确实是一只阿拉斯加,标注员搞混了这两个品种。还有几张图是同一个物体从不同角度拍的,被标成了不同类别,这种属于标注规范不统一的问题。
最离谱的是有几张图,标签是某个类别,但打开一看是纯黑图或者损坏的图片。这种脏数据如果不清理,训练时就是纯噪声。
4. 目标检测的挑错实操
4.1 目标检测挑错的难点在哪
分类任务一张图一个标签,对错一目了然。目标检测一张图可能有几十个框,每个框都要判断:位置准不准、类别对不对、有没有漏标、有没有多标。这就复杂多了。
我用的是 YOLOv8 做基线,因为它的推理速度快、输出格式清晰,适合做全量推理。数据集是一个自建的交通场景数据集,大概五千张图,包含车辆、行人、交通标志等类别。
4.2 从模型输出里能提取哪些挑错信号
YOLOv8 推理后会输出每个检测框的坐标、类别、置信度。把这些和标注框对比,可以提取出几类信号:
信号一:IoU 异常
对每个标注框,找模型预测框里和它 IoU 最大的那个,看 IoU 值。如果 IoU 很低(比如小于 0.3),说明模型认为这里没有物体,或者物体位置和标注差很远。
# 伪代码:计算每个标注框和预测框的最大IoU for gt_box in gt_boxes: max_iou = 0 for pred_box in pred_boxes: iou = compute_iou(gt_box, pred_box) max_iou = max(max_iou, iou) if max_iou < 0.3: suspicious.append(gt_box)信号二:类别不一致
如果标注框和预测框 IoU 很高(比如大于 0.7),但类别不一样,那大概率是类别标错了。
信号三:置信度异常
模型对某个区域的预测置信度很低,但那里有标注框,说明模型"看不清"或者标注有问题。
信号四:漏标检测
模型预测出了高置信度的框,但标注里没有对应的框,可能是漏标了。
for pred_box in pred_boxes: if pred_box.conf > 0.7: max_iou = max(compute_iou(pred_box, gt) for gt in gt_boxes) if max_iou < 0.3: suspicious_missing.append(pred_box)4.3 目标检测挑错的参数选择
目标检测的阈值比分类更难定,因为涉及 IoU 和置信度两个维度。我整理了一个参数表:
| 参数 | 取值 | 说明 |
|---|---|---|
| IoU 低阈值 | 0.3 | 低于此值认为位置严重不符 |
| IoU 高阈值 | 0.7 | 高于此值认为位置基本吻合 |
| 置信度阈值 | 0.7 | 高于此值认为模型很确定 |
| 低置信度阈值 | 0.3 | 低于此值认为模型不确定 |
筛选逻辑组合:
- 位置可疑:IoU < 0.3 且标注框存在
- 类别可疑:IoU > 0.7 但类别不一致
- 漏标可疑:预测置信度 > 0.7 但无对应标注
- 多标可疑:标注框存在但模型置信度 < 0.3
4.4 目标检测复核的特殊注意事项
目标检测的复核比分类累得多,因为要判断框的位置。我总结了几个提高效率的技巧:
第一,优先复核"类别可疑"的样本。因为位置偏一点可能还能接受,但类别错了是硬伤,对模型训练影响最大。
第二,漏标检测要结合场景判断。有些小目标模型确实检测不到,这不一定是漏标,可能是模型能力问题。所以漏标可疑样本要人工确认"这里到底有没有物体"。
第三,批量查看同一类别的可疑样本。比如把所有"车辆类别可疑"的样本放一起看,容易发现标注规范不统一的问题。
实操心得:目标检测复核时,我习惯把原图和标注框、预测框叠加显示,用不同颜色区分。标注框用绿色,预测框用红色,一眼就能看出哪里对不上。这个可视化脚本值得花时间写,能省下大量复核时间。
4.5 一个目标检测的典型错误案例
我遇到过一个典型问题:数据集里有一批图,标注员把"卡车"和"公交车"标混了。因为有些公交车从侧面看和卡车很像。模型预测这些框的类别是"公交车"置信度 0.85,但标注是"卡车"。人工复核后确认是标注错误。
还有一类是"框太大"的问题。标注员把整个车身包括阴影都框进去了,而模型只框了车身主体。这种 IoU 可能在 0.5 左右,不算特别低,但确实标注不规范。这类问题要不要改,取决于你的标注规范怎么定的。
5. 常见问题与排查技巧
5.1 模型挑错本身靠谱吗
这是最常被问的问题。答案是:模型挑错不能保证 100% 准确,但能大幅缩小复核范围。它的价值在于把"大海捞针"变成"小范围排查"。
我做过一个统计:在一个标注质量中等的分类数据集上,模型筛出的可疑样本里,真正有问题的比例大概在 60%~75%。也就是说,你复核 100 张可疑样本,能发现 60~75 个真实错误。这个效率比随机抽查高太多了(随机抽查的错误率可能只有 5%)。
5.2 常见问题速查表
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| 筛出的可疑样本太多 | 阈值太宽松 | 提高置信度阈值,或增加筛选条件 |
| 筛出的可疑样本太少 | 阈值太严格 | 降低阈值,或增加筛选规则 |
| 模型把对的也标成可疑 | 模型本身不够好 | 重新训练基线模型,或换 backbone |
| 错误样本没被筛出来 | 模型过拟合了 | 用更早的 checkpoint,或加正则化 |
| 复核效率低 | 没有可视化工具 | 写一个叠加显示的脚本 |
| 改完标签后指标没提升 | 改错了或改的不够 | 重新训练验证,确认修改正确 |
5.3 几个容易踩的坑
坑一:用验证集挑错。挑错必须用训练集,因为你要找的是训练数据里的错误。用验证集挑错没有意义,验证集本来就是用来评估的。
坑二:基线模型训太久。训太久模型会把错误标签也拟合进去,反而不容易发现错误。我一般训到验证准确率连续 3 个 epoch 不提升就停。
坑三:一次性改太多标签。改完一批标签后要重新训练验证,确认指标有提升再继续改下一批。如果一次性改太多,出了问题都不知道是哪批改错了。
坑四:忽略"模型和标注都可能有错"。有时候模型预测错了,标注是对的。复核时要保持中立,不要预设"模型一定对"。
5.4 提升挑错效率的几个技巧
第一个技巧是分层抽样复核。把可疑样本按置信度排序,先复核置信度最高的那批,因为那批错误率最高。
第二个技巧是记录复核结果反哺模型。把复核确认的错误样本单独存起来,下次训练时可以用这些样本做难例挖掘。
第三个技巧是定期做全量挑错。数据标注不是一次性的,随着数据增加,新标注的数据也可能有错。建议每积累一批新数据就做一次挑错。
6. 这套方法还能怎么扩展
分类和目标检测做完了,我又把这套思路用到了其他任务上,发现逻辑是通用的。
语义分割:可以用模型预测的分割 mask 和标注 mask 计算 IoU,IoU 低的区域就是可疑区域。不过分割的复核成本更高,因为要逐像素判断。
关键点检测:可以计算预测关键点和标注关键点的距离,距离异常大的就是可疑样本。这个在人体姿态估计里特别有用。
多模态任务:如果是图文配对的数据,可以用模型判断图文是否匹配,不匹配的就是可疑样本。这个思路在训练多模态模型时很有价值。
还有一个进阶玩法:用多个模型交叉验证。训练两三个不同结构的模型,如果它们对同一个样本的预测都不一致,那这个样本大概率有问题。这个方法比单模型挑错更可靠,但成本也更高。
最后分享一个我在实操中的体会:数据挑错这件事,投入产出比极高,但很容易被忽略。很多人宁愿花一周调模型,也不愿意花一天清理数据。但从我的经验看,清理一遍数据带来的指标提升,往往比调模型明显得多。尤其是当你的模型指标卡在某个瓶颈上不去的时候,先别急着换模型,去看看数据里有没有错,大概率会有惊喜。