
简介一套面向目标检测任务的中文橘子数据集主要服务于计算机视觉入门、YOLO或Pascal VOC格式的迁移学习以及农业场景中的目标计数、成熟度检测等算法验证。压缩包内含jpg图片、VOC格式XML标注和YOLO格式TXT标注全部由labelImg工具按统一规则绘制矩形框类别仅为orange累计标注框达6302个标注样式准确一致。资源总数为2000个文件以1699个XML与配套TXT文件为核心整体压缩包约269.74MB文件命名规则清晰下载解压即可将图片与标注一一对应无冗余分割路径干扰便于直接拼接到现有训练流程。类别单一明确非常适合快速验证单类别检测算法的改进效果。目前已有658人学习浏览需要现成标注数据集来训练检测模型、练习格式转换的开发者或需要配套示例进行教学演示的技术博主都可直接参考使用。1. 就拿橘子检测来看 1690 张 VOCYOLO 双格式的训练链路在果园巡检和分拣场景里橘子是一种很典型的目标检测对象叶子遮挡多、果实互相重叠、远近尺度变化大。标题里的“橘子数据集1690张VOCYOLO格式”本质是一份带标注的目标检测训练数据标注同时给了两种格式。对大多数入手 YOLO 的人来说这份数据的价值不只是“有图片”而是可以直接进入“yolo训练自己的数据集”流程转换、划分、训练、验收。1690 张属于中小规模恰好适合做迁移学习和算法验证既不会因为数据太少完全学不到特征也不会因为数据太多而把调参问题掩盖掉。下面按拿到这份压缩包之后最常用的路径讲透VOC 与 YOLO 标注的换算关系、怎么把 1690 张图切成训练集、参数怎么设、坑在哪里。2. VOC 的 XML 与 YOLO 的 txt 坐标差在哪为什么转换不只是一行代码2.1 VOC 的 XML像素坐标且框是轴对齐矩形VOC 格式在 MMDetection、各类目标检测教材以及历史项目里出现率最高。每张图片对应一个 XML标注信息以 object 为单位循环出现。先看典型结构annotation folderimages/folder filenameorange_0001.jpg/filename size width640/width height480/height depth3/depth /size object nameorange/name bndbox xmin120/xmin ymin80/ymin xmax200/xmax ymax150/ymax /bndbox /object object nameleaf/name bndbox xmin300/xmin ymin200/ymin xmax360/xmax ymax240/ymax /bndbox /object /annotation这段 XML 里有三个信息点容易被忽略。第一size 节点里存的是原图宽高转换时必须以它作为分母而不是以你定义的图片边长。第二bndbox 只有 xmin/ymin/xmax/ymax 四个值表达的是“轴对齐矩形”。如果原始标注来自旋转框或分割掩码的外接框VOC 格式里已经丢失了角度信息。有人问“yolo切割只能切矩形图片吗”在标注层面同样存在这个问题倾斜的橘子串强转成 bndbox 后框内含大量背景后续训练时相当于给背景也打了正样本。第三XML 里的 name 是字符串而 YOLO 标签里没有字符串位置所有类别信息都靠一个序号承载这个差异是后面所有转换事故的源头。2.2 YOLO 的 txt归一化中心点宽高类别是序号YOLO 的标签是纯文本每行对应一个目标对象五个数字由空格隔开。以刚才那个橘子框为例若 classes.txt 内容为orange leaf则 XML 中的 orange 目标应该写成0 0.250000 0.239583 0.125000 0.145833换算关系如下YOLO 字段公式示例值center_x(xminxmax)/2 / width160/6400.250000center_y(yminymax)/2 / height115/4800.239583box_w(xmax-xmin) / width80/6400.125000box_h(ymax-ymin) / height70/4800.145833注意类别第一列是索引而不是字符串。同样的目标假如 classes.txt 把 leaf 放在第一行这个 txt 的第一列就应该写成 1。也就是说一份 YOLO 标签脱离了 classes.txt 内容就没有任何意义这是 VOC 转 YOLO 时最容易出错的地方也是为什么“转换”这件事必须写进流程而不只是批量改后缀。2.3 选哪套格式训练看框架不看个人偏好训练框架决定格式不取决于你习惯看 XML 还是 txt。用 Ultralytics YOLOyolov5/yolov8/yolo11训练默认吃 YOLO txt用 MMDetection 或部分检测竞赛脚本VOC XML 反而更方便。拿到双格式数据集后第一步不是“选一个喜欢的格式”而是确认底层训练代码加载标注的入口。以最常见的做法为例Ultralytics 需要目录按 images/ 和 labels/ 分好labels 内每张图一个同名 txtMMDetection 的 VOC 数据集则需要遵守 VOC 的 ImageSets/Main 索引文件。正因为框架要求不同“VOC 转 YOLO”不是一次后缀名替换而是一次坐标系换算加文件结构调整。下一章给一份可以直接跑的转换与划分代码。3. 把 1690 张 VOC 转成 YOLO转换、划分与最小训练命令3.1 转换前先检查标注完整性的三个点动手前花五分钟做三个检查能省掉后面排错的时间。第一XML 数量与图片数量是否一一对应有的数据集里存在只有图没有标注的负样本转换脚本里要跳过或生成空 txt。第二每个 XML 的 size 宽高是否与图片真实宽高一致不一致的框转换后会整体偏移。第三name 字段去重后是否与数据集说明里的类别一致。比如橘子数据集里如果混了 leaf 这类标注而项目只想检测果实转换时可以直接把非目标类别过滤掉减少对精度的影响。这三步很多人直接跳过后果是训练时类别数对不上或者标签错位报错信息又不会直接指向标注排查成本很高。3.2 VOC 转 YOLO 的 Python 脚本与逐行说明import os import xml.etree.ElementTree as ET CLASSES [orange] # 与训练时的 data.yaml 保持一致 def voc2yolo(xml_path, out_txt_path): tree ET.parse(xml_path) root tree.getroot() # 宽高必须从 XML 里读不能写死 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASSES: # 过滤非目标类别 continue cls_id CLASSES.index(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) # 坐标容错某些标注工具会把 xmax 写反 xmin, xmax min(xmin, xmax), max(xmin, xmax) ymin, ymax min(ymin, ymax), max(ymin, ymax) # 归一化中心点与宽高 cx ((xmin xmax) / 2.0) / img_w cy ((ymin ymax) / 2.0) / img_h bw (xmax - xmin) / img_w bh (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(out_txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) # 遍历 images/ 目录为每张图生成同名标签 for img_file in os.listdir(images): if not img_file.endswith(.jpg): continue stem os.path.splitext(img_file)[0] xml_path os.path.join(Annotations, stem .xml) if not os.path.exists(xml_path): # 无标注图片生成空 txt等价于负样本 open(os.path.join(labels, stem .txt), w).close() continue voc2yolo(xml_path, os.path.join(labels, stem .txt))这段代码里三个细节值得说明。第一取宽高用root.find(size)而不是直接假设 640×480因为 1690 张图里如果混入不同分辨率写死会让坐标全部错位。第二每个框都取一次 min/max 做容错VOC 标注的 xmin xmax 只是约定标注工具导出异常时会出现前大后小直接相减会让归一化宽高出现负值训练时被 Ultralytics 当成非法目标跳过。第三无标注图片生成空 txt 而不是跳过这是训练框架对“有图无标”图片的标准处理方式空文件被当作负样本参与背景学习比跳过更合理。3.3 划分 train/val/test 并配置 data.yaml 跑通最小训练转换完成后按目录结构整理orange_dataset/ ├── images/ │ ├── train/ # 1352 张 │ ├── val/ # 254 张 │ └── test/ # 84 张 ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml划分脚本随手可以写关键是乱序加固定种子import os import random random.seed(42) # 固定种子保证每次划分结果一致 images [f for f in os.listdir(images) if f.endswith(.jpg)] random.shuffle(images) n len(images) train_end int(n * 0.8) val_end int(n * 0.95) train images[:train_end] val images[train_end:val_end] test images[val_end:]seed 固定下来后后续复现、对比模型都会省事。1690 张按 8:1:1 切大约是 1352 张训练、254 张验证、84 张测试。切分时注意一点如果是连拍或者同一场景多角度拍摄的图片需要按场景分组再做随机否则相似度极高的帧会同时进 train 和 val让验证集指标虚高。严格的做法是先对文件名前缀分组再按组划分果园类数据经常有这个特征。数据配置 data.yamlpath: /path/to/orange_dataset train: images/train val: images/val test: images/test nc: 1 names: 0: orange然后跑最小训练验证链路是否通yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs50 \ imgsz640 \ batch16 \ patience20能正常出权重文件说明 yolo 数据集划分、yolo 训练这条链路已经通了。50 轮只用于链路验证真要出可用模型参数要按下一章的思路调整。4. 小数据集训练 YOLO 的 6 个关键参数以及过拟合怎么判断4.1 参数表与推荐量级1690 张图在目标检测里属于小规模数据集。这个量级下调参起的作用比换骨干网络更明显。以下是我在类似数据集上常用的起点参数推荐值说明modelyolov8n 或 yolov8s小数据集不适合大模型见 4.2epochs120-200配合早停不是越大越好imgsz640与原图比例一致避免拉变形batch16 或 32显存允许就取大值梯度更平滑patience30验证损失连续 30 轮不降就早停mosaic0.5或关闭小目标多时 mosaic 会切碎目标optimizerAdamW 或 SGD小数据量下主要看损失曲线走势batch 和 imgsz 是优先要定的两个参数。imgsz 决定输入分辨率橘子远处的小果实可能在原图里只有 20×20 像素强行缩到 640 以下时目标直接消失这时候宁可 batch 小一点也要用 960 输入。batch 决定每个 step 看到的样本数同样显存下小模型可以开更大的 batch梯度方向更平滑训练更好收敛。4.2 为什么不要一上来就选 YOLOv8x很多人拿到数据集习惯“模型越大越准”。在 1690 张数据上YOLOv8x 的参数量是 n 的十倍以上在少量数据和有限迭代下很容易把训练集背下来验证集效果反而不如模型小的版本。常见做法是用 yolov8n 先跑通确认损失下降趋势正常再升级到 s。迁移学习场景里预训练权重的质量比模型大小更关键同样的数据用 n 和 s 差距可能只在 2-3 个点以内而用 x 可能直接过拟合。另一个相关点是标签质量。橘子这种目标果实和小叶子面积接近、纹理又相似标注时很容易漏标远处的小果子。漏标会造成学习矛盾同一个位置有些样本标正、有些样本标负。如果 val 分数一直上不去先检查标注边界而不是急着改网络结构。训练前可以统计框宽高分布框太小比如小于 32×32 的数量占三成以上时优先提升输入分辨率而不是堆模型容量。4.3 yolo损失函数曲线和早停怎么配合Ultralytics 训练日志里会同时输出 box_loss、cls_loss 和 dfl_loss。训练时看两个关键信号一是训练损失持续下降、验证损失下降一段后掉头向上这是过拟合的直接特征二是验证损失不太平滑、一直在震荡常见原因是 batch 太小或者 mosaic 增强强度太高。应对手段按顺序做调大 batch、关掉 mosaic、降低学习率三件事分别对应梯度信息量、增强噪声幅度和收敛步长。早停参数 patience 控制验证损失连续多少轮不下降就停止训练。在 1690 张数据上 patience 建议设 20-30而不是默认的 100。数据量小训练后期每个 epoch 的提升已经很小多跑 100 轮纯属浪费算力。最后选模型也参考验证集Ultralytics 会自动保存 best.pt验证结果最好和 last.pt最后一轮后续推理务必用 best.pt不要因为 last.pt 训练轮次多就选它。5. 用 predict 可视化与混淆矩阵验收两个容易翻车的误用5.1 predict 与 val 命令快速验收训练结束后先用 best.pt 在测试集上跑一遍可视化看的是“框到底画在哪”yolo predict \ modelruns/detect/train/weights/best.pt \ sourceimages/test \ conf0.3 \ iou0.5 \ saveTrueconf 低于 0.3 会输出大量低置信度框适合做漏检分析测试集上做定量指标就调到 0.5 以上。iou0.5 控制 NMS 合并阈值两框重叠面积超过 0.5 认为是一个目标橘子在树上遮挡严重时这个值调到 0.4 会更激进一些。要出混淆矩阵和 F1 曲线用 val 命令并指定 splityolo val \ modelruns/detect/train/weights/best.pt \ datadata.yaml \ splittest \ plotTrueplotTrue 会生成 confusion_matrix.png 和 F1_curve.png。单类别数据集混淆矩阵比较简单重点看对角线之外还有多少背景误检框落在背景上的比例高意味着模型把背景纹理也学了常见原因是负样本不足或者框给得太松。5.2 两个容易翻车的误用第一个误用是划分完数据集后又手动往 train 里补图片补进去的图可能已经在 val 或 test 中。常见原因是原始 1690 张图外还有缓存目录或重复下载的副本。修复方法是重新按文件名生成校验清单用脚本对比 train/val/test 三个集合而不是人眼检查。第二个误用更隐蔽classes.txt 的类别顺序与训练时 data.yaml 的 names 不一致。听起来很简单但实际项目里经常先转了一版标签后来发现数据集说明里还有第二个类别直接往 CLASSES 列表前面加名字所有已生成 txt 的类别编号就整体偏移了。正确的做法是一旦类别列表确定重新执行一次 3.2 的转换脚本再训练或者在代码里保留“旧类别名到新序号”的映射表而不是靠记忆。检查方法也直接随机抽 5 张测试图打印 XML 与对应 YOLO txt 的类别名和坐标肉眼对比一遍即可。本文还有配套的精品资源点击获取