PAYATHON 2026

YOLOv11 垃圾分类模型测试指标良好,实时检测效果差的原因是什么?

排障赵师傅

测试指标好、实时检测效果差时,先检查测试集能否代表实际场景,再核对实时程序加载的模型、图像处理流程和结果过滤设置。目前还无法确定具体原因,数据量偏小、图片来源与摄像头场景不同,都需要重点排查。单纯把训练从 50 epochs 加到 150 epochs,或反复调整 batch size,通常解决不了这些问题。

类别清单中实际有 11 类,与“6 个 class”的描述不一致。这可能是记录错误,也可能涉及类别配置,需要先核实。

为什么测试分数很好,摄像头效果仍然很差?

测试集可能太小,或与训练集过于相似

列出的数据合计有 2,057 个标注框。一张图片可以包含多个框,同一个实物也可以出现在多张图片里,因此这个数字不能代表独立图片或不同实物的数量。

如果数据按 train 70%、valid 20%、test 10% 划分,且各类大致按比例分配,部分少数类的标注框数量估算如下:

类别划分前标注框数训练集预计标注框数测试集预计标注框数
straw47335
plasticcup74527
milkcarton1007010
mask1369514
bag19213419

实际数量需要在划分后统计。按图片划分时,如果一张图中有多个目标,各类标注框的分配比例未必与图片比例一致。

测试集中某类只有几个目标时,多检出或漏掉一个,指标就可能明显变化。即使全部检出,也不足以说明模型能识别不同品牌、形状和拍摄条件下的同类垃圾。

还要检查是否存在数据泄漏,例如:

  • 同一张网络图片的不同尺寸版本分别进入了训练集和测试集。
  • 同一段视频的相邻帧被随机分到了不同集合。
  • 同一个垃圾实物在相同背景下的连拍图片进入了不同集合。
  • 先做数据增强,再将原图与增强图分别划入训练集和测试集。

这些情况会让测试任务过于容易。应先按原始图片、视频、拍摄批次或实物分组划分数据,再在训练阶段做增强。

网络图片与实际摄像头画面存在分布差异

可以使用 Kaggle 等来源的图片,但要检查它们与实际部署画面的差别。

训练图片里的垃圾可能完整、居中,背景也干净;摄像头画面中的目标可能很小,以俯视角度出现,或受到手持遮挡、反光和运动模糊的影响。同一类别的外观也会变化,例如折叠的口罩、揉成团的袋子和压扁的奶盒。

如果某一类别的图片主要来自固定来源,模型还可能利用背景、拍摄风格等线索。换成学校现场的背景后,这些线索就失效了。

吸管需要单独检查目标尺寸。它在原始画面中可能清楚可见,缩放到模型输入尺寸后,宽度却只剩很少的像素。增加相似图片或继续训练,未必能补回缩放时丢失的视觉信息。

测试指标与实时观察使用了不同条件

目标检测中的“accuracy”含义不明确,建议分别记录:

  • Precision:预测出的目标中,有多少是正确的。
  • Recall:实际存在的目标中,有多少被检出。
  • F1:Precision 和 Recall 的调和平均。
  • mAP50、mAP50-95:在相应 IoU 条件下衡量检测性能。

一次检测要被判定为正确,类别必须正确,预测框与标注框也必须达到匹配要求。

F1 会随置信度阈值变化。测试报告中的较高 F1,可能对应一个较合适的阈值,而实时程序使用的是另一个阈值。mAP 也不能直接当作某个部署阈值下的检出率。Ultralytics 的验证流程和预测流程各有阈值设置,因此仅凭汇总分数无法判断摄像头表现。Ultralytics 验证文档

先用同一张画面定位问题

从摄像头保存几张没有绘制检测框、没有额外缩放的原始帧,包含漏检、误检和正常检测的画面。用训练得到的同一份权重对这些图片运行独立推理,先缩小排查范围,避免每次都重新训练。

对照结果优先检查方向
原测试图表现好,摄像头保存的图片表现差场景差异、目标尺寸、训练数据覆盖
同一张摄像头图片,独立推理正常,实时程序异常权重路径、预处理、类别过滤、后处理
降低置信度阈值后出现正确检测框分数偏低、阈值设置,需要同时评估误检
.pt 模型正常,导出模型异常导出与部署的输入输出处理、精度设置、NMS
物体静止时正常,快速经过时容易漏检运动模糊、处理速度、跳帧、端到端延迟

检查类别配置和标注导出

确认模型实际包含哪些类别

核对以下几处的类别配置是否一致:

  1. 数据配置文件中的 names,以及显式配置的 nc。
  2. 标签文件中的 class ID。
  3. 加载权重后的 model.names。
  4. 实时程序中“类别 ID 到名称”的映射表。

只调整 names 的顺序、保留原标签 ID,会改变 ID 对应的类别含义。部署程序中的类别顺序也要核对,不能假定它与训练时相同。

检查程序是否设置了 classes=[...],或者只显示某些类别。被过滤掉的类别即使有模型输出,也不会显示在界面上。

把导出的标签重新画到原图上

Label Studio 中的标注看起来正确,导出后的坐标和类别仍可能出错。

标准 YOLO 检测标签每行是:

class_id x_center y_center width height

class ID 从 0 开始,坐标使用相对于图片宽高归一化后的值。检查是否把左上角坐标误当成中心坐标,或把像素、百分比直接当成归一化值。Ultralytics 检测数据格式

每个类别抽查一些图片,将导出的标签画回原图,确认:

  • 框的位置、大小及类别名称正确。
  • 同一张图中属于目标类别的可见物体没有漏标。
  • 相同类别使用一致的标注规则。
  • 图片旋转或方向信息没有造成框与图像错位。

还要明确类别边界,例如 bag 包括哪些袋子、cutlery 包括哪些餐具。标注规则含糊,模型也难以学好。

用实际摄像头建立验证集

在摄像头的实际安装位置采集并标注一批画面,覆盖使用中的距离、光照、遮挡和垃圾状态。采集时要更换实物,分批拍摄,避免把大量相邻帧当作独立样本。

现场数据应划分为不同集合,分别用于:

  • 训练:补充模型没见过的外观和场景。
  • 验证:选择模型、输入尺寸与阈值。
  • 最终测试:保留独立拍摄批次,在方案确定后评估。

分别统计每类目标的数量、Precision、Recall 和混淆情况。对 can,重点看误检框落在什么物体上;对 mask、milkcarton、straw、bag,要区分是完全没有候选框、分数过低,还是被预测成了其他类别。

如果反复根据某个测试集中的错误改进模型,这个测试集就已经参与了开发,需要另外保留最终测试数据。

代码示例:固定权重,检查现场数据与阈值

运行以下示例需要使用 Ultralytics Python API,并安装支持 YOLO11 的 ultralytics。路径要替换为实际路径;640、0.001、0.05 等值仅用于建立排查基线,最终参数需要结合该系统确定。

先创建 live.yaml,沿用训练时完全一致的类别映射,将 val 指向已标注的现场验证图片。图片与标签按 YOLO 检测数据目录组织。

from ultralytics import YOLO


def main():
    model = YOLO("runs/detect/train/weights/best.pt")
    print("Model classes:", model.names)

    metrics = model.val(
        data="live.yaml",
        split="val",
        imgsz=640,
        rect=False,
        conf=0.001,
        iou=0.7,
        plots=True,
        verbose=True,
    )

    print("mAP50:", metrics.box.map50)
    print("mAP50-95:", metrics.box.map)


if __name__ == "__main__":
    main()

查看终端输出的逐类指标和目标数量,以及生成的预测示例、PR 曲线和混淆矩阵。较低的置信度门槛便于评估 PR 曲线,实时系统的阈值仍需单独选择。Ultralytics 验证接口

接着,对同一张检测失败的画面比较不同阈值:

from ultralytics import YOLO

model = YOLO("runs/detect/train/weights/best.pt")

for conf in (0.05, 0.15, 0.25):
    result = model.predict(
        source="camera_failure.jpg",
        imgsz=640,
        rect=False,
        conf=conf,
        iou=0.7,
        classes=None,
        agnostic_nms=False,
        verbose=False,
    )[0]

    print(f"\nconf={conf}")
    for box in result.boxes:
        class_id = int(box.cls.item())
        score = float(box.conf.item())
        print(model.names[class_id], f"{score:.3f}")

    result.save(filename=f"camera_failure_conf_{conf:.2f}.jpg")

conf 控制最低置信度;classes=None 保留所有类别。这里的 iou 是 NMS 阈值,与计算 mAP 时判断预测框是否匹配标注框的 IoU 阈值用途不同。Ultralytics 预测参数

如果降低阈值后出现了正确框,同时也增加了大量误检,就要在现场验证集上权衡检出与误检,不能仅凭一张图降低全局阈值。

实时程序还应核对哪些地方?

独立推理正常、实时程序异常时,逐项核对两者的差别:

  • 是否加载了同一份自定义权重,路径是否指向旧训练结果。
  • 是否有额外的类别筛选、置信度过滤或显示条件。
  • 是否重复缩放、重复归一化,或错误拉伸图像。
  • 自定义后处理是否正确还原了 letterbox 的缩放和填充偏移。
  • 是否错误解析了模型输出的坐标格式。
  • 是否误用 class-agnostic NMS,抑制了不同类别的重叠目标。

颜色通道需要按接口要求处理。Ultralytics 高层接口接收 OpenCV 的 NumPy 图像时按 BGR 处理,传入张量时则有不同的输入约定。先核对接口要求,再决定是否需要 BGR/RGB 转换。Ultralytics 输入格式说明

如果使用 ONNX、TensorRT 或量化模型,先让原始 .pt 模型与导出模型处理同一批图片,再比较部署结果,以判断差异是否来自模型转换或运行环境。

数据应该怎么补,训练应该怎么改?

目前的数据适合做原型,但仅凭标注数量无法保证现场效果。也没有“每类达到某个固定数量就一定够用”的标准。

优先针对检测失败的情况补充数据或调整设置:

当前问题优先补充或调整
straw 漏检不同颜色、背景和摆放方向;检查缩放后的宽度,必要时调整拍摄距离或输入分辨率
mask、bag 漏检展开、折叠、揉皱、部分遮挡等实际状态
milkcarton 漏检不同包装、朝向、压扁状态及现场光照
can 误检收集误检物体和背景,作为难负样本;属于已有目标类别的物体仍须正确标注
多类在手持时失效增加手持、遮挡和实际投放姿态的样本

补充数据时,要增加实物和拍摄条件的多样性。同一根吸管连续拍摄几百帧,覆盖的情况仍然有限。确认不包含任何目标类别的背景图,可以用作负样本。

完成数据与部署检查后,再调整训练:

  • 如果之前使用随机权重训练,可以改用预训练权重微调。
  • 根据现场验证结果决定是否增加输入尺寸,同时测量推理速度。
  • 使用符合现场条件的亮度、尺度、模糊和遮挡增强,避免增强后目标难以辨认。
  • 根据训练与现场验证指标的变化决定训练时长。训练指标继续提高,而现场验证表现停滞或下降时,应检查过拟合。
  • 每轮只改少量设置,并记录逐类结果,便于判断改进来自哪里。

还应记录物体经过检测区域时的成功检出率和端到端延迟。视频中偶尔出现一次检测框,未必足以支持后续分拣。单帧指标达标后,需要继续验证目标在实际停留时间内能否被稳定识别。

备注:内容仅供参考。