简介:辣椒缺陷检测数据集面向目标检测任务,可用于农业质检、食品分拣等场景中的辣椒外观缺陷识别与分级研究。每张图片均拍摄单个辣椒,涵盖Defect、Fly-bites、Grade-A、Grade-B、striped共5个类别,全部标注产生1219个边界框,等级标签与缺陷标签组合便于训练分类、检测及评估模型。包内共2000个文件,核心为jpg原图及对应的Pascal VOC格式xml文件、YOLO格式txt文件,适配主流检测框架;压缩包仅11.74MB,下载和部署都很轻量。目前已有253人学习浏览,适合作为目标检测入门数据或农产品品质分选项目的初期样本。该数据集解压后可直接用于YOLO、Faster R-CNN等常见模型训练,标注文件由labelImg生成,结构统一便于二次核验与扩充,配套博文另提供样本预览,帮助确认数据是否符合需求;对需要快速积累训练样本的开发者尤为实用。
1. 695张5类别的辣椒缺陷检测:为什么小数据集要按YOLO格式重做一遍
辣椒分拣线上的质检员,一天要对几万颗辣椒做外观判定,裂口、病斑、果柄脱落、畸形、机械损伤,全靠肉眼。只要连续盯两小时,漏检率就会肉眼可见地上涨。想让这套流程自动化,第一步不是买硬件,而是拿到一份能直接喂给YOLO系模型的标注数据。标题这份「辣椒缺陷检测数据集VOC+YOLO格式695张5类别.7z」就是一个典型的起步盘:695张图、5个缺陷类别,同时给了VOC的XML标注和YOLO的TXT标注,压缩成7z包分发。别觉得695张太少,做产线验证、跑通训练链路、给老板看效果,这个量级撑得起;真正要做的,是把它用对。
这篇文章不打算复述数据集介绍,而是按「拿到压缩包 → 解压核验 → 格式转换 → 训练调参 → 排错 → 部署验证」的顺序,把每个环节里容易翻车的地方都过一遍。内容面向两类人:刚把YOLOv8环境搭起来的新手,按步骤能跑通;已经在做工业质检的老手,可以直接看参数边界和踩坑清单。
2. 先认清VOC和YOLO两种格式:目录结构、文件规范与7z解压
2.1 VOC和YOLO的存储思路差异
VOC格式脱胎于PASCAL VOC竞赛,它的核心是一张图片配一个同名的XML文件,XML里记录图片尺寸、目标类别和每个目标的bbox坐标。目录结构通常是这样的:
VOCdevkit/ ├── VOC2007/ │ ├── Annotations/ # 存放 XML 文件 │ ├── JPEGImages/ # 存放原始图片 │ └── ImageSets/Main/ # 训练/验证的图片列表YOLO格式则更激进,把标注收敛成一行一行的纯文本。每张图片对应一个TXT文件,每行四个数字加一个类别ID:
class_id x_center y_center width height这里的四个坐标全部是归一化后的相对值,范围在0到1之间,基准是图片的宽和高。这种格式没有XML那么直观,但模型训练时不用每次解析XML,数据加载路径短,对大规模训练和边缘端部署都更友好。
标题说这份数据集同时给了VOC和YOLO两种格式,意味着拿到手应该是两套标注目录并存。我建议直接用YOLO格式做训练,VOC保留作为标注审计和二次转出的中间层。很多标注工具导出时只认VOC,后续想加类别、改标注,回VOC改完再统一转YOLO,比直接改TXT稳妥。
2.2 拿到7z包后第一步:解压与文件计数
7z压缩格式比zip更常见于数据集分发,原因很实际:压缩率高,对图片这种高冗余文件尤其明显。缺点是解压工具不通用,不少人在这一步就卡住了。
Windows下直接装7-Zip,右键解压即可。Linux服务器上做训练,就得用命令行:
# 安装 p7zip sudo apt-get install p7zip-full # 解压到指定目录,-o 后面不要留空格 7z x pepper_dataset.7z -o/path/to/dataset代码说明:7z x是解压到当前目录并保持目录结构,-o指定输出路径。Linux上如果只装了unzip,是无法解压7z的,需要 p7zip 系列。解压后先在JPEGImages和labels目录里分别数一下文件数量。
find . -name "*.jpg" | wc -l find . -name "*.txt" | wc -l常见做法的预期是图片数和TXT数对得上,各695。如果TXT数量比图片少,说明有漏标;多了,说明有重复标注文件。这一步只花半分钟,但能拦下一大半后续训练报错的隐患。
2.3 用脚本检查标注文件完整性
文件数量对上之后,还要检查标注内容本身。YOLO训练时最常见的错误是TXT里写了不存在的类别ID,或者坐标越界,模型训练时直接报assertion failed之类的错。
import os def inspect_labels(label_dir, expected_classes=5): bad_files = [] for root, _, files in os.walk(label_dir): for f in files: path = os.path.join(root, f) with open(path) as fp: for line in fp: parts = line.strip().split() if len(parts) != 5: bad_files.append((path, "字段数不为5")) continue cls_id = int(parts[0]) if cls_id < 0 or cls_id >= expected_classes: bad_files.append((path, f"类别ID越界: {cls_id}")) coords = [float(x) for x in parts[1:]] if any(x < 0 or x > 1.000001 for x in coords): bad_files.append((path, f"坐标超出[0,1]: {coords}")) return bad_files bad = inspect_labels("path/to/labels") print(bad if bad else "标签全部正常,classes=5")代码说明:expected_classes传5,对应标题里说的5类别。检查逻辑是逐行解析TXT,确认每行5个字段、类别ID落在0~4之间、坐标在0~1范围内。有任何一项不满足,先修数据,不要带着坏标注进训练。
提示:如果TXT里出现「负数坐标」或者「宽度/高度为0」,换算之后往往是标注框退化成了线或点。这种标注在训练中不会直接报错,但会让loss异常波动,排查起来很费劲。
3. 把VOC转成YOLO标签:转换脚本、类别映射与bbox边界
3.1 为什么要转换,以及双格式带来的陷阱
有些用户拿到数据后直接拿VOC的XML去训练TPH-YOLO或者自己写的检测器,走了弯路。YOLO系算法的数据加载器原生吃TXT格式,虽然Ultralytics框架提供了yolov8的数据适配,可以读ImageSets/Main自动生成训练集,但底层训练时还是会把XML转成TXT操作。与其让框架反复转换,不如一次性转完,转完还能人工复检。
双格式数据集还有一个隐蔽陷阱:VOC和YOLO两种标注可能不完全同步。数据集作者导出VOC后又改了几个标注,忘了重新导出YOLO,或者反过来。我一般会把解压后的两套标注都检查一遍,以YOLO为准做训练,发现不一致时先核对XML里的filename字段和图片实际文件名的对应关系。
3.2 从VOC XML转YOLO TXT:一个能直接跑的脚本
转换的核心是解析XML,取bndbox里的xmin, ymin, xmax, ymax,再除以图片宽高做归一化。注意YOLO需要的是中心点坐标和宽高,不是角点坐标,很多第一版脚本都会在这里搞混。
import xml.etree.ElementTree as ET import os # 类别名称必须和 data.yaml 里的顺序完全一致 class_map = { "stem_defect": 0, # 果柄损伤 "rot_spot": 1, # 病斑 "crack": 2, # 裂口 "deformity": 3, # 畸形 "mechanical_damage": 4 # 机械损伤 } def convert_xml_to_yolo(xml_path, out_path): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in class_map: continue # 不在映射表里的类别直接跳过 cls_id = class_map[name] box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) # 中心点与宽高归一化 cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") with open(out_path, "w") as fp: fp.write("\n".join(lines)) def batch_convert(xml_root, label_root): os.makedirs(label_root, exist_ok=True) for xml_file in os.listdir(xml_root): if not xml_file.endswith(".xml"): continue xml_path = os.path.join(xml_root, xml_file) txt_name = xml_file.replace(".xml", ".txt") convert_xml_to_yolo(xml_path, os.path.join(label_root, txt_name)) # 使用示例 batch_convert("VOCdevkit/Annotations", "VOCdevkit/labels")逻辑说明:class_map里的键名必须是XML里<name>的实际值,值对应最终训练的类别ID,从0开始连续编号。转换公式先求角点的中心点,再分别除以图片宽高,结果保留6位小数足够训练精度要求。输出TXT与XML同名不同后缀,放在YOLO模型默认读取的labels目录。
参数说明:如果发现某类缺陷的标注框特别小,比如病斑只占图片的1%,建议转换后在YOLO训练里开启scale和mosaic增强,单靠原始尺寸很难学出稳定特征。不要用fliplr对辣椒做水平翻转,辣椒的弯曲方向是有物理意义的,翻转会制造错误样本。
3.3 转换后必须检查的四项边界
第一,class_map的类别顺序必须和后续data.yaml文件里的names一致,顺序错了模型不会报错,但指标全乱。第二,有些标注工具导出的bbox里,xmax/ymax用的是1.0到w+1的索引制,转换后会出现0.98以上的异常坐标,需要用上一章的检查脚本再跑一遍。第三,图片的宽和高如果被标注工具记反了,转换出来的框全部错位——最典型的是方图看不出问题,一旦到64:1的彩色图像上错得离谱。第四,XML里可能混着difficult=1的对象,训练时应按数据集标注说明过滤,否则会把模糊目标当正样本喂进去。
4. 用YOLOv8训练辣椒缺陷模型:参数选择与时间预算
4.1 为什么选YOLOv8而不是自己写检测网络
495或695这个量级,自己从头训练一个检测器,效果基本靠运气。工业界在这个量级下的常规做法是加载预训练权重做迁移学习。YOLOv8的neck和head设计得比较稳,对中小目标检测相对友好,而辣椒缺陷里的病斑和裂口恰恰属于小目标。另一个实际因素是生态:网上找资料、换设备部署、找人接手,YOLO的路径最短,踩坑也能更快被搜索引擎捞出来。
版本上,YOLOv8仍然是最稳的选择。可以一行命令装好:
pip install ultralytics4.2 训练前的最后一步:写对data.yaml并划分数据集
我收到过不少人的报错截图,训练命令本身没写错,错在data.yaml里把路径写成了绝对路径,换台机器就跑不了。建议全部用相对路径,并让YAML与数据集目录保持固定关系。文件内容如下:
# pepper.yaml path: datasets/pepper # 数据集根目录 train: images/train val: images/val nc: 5 names: 0: stem_defect 1: rot_spot 2: crack 3: deformity 4: mechanical_damage训练集与验证集划分建议按8:2,先洗牌再划分。这点在695张的规模下尤其重要:如果某类缺陷恰好集中出现在前几十张图里,按顺序划分会让验证集缺失整类目标,出来的mAP会虚高或暴跌。
import os, random from shutil import copy2 img_root = "datasets/pepper/images" train_dir = "datasets/pepper/images/train" val_dir = "datasets/pepper/images/val" os.makedirs(train_dir, exist_ok=True) os.makedirs(val_dir, exist_ok=True) imgs = [f for f in os.listdir(img_root) if f.endswith(".jpg")] random.seed(42) random.shuffle(imgs) split = int(len(imgs) * 0.8) for f in imgs[:split]: copy2(os.path.join(img_root, f), train_dir) for f in imgs[split:]: copy2(os.path.join(img_root, f), val_dir)代码说明:random.seed(42)固定随机种子,保证每次划分结果相同。TXT标签不需要复制,YOLO读取时会按图片同名自动在labels/目录下寻找。仅复制图片、不复制标签的做法省事,但要求标签目录结构必须按YOLO的默认约定组织好。
4.3 小数据集下最有价值的5个训练参数
695张图的训练,调参重点和几千张的通用数据集不一样。以下是这个项目我建议手工指定的参数,不要全部用默认值:
| 参数 | 推荐值 | 理由 |
|---|---|---|
epochs | 200 | 小数据集需要更多轮次收敛,配合早停机制防过拟合 |
patience | 30 | 30轮指标不更新就停,节省时间 |
batch | 16 | 太小容易让BN层抖动,16是显存和稳定性之间的平衡点 |
imgsz | 640 | 太小的图会丢失裂口这类细节特征 |
optimizer | SGD | 小数据集上SGD比AdamW更容易收敛到稳定区间 |
mosaic | 0.5~1.0 | 小目标增强利器,但最后10轮建议关掉 |
close_mosaic | 10 | 最后10轮关闭mosaic,让模型适应真实尺度的分布 |
训练命令按下面的写法执行:
yolo detect train \ data=pepper.yaml \ model=yolov8n.pt \ epochs=200 patience=30 batch=16 imgsz=640 \ optimizer=SGD lr0=0.01 \ mosaic=1.0 close_mosaic=10 \ project=runs/pepper命令说明:yolov8n.pt是预训练权重,会从Ultralytics官方源自动下载,网络环境受限时提前手动下载放到项目目录里。lr0初始学习率用了0.01而不是默认的0.02,因为数据量小、过拟合风险高,学习率保守一点更稳。project参数指定输出目录,训练过程中的权重、PR曲线、混淆矩阵都会写到里面。
时间预算上,695张图、640分辨率、batch=16,在单张V100上跑到200轮大约半小时以内;换成云上的T4,大约一小时左右。如果等不了,可以先用yolov8n.pt的nano版本跑通链路,再换s或m版本提精度。
5. 小数据集上YOLO训练避坑:5个高频问题与排查顺序
5.1 7z密码正确但解压一直报错
现象:用7-Zip图形界面输入密码直接解压,提示密码错误或文件损坏;在命令行里复制粘贴同一串密码又能解压成功。
原因:图形界面输入时,密码首尾可能被误加空格;更常见的是Windows中文环境下的编码差异,压缩时用了UTF-8密码,本地解压工具按ANSI解析,导致密码明明正确却报错。另一个坑是压缩包本身是分卷文件,只下载了第一卷就解压,当然会一直失败。
解决:优先用命令行解压。Windows下进入目录执行7z x pepper_dataset.7z -p你的密码,Linux同理。如果命令行能解、图形界面不能解,不用怀疑密码,直接用命令行即可。分卷包则把.7z.001, .7z.002全部下载到同一目录,再对第一份执行解压。
5.2 bbox全部错位但训练不报错
现象:训练loss正常下降,验证集mAP却极低,画出来的预测框全部偏在目标旁边。把标注可视化之后发现GT框整体往一个方向偏移。
原因:这种几乎都是VOC转换脚本里的坐标取值问题和图片缩放历史有关。比如XML里的width/height写得是原始尺寸,但实际图片被人用脚本统一缩放过,转换时没有同步改归一化分母。另一种可能是xmin/xmax取反,导致宽度是负数,YOLO训练时强制取绝对值,框就歪了。
解决:转完立刻做可视化。用ultralytics内置的yolo detect train前,先挑两张图跑一段代码把TXT画回图片上,确认框与缺陷位置对齐再训练。可视化脚本半小时内必写,能省下整晚的排错时间。
5.3 训练中Loss变成nan或标签越界
现象:训练跑到第几十轮,loss突然从1左右跳到nan,之后权重文件出现.nan后缀。
原因:最常见的是labels里存在坐标为0或超过1的坏行,模型前向计算时bbox宽高为0导致损失函数对0取对数。其次是学习率过高,小数据集在200轮之内容易出现。
解决:先跑一遍标签检查脚本,把坐标越界和标签ID越界的文件全部清出来;然后降低初始学习率到0.005左右;最后在训练命令中加上weight_decay=0.0005。这三步按顺序来,其中标签检查能解决八成问题。
5.4 BN层崩溃与梯度爆炸
现象:训练前几十轮loss正常下降,某轮之后验证集mAP跌回0.1以下,训练集loss却正常,权重里BN层的均值方差出现异常波动。
原因:小数据集加上batch=8这种过小的batch,BN层在批次间统计量剧烈抖动。数据增强强度过高也会放大这个问题,mosaic在缺失样本类别的情况下会让BN梯度方向来回拉扯。
解决:把batch提到16以上;如果显存不够就降低imgsz,不要用更小batch硬撑。优化器切到SGD,配合适当降低l0。BN不稳定的问题在迁移学习冷启动阶段尤其明显。
5.5 混淆矩阵各行之和不为1,类别读取不对
现象:训练结束后模型把缺陷类判成背景,混淆矩阵中某个类别的行或者列始终为零,或者最终指标里all和empty分类错误。
原因:YOLO的data.yaml中names列表和TXT标签中的class_id顺序不一致。这种情况在双格式数据里最常见:原来VOC标注的作者用了A顺序,转换脚本用了B顺序,训练时模型读到的ID映射全错位。
解决:不用猜,直接检查。训练完后查看runs/pepper/confusion_matrix.png,如果某一行对应了错误的类别名,说明映射顺序错了,回去改class_map重新转换,而不是改训练参数。记住一个原则:标签自检的时间一定短于返工训练的时间。
6. 模型验证与产线落地:混淆矩阵、切片测试与部署前检查
训练结束后,先别急着看mAP。695张的小样本,mAP容易被少数难样本带偏。我自己的验证顺序是:先看混淆矩阵是否对角线占优,再看PR曲线在Recall=0.8附近的形态,最后挑几张最难分的切片图做单张预测。
单张预测命令:
yolo predict model=runs/pepper/weights/best.pt source=hard_case.jpg conf=0.45 iou=0.5参数说明:conf置信度阈值在产线场景通常设0.45~0.55,低于自己可视化的0.25是两码事,产线漏检成本高推荐偏高。iou是NMS的IoU阈值,病斑密集粘连时降到0.4能保住相邻目标,但也会让同一目标被预测两次,需要微调。
部署前检查清单一般就三件事:导出ONNX并核对输入尺寸与训练一致、确认NMS后输出的类别ID仍与data.yaml对齐、在边缘设备上实测一帧推理耗时。导出命令:
yolo export model=runs/pepper/weights/best.pt format=onnx imgsz=640到这里,这条链路就完整了。我第一次做类似的辣椒缺陷项目时,吃过「标签自检不全 → 训练返工三小时」的亏,从那以后给自己定了条规矩:任何数据集到手,先花半小时做标签完整性检查,再花半小时做可视化确认,之后才允许轮到训练。这个习惯让我后来的项目几乎没再因为数据翻过车。数据量越小,这条规矩越值钱。希望帮到你。
本文还有配套的精品资源,点击获取