简介:这份苹果树叶病害数据集面向智慧农业、病害智能识别App开发及课程作业、竞赛与科研项目,聚焦花叶病、斑点落叶病、叶枯病三类叶片病害的检测与识别任务。数据共916张实拍图像,背景丰富、目标大小与角度多样且分布均匀,整体多样性充足,适合训练与验证多种目标检测算法。压缩包共3665个文件,约84.45MB,包含916张jpg原图,以及一一对应的voc格式xml、yolo格式txt和json三种标注文件,另有少量说明类txt,可直接接入主流检测框架,省去格式转换成本。所有标注均为纯手工完成,目标框精准,算法拟合效果良好,数据质量可靠。目前已有1209人学习下载,无论用于课程设计、比赛打榜还是实际项目落地,都能获得一份开箱即用、标注规范的苹果叶病害检测数据基础。
1. 苹果叶病害数据集到底解决什么问题:从 916 张图说起
去年帮一个做智慧农业的团队看模型,他们信心满满地拿公开的 PlantVillage 数据集训了个分类网络,准确率刷到 98%,结果拉到陕西一个果园实测,斑点落叶病和花叶病几乎全认错。问题不在网络,在数据——PlantVillage 是实验室单叶、纯色背景、均匀光照,而果园里是逆光、遮挡、多叶重叠、病斑还处在早期。这就是为什么一个靠谱的苹果叶病害数据集,比换十个模型都重要。手上这个「苹果树叶三种病害数据集 916 张,含 VOC+YOLO+JSON 三种格式标签」,本质上是把目标检测落地到果园场景的第一块砖:它同时给了 PASCAL VOC 的 XML、YOLO 的 txt、以及 COCO 风格的 JSON,意味着你不管走 YOLO 系还是走 Detectron2、MMDetection 系,都能直接开训,不用再自己写格式转换脚本。它适合三类人:想入门智慧农业目标检测的学生、要快速验证果园病害识别可行性的算法工程师、以及需要给采摘机器人或巡检无人机做视觉模块的集成方。916 张不算大,但三种格式齐全、类别聚焦在花叶病和斑点落叶病这两类高发叶部病害上,做迁移学习和基线验证足够用。
2. 三种标签格式怎么选:VOC、YOLO、JSON 的取舍与转换
拿到一个数据集,第一件事不是急着训,而是搞清楚三种标签格式各自代表什么、你的训练框架吃哪一种。很多人栽在这一步:下载完发现 YOLO 的 txt 里全是归一化坐标,自己按 VOC 的绝对像素去读,框全飞到图外,白跑一晚上。
2.1 VOC XML、YOLO txt、COCO JSON 的本质差异
PASCAL VOC 格式用 XML 存每张图的标注,一个<object>对应一个框,<bndbox>里是xmin/ymin/xmax/ymax的绝对像素坐标,还带<name>类别名。它的好处是可读性强、工具链老牌,LabelImg 默认就吐这个。缺点是文件多、解析慢,一张图一个 XML,916 张就是 916 个文件。
YOLO 格式是每张图一个同名 txt,每行class_id x_center y_center width height,全部是相对图像宽高的归一化值,范围 0~1。它没有类别名,类别靠一个classes.txt或data.yaml里的names列表按索引对应。这个格式训练时读取最快,Ultralytics 系的 YOLOv5/v8/v11 都直接吃。
COCO JSON 是单个大文件,里面images、annotations、categories三个数组互相用 id 关联,框是[x, y, width, height]绝对像素。Detectron2、MMDetection、以及大部分做实例分割和多任务的框架都认它。它的坑在于 id 必须严格对应,category_id从 1 开始还是从 0 开始,不同框架要求不一样。
| 格式 | 坐标类型 | 类别信息 | 典型框架 | 单文件还是多文件 |
|---|---|---|---|---|
| VOC XML | 绝对像素 | 内嵌 name | LabelImg、老版 TF | 每图一个 |
| YOLO txt | 归一化 0~1 | 靠索引+names | Ultralytics YOLO 系 | 每图一个 |
| COCO JSON | 绝对像素 | categories 数组 | Detectron2、MMDetection | 单个大文件 |
2.2 用脚本在三种格式间互转并校验
实际项目里你经常需要从一种转到另一种。下面这个脚本把 VOC XML 转成 YOLO txt,同时做边界校验,防止出现坐标越界或宽高为负的脏标注。
import os import xml.etree.ElementTree as ET # 类别映射:顺序决定 YOLO 的 class_id,务必和 data.yaml 的 names 一致 CLASS_MAP = {"huayebing": 0, "bandianluoyebing": 1} def voc_to_yolo(xml_dir, out_dir, img_w, img_h): 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() # 优先从 XML 里读真实尺寸,读不到才用传入的默认值 size = root.find("size") w = int(size.find("width").text) if size is not None else img_w h = int(size.find("height").text) if size is not None else img_h lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in CLASS_MAP: continue # 遇到未登记类别直接跳过,避免索引错乱 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) # 裁剪到图像范围内,防止标注越界 xmin, xmax = max(0, xmin), min(w, xmax) ymin, ymax = max(0, ymin), min(h, ymax) if xmax <= xmin or ymax <= ymin: continue # 宽高非法,丢弃这个框 xc = (xmin + xmax) / 2.0 / w yc = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{CLASS_MAP[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}") out_name = os.path.splitext(xml_file)[0] + ".txt" with open(os.path.join(out_dir, out_name), "w") as f: f.write("\n".join(lines)) voc_to_yolo("./Annotations", "./labels", 640, 640)逻辑上分四步:解析 XML、按类别映射取 id、把绝对坐标转成归一化中心点加宽高、写同名 txt。参数上CLASS_MAP是最容易翻车的地方,它的顺序必须和训练时data.yaml里names的顺序严格一致,否则模型学出来的类别会整体错位。img_w/img_h只是兜底,真正靠谱的做法是从 XML 的<size>里读,因为数据集里图片尺寸可能不统一。校验部分做了两件事:坐标裁剪到图像边界、丢弃宽高非法的框,这两条能挡掉大部分脏标注导致的 loss 爆炸。
2.3 转换后必须做的三项一致性检查
转完不是就完事了,我一般会跑三个检查。第一,数量对齐:labels目录下的 txt 数量应该等于images目录下的图片数量,少一个都说明有图没标注或文件名不匹配。第二,类别分布统计:把所有 txt 的 class_id 拉出来计数,如果某一类为 0,说明映射写错了。第三,可视化抽检:随机抽 20 张,把归一化框还原画回图上,肉眼看框有没有偏移。这三步加起来不到十分钟,能省掉你训到一半发现 mAP 死活上不去的痛苦。
# 数量对齐检查 ls images | wc -l ls labels | wc -l # 类别分布统计(统计每行第一个字段) cat labels/*.txt | awk '{print $1}' | sort | uniq -cawk '{print $1}'取每行第一列即 class_id,uniq -c统计频次。如果输出里只有 0 没有 1,或者某一类数量明显异常少,回去查CLASS_MAP和原始标注。
3. 用 YOLOv8 在 916 张图上跑通第一个基线
格式理清之后,最实际的目标是先跑出一个能看的基线。916 张图属于小数据集,直接从头训容易过拟合,正确姿势是用预训练权重做迁移学习。这一章按 Ultralytics YOLOv8 的流程走,因为它的数据配置最简单,适合快速验证。
3.1 目录结构与 data.yaml 的正确写法
YOLO 系对目录结构有约定,最稳妥的是这样组织:
apple_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml内容如下:
path: ./apple_dataset train: images/train val: images/val nc: 2 names: 0: huayebing 1: bandianluoyebingpath是数据集根目录,train/val是相对path的路径,别写成绝对路径,换机器就废。nc是类别数,这里 2 类。names的索引必须和前面转换脚本里CLASS_MAP完全对应,这是血泪经验——顺序错了模型不会报错,只会安静地学错。
3.2 训练命令与关键超参设置
yolo detect train \ data=apple_dataset/data.yaml \ model=yolov8n.pt \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=30 \ project=runs/apple \ name=baseline逐项说参数。model=yolov8n.pt用 nano 版预训练权重,916 张图用大模型纯属浪费还算得慢,n 版足够跑基线。epochs=150对小数据集偏多,但配合patience=30早停,实际可能 80 轮就停了。imgsz=640是 YOLO 的默认输入尺寸,如果你的原图分辨率远大于 640,病斑又小,可以提到 960 试试,但显存和速度会涨。batch=16在 8G 显存上跑 640 尺寸基本安全,爆显存就降到 8。lr0=0.01是初始学习率,迁移学习场景下这个值偏大,如果 loss 前几轮就震荡,降到 0.001。patience=30表示 30 轮验证指标不涨就停,省时间。
3.3 训练过程看什么指标、怎么判断过拟合
训练时终端会打印每轮的 box_loss、cls_loss、mAP50、mAP50-95。重点看两个:cls_loss是否稳定下降,mAP50是否在涨。如果box_loss一直降但mAP50不涨,多半是分类头没学好,检查类别映射。如果训练集 loss 降得很低但验证集 mAP 早早到顶然后回落,就是过拟合,这时候加数据增强(mosaic、hsv_h、hsv_s)或者减模型容量。
# 训练完在验证集上单独评估 yolo detect val model=runs/apple/baseline/weights/best.pt data=apple_dataset/data.yamlval命令会输出每类的 P、R、mAP50。如果花叶病和斑点落叶病两类指标差距很大,说明其中一类样本太少或标注质量差,回去补数据比调参有用。
4. 小数据集训练避坑:从标注脏到过拟合的五个真实翻车
916 张图做检测,坑基本集中在数据和训练配置上,模型结构反而没什么可调的。下面五条是我和身边人真实踩过的。
4.1 现象:mAP 卡在 0.3 上不去,loss 也不降
原因:标注框大面积偏移或类别索引错位。常见于自己用脚本转格式时,CLASS_MAP顺序和data.yaml的names不一致,或者 VOC 转 YOLO 时忘了归一化。
解决:先跑 2.3 里的可视化抽检,把框画回图上肉眼确认。再核对CLASS_MAP和names的索引顺序。这两步能解决八成「训不动」的问题。
4.2 现象:训练正常,但验证时提示找不到标签
原因:YOLO 要求images/train/xxx.jpg对应labels/train/xxx.txt,路径是替换images为labels并改后缀。如果你把图片和标签放在不同层级,或者文件名有大小写差异,就会找不到。
解决:保证 images 和 labels 目录结构完全镜像,文件名(不含后缀)严格一致。用diff <(ls images/train | sed 's/\..*//') <(ls labels/train | sed 's/\.txt//')对比两边文件名列表。
4.3 现象:某一类几乎检测不出来
原因:类别不平衡。花叶病和斑点落叶病在 916 张里数量可能差好几倍,少的那类模型学不到。
解决:统计两类框数量,差距超过 3 倍就考虑对少样本类做过采样,或者用copy_paste增强。也可以在 loss 里给少样本类加权,但 YOLOv8 默认不直接暴露这个参数,改起来麻烦,优先补数据。
4.4 现象:训练 loss 很低,实测新图全错
原因:过拟合加域偏移。916 张如果都来自同一批次、同一光照条件,模型记住的是这批图的纹理而不是病斑特征。
解决:加强数据增强,特别是hsv_h=0.015、hsv_s=0.7、hsv_v=0.4这类颜色扰动,模拟不同光照。mosaic=1.0默认开着,别关。如果条件允许,混入少量其他来源的苹果叶图做验证。
4.5 现象:显存溢出,batch 降到 1 还是爆
原因:imgsz设太大,或者workers开太多导致内存泄漏。
解决:先把imgsz降到 640 甚至 512,再把workers设为 4 或 2。如果还爆,检查是不是cache=True把整个数据集缓存进内存了,小显存机器关掉缓存。
5. 从基线到可用:提升小样本病害检测的几个具体技巧
基线跑通只是开始,真正要落地到果园,还得在有限数据上榨出更多性能。这一章讲几个我实际用过、对 916 张这种量级有效的技巧。
5.1 用预训练权重的冻结策略省数据
YOLOv8 支持冻结 backbone 前若干层,让模型先学检测头,再解冻微调。数据少的时候这招很管用。
# 先冻结 backbone 训 50 轮,让检测头适应新类别 yolo detect train data=apple_dataset/data.yaml model=yolov8n.pt \ epochs=50 freeze=10 lr0=0.01 name=frozen # 再解冻全部,用更小学习率微调 yolo detect train data=apple_dataset/data.yaml \ model=runs/apple/frozen/weights/best.pt \ epochs=100 lr0=0.001 name=finetunefreeze=10表示冻结前 10 层,具体层数看模型结构,n 版可以试 10。第一阶段学习率可以大一点,因为只训检测头;第二阶段解冻后学习率必须降下来,否则预训练特征会被破坏。这个两段式训练在小数据集上通常比直接训涨 2~5 个点 mAP。
5.2 针对病斑尺寸调整 anchor 和输入分辨率
苹果叶病斑在整张图里占比可能很小,尤其是早期斑点。默认 anchor 是为 COCO 那种中大目标设计的,小目标召回差。两个办法:一是把imgsz提到 960,让病斑在特征图上占更多像素;二是用 YOLOv8 的自适应 anchor,它训练时会自动聚类,但前提是你的标注框尺寸分布合理。
# 提高分辨率重训,注意 batch 要相应减小 yolo detect train data=apple_dataset/data.yaml model=yolov8n.pt \ imgsz=960 batch=8 epochs=150 name=highresimgsz=960相比 640,小目标召回通常有提升,代价是显存翻倍、速度减半。如果部署端是边缘设备,得权衡。
5.3 用验证集上的混淆矩阵定位类别混淆
训练完 YOLO 会在 runs 目录生成confusion_matrix.png。如果花叶病和斑点落叶病互相误判严重,说明两类视觉特征在模型眼里太像,可能是标注时边界没划清,或者某些图里两类同时出现但只标了一类。
# 生成混淆矩阵和 PR 曲线 yolo detect val model=runs/apple/finetune/weights/best.pt \ data=apple_dataset/data.yaml plots=Trueplots=True会输出 PR 曲线、混淆矩阵、F1 曲线。重点看混淆矩阵对角线,非对角线上的大数值就是主要错误来源。如果两类混淆严重,回去复查标注规范,明确花叶病(叶片黄绿相间斑驳)和斑点落叶病(褐色圆形斑点)的判定边界,重新过一遍有争议的图。
5.4 导出与部署前的最后验证
训完的模型要部署,先导出成通用格式。边缘设备常用 ONNX 或 TensorRT。
# 导出 ONNX yolo export model=runs/apple/finetune/weights/best.pt format=onnx imgsz=640 # 导出 TensorRT(需要 GPU 环境) yolo export model=runs/apple/finetune/weights/best.pt format=engine imgsz=640 half=Truehalf=True用 FP16 推理,速度大约翻倍,精度损失通常可接受。导出后务必用几张真实果园图跑一遍推理,确认框的位置和类别和 PyTorch 版一致,格式转换偶尔会有坐标偏移的玄学问题。
我自己的习惯是,任何数据集拿到手,先花半天把格式、类别分布、可视化抽检做完,再动训练命令。这半天看着慢,实际是给自己买后悔药——数据层面的问题,训到一半才发现,浪费的是整晚的算力和第二天的调试时间。916 张不算多,但把上面这些步骤走扎实,跑出一个能在果园里认出花叶病和斑点落叶病的基线模型,是完全可行的。希望帮到你。
本文还有配套的精品资源,点击获取