简介:这是一份面向计算机视觉初学者与目标检测开发者的橘子识别数据集,适用于水果分拣、农业采摘机器人、零售结算等场景下的模型训练与算法验证。数据集采用Pascal VOC与YOLO双格式标注,包含1699张jpg图片,并配套1699个VOC格式xml文件与1699个YOLO格式txt文件,标注类别仅orange一类,共6302个矩形框,全部由labelImg工具人工绘制,标注准确合理。压缩包为7z格式,共2000个文件,其中xml与txt标注文件占主体,整体约269.74MB,目录结构清晰,可直接用于YOLO系列或VOC接口的训练流程。目前已有661人学习下载,适合需要快速获取单类别检测数据、验证数据增强与训练管线的读者参考使用。需注意,该数据集不对训练所得模型或权重文件的精度作任何保证。
1. 橘子数据集1690张VOC+YOLO格式:从拿到压缩包到跑通第一轮训练
你从某个渠道拿到一个名为「橘子数据集1690张VOC+YOLO格式」的压缩包,解压后大概率会看到两个并列的目录:一个装着 JPEG 图片和同名 XML 标注,另一个装着 images 和 labels 的 txt 文件。很多人第一反应是「直接丢进 YOLO 训练脚本」,结果要么路径对不上,要么类别号错位,要么验证集里一张橘子都没有。这个数据集真正解决的问题,是给目标检测任务提供一个规模适中、双格式对齐的柑橘类果实样本集合,1690 张的体量既不会让新手在 CPU 上等到崩溃,也足够让熟手验证一个改进模块是否真的涨点。它适合三类人:想跑通 YOLO 全流程的入门者、需要做农业视觉检测原型的开发者、以及拿它当小样本基线来对比自己模型的研究者。VOC 和 YOLO 两种格式同时存在,意味着你既能用 XML 做数据清洗和可视化,也能直接喂给 YOLO 训练器,省掉一次格式转换的折腾。
2. 拆开压缩包:VOC 与 YOLO 两套目录到底怎么对应
2.1 VOC 格式的目录结构与字段含义
VOC 格式的核心是每张图片配一个同名 XML,里面记录了图片尺寸、目标类别和边界框的左上角与右下角坐标。一个标准的 VOC 目录通常长这样:
橘子数据集/ ├── JPEGImages/ # 所有 jpg 图片 ├── Annotations/ # 与图片同名的 xml 标注 ├── ImageSets/ │ └── Main/ # train.txt / val.txt / trainval.txt └── SegmentationClass/ # 本数据集一般没有,可忽略XML 里真正影响训练的是这几个字段:size下的width和height决定坐标归一化的分母,object下的name是类别字符串,bndbox下的xmin/ymin/xmax/ymax是绝对像素坐标。很多翻车现场就出在xmax等于width或ymin大于ymax,这种框在 YOLO 里会直接产生负宽高,训练时 loss 变 NaN。
2.2 YOLO 格式的 txt 行与归一化规则
YOLO 格式每张图对应一个 txt,每行五个数:class_id x_center y_center width height,全部是相对于图片宽高的归一化值,范围 0 到 1。注意这里的 width 和 height 是框的宽高,不是右下角坐标。转换公式是:
x_center = (xmin + xmax) / 2 / img_w y_center = (ymin + ymax) / 2 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h如果数据集里只有橘子一个类别,class_id 就是 0;如果有青橘、熟橘、烂橘,就要先确认类别映射表,别想当然按字母序排。
2.3 用脚本核对两套格式是否一一对应
拿到数据集先别急着训练,跑一段核对脚本,确认图片数、XML 数、txt 数三者一致,且没有空标注文件。
import os from pathlib import Path root = Path("橘子数据集") img_dir = root / "JPEGImages" xml_dir = root / "Annotations" lbl_dir = root / "labels" imgs = {p.stem for p in img_dir.glob("*.jpg")} xmls = {p.stem for p in xml_dir.glob("*.xml")} lbls = {p.stem for p in lbl_dir.glob("*.txt")} print("图片数:", len(imgs)) print("XML数:", len(xmls)) print("txt数:", len(lbls)) print("图片有但XML缺失:", imgs - xmls) print("XML有但图片缺失:", xmls - imgs) print("图片有但txt缺失:", imgs - lbls) # 检查空标注 empty = [p.name for p in lbl_dir.glob("*.txt") if p.stat().st_size == 0] print("空标注文件:", empty[:10], "共", len(empty))这段脚本的逻辑很直白:用文件名主干做集合运算,差集就是缺失项。参数上唯一要注意的是图片扩展名,有的数据集混用.jpg和.JPG,在 Linux 下大小写敏感,glob("*.jpg")会漏掉大写。如果发现空标注,要么是负样本,要么是标注漏了,负样本可以保留但要在训练配置里允许,漏标的必须补。
3. 从 VOC 转 YOLO:转换脚本与四个边界坑
3.1 转换脚本的完整实现
虽然标题说数据集已经带了 YOLO 格式,但实际拿到的包经常只有 VOC 是完整的,YOLO 目录可能是空的或者类别号不对。自己写一遍转换脚本,比盲目信任压缩包靠谱。
import xml.etree.ElementTree as ET from pathlib import Path from PIL import Image classes = ["orange"] # 按你的实际类别顺序改 cls2id = {c: i for i, c in enumerate(classes)} def convert(xml_path, img_path, out_path): tree = ET.parse(xml_path) root = tree.getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) # 用实际图片尺寸兜底,防止 XML 里的 size 写错 with Image.open(img_path) as im: real_w, real_h = im.size if (w, h) != (real_w, real_h): w, h = real_w, real_h lines = [] for obj in root.iter("object"): name = obj.find("name").text.strip() if name not in cls2id: continue bnd = obj.find("bndbox") xmin = float(bnd.find("xmin").text) ymin = float(bnd.find("ymin").text) xmax = float(bnd.find("xmax").text) ymax = float(bnd.find("ymax").text) # 裁剪到图片范围内 xmin, xmax = max(0, xmin), min(w, xmax) ymin, ymax = max(0, ymin), min(h, ymax) if xmax <= xmin or ymax <= ymin: continue xc = (xmin + xmax) / 2 / w yc = (ymin + ymax) / 2 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls2id[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}") out_path.write_text("\n".join(lines), encoding="utf-8") img_dir = Path("橘子数据集/JPEGImages") xml_dir = Path("橘子数据集/Annotations") out_dir = Path("橘子数据集/labels") out_dir.mkdir(exist_ok=True) for xml_path in xml_dir.glob("*.xml"): stem = xml_path.stem img_path = img_dir / f"{stem}.jpg" if not img_path.exists(): continue convert(xml_path, img_path, out_dir / f"{stem}.txt")逻辑说明:先读 XML 的 size,再用 PIL 读真实尺寸做校验,两者不一致时以真实图片为准,这是防止坐标越界的第一道保险。坐标裁剪到[0, w]和[0, h]是第二道保险。宽高小于等于零的框直接跳过,避免产生 NaN。归一化保留六位小数,足够 YOLO 使用。
参数说明:classes列表的顺序决定 class_id,必须和训练时的data.yaml里names顺序完全一致。:.6f是格式化精度,改成.4f也能跑,但小目标框会有精度损失。
3.2 边界坑一:坐标越界与零宽高框
现象是训练几个 epoch 后 loss 突然变 NaN。原因通常是某个框的xmax等于xmin,或者xmax超过了图片宽度导致归一化后 width 大于 1。YOLO 的损失函数里对宽高取 log,零宽高直接产生负无穷。解决办法就是在转换阶段做裁剪和过滤,上面脚本已经处理。如果你拿到的是现成 YOLO 格式,也要写个校验脚本扫一遍:
bad = [] for p in Path("橘子数据集/labels").glob("*.txt"): for i, line in enumerate(p.read_text().splitlines()): parts = line.split() if len(parts) != 5: bad.append((p.name, i, "字段数不对")) continue _, xc, yc, bw, bh = map(float, parts) if not (0 <= xc <= 1 and 0 <= yc <= 1 and 0 < bw <= 1 and 0 < bh <= 1): bad.append((p.name, i, line)) print("异常行数:", len(bad))3.3 边界坑二:类别名大小写与空格
VOC 的name字段里可能出现Orange、orange(带尾空格)、orange_ripe等变体。如果不做strip()和统一映射,转换后会出现同一类被拆成多个 class_id,或者本该保留的类被continue跳过。我一般会在转换前先统计所有出现的 name:
from collections import Counter names = Counter() for xml_path in Path("橘子数据集/Annotations").glob("*.xml"): root = ET.parse(xml_path).getroot() for obj in root.iter("object"): names[obj.find("name").text] += 1 print(names)看到统计结果再决定classes列表,比拍脑袋写靠谱。
3.4 边界坑三:训练集与验证集划分泄漏
1690 张如果按 8:2 随机划分,同一棵树上拍的连续帧可能同时进训练和验证,导致验证指标虚高。农业数据集常见的问题是同一场景多张连拍,划分时要按图片文件名前缀或拍摄批次分组,保证同一组只出现在一个集合里。简单做法是按文件名排序后每隔 5 张取 1 张做验证,而不是用random.shuffle。
import random stems = sorted(p.stem for p in Path("橘子数据集/JPEGImages").glob("*.jpg")) val = stems[::5] train = [s for s in stems if s not in set(val)] Path("train.txt").write_text("\n".join(f"images/{s}.jpg" for s in train)) Path("val.txt").write_text("\n".join(f"images/{s}.jpg" for s in val))3.5 边界坑四:data.yaml 路径与类别数不匹配
YOLO 训练报AssertionError: nc != len(names)或者找不到图片,基本都是data.yaml写错。一个可用的配置长这样:
path: /home/user/橘子数据集 train: train.txt val: val.txt nc: 1 names: ["orange"]path是数据集根目录,train和val是相对于path的列表文件。列表文件里写的是相对path的图片路径。nc必须等于names长度,也必须等于转换时classes的长度。三者不一致,训练要么直接报错,要么静默把类别学乱。
4. 用 YOLO 训练橘子检测模型:配置、命令与参数怎么调
4.1 环境与预训练权重选择
训练前先确认环境:Python 3.8 以上,PyTorch 与 CUDA 版本匹配,ultralytics 包能正常 import。预训练权重用官方发布的 YOLO 系列权重即可,新手从 yolov8n 或 yolov5s 这种小模型起步,1690 张数据量下小模型收敛快、过拟合风险低。如果你要做模型改进对比,固定同一个预训练权重和同一组超参,只改网络结构,否则涨点来源说不清。
4.2 最小训练命令与每个参数的含义
yolo detect train \ data=/home/user/橘子数据集/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/orange \ name=baselinedata指向配置文件,model是预训练权重,epochs是最大轮数,imgsz是输入分辨率,batch是批大小,lr0是初始学习率,patience是早停耐心值,project和name决定输出目录。1690 张在单张 12G 显存的卡上,batch=16和imgsz=640通常能跑,显存不够就降到 8 或把imgsz降到 512。lr0=0.01是 YOLO 的常用默认值,如果你换了更大的模型或者做了注意力改进,可以降到 0.001 观察前 10 个 epoch 的 loss 曲线。
4.3 训练中该盯哪些指标
训练日志里重点看三列:box_loss、cls_loss、mAP50。box_loss持续下降说明框回归正常,如果它震荡或者上升,检查标注质量。cls_loss在单类别任务里应该很快降到接近零,降不下去说明类别号有噪声。mAP50是验证集上的主指标,1690 张单类橘子,正常收敛后能到 0.85 以上;如果卡在 0.3 不动,八成是验证集划分泄漏或者标注框大面积偏移。混淆矩阵在单类任务里只有一个格子,看的是漏检和误检比例,别被「总合不唯一」这种说法绕进去,单类就是对角线一个数。
4.4 训练完成后的推理验证
yolo detect predict \ model=runs/orange/baseline/weights/best.pt \ source=/home/user/橘子数据集/JPEGImages \ conf=0.25 \ save=Trueconf=0.25是置信度阈值,低于它的框不显示。验证时挑几张有遮挡、有逆光、有青橘混熟的图,看框是否贴合。如果框普遍偏大,可能是训练时imgsz和推理时不一致;如果漏检小橘子,把imgsz提到 960 再训一轮,或者检查标注里小目标框是否被过滤掉了。
5. 避坑与排查:橘子数据集训练中最容易翻车的五件事
5.1 现象:训练 loss 正常但 mAP 始终为 0
原因:验证集列表文件里的路径写错,YOLO 找不到验证图片,或者验证集图片全部没有对应标注。解决:打开val.txt逐行核对路径,用ls确认文件存在;再检查验证集对应的 labels 目录是否有非空 txt。
5.2 现象:训练到一半报 CUDA out of memory
原因:batch或imgsz超过显存,或者workers开太多导致内存碎片。解决:把batch减半,imgsz降到 512,workers设为 4 或 8。如果还不行,用yolo detect train ... amp=False关闭混合精度,显存会省一点但速度变慢。
5.3 现象:推理时框全图乱飞,置信度很低
原因:类别数不匹配,模型输出的通道数和data.yaml的nc不一致,或者加载了错误的权重。解决:确认nc和names长度一致,确认best.pt是本次训练产出的,不要拿别的数据集的权重来推理。
5.4 现象:同一张图重复检测出多个橘子框
原因:NMS 的iou阈值设得太高,重叠框没被抑制。解决:推理时加iou=0.45,训练时检查标注里是否有重复框。VOC 转 YOLO 时如果同一个 object 被写了两次,就会产生重复标注。
5.5 现象:验证集指标很高但实际部署效果差
原因:训练集和验证集来自同一批连拍,分布太接近。解决:重新按拍摄批次划分,或者留出一个完全独立的场景做测试集。1690 张如果全是同一果园同一角度,模型换到另一个果园就会掉点,这是数据集的边界,不是模型的问题。
6. 把 1690 张用透:小数据集的增强策略与基线对比技巧
1690 张在目标检测里属于小数据集,直接训容易过拟合,但也不是只能靠堆数据。我一般会做三件事:第一,开启 YOLO 内置的增强,mosaic=1.0、mixup=0.1、hsv_h=0.015、hsv_s=0.7、hsv_v=0.4,这些参数在data.yaml同级传进去或者写在训练命令里。第二,把close_mosaic设为最后 10 个 epoch 关闭 mosaic,让模型在真实分布上收尾。第三,如果要做模型改进对比,固定随机种子seed=42,跑三次取平均,单次涨点 0.5 个 mAP 说明不了问题。
yolo detect train \ data=/home/user/橘子数据集/data.yaml \ model=yolov8n.pt \ epochs=150 \ imgsz=640 \ batch=16 \ mosaic=1.0 \ mixup=0.1 \ close_mosaic=10 \ seed=42 \ project=runs/orange \ name=aug增强参数不是越大越好。mixup超过 0.2 在单类任务里会让框变得模糊,hsv_v超过 0.5 会让暗光橘子过曝。我习惯先跑一轮默认增强做基线,再逐项加,每次只改一个参数,看验证集 mAP 的变化方向。
还有一个容易被忽略的点:把 VOC 的 XML 留着别删。当你发现某些验证集误检时,可以回到 XML 里查原始标注,确认是标注错了还是模型错了。YOLO 格式的 txt 没有图片尺寸信息,单独看 txt 无法还原框的绝对位置,XML 是唯一的后悔药。
最后说个习惯:每次训练完,把data.yaml、训练命令、results.csv和best.pt的路径记在一个run_notes.md里。1690 张的数据集你可能要跑十几轮对比,没有记录,两周后根本想不起来哪组参数对应哪个结果。这个习惯帮我省过很多重复劳动,希望帮到你。
本文还有配套的精品资源,点击获取