叶片目标检测数据集制作与YOLO训练全流程指南

发布时间:2026/9/15 1:12:30
叶片目标检测数据集制作与YOLO训练全流程指南 简介面向目标检测学习者与开发者的YOLO树叶分类数据集精选真实场景下的树叶图像覆盖不同光照、角度与背景场景丰富且标注准确直接提供VOC格式xml与YOLO格式txt两类标注文件可无缝接入YOLOv5、YOLOv8等主流框架训练免去手工标注和格式转换的繁琐。随包共2000个文件除标注外还有6个图文教程、3个Python脚本与1个YAML配置文件整体压缩包约27.93MB目录按标签格式分文件夹存放方便对应不同框架读取结构清晰直观。教程覆盖Linux和Windows双系统下的YOLO环境搭建、训练案例修改以及如何训练自己的数据集脚本则支持一键划分训练集、验证集和测试集便于灵活组织实验数据快速完成从数据准备到模型训练的闭环。目前已有469人学习适合希望快速获得高质量树叶检测数据并跑通YOLO训练流程的学生、工程师或科研人员。1. 叶片目标检测为什么值得单独做一套数据集YOLO树叶分类目标检测数据集的价值不是“多了一个可以跑通的公共数据集”而是把叶片识别从分类问题升级成了定位问题。很多人刚开始接触叶片识别时默认想到的是图片分类网络给一张叶子照片输出“这是苹果叶”或“这是葡萄叶”。但真实的农业场景里田间随手拍的照片往往有大面积土壤和杂草背景无人机航拍的树冠上叶片层层叠叠分类网络既说不出叶子在哪也数不清一株上有多少片病叶。此时需要的是目标检测模型每个叶片输出一个边界框框上带着类别和置信度。这套数据集的1000张图片配的是VOC、COCO、YOLO三种标签格式再加划分脚本意味着拿到手不用写转换工具就能直接进训练流程。对刚接触目标检测的开发者来说完整跑通一遍“标注格式→划分→训练→验证”的链路比收集上万张图更有实际意义。2. VOC、COCO、YOLO三种标注格式的差异与转换2.1 三种格式的适用场景与坐标体系VOC格式的标签是每张图片对应一个XML文件。XML里每个object代表一个目标内部挂name、bndbox等节点bndbox的子节点xmin、ymin、xmax、ymax是绝对像素坐标。这个结构来自早期的Pascal VOC比赛可读性好任何文本编辑器都能打开检查。缺点是文件数量多每次读取要做磁盘I/O而且类别是字符串训练前还得多一步字符串到整数id的映射。COCO格式则把整个数据集的标注合并进一个JSON文件顶层包含licenses、info、images、annotations、categories五个字段。images数组记录每张图片的id、宽高和文件名annotations数组里每条记录包含image_id、category_id、bbox和areabbox是[x, y, width, height]绝对像素坐标。这种集中式设计方便按id索引配合Detectron2、MMDetection这类框架时很顺手但缺点是文件结构复杂手工编辑容易把一个逗号放错位置就整体解析失败而且image_id与图片文件的对应关系一旦错位排查起来相当费劲。YOLO格式的标签又是另一种思路每张图片对应一个同名txt文件文件里每一行是一条标注五个数字分别是类别id、中心点x、中心点y、框宽w、框高h全部是归一化到0到1之间的浮点数。归一化的好处是图片分辨率变化时不需要重新标注Ultralytics在做训练前缩放时直接沿用标签不会出现像素坐标和缩放后尺寸不匹配的情况。坏处则是肉眼检查困难五个数字没有直观的“框”的形态想要快速确认标注质量必须依赖可视化脚本。格式文件组织坐标描述归一化典型消费框架VOC每张图一个XMLxmin, ymin, xmax, ymax否Faster R-CNN等COCO全数据集一个JSONx, y, width, height否Detectron2、MMDetectionYOLO每张图一个txtcx, cy, width, height是Ultralytics、Darknet2.2 一个脚本处理VOC到YOLO的转换手头有VOC标签而想用YOLO训练常见做法是先把XML解析成标注数组再做一次坐标映射。反过来YOLO标签转VOC时容易踩一个坑txt里只有归一化坐标要还原成绝对像素必须读对应jpg的宽高很多转换脚本报错就报在这一步——只盯着标签目录没有把图片路径一并传进去。写转换逻辑时类别id的分配也是一个容易炸的点XML里存的是字符串类名而YOLO的txt只存整数id两者的对应关系完全由类别列表顺序决定。import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, img_w, img_h, class_names): 将Pascal VOC的XML转成YOLO格式的文本行。 VOC记录的是左上角与右下角绝对像素坐标 YOLO需要中心点坐标加宽高且归一化到0~1。 tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.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{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) return lines这段代码的关键是中心点换算xmin与xmax的平均值除以图片宽度才是归一化的中心点横坐标而不是xmin加宽高的一半。class_names参数是外部传入的类别列表列表顺序决定了类别id后续写训练配置yaml时names的顺序必须和这里完全一致否则所有目标都会错位一个id。img_w和img_h的取值也有讲究如果图片在标注后被裁剪或缩放必须使用裁剪后的实际尺寸否则转换出的YOLO标签分布整体偏移。2.3 坐标越界与空标注的默认检查训练前最影响结果的是标签和图片没对齐。很多标注工具在手动框选时会把边界框拉出图片边界转成YOLO后w或h归一化大于1训练时损失函数直接算出一个异常值轻则mAP掉点重则loss变成nan。所以转换脚本里必须把越界检查一起做掉。def normalize_box(xmin, ymin, xmax, ymax, img_w, img_h): 对VOC坐标做裁剪返回YOLO归一化坐标。 xmin max(0.0, min(xmin, img_w - 1)) xmax max(0.0, min(xmax, img_w - 1)) ymin max(0.0, min(ymin, img_h - 1)) ymax max(0.0, min(ymax, img_h - 1)) if xmax xmin or ymax ymin: return None cx ((xmin xmax) / 2.0) / img_w cy ((ymin ymax) / 2.0) / img_h bw (xmax - xmin) / img_w bh (ymax - ymin) / img_h return cx, cy, bw, bh先对坐标做clip再判断边界框面积是否为正能同时过滤掉完全越界的无效标注。叶片目标不是极端长条形宽度或高度小于3像素的框在后续增强和缩放后基本学不到信息这种情况直接丢弃比保留更有利于训练。需要说明的是这类检查要在转换时做而不是依赖训练框架的容错机制因为不同版本的YOLO训练脚本对越界标签的处理方式并不一致有的静默忽略有的直接中断训练。3. 用划分脚本切分叶片数据集并做三项校验3.1 标准目录结构images和labels必须严格对齐拿到一套含1000张图片的数据集后第一步不是急着写训练命令而是把目录结构梳理成训练框架可解析的形态。常见做法是分成train、val、test三个子集images下按子集放图片labels下按同样结构放标签文件。对YOLO训练来说图片是leaf_0001.jpg标签就必须是同目录同名的leaf_0001.txt大小写不一致、后缀名不规范都会导致图片被静默跳过。leaf_dataset/ ├── images/ │ ├── train/ │ │ ├── leaf_0001.jpg │ │ └── ... │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ │ ├── leaf_0001.txt │ │ └── ... │ ├── val/ │ └── test/ ├── leaf.yaml └── split.py这个目录结构本身没有什么玄机真正容易出错的是划分方式。叶片数据往往是同一时间在同一片果园拍摄的如果按文件名前缀做随机划分同一棵树的照片可能既出现在训练集又出现在验证集测出来的mAP虚高换到真实场景立刻掉点。正确做法是尽量让同一来源的图片留在同一个子集里。1000张图不算多但样本分桶这一步做正确后续训练结果的参考价值才是真实的。3.2 划分脚本随机种子、分层与标签对齐一个合格的划分脚本至少做三件事固定随机种子、按类别分布做分层采样、同时移动图片和标签。只取前80%的粗暴做法在数据量充足时问题不大但叶片数据集的类别往往是不均衡的比如健康叶占600张病斑叶只有150张如果随机划分后病斑叶在测试集里只有几张评估结果的随机波动会非常大。import os import random import shutil random.seed(42) IMG_DIR images LBL_DIR labels TRAIN_RATIO 0.8 TEST_RATIO 0.1 all_images [f for f in os.listdir(IMG_DIR) if f.lower().endswith(.jpg)] all_images.sort() random.shuffle(all_images) n_train int(len(all_images) * TRAIN_RATIO) n_test int(len(all_images) * TEST_RATIO) train_imgs all_images[:n_train] val_imgs all_images[n_train:-n_test] test_imgs all_images[-n_test:] def split_by_name(img_names, prefix): os.makedirs(os.path.join(IMG_DIR, prefix), exist_okTrue) os.makedirs(os.path.join(LBL_DIR, prefix), exist_okTrue) for img in img_names: stem os.path.splitext(img)[0] lbl stem .txt src_img os.path.join(IMG_DIR, img) src_lbl os.path.join(LBL_DIR, lbl) dst_img os.path.join(IMG_DIR, prefix, img) dst_lbl os.path.join(LBL_DIR, prefix, lbl) if os.path.exists(src_lbl): shutil.move(src_img, dst_img) shutil.move(src_lbl, dst_lbl) else: print(f标签缺失: {src_lbl}) split_by_name(train_imgs, train) split_by_name(val_imgs, val) split_by_name(test_imgs, test)脚本的核心是把图片和标签当作一个整体移动缺标签的图片单独打印出来方便后续回去修复标注阶段的问题。使用80/10/10的比例时训练集约800张图验证集和测试集各约100张。如果数据集本身类别不平衡比较严重建议改成90/5/5优先保证训练图像数量充足。随机种子固定成42是为了让每次切分结果一致实验之间可对比。3.3 划分后必做的三项校验第一项逐张对比images和labels的文件名找出缺失标签和空标签。如果一张图片有jpg但没有对应的txt训练时该图片会被直接跳过影响的是有效数据量。一个只有背景没有叶片的图片如果连空txt都没有训练框架根本不会加载它最终训练集的有效样本数和目录文件数对不上损失曲线看起来一切正常实际却少学了很多样本。第二项检查每个类别的目标数在train和val中的分布。类别在train里有大量标注在val里却是0这种划分方式会让验证指标失真必须回到划分阶段调整。import os from collections import Counter label_dir labels/val valid_cnt Counter() empty_cnt 0 for f in os.listdir(label_dir): path os.path.join(label_dir, f) with open(path, r, encodingutf-8) as fp: lines [ln.strip() for ln in fp if ln.strip()] if not lines: empty_cnt 1 continue for ln in lines: parts ln.split() cls int(parts[0]) cx, cy, w, h map(float, parts[1:5]) if not (0.0 cx 1.0 and 0.0 w 1.0): print(f越界: {f} - {ln}) valid_cnt[cls] 1 print(类别统计:, dict(valid_cnt)) print(空标签文件数:, empty_cnt)第三项检查标签坐标是否全部落在合法区间。这里有个容易被忽略的细节cx、cy、w、h各自在0到1之间不代表边界框合法还要看cx加w的一半是否超过1或者cy加h的一半是否小于0。越界方向朝图片外的框在训练增强做旋转和缩放后会产生更离谱的标注。对于只越界0.01的目标用代码裁剪到边界即可不必直接丢弃。4. 用YOLOv8训练叶片数据集的最小流程4.1 选择YOLO版本与安装依赖谈到训练自己的数据集最常用的选择是YOLOv5或Ultralytics YOLOv8。网上经常有人问yolo第几代了版本确实更新快但对叶片这类类别不超过十种、目标偏小的任务YOLOv8和YOLOv5的差异不足以左右最终结果。我的建议是固定一个版本把流程跑通把时间花在数据清洗和增强策略上比追新版本收益大得多。安装依赖时最容易栽在CUDA版本不匹配上。Ultralytics对PyTorch版本有明确要求先确认显卡驱动支持的CUDA版本再安装对应版本的PyTorch最后安装ultralytics包。CPU环境也能训练但1000张图、200轮迭代在CPU上可能要跑几个小时建议先用GPU。pip install ultralytics这个包把数据集解析、模型加载和训练入口统一起来安装后直接用yolo命令行即可不需要手工指定网络结构文件。4.2 配置leaf.yaml与训练参数训练入口需要一个数据配置文件核心是训练集路径、验证集路径和类别名。路径用绝对路径最稳妥也可以写相对于yaml文件所在目录的相对路径。常见做法是把leaf.yaml放在数据集根目录下这样路径配置最不容易写错。类别名的顺序必须与标签文件里的类别id完全一致这是无数人折腾半天后发现自己栽的地方训练框架读取的是整数id而不是names字符串本身。# leaf.yaml path: /home/user/leaf_dataset train: images/train val: images/val test: images/test names: 0: healthy_leaf 1: yellow_leaf 2: spot_leaf 3: insect_leaf配置文件就绪后训练一行命令启动。默认输入尺寸640训练轮数这里给200batch大小看显存16不够就降到8。Ultralytics官方推荐迁移学习的初始学习率是0.01数据量小时过大的学习率会让损失在开局阶段震荡。yolo detect train dataleaf.yaml modelyolov8n.pt epochs200 imgsz640 batch16 lr00.01参数建议取值作用epochs150-3001000张图足以充分收敛batch8-32受显存限制太大易过拟合imgsz640或960目标偏小时调大lr00.01迁移训练常用初始学习率patience50-100验证指标多轮不涨自动停止4.3 训练过程监控与中断恢复训练信息里三类损失中最值得关注的是box_loss和cls_loss。box_loss反映边界框回归的收敛状态cls_loss反映分类置信度的收敛状态。叶片数据集里类间差异较小黄叶和病斑叶视觉相似度很高cls_loss下降速度会比通用数据集慢看到分类损失震荡先不要急着改结构优先确认数据增强里是否开了不合适的翻转。对于叶片水平翻转通常没问题但如果病斑具有明显的左右不对称特征开启翻转会引入与实物分布不符的噪声。训练中断是常事Ultralytics会在输出目录保存last.pt从中断处恢复训练的命令是yolo detect train dataleaf.yaml modelruns/detect/train/weights/last.pt epochs120注意这里的epochs需要重新计算为剩余轮数而不是把总轮数再抄一遍。如果上次停在180轮目标总轮数300恢复时就写120。框架保存的last.pt是完整训练状态包含优化器状态恢复后学习率调度也接着走不会出现从头再来导致的重复训练浪费。4.4 用验证集观察边界框效果训练生成的best.pt位于runs/detect/train/weights/目录验证集上执行yolo detect val modelruns/detect/train/weights/best.pt dataleaf.yaml验证输出里重点看mAP50和mAP50-95。mAP50只判断IoU大于0.5算正样本对边界框位置不敏感。mAP50很高而mAP50-95偏低常见原因是边界框位置不够精确或目标本身小而密集此时把imgsz从640提到960往往比改损失函数更直接。验证集还输出混淆矩阵从中能看到spot_leaf被误判成yellow_leaf这类情况。出现这种情况时先不要急着调模型回头看标注规则看类别边界定义是否本身就很模糊。一张叶片同时有黄化和斑点时两个框如何分配直接决定了模型能学到的区分信息量。5. 格式互转与标注再校准的三个实用技巧5.1 类别id漂移问题VOC转COCO时最隐蔽的错误来自categories列表的排序。叶片数据集四类标签在VOC里按字符串名排序是healthy_leaf、insect_leaf、spot_leaf、yellow_leaf如果在COCO的categories里按另外的语义顺序写模型训练阶段所有类别id整体错位预测结果每个框的类别都偏移一位。最稳妥的做法是转换脚本里写死一个类别映射表不依赖XML的解析顺序或文件夹的遍历顺序。转换完成后从验证集里挑出包含全部四类的图片逐一画框比对类别名效率最高。5.2 用可视化方式复核标注叶片标注里最难发现的问题是同一片叶子被两个框重复标注或一个框里包含了两片重叠的叶子。这类问题无法靠坐标越界检查发现只能叠加标注框可视化后肉眼判断。训练完成后把预测结果叠加到原图上重点看三个位置叶片边缘预测框是否贴合病斑叶的边界框是否框住了整个病斑区域密集叶片区域是否存在漏检。大多数标注质量问题是在训练出结果之后才暴露的比如某个类别整体漏检往往说明该类别在训练集里的标注框普遍偏大或偏小。5.3 关闭针对性过强的数据增强最后给一个效果明显的小技巧。Ultralytics默认的HSV增强会随机扰动色调、饱和度和明度对通用目标检测有用但叶片类别中颜色正是判别特征黄叶和病斑叶的颜色本身就承载着分类信息扰动幅度过大等于直接抹掉了判别线索。我通常把saturation的默认值0.7降到0.3hue保持0.015左右。另一项是垂直翻转增强叶片受重力影响存在自然的上下方向开启垂直翻转等于制造了一部分现实中基本不出现的样本。训练到中期如果训练集损失持续下降而验证集损失不降反升先检查增强配置是否太强再考虑换更深的backbone。关闭随机擦除和一大部分mixup保留Mosaic和水平翻转对1000张叶片数据来说这比单纯加深网络更有效。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询