ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLO的树叶分类目标检测数据集全解析:格式转换、划分与训练

基于YOLO的树叶分类目标检测数据集全解析:格式转换、划分与训练 简介面向目标检测学习者和算法工程师的YOLO树叶分类数据集资源基于真实场景图像构建使用LabelImg标注提供VOC、COCO、YOLO三种主流格式标签分别存放于独立文件夹可直接用于YOLO系列模型训练与评测适配课程设计、毕业设计及工程落地等多种场景。压缩包共2000个文件主要由1000个xml标注文件、990个txt标签文件及环境配置与训练教程HTML页面、Python划分脚本、yaml配置文件组成整体大小约27.93MB目录规划清晰便于按需调用。资源附赠Linux与Windows双平台YOLO环境搭建和训练教程以及训练集/验证集/测试集划分脚本支持自定义数据划分能够帮助初学者快速完成从环境准备到模型训练的全流程同时提供训练列表文件减少上手成本。已有469人学习下载适合需要高质量标注数据与完整流程指导的目标检测入门者使用。1. 1000 张树叶图带三格式标签这个数据集包到底怎么用拿到一个名为“YOLO树叶分类目标检测数据集(含1000张图片)对应voc、coco和yolo三种格式标签划分脚本训练教程.rar”的压缩包第一反应通常是解压后对着三个 labels 文件夹发愣VOC 是一堆 xmlCOCO 是一个巨大的 jsonYOLO 是一堆 txt。你真正要训练的是 YOLO但 YOLO 只认 txtCOCO 和 VOC 格式在这一刻像是冗余。其实这三份格式恰恰对应了三条典型工作流VOC 格式方便用 LabelImg 复查和二次标注COCO 格式可以直接喂给 mmdetection 或 Detectron2 做对比实验YOLO 格式才是你马上要拿来训练的那一份。这个标题面向的对象很明确要做树叶检测、又不想从零标注的人。树叶检测不是通用目标检测的简单子集它有几个独有的麻烦——叶片边缘不规则、同株叶片互相遮挡、不同树种叶片尺寸差异巨大、背景里的树干和杂草都是干扰。1000 张图的规模说大不大说小也不小关键看你怎么划分、怎么配参数。这篇不走教科书路线直接从解压后的目录结构讲起把三种格式的来龙去脉、划分脚本的设计思路、训练时的具体命令和参数、以及最容易翻车的几个坑一次说清。2. 先吃透三种标签格式VOC、COCO、YOLO 的数据组织与互转逻辑2.1 从目录结构看数据集包的设计意图正常情况下解压后的目录应该是这样的leaves_dataset/ ├── images/ │ ├── leaf_001.jpg │ ├── leaf_002.jpg │ └── ... ├── annotations/ │ ├── xml/ # VOC 格式每个图片对应一个 xml │ │ ├── leaf_001.xml │ │ └── ... │ ├── coco.json # COCO 格式所有标注合在一个 json │ └── yolo/ # YOLO 格式每个图片对应一个 txt │ ├── leaf_001.txt │ └── ... ├── split/ │ └── split.py # 划分脚本 └── train.ipynb # 训练教程如果你拿到的包结构略有出入比如 yolo 标签直接放在 labels 目录下也不要紧核心逻辑一样图片是一份标注是同一份语义的不同序列化方式。理解这一点后你会发现所谓“格式转换”没有技术含量真正的技术含量在理解三种格式各自 punish 什么、reward 什么。2.2 VOC 格式xml 里的坐标是绝对值修改最直接VOCVisual Object Classes格式源自 PASCAL VOC 挑战赛每个 xml 文件描述一张图片里的所有目标。核心字段只有几个annotation filenameleaf_001.jpg/filename size width1920/width height1080/height depth3/depth /size object nameleaf_diseased/name bndbox xmin320/xmin ymin240/ymin xmax860/xmax ymax720/ymax /bndbox /object /annotationxmin/xmax/ymin/ymax 都是像素绝对值必须落在图片尺寸范围内。400 × 400 的图上不可能出现 xmax500 的标注这种错误只可能在手工标注或脚本转换时生成。所以拿到 xml 标签后第一个动作应该是写脚本校验边界import xml.etree.ElementTree as ET import os xml_dir annotations/xml img_dir images for xml_name in os.listdir(xml_dir): tree ET.parse(os.path.join(xml_dir, xml_name)) root tree.getroot() # 解析图片尺寸 size root.find(size) w, h int(size.find(width).text), int(size.find(height).text) # 逐个目标检查边界 for obj in root.findall(object): bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) # 越界即报错不合法目标直接跳过 if xmin 0 or ymin 0 or xmax w or ymax h: print(f{xml_name} 边界错误: {xmin},{ymin},{xmax},{ymax}) if xmax xmin or ymax ymin: print(f{xml_name} 宽高为负: {xml_name})这个脚本的检查逻辑并不是多余动作。很多转换脚本在把 COCO 转回 VOC 时会把整数坐标和浮点坐标混在一起四舍五入之后正好产生 xmaxxmin 的退化框。YOLO 训练对这种标签完全不报错但损失函数会一直居高不下。2.3 COCO 格式json 里的 id 链是新手最容易被绕晕的地方COCO 格式把整份数据集的标注塞进一个 json顶层结构是 info、images、annotations、categories 四个数组。关键在 id 关联关系{ images: [ {id: 1, file_name: leaf_001.jpg, width: 1920, height: 1080} ], annotations: [ { id: 1, image_id: 1, category_id: 1, bbox: [320, 240, 540, 480], area: 259200, iscrowd: 0 } ], categories: [ {id: 1, name: leaf_diseased} ] }注意这里 bbox 不再是 xmin/ymin/xmax/ymax而是[x, y, width, height]即左上角坐标加宽高。COCO 的 categories id 从 1 开始而 YOLO 的类别 id 从 0 开始转换时务必减 1。area 字段在树叶数据集里影响不大但在某些检测框架里会参与 loss 加权转换脚本生成 area 时最好重新计算宽高乘积而不是继承原值。2.4 YOLO 格式归一化坐标才是训练真正要喂的东西YOLO 标签的每个 txt 文件里每行代表一个目标0 0.4625 0.4028 0.2813 0.4444五个数字依次是类别 id从 0 开始、x_center / width 的归一化值、y_center / height 的归一化值、bbox 宽度 / 图片宽度、bbox 高度 / 图片高度。全部是 0 到 1 之间的浮点数。归一化意味着标签不依赖图片分辨率1920 宽的图和 640 宽的图用同一套 txt 都能训练coco 数据集下载后转成 YOLO 格式也是这个逻辑。从 VOC 转 YOLO 的核心代码就几行def voc_to_yolo(xml_path, out_path, class_map): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in class_map: # 未注册的类直接跳过 continue cls_id class_map[cls_name] bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) # 转成 YOLO 所需的中心点 宽高 归一化 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines))class_map 是{leaf_diseased: 0, leaf_healthy: 1}这种字典。这里有个容易踩的坑如果 class_map 里漏了某个类脚本会静默跳过最后标签总行数比目标数少但不会报错。所以转换完一定要数一下行数。2.5 三格式对比表与格式选型建议维度VOC (xml)COCO (json)YOLO (txt)坐标类型绝对值 int绝对值 float归一化 float每张图存储独立文件合并在一个 json独立文件类别 id 起点字符串名10适合工具LabelImg 复查mmdetection、Detectron2YOLO 系列直接训练缺陷定位难度低逐文件可查高json 结构错误全局崩中坐标错误不报错实际做项目时我的习惯是以 VOC 为中间格式。LabelImg 标注原生出 VOC脚本转 YOLO 训练需要对比实验时再转 COCO。这个包里三份都给齐了省掉了自己写转换的时间但你不能直接拿来就用——先跑一遍校验脚本确认三份格式的语义一致再谈训练。提示如果你从 cvat yolo 导出或用 kitti 标注转 yolo大概率会拿到非归一化的 txt。判别方法很简单如果某个坐标值大于 1一定不是标准 YOLO 格式不要硬着头皮训练。3. 划分脚本的核心逻辑不只看比例还要看叶片的分布均衡3.1 一副 1000 张图的数据集划分脚本要考虑什么划分脚本在整个包里是最不起眼、但直接影响模型泛化能力的文件。train/val/test 的比例通常按 8:1:1 或 7:2:1 来切比如 1000 张图切成 train 800、val 100、test 100。单纯按比例随机划分在树叶检测场景下会出问题同一个树种的叶片纹理高度相似如果某一种类恰好被集中在某个时间段拍摄比如上午拍的全部是健康叶下午拍的全是病叶随机划分会把“时间段”这个无关变量学进去。所以划分脚本的价值不止于把文件挪到三个文件夹至少要做三件事按文件夹或文件名前缀分源、保证每个类别的图片数量在所有划分中占比一致、记录划分结果到文本文件以便复现。第三点看似多余实际排查时极其重要。3.2 能直接用的划分脚本import os import random import shutil from collections import defaultdict random.seed(42) img_dir images yolo_label_dir annotations/yolo train_dir dataset/train val_dir dataset/val test_dir dataset/test # 结构化数据集YOLO 训练时直接喂 train 和 val 目录 for d in [train_dir, val_dir, test_dir]: os.makedirs(os.path.join(d, images), exist_okTrue) os.makedirs(os.path.join(d, labels), exist_okTrue) # 按类别统计图片保证分层采样 class_to_images defaultdict(list) for img_name in os.listdir(img_dir): stem os.path.splitext(img_name)[0] label_path os.path.join(yolo_label_dir, stem .txt) if not os.path.exists(label_path): continue # 读取该图所有类别 with open(label_path) as f: classes_in_img set() for line in f: cls_id line.split()[0] classes_in_img.add(cls_id) # 每张图归属第一个类别多类图片会重复计数 for cls_id in classes_in_img: class_to_images[cls_id].append(img_name) train_ratio, val_ratio 0.8, 0.1 train_imgs, val_imgs, test_imgs [], [], [] for cls_id, img_list in class_to_images.items(): # 去重一张多标签图可能出现在多个类里 img_list list(set(img_list)) random.shuffle(img_list) n_train int(len(img_list) * train_ratio) n_val int(len(img_list) * val_ratio) train_imgs img_list[:n_train] val_imgs img_list[n_train:n_train n_val] test_imgs img_list[n_train n_val:] # 复制图片和标签 def copy_files(img_list, target_dir): for img_name in img_list: stem os.path.splitext(img_name)[0] shutil.copy(os.path.join(img_dir, img_name), os.path.join(target_dir, images, img_name)) label_src os.path.join(yolo_label_dir, stem .txt) label_dst os.path.join(target_dir, labels, stem .txt) if os.path.exists(label_src): shutil.copy(label_src, label_dst) copy_files(train_imgs, train_dir) copy_files(val_imgs, val_dir) copy_files(test_imgs, test_dir) # 保存划分记录方便事后复现 with open(split_record.txt, w) as f: f.write(train:\n \n.join(train_imgs) \n\n) f.write(val:\n \n.join(val_imgs) \n\n) f.write(test:\n \n.join(test_imgs) \n)这段脚本的要点在于class_to_images的构建方式不是按文件名前缀猜类别而是直接读 YOLO 标签文件从每行第一个数字拿类别 id。这样不管数据集包里图片怎么命名都能正确分层。随机种子固定为 42 是为了可复现——你换台机器跑得到一模一样的划分。3.3 划分完必须做的数量自检训练前跑一个统计确认 train/val/test 三个集合中各类别占比接近。如果 val 里某些类只有一两个目标这个模型的 mAP 波动会非常大val 曲线看起来像在做布朗运动。for split in train val test; do echo $split cat dataset/$split/labels/*.txt | awk {print $1} | sort | uniq -c done如果发现某个类在 test 里完全没有出现说明分层采样代码有 bug或者这个类本身图片太少。1000 张的数据集里一个类只有 30 张图已经算尾部类别val 里分到 3 张好坏全靠运气。这个坑在训练时看不出来直到你看 per-class mAP 才会意识到——所以划分阶段就要留意。4. 训练教程的骨架模型选型、配置文件、命令行参数与损失函数读数4.1 用哪一代 YOLO 跑树叶检测v5、v8 还是 v11“yolo 第几代了”是搜训练教程时最常见的问题之一。截至现在Ultralytics 主线已经是 YOLOv11但 yolov5n/yolov5s 的权重文件小、部署生态成熟在 RK3588 这类边缘设备上依然高频出现。针对树叶检测这种单类目标、1000 张图的小规模任务我的建议是首次跑通用 YOLOv8n。nano 模型参数最少CPU 也能硬跑迭代一轮验证环境没问题再换大模型。追求精度用 YOLOv8s 或 YOLOv11s。树叶检测不是极简任务叶片边缘和纹理需要足够的特征表达能力。提前确认部署目标。如果后续要在 AMD 显卡或嵌入式设备上跑环境配置的坑先踩模型选型反而次要。AMD 显卡用 DirectML 后端跑 YOLO 时某些算子的支持度和 CUDA 不一样训练前先跑一次前向验证。4.2 数据集配置与训练命令YOLOv8 以后的数据集配置是一个 yaml 文件path: dataset # 数据集根目录 train: train/images # 训练图片相对路径 val: val/images # 验证图片相对路径 test: test/images # 测试图片相对路径 nc: 2 # 类别数量改成你自己的 names: [leaf_diseased, leaf_healthy] # 类别名与标签 id 对应训练命令yolo detect train \ modelyolov8n.pt \ dataleaves.yaml \ epochs100 \ batch16 \ imgsz640 \ patience20 \ save_dirruns/train/leaves_v8n \ nameleaves_001参数说明epochs1001000 张图的小数据集100 轮足够收敛。如果你的显卡显存小可以降到 60 轮配合早停。batch16imgsz640 时16 张图大约需要 10GB 显存8GB 的卡建议降到 8。patience20连续 20 轮 val loss 没有提升就停止省时间的关键参数。imgsz640树叶检测默认用 640。但如果你的图片里叶片普遍偏小可以试imgsz1280小目标召回率会明显上升代价是显存翻两倍以上。4.3 从训练日志看损失函数什么时候该停什么时候该调训练日志里最值得盯的四条曲线是train/box_loss、train/cls_loss、val/box_loss、val/cls_loss。小数据集最常见的异常是 val loss 在训练初期就一路走高但 train loss 正常下降这是过拟合的典型信号。1000 张图、单类目标理论上 20 到 30 轮就够超过 50 轮还在过拟合先加数据增强而不是加模型容量。如果你在 Ultralytics 的新版本里启动训练注意到日志里出现了amp_checks或自动混合精度的相关输出那是新版默认打开了 AMP。在部分显卡上 AMP 会导致 val loss 抖动遇到这种情况用ampFalse关掉再试一轮但训练时间会变长。4.4 训练完成后验证模型的固定动作训练结束不要急着看results.png跑一段独立验证yolo detect val \ modelruns/train/leaves_v8n/weights/best.pt \ dataleaves.yaml \ splittest \ conf0.25 \ iou0.5splittest是关键。很多人默认验证集就是 val但 val 在训练时被用来早停过指标偏高。用 test 集跑出来的 map50 才是你可以写进汇报里的数字。树叶检测项目的验收标准通常要求 mAP50 在 0.85 以上低于 0.7 基本不可用。5. 训练过程中的疑难杂症与提速技巧5.1 症状到对策三个最常见的故障排查症状可能原因处理办法Box loss始终在 1.2 以上不降YOLO 标签类别 id 与 yaml 不匹配或坐标归一化错误查看 txt 内容确认第一个数字在 0~nc-1 范围内训练时报FileNotFoundError划分脚本只复制了有标签的图片但 yaml 里 train 目录仍然索引了全部图片确认 train/images 里的文件名与 train/labels 一一对应val 集 mAP 忽高忽低val 集太小划分时没有做分层采样检查 val 数据集每个类别的目标数量不足 30 个目标就重新划分第二个问题的排查方法一条 find 命令找出来。for img in dataset/train/images/*.jpg; do labeldataset/train/labels/$(basename ${img%.jpg}.txt) if [ ! -f $label ]; then echo 缺少标签: $img; fi done5.2 模型加速技巧预热、冻结与剪枝的取舍1000 张图的数据集显存充足时最快的提速手段是冻结主干。前 10 轮用freeze10只训练检测头让 loss 先降到一个合理区间再解冻所有层。原因很简单树叶数据集的背景和 ImageNet 预训练特征相似主干不需要大动直接微调反而容易在小数据集上把预训练特征改坏。yolo detect train \ modelyolov8s.pt \ dataleaves.yaml \ epochs60 \ batch16 \ imgsz640 \ freeze10 \ warmup_epochs3warmup_epochs3让学习率在前 3 轮从接近 0 慢慢爬升配合冻结主干训练稳定性会好很多。这里的代价是总训练时间变长但对小数据集来说这样的换时提精是值得的。5.3 迁移学习的一个正确用法先在大图上预训练再回 640 微调如果叶片尺寸偏小可以先用 1280 的输入训练 30 轮然后加载这个权重在 640 下微调 20 轮。这种做法利用了 YOLO 的多尺度训练特性模型先学会在细节丰富的分辨率下定位叶片再在低分辨率下统一推理速度。脚本层面只需要把 imgsz 改掉其余参数保持一致。用迁移学习最忌讳的是把 target 域的标签当成 pre-train 的负样本所以这里必须加载的是树叶模型自己的last.pt而不是新的yolov8n.pt。最后的验证环节打开runs/detect/val里的val_batch0_pred.jpg用眼睛看第二行检测框的边缘贴合度。树叶数据集中边框稍微偏大通常不影响 mAP 计算但部署到实际场景时会框到背景里的树干召回率达标但精确率受影响。发现这种情况推理阶段把conf从 0.25 提到 0.4比重新训练快得多。本文还有配套的精品资源点击获取
返回列表