news 2026/9/10 15:36:22

1690张橘子数据集VOC+YOLO格式训练全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
1690张橘子数据集VOC+YOLO格式训练全流程解析

简介:一套面向目标检测任务的中文橘子数据集,主要服务于计算机视觉入门、YOLO或Pascal VOC格式的迁移学习,以及农业场景中的目标计数、成熟度检测等算法验证。压缩包内含jpg图片、VOC格式XML标注和YOLO格式TXT标注,全部由labelImg工具按统一规则绘制矩形框,类别仅为orange,累计标注框达6302个,标注样式准确一致。资源总数为2000个文件,以1699个XML与配套TXT文件为核心,整体压缩包约269.74MB;文件命名规则清晰,下载解压即可将图片与标注一一对应,无冗余分割路径干扰,便于直接拼接到现有训练流程。类别单一明确,非常适合快速验证单类别检测算法的改进效果。目前已有658人学习浏览,需要现成标注数据集来训练检测模型、练习格式转换的开发者,或需要配套示例进行教学演示的技术博主,都可直接参考使用。

1. 就拿橘子检测来看 1690 张 VOC+YOLO 双格式的训练链路

在果园巡检和分拣场景里,橘子是一种很典型的目标检测对象:叶子遮挡多、果实互相重叠、远近尺度变化大。标题里的“橘子数据集1690张VOC+YOLO格式”,本质是一份带标注的目标检测训练数据,标注同时给了两种格式。对大多数入手 YOLO 的人来说,这份数据的价值不只是“有图片”,而是可以直接进入“yolo训练自己的数据集”流程:转换、划分、训练、验收。1690 张属于中小规模,恰好适合做迁移学习和算法验证,既不会因为数据太少完全学不到特征,也不会因为数据太多而把调参问题掩盖掉。下面按拿到这份压缩包之后最常用的路径讲透:VOC 与 YOLO 标注的换算关系、怎么把 1690 张图切成训练集、参数怎么设、坑在哪里。

2. VOC 的 XML 与 YOLO 的 txt 坐标差在哪,为什么转换不只是一行代码

2.1 VOC 的 XML:像素坐标,且框是轴对齐矩形

VOC 格式在 MMDetection、各类目标检测教材以及历史项目里出现率最高。每张图片对应一个 XML,标注信息以 object 为单位循环出现。先看典型结构:

<annotation> <folder>images</folder> <filename>orange_0001.jpg</filename> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <object> <name>orange</name> <bndbox> <xmin>120</xmin> <ymin>80</ymin> <xmax>200</xmax> <ymax>150</ymax> </bndbox> </object> <object> <name>leaf</name> <bndbox> <xmin>300</xmin> <ymin>200</ymin> <xmax>360</xmax> <ymax>240</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(xmin+xmax)/2 / width160/640=0.250000
center_y(ymin+ymax)/2 / height115/480=0.239583
box_w(xmax-xmin) / width80/640=0.125000
box_h(ymax-ymin) / height70/480=0.145833

注意类别第一列是索引而不是字符串。同样的目标,假如 classes.txt 把 leaf 放在第一行,这个 txt 的第一列就应该写成 1。也就是说,一份 YOLO 标签脱离了 classes.txt 内容就没有任何意义,这是 VOC 转 YOLO 时最容易出错的地方,也是为什么“转换”这件事必须写进流程而不只是批量改后缀。

2.3 选哪套格式训练:看框架,不看个人偏好

训练框架决定格式,不取决于你习惯看 XML 还是 txt。用 Ultralytics YOLO(yolov5/yolov8/yolo11)训练,默认吃 YOLO txt;用 MMDetection 或部分检测竞赛脚本,VOC XML 反而更方便。拿到双格式数据集后,第一步不是“选一个喜欢的格式”,而是确认底层训练代码加载标注的入口。以最常见的做法为例:Ultralytics 需要目录按 images/ 和 labels/ 分好,labels 内每张图一个同名 txt;MMDetection 的 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', encoding='utf-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.yaml:

path: /path/to/orange_dataset train: images/train val: images/val test: images/test nc: 1 names: 0: orange

然后跑最小训练验证链路是否通:

yolo detect train \ data=data.yaml \ model=yolov8n.pt \ epochs=50 \ imgsz=640 \ batch=16 \ patience=20

能正常出权重文件,说明 yolo 数据集划分、yolo 训练这条链路已经通了。50 轮只用于链路验证,真要出可用模型,参数要按下一章的思路调整。

4. 小数据集训练 YOLO 的 6 个关键参数,以及过拟合怎么判断

4.1 参数表与推荐量级

1690 张图在目标检测里属于小规模数据集。这个量级下,调参起的作用比换骨干网络更明显。以下是我在类似数据集上常用的起点:

参数推荐值说明
modelyolov8n 或 yolov8s小数据集不适合大模型,见 4.2
epochs120-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 \ model=runs/detect/train/weights/best.pt \ source=images/test \ conf=0.3 \ iou=0.5 \ save=True

conf 低于 0.3 会输出大量低置信度框,适合做漏检分析;测试集上做定量指标就调到 0.5 以上。iou=0.5 控制 NMS 合并阈值,两框重叠面积超过 0.5 认为是一个目标,橘子在树上遮挡严重时,这个值调到 0.4 会更激进一些。

要出混淆矩阵和 F1 曲线,用 val 命令并指定 split:

yolo val \ model=runs/detect/train/weights/best.pt \ data=data.yaml \ split=test \ plot=True

plot=True 会生成 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 的类别名和坐标,肉眼对比一遍即可。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 15:33:56

Telegram Bot API中间件开发终极指南:扩展机器人功能的10个技巧

Telegram Bot API中间件开发终极指南&#xff1a;扩展机器人功能的10个技巧 Telegram Bot API中间件是扩展机器人功能的强大工具&#xff0c;让开发者能够轻松实现消息处理、用户认证、日志记录等核心功能。在当今即时通讯应用蓬勃发展的时代&#xff0c;掌握Telegram Bot中间…

作者头像 李华