PAYATHON 2026

如何检测按目录划分类别的图像分类数据集中的错标图片?

AI 小顾

可以结合图像特征近邻分析和模型预测,筛选出可能放错目录的图片,再由人工确认。目录只能反映当前标签,不能证明标签正确;模型预测与目录标签不一致,也只能作为复查线索。

没有一种方法适合所有数据集。类别少、图片不多时,先浏览各类别的缩略图,通常就能发现明显错误;数据量较大时,可以借助自动方法缩小检查范围。

先确认类别定义和目录映射

排查前,先检查读取程序是否把目录正确映射到了类别。如果训练和验证使用了不同的类别索引,同一张图片可能被解释成不同标签。这属于读取问题,移动图片无法解决。

还要确认类别是否互斥。例如,一张图片同时包含猫和狗,而任务只允许一个标签,就需要明确标注规则。否则,所谓的错标可能来自类别定义不清。

常用的三种检测方法

1. 按类别查看缩略图

把每个目录下的图片生成缩略图网格,逐页检查。对于外观差异明显的类别,这种方法直观,也容易确认。

如果同一批图片可能被重复收集,还可以检查跨目录重复图。完全相同的图片出现在不同类别中,说明标签存在冲突,需要核对。文件哈希适合发现内容完全相同的文件;经过压缩、裁剪或缩放的重复图,需要借助其他相似度方法。

2. 比较图像特征及其近邻

使用适合当前图像领域的预训练模型,为每张图片提取 embedding,再按余弦相似度等指标寻找近邻。

优先检查以下情况:

  • 图片所在目录与大多数近邻的标签不同。
  • 图片与本类别中的其他图片差异很大。
  • 某个类别中出现了一组外观接近另一类别的图片。

展示近邻通常比单独展示异常分数更方便判断。复查时,可以同时显示目标图片、当前标签和若干相似图片。

少见视角、特殊背景或稀有子类型也可能成为离群点;外观相似的细粒度类别则可能互为近邻。这些现象都不能直接证明错标。

3. 使用交叉验证预测寻找标签冲突

如果已经能够训练分类模型,可以生成 out-of-fold 预测:将数据分成若干折,轮流用其他折训练模型并预测当前折,直到每张图片都得到未使用该图片训练的模型的预测结果。

这样可以减少模型记住训练标签后掩盖错误的影响。直接检查模型对自身训练集的预测也能提供线索,但容易漏掉已经被记住的错标样本。

重点关注模型给当前标签的概率较低、同时给另一个类别的概率较高的图片。如果模型对多个类别都不确定,图片也可能只是模糊、遮挡严重,或类别本身难以区分。

用预测结果生成复查列表

下面的 Python 示例假设已经得到每张图片的 out-of-fold 概率。它只负责排序,不包含模型训练,也不会移动文件。

其中,class_names 必须与概率矩阵的列顺序一致,labels 是根据图片所在目录得到的类别名称。

import numpy as np


def rank_suspects(paths, labels, class_names, oof_probs):
    probs = np.asarray(oof_probs, dtype=float)
    class_names = list(class_names)

    if probs.shape != (len(paths), len(class_names)):
        raise ValueError("Probability matrix shape does not match inputs")
    if len(labels) != len(paths):
        raise ValueError("Labels and paths must have the same length")
    if not np.isfinite(probs).all():
        raise ValueError("Probabilities must be finite")
    if np.any((probs < 0) | (probs > 1)):
        raise ValueError("Probabilities must be between 0 and 1")
    if not np.allclose(probs.sum(axis=1), 1.0):
        raise ValueError("Each probability row must sum to 1")

    class_to_idx = {name: i for i, name in enumerate(class_names)}
    label_idx = np.array([class_to_idx[label] for label in labels])
    pred_idx = probs.argmax(axis=1)
    row_idx = np.arange(len(paths))

    p_label = probs[row_idx, label_idx]
    p_pred = probs[row_idx, pred_idx]

    # Review disagreements with low assigned-label probability first.
    candidates = np.flatnonzero(pred_idx != label_idx)
    order = candidates[np.argsort(p_label[candidates])]

    return [
        {
            "path": str(paths[i]),
            "current_label": labels[i],
            "predicted_label": class_names[pred_idx[i]],
            "p_current_label": float(p_label[i]),
            "p_predicted_label": float(p_pred[i]),
        }
        for i in order
    ]


# paths, labels, class_names and oof_probs are prepared beforehand.
# suspects = rank_suspects(paths, labels, class_names, oof_probs)
# for item in suspects[:50]:
#     print(item)

这个排序是一种筛查启发式,不能保证排在前面的图片一定错标,也会漏掉模型预测与错误标签一致的图片。概率数值也不应直接解释为“标签错误的概率”。

如何确认和修正

建议先检查排序靠前的一批图片,结合原图、相似图片和类别定义进行确认,再决定是否调整目录。保留图片路径、原标签、修正标签和复查结果,便于追踪和撤回。

如果数据中有连拍、同一对象的多张图片或近重复图片,交叉验证时应尽量把相关图片放在同一折,避免相似内容同时进入训练折和预测折,影响筛查判断。

对于类别不平衡的数据,少数类可能更容易被模型判错,因此复查列表也应按类别查看。无法确定标签的样本可以先单独标记,待规则明确后再处理,不宜仅凭模型输出批量改标签。

备注:内容仅供参考。