ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

植物叶片病害检测数据集:10类别YOLO格式解析与训练实战

2026/10/1 22:38:47 拓冰建站 浏览量
植物叶片病害检测数据集:10类别YOLO格式解析与训练实战 简介这份资源面向从事农作物病害识别、目标检测算法实践的学生与开发者提供一套可直接投入训练的YOLO格式植物叶片缺陷病害检测数据集省去自行采集与标注的繁琐环节。压缩包共2000个文件以1999个txt标注文件和1个Python可视化脚本为主整体约240MB按YOLOv5目录结构组织分为训练集13665张图片及对应标签、验证集1317张图片及对应标签图像为416×416的RGB大分辨率样本每张含多个目标边界框标注完整采用classes与x_centre、y_centre、w、h的相对坐标格式。数据集覆盖格鲁病、早叶斑病、黑斑等10个类别并附类别class文本文件便于直接配置模型。配套的show.py脚本可随机读取一张图片绘制边界框并保存到当前目录无需修改即可运行方便快速核验标注质量。目前已有245人学习适合用作目标检测训练、课程设计或算法对比实验的基础数据。1. 植物叶片缺陷病害检测数据集10 类别 YOLO 格式到底长什么样拿到一个植物叶片病害数据集第一反应通常不是「赶紧训练」而是先确认三件事类别定义是否互斥、标注框是否贴合病斑、划分是否已经做好。这个标题里的数据集把这三件事都替你做了——10 个类别、划分好的训练/验证/测试集、独立的 class 文件、外加一个数据可视化脚本。它解决的是「从零标注一批叶片病害图要花两三周」这个最耗人的环节让你把时间花在模型选型和调参上。适合谁用做农业视觉检测的算法工程师、要交课程设计或毕设的学生、以及想快速验证 YOLO 在细粒度病斑上表现的从业者。植物叶片缺陷病害检测的难点不在「有没有病」而在「是哪一种病、病斑在哪个位置、多个病斑是否同时出现」。10 个类别意味着模型要区分的不只是健康与病害而是不同病原造成的纹理差异这对数据集的标注一致性要求很高。下面从目录结构讲起一路讲到怎么把它喂进 YOLO 训练流程。2. 数据集目录结构与 class 文件先看懂再动手2.1 典型目录长什么样一个划分好的 YOLO 数据集目录结构基本是固定的。你拿到手大概率是下面这种形态具体文件夹名可能略有差异但层级逻辑一致leaf_disease_dataset/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片 ├── labels/ │ ├── train/ # 训练集标注.txt │ ├── val/ │ └── test/ ├── classes.txt # 类别名列表一行一个 ├── data.yaml # YOLO 训练配置入口 └── visualize.py # 数据可视化脚本images和labels必须严格对应images/train/abc.jpg对应labels/train/abc.txt。YOLO 训练时是按文件名去匹配的图片有、标签没有或者反过来都会在训练启动阶段报错或静默跳过。我一般拿到数据集第一件事就是写个脚本核对两边文件名集合是否完全相等这一步能省掉后面一堆玄学问题。2.2 class 文件与 data.yaml 的对应关系classes.txt里是 10 行类别名顺序即类别索引。第 0 行对应标签里的0第 9 行对应9。这个顺序一旦定了就不能改改了标签就全错位。data.yaml则是把路径和类别数告诉 YOLOpath: /abs/path/leaf_disease_dataset train: images/train val: images/val test: images/test nc: 10 names: 0: leaf_spot 1: rust 2: blight 3: powdery_mildew 4: bacterial_spot 5: anthracnose 6: mosaic_virus 7: scab 8: healthy 9: unknown_defectnc必须等于classes.txt的行数names的键必须从 0 连续到 9。常见翻车点是nc写了 10 但names只列了 8 个训练能启动但类别 8、9 的预测结果会错乱。改完data.yaml后用下面这段代码做一次自检确认类别索引和标签里的最大值对得上import os import yaml with open(data.yaml, r, encodingutf-8) as f: cfg yaml.safe_load(f) nc cfg[nc] names cfg[names] assert len(names) nc, fnames 数量 {len(names)} 与 nc {nc} 不一致 max_cls -1 for split in [train, val, test]: label_dir os.path.join(cfg[path], labels, split) for fn in os.listdir(label_dir): if not fn.endswith(.txt): continue with open(os.path.join(label_dir, fn)) as lf: for line in lf: line line.strip() if not line: continue cls_id int(line.split()[0]) max_cls max(max_cls, cls_id) print(标签中出现的最大类别索引:, max_cls) assert max_cls nc, 标签类别索引超出 nc 范围检查 classes.txt 顺序 print(类别自检通过)这段逻辑很直白先校验names长度和nc一致再遍历所有标签文件找出最大类别索引确认它小于nc。如果max_cls等于或大于nc说明标签里的类别号超出了配置范围多半是classes.txt顺序和标注时用的顺序不一致。参数上唯一要注意的是cfg[path]必须是绝对路径相对路径在不同工作目录下会解析失败。2.3 标签格式归一化坐标的四个数YOLO 标签每行是class_id x_center y_center width height后四个都是相对图片宽高的归一化值范围 0 到 1。这一点和 VOC 的绝对像素坐标完全不同转换时最容易错。下面这段代码检查标签数值是否越界import os def check_label_range(label_dir): bad [] for fn in os.listdir(label_dir): if not fn.endswith(.txt): continue with open(os.path.join(label_dir, fn)) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: bad.append((fn, i, 字段数不为5)) continue vals list(map(float, parts[1:])) if any(v 0 or v 1 for v in vals): bad.append((fn, i, 坐标越界)) return bad for split in [train, val, test]: issues check_label_range(flabels/{split}) print(split, 异常条数:, len(issues)) for it in issues[:5]: print( , it)归一化坐标越界通常意味着标注工具导出时用了像素坐标没除宽高或者框超出了图片边界。前者要重新转换后者可以裁剪到 [0,1] 但会轻微改变框形状建议直接丢弃这类样本。字段数不为 5 则可能是空行或格式错乱空行可以忽略其他格式问题要人工核对。3. 数据可视化脚本训练前必须亲眼确认标注质量3.1 可视化脚本要解决什么问题标注文件是纯文本肉眼看不出来框画得对不对。数据可视化脚本的作用就是把labels里的框画回图片上让你一眼看出有没有漏标、错标、框歪。标题里专门带了这个脚本说明作者也知道「不看一眼就训练」是高风险操作。我一般会在训练前把三个 split 各抽 20 张画出来看重点看小病斑有没有被框住、多个病斑是否都标了。3.2 用 OpenCV 画框的最小实现下面是一个可直接跑的可视化脚本读data.yaml拿类别名按 split 抽样画框import os import cv2 import yaml import random with open(data.yaml, r, encodingutf-8) as f: cfg yaml.safe_load(f) names cfg[names] root cfg[path] split train sample_num 20 img_dir os.path.join(root, images, split) lbl_dir os.path.join(root, labels, split) imgs [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png, .jpeg))] random.seed(42) random.shuffle(imgs) imgs imgs[:sample_num] colors [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255), (0, 255, 255), (128, 0, 128), (0, 128, 255), (128, 128, 0), (255, 128, 0)] for img_name in imgs: img_path os.path.join(img_dir, img_name) img cv2.imread(img_path) if img is None: print(读取失败:, img_path) continue h, w img.shape[:2] lbl_path os.path.join(lbl_dir, os.path.splitext(img_name)[0] .txt) if not os.path.exists(lbl_path): print(缺标签:, img_name) continue with open(lbl_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id int(parts[0]) xc, yc, bw, bh map(float, parts[1:]) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) color colors[cls_id % len(colors)] cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) label names.get(cls_id, str(cls_id)) cv2.putText(img, label, (x1, max(y1 - 5, 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) cv2.imwrite(fvis_{split}_{img_name}, img) print(已保存:, fvis_{split}_{img_name})逻辑说明先把归一化中心点和宽高还原成左上、右下像素坐标再用cv2.rectangle画框cv2.putText标类别名。colors列表按类别索引取模保证 10 个类别颜色不重复。参数上sample_num控制抽样数量random.seed(42)保证每次抽到同一批图方便对比。跑完在当前目录会生成vis_train_xxx.jpg逐张看过去重点检查三类问题框明显偏离病斑、同一病斑被拆成多个框、健康叶片被标了病害类别。3.3 从可视化结果反推标注问题看完图之后如果发现某类病斑普遍框得偏大说明标注时把周围健康组织也框进去了这会稀释病斑特征训练出的模型定位偏松。如果发现小病斑漏标模型会学到「小斑点不算病」的错误先验。这两类问题在 loss 曲线上表现为定位损失降不下去或召回率偏低但根因在数据不在模型。可视化脚本的价值就在于把这些问题提前暴露而不是等训练完看混淆矩阵才后悔。4. 把数据集接进 YOLO 训练配置、命令与参数4.1 环境与依赖确认训练前先确认环境。YOLO 系列目前主流用 Ultralytics 的实现安装和验证命令如下pip install ultralytics yolo checksyolo checks会打印 Python 版本、PyTorch 版本、CUDA 是否可用。如果 CUDA 不可用训练会退回 CPU10 类别几千张图在 CPU 上跑一轮可能要几小时基本没法调参。确认 CUDA 可用后再往下走。显卡显存方面imgsz640、batch16的配置在 8GB 显存上通常能跑显存不够就把batch降到 8 或 4。4.2 训练命令与关键参数假设data.yaml已经配好一条最小训练命令是yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/leaf \ nameexp1参数逐个说modelyolov8n.pt用 nano 版预训练权重起步速度快、适合先跑通流程epochs100是上限配合patience20做早停验证指标 20 轮不提升就停imgsz640是输入分辨率病斑较小的话可以提到 800 或 1024但显存和耗时同步上升lr00.01是初始学习率用预训练权重时这个值比较稳从零训练才需要更小的值batch16按显存调。project和name决定结果保存路径方便多次实验对比。4.3 训练中该盯哪些指标训练日志里重点看三个box_loss、cls_loss、mAP50。box_loss反映定位精度cls_loss反映分类精度。如果box_loss一直高位不降回去看可视化结果多半是框质量问题。如果cls_loss降但mAP50不涨可能是类别不平衡某些类别样本太少。10 类别里如果healthy占了大多数模型会偏向预测健康这时要在data.yaml同级目录确认各类别样本数必要时对少样本类别做过采样或加权。混淆矩阵在runs/leaf/exp1/下训练结束后打开看哪两类互相混淆最多那通常是纹理相近的病害比如早期斑点类。5. 避坑与排查10 类别叶片检测最容易翻车的地方5.1 类别索引错位导致全盘皆错现象训练正常启动loss 也降但预测时类别名和实际病害对不上比如把锈病预测成叶斑。原因classes.txt的顺序和标注时用的顺序不一致或者data.yaml里names的键值对写错了顺序。解决用第 2 章的自检脚本确认标签最大类别索引小于nc再人工核对classes.txt第 N 行是否就是标签里类别 N 对应的病害。这个坑最隐蔽因为训练过程不报错只有看预测结果才发现。5.2 图片与标签文件名不匹配现象训练日志显示train: 0 images或者实际参与训练的图片数远少于预期。原因images/train/abc.jpg对应的标签是labels/train/abc.txt如果标签文件名带了多余后缀或大小写不一致YOLO 匹配不上就跳过。解决写脚本比对两个目录的文件名主干集合差集就是问题文件。常见的是.JPG和.jpg大小写不一致在 Linux 下会被当成不同文件。5.3 归一化坐标被当成像素坐标现象可视化时框全部挤在图片左上角一小块区域。原因标注导出时用了绝对像素坐标没除以图片宽高。解决确认标签值是否都在 0 到 1 之间如果出现几十、几百的数值就是像素坐标需要按图片尺寸归一化。转换时注意每张图的宽高不同不能用一个固定值除。5.4 验证集和训练集图片重复现象验证集mAP50高得离谱接近 0.99但测试集或实际推理效果差很多。原因划分时没去重同一张图或高度相似的图同时出现在 train 和 val。解决对图片做感知哈希或简单的文件 MD5 去重确认 train 和 val 的图片集合交集为空。叶片病害图如果是在同一株植物上连续拍摄相邻帧相似度极高划分时要按植株或拍摄批次分不能随机按图分。5.5 小病斑在 640 分辨率下丢失现象模型对大块病斑检测正常对早期小斑点漏检严重。原因imgsz640下原图里几十像素的小病斑缩到几个像素特征几乎消失。解决把imgsz提到 1024 或 1280或者用切片推理把大图切成小块分别检测再合并。代价是显存和推理时间上升需要在精度和速度之间权衡。如果部署端算力有限优先保证输入分辨率而不是换更大的模型。6. 让这个数据集真正发挥价值的两个进阶技巧第一个技巧是类别合并与分层验证。10 个类别里如果有几类病害在视觉上高度相似强行让模型区分只会拉低整体指标。我的习惯是先跑一次全类别训练看混淆矩阵把互相混淆超过 30% 的类别合并成一个大类重新训练对比mAP50。如果合并后指标明显提升说明原始类别划分对当前模型容量来说过细部署时按合并后的类别输出反而更实用。反过来如果某一类样本数特别少但业务上必须单独识别就对该类做过采样并在验证时单独看这一类的召回率而不是只看总体mAP。第二个技巧是用测试集做一次完整的推理验证而不是只看训练日志。训练结束后用下面命令在测试集上跑推理并保存结果yolo detect predict \ modelruns/leaf/exp1/weights/best.pt \ sourceimages/test \ conf0.25 \ saveTrue \ projectruns/leaf \ nametest_predconf0.25是置信度阈值低于它的框不输出。这个值不是固定的病斑检测里如果漏检代价高就降到 0.1如果误检代价高就提到 0.4。跑完后把test_pred里的结果图和原图对比重点看漏检和误检分别出现在什么场景。这一步能发现训练指标看不出的问题比如模型在逆光或叶片重叠时表现骤降。我自己踩过最深的一个坑是拿到数据集直接开训跑完 100 轮看mAP50有 0.85 就觉得成了结果部署到实际拍摄的叶片上效果一塌糊涂。后来回头看可视化结果发现训练集里的图都是实验室均匀光照下拍的病斑清晰、背景干净而实际场景有阴影、有水珠、有虫咬痕迹。数据集本身没问题问题是我没确认它的采集条件和我的应用场景是否匹配。所以现在拿到任何数据集我都会先花半小时看可视化结果和采集环境再决定要不要做数据增强或补充场景数据。希望这个习惯能帮到你。本文还有配套的精品资源点击获取