ARTICLE DETAIL

资讯详情

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

苹果缺陷检测YOLO数据集:从标注格式到训练避坑实战

苹果缺陷检测YOLO数据集:从标注格式到训练避坑实战 简介这是一份面向目标检测初学者、课程设计及苹果品质分拣场景的YOLO缺陷检测数据集资源。图片来自真实果园与产线环境光照、角度、成熟度差异明显场景覆盖度好所有图像均使用LabelImg人工标注边界框贴合缺陷区域质量较高。标签同时提供VOC(xml)、COCO(json)、YOLO(txt)三种格式分别存放于独立文件夹无需转换即可接入YOLOv5、YOLOv8等主流框架。压缩包内含2000个文件主要由1000个xml标注文件、990个txt标签文件、3个Python划分脚本、1个yaml配置及6个图文教程组成目录按格式分门别类整体仅14.2MB便于快速下载与解压。随包附赠YOLO环境搭建教程含Linux/Windows版本和训练案例教程并配有三个数据集划分脚本可自由生成训练集、验证集、测试集。目前已有869人学习下载适合作为毕业设计、实训项目或企业预研的快速验证数据集。1. 苹果缺陷检测为什么不能拿通用数据集硬训在果园分选、包装线和质检设备上苹果的碰伤、腐烂、果锈和划痕是最常见的几类缺陷而这类检测任务和行人、车辆检测有一个本质区别苹果表面缺陷的类间差异小、类内差异大同一个「碰伤」在不同品种、不同光照下拍出来可能完全不像同一个东西。如果直接拿 COCO 上预训练好的权重去跑几乎没有实用价值——背景太杂、目标太小、缺陷形态太细碎模型很难收敛。这份「YOLO苹果缺陷目标检测数据集(含1000张图片)对应voc、coco和yolo三种格式标签划分脚本训练教程.rar」解决的就是这个断档1000张已经标注好的苹果缺陷图三种主流标注格式都给你备齐划分脚本和训练步骤也一并给你。它的价值在于省掉了从采集到清洗、标注、格式转换这一周起步的脏活适合刚接触目标检测的入门者也适合产线上需要快速验证「苹果缺陷能不能用YOLO检出」的工程师。接下来我从数据结构、划分逻辑、训练参数和踩坑记录四个方面把这套方案完整拆开讲。2. 先看清手里有什么1000张苹果图的标注格式与目录结构2.1 三种标注格式的长相差异VOC、COCO、YOLO分别存了什么拿到压缩包后别急着跑训练先花十分钟把目录结构理清楚。常见做法是把images、labels、annotations分开放里面再按train、val分子目录。如果你打开后看到的是VOC2007风格目录JPEGImagesAnnotations那说明作者保留了最早的 Pascal VOC 组织方式。三种格式的标签差的不是一星半点格式存储方式坐标表示适合的框架VOCXML 文件每个图片对应一个.xmlxmin, ymin, xmax, ymax绝对像素坐标老牌检测框架、SSD、Faster R-CNNCOCO单个 JSON 文件所有图片标注汇总绝对像素坐标带segmentation多边形Detectron2、MMDetectionYOLO每个图片对应一个.txt一行一个目标归一化的cx, cy, w, h取值 0~1YOLOv5/v8、Ultralytics 全家桶VOC 和 COCO 是同一种坐标哲学的两种载体读起来直观YOLO 格式则牺牲了可读性换效率它不存类别名只存类别 ID 加四个归一化小数所以训练时需要一个单独的classes.txt或 YAML 文件把 ID 映射回名称。拿一张图实际打开看最有体感。比如apple_001.jpg对应的三个文件第一行内容长这样。VOC 的 XML 里关键区块是object namebruise/name bndbox xmin142/xmin ymin210/ymin xmax238/xmax ymax296/ymax /bndbox /objectCOCO 的 JSON 里这张图会被拆成images、annotations、categories三段标注框通过image_id关联{ images: [{id: 1, file_name: apple_001.jpg, width: 640, height: 480}], annotations: [{id: 1, image_id: 1, category_id: 1, bbox: [142, 210, 96, 86]}], categories: [{id: 1, name: bruise}] }YOLO 的 TXT 里只有一行0 0.296875 0.526042 0.15 0.179167注意 COCO 的bbox存的是[x, y, width, height]左上角坐标加宽高VOC 存的是两个对角点。YOLO 的cx, cy是中心点归一化坐标分母是图片宽和高。这三个格式之间的转换是后续一切工作的地基。2.2 打开格式文件核对数据的3个命令不要相信压缩包自带的所有文件都是对的——标注软件导出、脚本批量转换的过程中经常产生比例错误和越界坐标。我拿到数据集的第一件事永远是抽样核验而不是直接开训。用 Python 快速检查一张图的标注是否落在画面内# check_annotation.py import cv2 import numpy as np img cv2.imread(images/train/apple_001.jpg) h, w img.shape[:2] # 读 YOLO 格式标签 with open(labels/train/apple_001.txt, r) as f: lines f.read().strip().splitlines() for line in lines: cls, cx, cy, bw, bh map(float, line.split()) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) print(fclass{cls} box({x1},{y1})-({x2},{y2}) 越界{x10 or y10 or x2w or y2h})这段脚本把归一化坐标还原成像素坐标然后检查越界。cx加bw / 2超过图片宽度就是标注越界常见于标注框贴着边缘时四舍五入出了 1.0。如果越界样本超过 5%建议放弃这份标注重新标或者用脚本把所有越界框裁剪回边界——损失一点精度但比全部返工快。再抽查一下类别分布避免某个类别只有十几张图模型根本学不到特征# 统计所有标签文件的类别频次 cat labels/train/*.txt | awk {print $1} | sort | uniq -c | sort -rnawk {print $1}取每行第一个字段类别 IDsort | uniq -c完成计数。看到频次差距超过一个数量级就要考虑类别重采样否则小样本类别会被大样本类别吃掉。1000 张图的缺陷数据集正常情况单类别应该有两三百个实例如果某个类只有 20 个实例后续训练时它的 mAP 会惨不忍睹。3. 划分脚本怎么用训练/验证/测试集划分与格式联动3.1 划分脚本的核心逻辑按比例拆目录还是拆标注压缩包里的划分脚本核心作用是把数据拆成train / val / test三个互不重叠的集合。这里有一个关键决策是按 7:2:1 还是 8:2 划分对 1000 张的小数据集我建议保留测试集但比例不要太大——训练集 700、验证集 150、测试集 150 是稳妥的起点。验证集负责调参和早停判断测试集只在全部训练结束后用来做最终评估这个分隔不能混淆。另一个容易犯的错是只分图片不分标签。划分脚本必须同时移动图片和对应标注文件否则训练到一半报FileNotFoundError。好的划分脚本还会把三个集合写成三个 TXT 清单文件Ultralytics YOLO 直接读清单就能定位图片不需要目录级硬编码。划分前一定要确认一件事这个压缩包里 VOC、COCO、YOLO 三种格式是否各自维护一套train/val/test结构。如果是直接用其中一套就行如果只有一套全量标注然后靠脚本划分那脚本会先转换格式再划分跑一次脚本等于同时完成两件事。跑之前看一眼脚本开头的参数区即可。3.2 动手跑一遍划分命令行参数与产物检查一个典型的划分脚本会接受输入目录、输出目录、比例三个参数在命令行里这么调python split_dataset.py \ --image_dir datasets/images \ --label_dir datasets/labels \ --output_dir datasets/split \ --train_ratio 0.7 \ --val_ratio 0.15 \ --test_ratio 0.15 \ --seed 42--seed 42很关键。目标检测的划分虽然是随机抽样但如果不固定随机种子每次跑出来的集合都不一样复现实验结果时你会疯掉——模型精度差 0.5 个点你根本说不清是数据划分变了吗还是模型变了。固定随机种子是能省掉大量返工的一个好习惯。脚本内部的大致步骤是读所有图片文件名列表用random.shuffle打乱按比例切三段然后把每张图的标签文件跟随图片移动到对应子目录。脚本跑完后检查一下产物# 检查划分后的数据完整性 for split in train val test; do img_count$(ls datasets/split/images/$split/*.jpg 2/dev/null | wc -l) lbl_count$(ls datasets/split/labels/$split/*.txt 2/dev/null | wc -l) echo $split: $img_count images, $lbl_count labels done图片数和标签数必须完全一致差一个都说明有图片漏标或标签没配对。另一个检查维度是看三个集合的类别分布是否接近整体分布——如果train里碰伤占 40%test里碰伤突然只占 10%那模型的评估结果就没有参考价值了。遇到分布偏移把划分种子换几个值重新跑选分布最接近的一次。1000 张图的数据集划分结果大约就是 700 / 150 / 150 这个量级。如果发现test集里某类样本数小于 10建议把它并入val评估时单独说明。小数据集上追求比例完美不如保证每个类别在三个集合里都出现。4. 用 YOLOv8 训练苹果缺陷检测模型最小可用流程4.1 数据配置文件与目录结构的匹配训练脚本通常由三个文件构成data.yaml、yolov8n.pt预训练权重、训练入口脚本。这三样对应了「数据是什么、从哪开始学、怎么学」三个问题。把压缩包里的标签转成 Ultralytics 结构后最核心的配置文件data.yaml长这样# data.yaml path: /home/user/apple_defect # 数据集根目录 train: images/train # 训练图片目录相对 path val: images/val # 验证图片目录 test: images/test # 测试图片目录 nc: 4 # 类别数量 names: [bruise, rot, russet, scratch] # 类别名按ID顺序nc必须和names的长度一致YOLO 格式标签里的类别 ID 直接索引names列表。这里最容易翻车的是类别顺序——训练时用的names顺序必须和标注时的类别 ID 顺序完全一致否则模型学到的映射就是错的推理时class 0变成了另一个类。如果你不确定原始标注的 ID 顺序先读一份标签文件把类别 ID 和图片内容对照一遍。path建议写绝对路径。相对路径在本地实验时没问题一旦换机器跑相对路径的根目录变了就会报image not found。我一般会用pwd先确认路径再填进 YAML。4.2 预训练权重怎么选、训练命令怎么敲YOLOv8 的预训练权重需要单独下载压缩包里通常不内置。V100 这类显卡上可以直接用官方预训练权重下来后先确认模型的输入尺寸和类别数是否匹配你的数据。下载预训练权重这个环节建议直接走官方渠道文件名带yolov8n.pt的是 nano 版本yolov8s.pt是 small 版本yolov8m.pt是 medium。对于 1000 张图、4 个类别的缺陷检测nano 和 small 是甜点区——模型小、训练快、不容易过拟合。medium 以上在这个数据量级几乎必然过拟合。训练命令yolo detect train \ modelyolov8n.pt \ dataapple_defect.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience15 \ projectapple_defect_runs \ nameexp_bruise_v1判读这些参数epochs100是训练轮数上限配合patience15实现早停——连续 15 轮验证集指标不提升就自动停能省掉大量无效训练时间。imgsz640是输入分辨率苹果缺陷这种小目标如果原始图片里缺陷只占几十个像素建议尝试imgsz960甚至1280代价是训练时间翻倍。batch16受显存约束V100 16GB 显存跑 nano 用 16 没问题8GB 显存就降到 8。lr00.01是初始学习率预训练微调场景下 0.01 是安全值如果训练曲线震荡降到 0.005。数据量小的时候加数据增强有奇效# 在训练配置中追加 augment: true hsv_h: 0.015 # 色调扰动 hsv_s: 0.7 # 饱和度扰动 hsv_v: 0.4 # 明度扰动 degrees: 15 # 旋转 fliplr: 0.5 # 水平翻转苹果缺陷检测尤其吃光照泛化——产线上的打光是固定的但不同批次苹果的颜色深浅差异大饱和度扰动往 0.7 调能明显提升鲁棒性。4.3 训练后看哪几个指标训练结束后runs/detect/exp_bruise_v1目录下会生成results.csv、confusion_matrix.png、PR_curve.png等文件。我每次训练完会先看三个数mAP0.5、mAP0.5:0.95、precision。对苹果缺陷检测mAP0.5能上 0.85 就算可用mAP0.5:0.95在 0.6 左右是正常的——缺陷框小、边缘模糊高 IoU 阈值下得分下滑是必然。results.csv里看val/box_loss和val/cls_loss两条曲线。两条曲线在训练后期不应该有剧烈上翘如果出现「训练 loss 下降但验证 loss 抬头」的剪刀差就是过拟合信号回退到验证 loss 最低的那个 epoch 的权重文件。5. 苹果缺陷数据集训练避坑从标注翻车到训练崩坏5.1 类别名带中文或特殊字符导致解析失败现象训练启动时报unable to parse label定位到某个.txt文件时直接终止训练。原因部分标注工具导出的类别名带中文或下划线前缀比如碰伤_1YOLO 解析器按空格分词后拿到非数字字段就崩了。VOC 转 YOLO 时经常保留原始 XML 里的中文类别名这个坑在国产标注工具里尤其常见。解决统一把类别名改成纯英文小写加下划线。写个脚本读取所有标签文件把非数字字符全部替换成对应的英文映射。更彻底的做法是全部重新编号类别名只存在于data.yaml的names列表里标签文件里只允许出现0到nc-1的整数。改完后再跑一遍 2.2 节的类别频次统计确认没有奇怪的类别 ID 混进来。5.2 归一化坐标超出 0~1 导致训练 loss 异常现象训练正常启动但 loss 在初期就剧烈抖动部分批次直接出现nan。原因YOLO 格式的坐标必须是 [0, 1] 区间的小数但 VOC 转 YOLO 时如果图片尺寸读取错误比如把缩略图的宽高当作原图宽高转换出来的cx或bw会大于 1。训练时模型预测的边界是 0~1 的 sigmoid 输出标签却给了 1.3损失函数直接爆炸。解决在训练前跑一次全量标签扫描# check_yolo_format.py import os for split in [train, val, test]: label_dir fdatasets/split/labels/{split} for f in os.listdir(label_dir): with open(os.path.join(label_dir, f)) as fp: for line in fp: parts list(map(float, line.split())) if len(parts) ! 5: print(f{f}: 字段数异常 {len(parts)}) elif not all(0 v 1 for v in parts[1:]): print(f{f}: 坐标越界 {parts})如果查出大量越界坐标回头检查 VOC 转 YOLO 脚本里读取图片尺寸的部分——大概率是用cv2.imread读错了文件路径拿到了默认尺寸的占位图。这个脚本必须在划分之后、训练之前跑是训练前最后一道保险。5.3 训练中 BN 崩溃loss 突然变成 NaN现象训练跑到第 30~50 轮时loss 突然输出nan日志里出现RuntimeError: CUDA error: device-side assert triggered。原因这是目标检测训练里一个有名的崩坏模式。要么是标签里出现了nc之外的类别 ID要么是某个类别的样本在 batch 里全部被增强策略裁掉了导致该类别梯度异常。前 20 轮没事、后面才炸通常是某个 batch 抽样恰好把某个罕见类别全部聚到一起BN 统计量瞬间失控。解决先降学习率重试看是否延后出现。如果降学习率无效检查训练集里是否存在类别 ID 大于nc-1的标签行。还有一种情况是图片损坏解码失败Ultralytics 会返回一张全黑图但标注框还在相当于拿错误数据去拟合。排查办法是把训练集逐个用cv2.imread读一遍出错的文件直接移出训练集。5.4 混淆矩阵总和不为 1 的困惑现象训练完看confusion_matrix.png发现矩阵里所有格子的数值加起来不等于 1开始怀疑计算有 bug。原因YOLO 的混淆矩阵是「按实际类别归一化」的每行的和是 1不是整个矩阵的和是 1。每一行代表一个真实类别行内数值表示该类别被预测成了哪些目标列代表预测类别。某一行全是背景background说明这个类别一个都没检出这是比和不为 1 更需要关注的信号。解决看混淆矩阵时按行读。看某个真实类有多少比例被判成了别的类那才是欠拟合或类别混淆的线索。比如碰伤bruise有 30% 被预测成果锈russet说明两个类别在视觉上高度重叠要么增加标注精度要么考虑合并类别。5.5 小数据集上过早过拟合现象训练 loss 一直在降验证集的 mAP 在 40 轮之后开始缓慢下降但验证 loss 曲线没有明显抬头。原因1000 张图对 YOLO 来说属于小数据模型容量有余但数据不足模型开始记住训练集里的纹理细节和背景噪声。这是小数据集上最普遍的现象不是 bug。解决三件事同时做。一、把patience从默认值调到 15让它自动停在验证集指标最高点。二、增强程度拉满degrees30旋转、scale0.5缩放随机、mosaic1.0开马赛克增强让模型看不到「原封不动的同一张图」。三、换更小的模型yolov8n比yolov8s过拟合得更慢对小数据集更友好。6. 验证与进阶从 mAP 数字到产线可用的最后一步训练完的模型先在测试集上做一次彻底评估别直接拿去跑视频。yolo detect val跑完后用混淆矩阵判断哪些类别互相打架然后翻几十张测试集的可视化预测图看漏检和误检的具体形态——这一步的体感比任何指标都直接。苹果缺陷检测和通用目标检测有一个显著差异多数场景下你最终关心的不是「有没有缺陷」而是「这个苹果能不能上产线」。把检测结果换算成缺陷面积占比是个简单有效的改良方案。苹果的缺陷面积占比通常按像素估算检测框面积不等于缺陷面积但在缺陷形态接近椭圆时用框面积近似是工程上可接受的。写一个后处理脚本把检测框大小换算为缺陷占比# estimate_defect_ratio.py import cv2 from ultralytics import YOLO model YOLO(apple_defect_runs/exp_bruise_v1/weights/best.pt) img cv2.imread(test/rotten_apple_017.jpg) h, w img.shape[:2] results model.predict(img, conf0.25, imgsz640)[0] apple_area 0 defect_area 0 for box in results.boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) cls_id int(box.cls[0]) if cls_id 0: # 假设类别0是整果 apple_area (x2 - x1) * (y2 - y1) else: # 其他类别都是缺陷 defect_area (x2 - x1) * (y2 - y1) ratio defect_area / apple_area if apple_area 0 else 0 print(f缺陷面积占比: {ratio:.1%})conf0.25是置信度阈值产线场景通常比学术场景更保守建议调到 0.35 以上减少误触发imgsz640要和训练时保持一致否则精度会下降。模型在测试集上的 mAP 是 0.85但到了实际产线可能会掉到 0.7 以下——光照、相机角度、苹果品种差异都会拉低精度所以上线前必须用小批量真实数据做一轮验证。我现在的习惯是任何检测项目训练只是第一步上线前先采集 20~30 张产线实拍图用模型跑一遍肉眼看完结果再决定是否微调。苹果缺陷检测里果锈和碰伤在低光照下极容易混淆肉眼扫一遍往往能发现指标上没有暴露的问题。希望这份数据集加训练流程的拆解能帮你把第一版缺陷检测模型更快跑到产线上去验证。本文还有配套的精品资源点击获取
返回列表