ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

苹果腐烂识别数据集与YOLO目标检测实战:从标注到产线部署

苹果腐烂识别数据集与YOLO目标检测实战:从标注到产线部署 简介这是一份面向深度学习目标检测方向的苹果腐烂识别数据集适合从事农产品分拣、智能质检及计算机视觉研究的学生与工程师使用。数据集包含fresh_apple与rotten_apple两个类别共978张图片同时提供YOLO格式的txt标签与VOC格式的xml标签并附有指定类别信息的yaml配置文件可直接用于YOLOv5至YOLOv10等YOLO系列算法以及Faster R-CNN、SSD等模型的训练与验证。压缩包内文件总数约2000个以978个txt标签、978个xml标签和978张jpg图像为主另含2个cache缓存文件与1个yaml文件整体大小约37.42MB图片与标签已预先划分为训练集、验证集和测试集省去手动切分环节。目前已有282人学习下载读者可借助该数据集快速复现苹果腐烂检测实验对比不同检测算法的精度与速度并在此基础上完成模型调优与部署验证。1. 苹果腐烂识别数据集与目标检测从烂果分拣线说起去年秋天一个做水果分选设备的朋友找我说他们的产线想加一道视觉检测把腐烂的苹果从好果里挑出来。他们试过用分类模型把每个苹果抠出来单独判断好坏但产线上苹果是堆叠滚动的抠图本身就很难做干净而且一个苹果上可能只有一小块腐烂区域整图分类的标签根本标不准。后来我们换了个思路不判断“这个苹果坏没坏”而是直接检测“腐烂区域在哪里”。这就是目标检测在这类场景里的价值——它输出的是边界框和类别而不是一个整图标签。苹果腐烂识别数据集配合目标检测模型能定位到具体哪一块果皮出现了霉斑、软腐或炭疽病斑后续不管是机械臂剔除还是气吹分选都有明确的坐标可用。这篇文章面向的是手里有类似分拣需求、想用目标检测跑通苹果腐烂识别的工程师从数据集结构、标注格式、模型选型到训练排错按我实际做过的路径讲一遍。2. 苹果腐烂识别数据集长什么样标注格式与类别设计2.1 目标检测数据集的目录结构与标注文件拿到一个苹果腐烂识别数据集第一件事不是急着喂给模型而是把目录结构和标注格式看清楚。常见的目标检测数据集有两种组织方式一种是 VOC 风格的 XML 标注每个图片对应一个同名 XML 文件另一种是 YOLO 风格的 TXT 标注每张图对应一个 TXT每行是class_id x_center y_center width height坐标全部归一化到 0 到 1 之间。苹果腐烂识别数据集如果是从实际分拣线采集的大概率是 YOLO 格式因为标注工具里 labelImg 和 Roboflow 导出 YOLO 格式最顺手。一个典型的目录长这样apple_rot_dataset/ ├── images/ │ ├── train/ │ │ ├── apple_001.jpg │ │ └── apple_002.jpg │ └── val/ │ ├── apple_101.jpg │ └── apple_102.jpg ├── labels/ │ ├── train/ │ │ ├── apple_001.txt │ │ └── apple_002.txt │ └── val/ │ ├── apple_101.txt │ └── apple_102.txt └── data.yamldata.yaml是 YOLO 系列训练时的数据集描述文件内容一般包括训练集和验证集的路径、类别数量、类别名称。苹果腐烂识别数据集的类别设计通常不会太复杂常见的是两类rot和healthy或者只标rot一类把好果当背景。如果要做更细的分级可能会分成soft_rot、bitter_rot、anthracnose等但类别越多标注一致性越难保证小类别样本也越容易不够。我一般建议第一版先做单类rot把召回率跑上去再考虑细分。2.2 标注质量检查三个必须做的脚本数据集拿到手别直接开训。标注里如果有框错位、漏标、类别 ID 写错训练 loss 会震荡mAP 上不去你还以为是模型问题。下面三个检查脚本是我每次都会跑的。第一个检查图片和标注文件是否一一对应import os img_dir apple_rot_dataset/images/train lbl_dir apple_rot_dataset/labels/train imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)} lbls {os.path.splitext(f)[0] for f in os.listdir(lbl_dir) if f.endswith(.txt)} print(图片无标注:, imgs - lbls) print(标注无图片:, lbls - imgs)这个脚本输出两个集合的差集。如果“图片无标注”里有文件说明这些图是负样本可以保留但要在训练配置里允许空标注“标注无图片”里有文件说明标注文件是多余的直接删掉否则训练时 dataloader 会报错。第二个检查标注框的坐标是否越界或宽高为零import os lbl_dir apple_rot_dataset/labels/train bad_files [] for fname in os.listdir(lbl_dir): if not fname.endswith(.txt): continue path os.path.join(lbl_dir, fname) with open(path) as f: for line_no, line in enumerate(f, 1): parts line.strip().split() if len(parts) ! 5: bad_files.append((fname, line_no, 字段数不对)) continue cls, x, y, w, h map(float, parts) if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): bad_files.append((fname, line_no, f坐标越界: {x},{y},{w},{h})) for item in bad_files: print(item)归一化坐标必须在 0 到 1 之间宽高必须大于 0。越界的框通常是标注时拖出了图片边界或者转换脚本里除了一个错误的尺寸。这种框在训练时会被 clamp 或者直接跳过但最好在数据层面就修掉。第三个统计每个类别的实例数量import os from collections import Counter lbl_dir apple_rot_dataset/labels/train counter Counter() for fname in os.listdir(lbl_dir): if not fname.endswith(.txt): continue with open(os.path.join(lbl_dir, fname)) as f: for line in f: parts line.strip().split() if parts: counter[int(parts[0])] 1 print(counter)如果rot类有几千个框而某个细分病斑类只有几十个那训练时就要考虑过采样或者 focal loss。类别不平衡在腐烂识别里很常见因为健康苹果上不会标框背景区域远多于前景。提示检查完标注后把data.yaml里的nc和names跟实际类别 ID 对齐。我见过有人把nc写成 2但标注里只有 0 和 1 两个类names 却写了三个名字训练时直接报索引越界。3. 用 YOLO 跑通苹果腐烂检测训练命令与参数调法3.1 从预训练权重到自定义数据集的迁移训练苹果腐烂识别数据集通常不会特别大几千张图已经算不错了。从零训练一个检测模型不现实常见做法是用 COCO 预训练的 YOLO 权重做迁移学习。以 YOLOv8 为例训练命令一行就能跑yolo detect train \ dataapple_rot_dataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectapple_rot_runs \ nameexp1data指向数据集描述文件model是预训练权重yolov8s是 small 版本参数量适中在分拣线边缘设备上也能跑。epochs100是上限patience20表示验证集指标 20 轮不提升就早停。imgsz640是输入分辨率苹果腐烂区域如果很小可以提到 1280但显存和推理时间会翻倍。batch16看显存调8G 显存跑yolov8s加 640 分辨率16 差不多是上限。训练开始后重点看三个输出box_loss、cls_loss、mAP50。box_loss下降说明框的位置在收敛cls_loss下降说明类别判断在变好。如果box_loss一直震荡不降大概率是学习率太大或者标注框有问题。mAP50是 IoU 阈值 0.5 时的平均精度苹果腐烂检测里mAP50能到 0.85 以上就算可用0.9 以上算不错。3.2 关键参数怎么调学习率、锚框与数据增强YOLO 默认的lr00.01对苹果腐烂数据集不一定合适。如果数据集只有一两千张图学习率降到 0.001 到 0.005 更稳否则前期 loss 会炸。我一般会先跑 10 个 epoch 看 loss 曲线如果box_loss从 2.0 直接跳到 5.0 再震荡就把lr0减半。锚框方面YOLOv8 是 anchor-free 的不需要手动设锚框但 YOLOv5 需要。如果你用的是 YOLOv5苹果腐烂区域的宽高比跟 COCO 里的通用物体差别很大最好用kmeans重新聚类锚框python utils/autoanchor.py --data apple_rot_dataset/data.yaml --weights yolov5s.pt数据增强是提升小目标检测效果的关键。苹果腐烂区域往往只占整图的百分之几默认的mosaic增强会把四张图拼在一起小目标变得更小有时候反而有害。我一般会关掉mosaic的最后 10 个 epoch让模型在真实分布上收尾yolo detect train \ dataapple_rot_dataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.005 \ mosaic0.5 \ close_mosaic10 \ scale0.3 \ fliplr0.5mosaic0.5表示一半概率做 mosaicclose_mosaic10表示最后 10 轮关闭。scale0.3是随机缩放fliplr0.5是水平翻转。垂直翻转要慎用苹果在分拣线上翻滚方向不固定但垂直翻转后的腐烂纹理可能不自然我一般不开。注意如果验证集mAP50在 50 轮左右突然掉下去先检查是不是学习率调度到了余弦退火的末端或者数据增强太强导致验证集分布对不上。把close_mosaic提前到 20 轮试试。4. 苹果腐烂检测的避坑与排查从漏检到误检4.1 小目标漏检现象、原因与解决现象模型对大面积腐烂识别很好但小米粒大小的霉斑经常漏检recall明显低于precision。原因下采样倍数太高。YOLO 的 P3 特征图是 8 倍下采样如果腐烂区域在原图里只有 10 个像素宽到 P3 上就剩一个多像素特征几乎消失。另外如果训练时imgsz设成 640而原图是 2000 万像素缩放后小目标更小。解决把imgsz提到 1024 或 1280让输入保留更多细节或者在模型里加一个 P2 检测头4 倍下采样专门抓小目标。YOLOv8 可以通过修改配置文件加 P2但计算量会上去。更简单的做法是切图推理把大图裁成 640×640 的小块分别检测再合并适合产线相机固定、苹果位置相对可控的场景。4.2 误检背景纹理现象、原因与解决现象模型把苹果表面的高光、水珠或者果梗凹陷当成腐烂区域precision上不去。原因训练集里负样本太少或者负样本的背景不够多样。如果数据集里只有腐烂苹果的图模型没见过正常苹果的高光纹理就会把类似颜色和形状的区域误判。解决往训练集里加正常苹果的图标注为空文件。空标注文件在 YOLO 里是合法的表示这张图没有目标。负样本和正样本的比例控制在 1:3 到 1:5 之间。另外颜色抖动增强可以模拟不同光照下的高光变化hsv_v调大一点比如 0.5。4.3 类别不平衡导致小类被吞现象如果数据集里soft_rot有 2000 个框anthracnose只有 100 个框训练后模型几乎不预测anthracnose全判成soft_rot或背景。原因交叉熵损失被大类主导小类的梯度被淹没。解决用 focal loss 替代默认的 BCE loss或者对小类过采样。YOLOv8 默认的cls损失是 BCE可以在配置文件里换成focal。更直接的办法是把小类的图片复制几份文件名加后缀让采样器多抽到。但注意别复制太多否则小类过拟合验证集上看着好实际产线一跑就崩。4.4 验证集指标高但产线翻车现象验证集mAP500.92部署到分拣线上漏检和误检都比预期多。原因验证集和训练集来自同一批数据光照、相机、苹果品种都一样模型过拟合了这批数据的分布。产线上的光照变化、苹果表面水渍、相机白平衡偏移都会让输入分布偏移。解决验证集要留出不同时间段、不同批次的数据。如果做不到至少在训练时加更强的颜色增强和噪声增强让模型对光照变化鲁棒。部署前用产线相机实拍几百张图做一次测试别只看验证集数字。4.5 标注框贴边导致训练不稳定现象训练 loss 偶尔出现 NaN或者某些 batch 的 loss 突然飙升。原因标注框的归一化坐标刚好在 0 或 1 边界上计算 IoU 时出现除零或数值溢出。解决把坐标 clamp 到 0.001 到 0.999 之间重新生成标注文件。这个坑很隐蔽因为大部分框架会静默处理但某些版本会直接报错。import os lbl_dir apple_rot_dataset/labels/train for fname in os.listdir(lbl_dir): if not fname.endswith(.txt): continue path os.path.join(lbl_dir, fname) lines [] with open(path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls parts[0] coords [min(max(float(v), 0.001), 0.999) for v in parts[1:]] lines.append(f{cls} { .join(f{v:.6f} for v in coords)}) with open(path, w) as f: f.write(\n.join(lines) \n)这段脚本把每个坐标限制在 0.001 到 0.999 之间重新写回文件。跑之前先备份原始标注。5. 把模型推到产线导出、量化与一个验证技巧训练完的.pt权重不能直接上产线得先导出成推理引擎。如果分拣线用的是 NVIDIA 边缘设备导出 TensorRT 引擎如果是瑞芯微或者晶晨的 NPU导出 ONNX 再转 RKNN 或其他的。以 TensorRT 为例yolo export \ modelapple_rot_runs/exp1/weights/best.pt \ formatengine \ imgsz640 \ halfTrue \ device0halfTrue表示 FP16 量化速度能快一倍精度掉得不多。如果产线对延迟要求极高可以试 INT8 量化但需要准备几百张校准图而且苹果腐烂区域的细微颜色变化可能在量化后丢失导致漏检。我一般先上 FP16实测延迟和精度都够用就不折腾 INT8。导出后写一个最小推理脚本验证引擎是否正常import cv2 from ultralytics import YOLO model YOLO(apple_rot_runs/exp1/weights/best.engine, taskdetect) img cv2.imread(test_apple.jpg) results model(img, conf0.25, iou0.45) for box in results[0].boxes: cls_id int(box.cls) conf float(box.conf) xyxy box.xyxy.tolist()[0] print(f类别: {model.names[cls_id]}, 置信度: {conf:.3f}, 框: {xyxy})conf0.25是置信度阈值低于这个值的框不输出。产线上如果漏检代价高把conf降到 0.15宁可多报几个误检也别放过腐烂果。iou0.45是 NMS 的 IoU 阈值苹果堆叠时框重叠多iou可以适当调高到 0.5 到 0.6避免把相邻苹果的腐烂框合并掉。一个验证技巧别只看 mAP把验证集里所有漏检的图挑出来按腐烂面积从小到大排序。如果漏检集中在最小的那 10%说明是小目标问题回去调imgsz或者加 P2 检测头如果漏检分散在各个尺寸说明是特征判别力不够考虑换更大的 backbone 或者加注意力模块。这个排序分析比盯着 mAP 数字有用得多。我自己踩过最深的坑是早期太相信验证集指标模型在实验室里 mAP 0.93拉到分拣线上第一天就漏了三个烂果。后来养成习惯任何模型上线前必须用产线相机在真实光照下拍至少 200 张图人工标一遍跑一次推理看漏检和误检的具体案例。这个习惯帮我省了至少两次返工。希望帮到你。本文还有配套的精品资源点击获取
返回列表