YOLO树叶分类检测数据集:1000张图+VOC/COCO/YOLO标签+划分脚本+训练教程
2026/9/23 12:18:12 网站建设 项目流程

简介:这是一份面向目标检测初学者与算法工程师的YOLO树叶分类检测数据集,针对真实场景下树叶类别识别任务,解决自建数据集标注成本高、格式转换繁琐的问题。资源包共2000个文件,约27.93MB,包含1000张高质量实拍图片,以及voc(xml)、coco(json)和yolo(txt)三种格式标签,分别存放于不同文件夹,可直接接入YOLO系列模型训练;另附yaml配置文件、数据集划分脚本与训练教程文档,方便按需切分训练集、验证集和测试集。已有470人学习下载,适合课程设计、科研实验与竞赛练手。读者可获得一套开箱即用的标注数据、多格式标签转换参考、环境搭建与训练案例说明,以及可复用的划分脚本,帮助快速跑通从数据准备到模型训练的全流程。

1. 树叶分类检测数据集到底解决什么问题:从 1000 张图到三种标签格式

拿到「YOLO树叶分类目标检测数据集(含1000张图片)+对应voc、coco和yolo三种格式标签+划分脚本+训练教程」这个标题,很多人第一反应是「又一个数据集包」。但真正做过树叶识别项目的人知道,难点从来不是模型结构,而是数据这一层:树叶类别细、类间差异小、背景杂乱、标注框容易画歪,最后训练出来的模型在验证集上 mAP 看着还行,一到真实场景就集体翻车。这个数据集的价值在于它把「图片 + 三种格式标签 + 划分脚本 + 训练教程」打包成一条完整链路,省掉了从零标注和格式转换的时间。

它适合三类人:一是刚接触 YOLO 目标检测、想找一个类别明确的小数据集跑通全流程的新手;二是做植物识别、林业调查、农业病虫害相关项目,需要树叶检测作为前置模块的从业者;三是想验证自己改进的 YOLO 结构(比如小目标检测、注意力机制)在细粒度类别上是否有效的研究者。1000 张图不算多,但足够跑通一次完整的训练-验证-推理闭环,也足够暴露数据层面的坑。下面按「数据长什么样 → 怎么转格式 → 怎么划分 → 怎么训练 → 怎么避坑」的顺序拆开讲。

2. 三种标签格式的差异与转换:VOC、COCO、YOLO 到底该用哪个

2.1 三种格式的字段结构和适用场景

VOC 格式是 XML 文件,每个图片对应一个 XML,里面用<object>标签记录类别名和xmin/ymin/xmax/ymax四个坐标。它的优点是可读性强,LabelImg 默认就输出这个格式,人工检查标注质量时最方便。缺点是文件数量翻倍,1000 张图就是 1000 个 XML,批量处理时 IO 开销大。

COCO 格式是一个大的 JSON 文件,所有图片、标注、类别都塞在一个annotations.json里。它的bbox[x, y, width, height],注意是左上角坐标加宽高,不是右下角坐标。COCO 适合做多任务(检测+分割+关键点),也是很多预训练模型的默认输入格式,但单文件体积大,改一个标注要重写整个 JSON。

YOLO 格式是每张图对应一个.txt,每行是class_id x_center y_center width height,全部归一化到 0~1。它最轻量,训练时读取最快,但可读性差,类别用数字索引表示,必须配合classes.txtdata.yaml才能知道数字对应什么树叶。

格式文件形态坐标表示优点缺点
VOC每图一个 XMLxmin/ymin/xmax/ymax可读性好,工具支持广文件多,IO 慢
COCO单个 JSONx/y/width/height多任务统一,生态好单文件大,改标注麻烦
YOLO每图一个 txt归一化中心点+宽高轻量,训练读取快可读性差,需类别映射

2.2 用脚本在三种格式之间转换

实际项目里最常见的需求是:标注工具输出 VOC,但训练要用 YOLO,中间可能还要转一份 COCO 给别的模型用。下面这个脚本把 VOC 转成 YOLO,同时生成classes.txt

import os import xml.etree.ElementTree as ET # 类别列表,顺序决定 class_id,必须和训练时的 data.yaml 一致 CLASSES = ['leaf_type_a', 'leaf_type_b', 'leaf_type_c'] def voc_to_yolo(xml_dir, out_dir, classes): os.makedirs(out_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if not xml_file.endswith('.xml'): continue tree = ET.parse(os.path.join(xml_dir, xml_file)) root = tree.getroot() size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) lines = [] for obj in root.iter('object'): cls_name = obj.find('name').text if cls_name not in classes: continue # 跳过不在类别表里的标注,避免训练时报错 cls_id = classes.index(cls_name) bbox = obj.find('bndbox') xmin = float(bbox.find('xmin').text) ymin = float(bbox.find('ymin').text) xmax = float(bbox.find('xmax').text) ymax = float(bbox.find('ymax').text) # 归一化并转成中心点+宽高 x_center = (xmin + xmax) / 2.0 / w y_center = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}") out_path = os.path.join(out_dir, xml_file.replace('.xml', '.txt')) with open(out_path, 'w') as f: f.write('\n'.join(lines)) voc_to_yolo('annotations_xml', 'labels_yolo', CLASSES)

逻辑说明:先读 XML 里的图片宽高,再把绝对坐标归一化。CLASSES的顺序就是 YOLO 的class_id,一旦训练开始就不能改,否则类别会错位。参数上,xml_dir是 VOC 标注目录,out_dir是输出 txt 目录,classes必须和数据集实际类别完全一致。如果 XML 里有拼写错误的类别名,脚本会跳过,训练时那些目标就丢了,所以转换后要统计一下每类数量。

反过来,YOLO 转 COCO 的核心是把所有 txt 聚合成一个 JSON,同时生成imagesannotationscategories三个数组。这里不展开完整代码,但要注意 COCO 的id必须全局唯一,image_idannotation_id不能重复,否则评估脚本会报错。

2.3 转换后必须做的两项校验

第一项是坐标越界检查。归一化后如果出现负数或大于 1 的值,说明原始标注框超出了图片边界,训练时会被裁掉或报错。用下面这段快速扫一遍:

import glob bad = [] for txt in glob.glob('labels_yolo/*.txt'): with open(txt) as f: for i, line in enumerate(f): parts = line.strip().split() if len(parts) != 5: bad.append((txt, i, '字段数不对')) continue vals = list(map(float, parts[1:])) if any(v < 0 or v > 1 for v in vals): bad.append((txt, i, '坐标越界')) print(f'异常标注数: {len(bad)}') for b in bad[:10]: print(b)

第二项是类别分布统计。1000 张图里如果某一类只有十几张,训练时该类几乎学不到,mAP 会很难看。统计每类框数后,如果发现长尾严重,要么补数据,要么在data.yaml里给该类加权,要么直接合并相近类别。

3. 划分脚本怎么写:训练集、验证集、测试集的比例和随机种子

3.1 为什么不能随便按 8:1:1 切

树叶数据集的图片往往来自同一批采集,同一棵树的叶子可能连续拍了很多张。如果纯随机划分,训练集和验证集里会出现同一棵树的相似图片,验证集精度虚高,实际部署时性能掉一大截。常见做法是先把图片按「采集批次」或「树种个体」分组,再按组划分,保证同一组不出现在两个集合里。如果数据集没有分组信息,至少固定随机种子,让每次划分结果可复现。

比例上,1000 张图建议 7:2:1 或 8:1:1。验证集不能少于 100 张,否则评估指标波动大;测试集留 100 张左右,只在最后跑一次,不要用来调参。

3.2 一个可复现的划分脚本

import os import random import shutil random.seed(42) # 固定种子,保证每次划分一致 img_dir = 'images' label_dir = 'labels_yolo' out_dir = 'dataset_split' train_ratio, val_ratio = 0.7, 0.2 images = [f for f in os.listdir(img_dir) if f.endswith(('.jpg', '.png'))] random.shuffle(images) n = len(images) n_train = int(n * train_ratio) n_val = int(n * val_ratio) splits = { 'train': images[:n_train], 'val': images[n_train:n_train + n_val], 'test': images[n_train + n_val:] } for split, files in splits.items(): img_out = os.path.join(out_dir, split, 'images') lbl_out = os.path.join(out_dir, split, 'labels') os.makedirs(img_out, exist_ok=True) os.makedirs(lbl_out, exist_ok=True) for img in files: shutil.copy(os.path.join(img_dir, img), os.path.join(img_out, img)) txt = os.path.splitext(img)[0] + '.txt' src_txt = os.path.join(label_dir, txt) if os.path.exists(src_txt): shutil.copy(src_txt, os.path.join(lbl_out, txt)) else: print(f'警告: {txt} 缺失,该图无标注')

逻辑说明:random.seed(42)是后悔药,没有它每次跑出来的划分都不一样,实验无法对比。shutil.copy保留原文件,避免误删。如果某张图没有对应 txt,脚本会打印警告,这种图要么是负样本(可以保留,但要在训练配置里允许无标注图),要么是漏标,需要人工补。

参数上,train_ratioval_ratio按需调整,剩下的自动归测试集。如果数据集有分组信息,把random.shuffle换成按组打乱再展开。

3.3 划分后生成 data.yaml

YOLO 训练需要一个data.yaml告诉它数据在哪、有几类、类别名是什么:

path: ./dataset_split train: train/images val: val/images test: test/images nc: 3 names: ['leaf_type_a', 'leaf_type_b', 'leaf_type_c']

nc必须和names长度一致,names的顺序必须和转换脚本里的CLASSES完全一致。路径可以用相对路径,但建议用绝对路径,避免从不同目录启动训练时找不到数据。

4. 用 YOLOv8 跑通第一次训练:环境、命令和关键参数

4.1 环境配置的最小依赖

YOLOv8 用 ultralytics 包,Python 3.8 以上即可。常见做法是建一个 conda 环境:

conda create -n leaf_yolo python=3.10 -y conda activate leaf_yolo pip install ultralytics

如果要用 GPU,先确认 CUDA 版本和 PyTorch 匹配。pip install ultralytics会自动装 PyTorch,但可能装成 CPU 版。GPU 用户建议先按 PyTorch 官网命令装好对应 CUDA 版本的 torch,再装 ultralytics。装完后用yolo checks看环境是否正常,它会打印 CUDA 是否可用、版本号等信息。

4.2 训练命令和参数含义

yolo detect train \ data=dataset_split/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/leaf \ name=exp1

逐项说明:data指向刚才生成的 yaml;model用预训练权重,树叶数据集小,从零训练容易过拟合,用 COCO 预训练权重迁移学习效果更稳;epochs=100是上限,配合patience=20早停,验证集 20 轮不提升就停;imgsz=640是输入尺寸,树叶目标通常不大,如果图片里叶子占像素很少,可以提到 1024,但显存占用会翻倍;batch=16按显存调,8G 显存跑 640 大概能到 16,不够就降到 8;lr0=0.01是初始学习率,小数据集可以降到 0.001 避免震荡。

训练过程中重点看三个指标:box_loss是否稳定下降、mAP50是否上升、验证集 loss 是否在训练 loss 还在降时提前反弹。如果验证 loss 反弹而训练 loss 继续降,就是过拟合,要么加数据增强,要么减模型复杂度。

4.3 推理和置信度门限调整

训练完用yolo detect predict跑推理:

yolo detect predict \ model=runs/leaf/exp1/weights/best.pt \ source=test_images \ conf=0.25 \ save=True

conf=0.25是置信度门限,低于这个值的框不输出。树叶检测里这个值很关键:调高到 0.5 会漏掉遮挡或模糊的叶子,调低到 0.1 会冒出大量误检。建议在验证集上画一条conf从 0.1 到 0.9 的 Precision-Recall 曲线,选 F1 最高的点作为门限。如果业务更怕漏检,就选 recall 高的点;更怕误检,就选 precision 高的点。

5. 树叶数据集训练的避坑清单:从标注到评估的 5 个翻车现场

5.1 标注框贴边导致训练报错

现象:训练启动几轮后报AssertionError或坐标越界警告,loss 变成 NaN。原因:VOC 转 YOLO 时,某些标注框的xmax等于图片宽度,归一化后x_center + width/2刚好等于 1,浮点误差可能让它略大于 1。解决:转换时对坐标做裁剪,x_center = min(max(x_center, 0), 1),宽高同理,或者统一减一个极小值1e-6

5.2 类别名大小写不一致导致类别数翻倍

现象:data.yaml里写nc: 3,但训练日志显示nc: 5或类别索引对不上。原因:VOC 标注里同一类叶子有的写Leaf_A,有的写leaf_a,转换脚本按字符串匹配,生成了两个 class_id。解决:转换前统一转小写并去空格,或者先用脚本统计所有name字段的唯一值,人工确认后再生成CLASSES

5.3 验证集 mAP 很高但实际推理全错

现象:训练完mAP50到 0.9,拿真实场景照片推理却几乎检不出。原因:划分时没有按采集批次分组,验证集图片和训练集高度相似,模型只是记住了背景。解决:重新按批次或树种个体划分,确保验证集里的树在训练集里没出现过。如果做不到,至少把验证集换成另一批采集的图片。

5.4 小目标叶子被大量漏检

现象:大叶子检测正常,小叶子(占图比例小于 1%)几乎检不到。原因:YOLO 默认下采样到 640 后,小目标在特征图上只剩几个像素,anchor 匹配不上。解决:把imgsz提到 1024 或 1280;在data.yaml里开启mosaiccopy_paste增强;如果还不行,换用带 P2 小目标检测层的模型结构,或者把图片切块后再训练。

5.5 训练中断后无法续跑

现象:跑到第 60 轮机器断电,重新训练从第 0 轮开始,前面的时间白费。原因:没有保存 checkpoint 或没指定resume。解决:YOLO 默认每轮保存last.pt,续跑用yolo detect train resume model=runs/leaf/exp1/weights/last.pt。注意resume会读取原训练的所有参数,不要手动改datamodel,否则可能报错。

6. 把 1000 张图用到极致:数据增强、交叉验证和类别加权的组合技巧

1000 张图在目标检测里属于小数据集,直接训容易过拟合。我一般会做三件事:第一,开启 YOLO 内置的增强组合,mosaic=1.0mixup=0.1copy_paste=0.1degrees=15translate=0.1scale=0.5。这些参数在data.yaml同级传进去即可,比如yolo detect train ... mosaic=1.0 mixup=0.1。注意mixup太大会让树叶和背景混在一起,小数据集上 0.1 到 0.2 比较稳。

第二,如果类别长尾严重,不要只靠增强,要在 loss 里给稀有类加权。YOLOv8 默认用cls损失,可以通过自定义class_weights传入,或者在数据集层面做重采样:把稀有类的图片复制多份,但复制时只保留稀有类标注,其他类删掉,避免重复框。更稳妥的做法是分层采样,每个 batch 里保证每类至少出现一次。

第三,用 5 折交叉验证代替单次划分。把 1000 张图按组分成 5 份,每次取 4 份训练、1 份验证,跑 5 次后取平均 mAP。这样得到的指标更可靠,也能看出模型在不同数据子集上的稳定性。代价是训练时间翻 5 倍,但小数据集单次训练快,总体可接受。

最后分享一个我踩过的坑:有一次为了提升小目标检测,把imgsz从 640 提到 1280,结果显存爆了,batch 降到 4,训练速度慢到无法接受。后来改成先切图再训练,把每张原图切成 4 块 640×640 的子图,标注框按切图坐标重新计算,小目标占比立刻上来了,显存也没爆。切图脚本的核心是记录每块的偏移量,把原图坐标减去偏移再归一化。这个思路对树叶、病虫害、小目标检测都通用。

希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询