苹果叶病害数据集与YOLOv8训练全流程解析
2026/9/1 1:43:43 网站建设 项目流程

简介:本资源是面向农业AI、植物病害识别研究者与计算机视觉初学者的高质量苹果叶病害检测数据集,聚焦于果园真实场景下的细粒度分类与目标检测任务。数据涵盖5类常见病害:Alternaria斑点病、褐斑病、花叶病、灰斑病与锈病,全部图像源自实地果园采集与专业标注,并经系统性数据增强,确保类别均衡性与场景多样性,适用于YOLOv5/v8、Faster R-CNN等模型训练与算法验证。压缩包共2000个文件,含21119张JPG原图、21119份PASCAL VOC格式XML标注及21120份YOLO格式TXT标签,完整支持双标准训练流程;整体大小713.92MB,结构规范、开箱即用。目前已有469人学习下载,所附标签精准、样本丰富、项目实测可用,配套博主实际科研与Demo验证经验,可直接用于课程设计、毕业课题或轻量级部署实验。 拿到这个压缩包的时候,我第一反应是终于有人把农业场景的数据集整理得这么规整了。21119张苹果叶病害图片、5个类别、VOC和YOLO两种标注格式都给你备齐,这意味着你不需要再耗费几周时间去爬图、清洗、标注,直接解压就能开始训练。对于正在做目标检测、农业AI、植物病害识别相关项目的开发者来说,这个数据集省下的时间成本相当可观。这篇文章我会从数据格式解析、训练前数据体检、YOLOv8完整训练流程、常见坑排查到落地部署建议,把整个链路拆开讲透,尽量让你拿到手就能跑通。

1. 这个数据集到底能做什么:5类苹果叶病害的识别与检测逻辑

1.1 5类病害分别是哪些,为什么要识别它们

苹果种植过程中,叶部病害是影响产量和果品质量的核心因素之一。早期发现、精准定位病灶,是果园管理里最耗时也最关键的一环。这个数据集覆盖的5类病害,对应苹果叶部最常见的几种真菌性和病毒性病害,我在实际项目里遇到过的大致是这几类:苹果黑星病(叶片上出现橄榄色到黑色绒状斑点)、苹果锈病(叶片正面有橙黄色圆形病斑,后期背面凸起)、苹果褐斑病(病斑呈褐色,边缘不明显,严重时叶片枯焦)、苹果花叶病(叶片上出现黄绿色不规则斑驳,属于病毒病)、苹果炭疽病(病斑圆形,褐色到深褐色,有轮纹状排列的小黑点)。

这5类病害的形态差异其实挺明显的,有的靠颜色、有的靠纹理、有的靠边缘形状,普通目标检测模型只要数据量够、标注质量过关,识别难度不算太高。但要注意的是,苹果叶片在自然光照下的颜色变化很大,同一类病害在不同品种、不同生长阶段、不同光照条件下表现差异也很大。这个数据集有21119张图,平均下来每个类别约4223张,足够撑起一个小型专用检测模型的训练,尤其适合做迁移学习微调。

1.2 数据集的规模与标注格式,决定了它能适配哪些任务

21119张图这个规模,在农业病害检测领域算是比较充裕的。对比一下:很多公开的植物病害数据集只有几百到两三千张,做深度学习训练很容易过拟合;而超过两万张图的数据集,配上预训练权重做微调,在YOLOv8这种模型上基本能收敛到比较理想的效果。

数据集的标注格式是VOC和YOLO双格式。VOC格式是XML文件存储,记录了目标类别和边界框坐标,是很多传统检测框架(如Faster R-CNN、SSD)的标准输入格式;YOLO格式是TXT文件存储,每行一个目标,记录类别索引和归一化坐标,是YOLO系列模型的直接输入格式。双格式的好处很直接:你不用再拿标注工具导出互转,想用哪个框架都能直接开工。

从任务定位来说,这个数据集适合两个方向:

  • 检测任务:在叶片图像中框出病害区域,输出位置和类别。
  • 分类的预处理:如果你只想判断“有没有病”,可以把检测结果裁剪出来再做细粒度分类。

如果你做的是语义分割,这个数据集不适用,需要自己去补充分割标注。但用检测结果生成伪分割标签也不是不行,只是精度看运气,不建议。

2. 深入理解VOC与YOLO两种标注格式:目录结构与坐标系差异

2.1 解压后第一眼要看的目录结构

拿到压缩包解压后,正常的目录结构大概是这样的:

苹果叶病害数据集/ ├── annotations/ # VOC格式的XML标注 │ ├── train/ │ │ ├── 000001.xml │ │ ├── 000002.xml │ │ └── ... │ ├── val/ │ └── test/ ├── images/ # 原始图片 │ ├── train/ │ │ ├── 000001.jpg │ │ ├── 000002.jpg │ │ └── ... │ ├── val/ │ └── test/ ├── labels/ # YOLO格式的TXT标注 │ ├── train/ │ │ ├── 000001.txt │ │ ├── 000002.txt │ │ └── ... │ ├── val/ │ └── test/ ├── classes.txt # 类别列表,按索引排列 ├── train.txt # 训练集图片路径列表 ├── val.txt # 验证集图片路径列表 └── test.txt # 测试集图片路径列表

有的打包者会把train/val/test直接合并放在images根目录下,再用txt文件划分,这种也没问题,只要你的训练脚本能正确读取列表就行。先确认一点:classes.txt里的类别顺序,一定要和labels目录下TXT文件里的数字索引对应上。这个顺序错一位,模型训练的损失曲线看着正常,实际预测结果就全乱了。

2.2 VOC(XML)标注格式详解

VOC格式的XML文件长这样:

<annotation> <folder>images</folder> <filename>000001.jpg</filename> <path>/your/path/images/000001.jpg</path> <source> <database>Unknown</database> </source> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <segmented>0</segmented> <object> <name>black_rot</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>100</xmin> <ymin>120</ymin> <xmax>280</xmax> <ymax>310</ymax> </bndbox> </object> </annotation>

几个关键点:

  • <size>节点里的宽高是原始图片的像素尺寸,转换坐标时必须用到。
  • <object>节点里每个目标对应一个<name>(类别名)和<bndbox>(左上角xmin/ymin、右下角xmax/ymax,都是整数像素坐标)。
  • <difficult>为1表示该目标难以辨认,训练时通常会被忽略。如果标注者把难例标得很多,可能会影响模型收敛,训练前可以统计一下。

VOC的坐标是绝对的像素坐标,对于模型来说,直接输入时通常需要换算成归一化或者相对坐标。如果你用的是Faster R-CNN这类框架,它们有内置的VOC数据加载器,可以直接用;但如果你用YOLO系列,必须转成YOLO格式。

2.3 YOLO(TXT)标注格式详解

YOLO格式的TXT文件是每行一个目标:

2 0.468750 0.520833 0.156250 0.245833 0 0.625000 0.458333 0.093750 0.187500

从左到右分别是:

  1. 类别索引,整数,从0开始,对应classes.txt里的行号。
  2. 归一化中心点x坐标:(xmin + xmax) / 2 / image_width
  3. 归一化中心点y坐标:(ymin + ymax) / 2 / image_height
  4. 归一化宽度:(xmax - xmin) / image_width
  5. 归一化高度:(ymax - ymin) / image_height

所有坐标值都是0到1之间的小数。

很多人在这一步栽跟头:YOLO的TXT里存的是绝对像素坐标,而不是归一化坐标,导致训练时loss直接爆炸、指标全是0,因为模型输入的标签范围是0到1,结果却收到了几千像素的框。判断一份TXT是否归一化,很简单,打开看看数值是否都在0到1之间就行。

2.4 两种格式如何互相转换

VOC转YOLO的Python脚本,核心代码大概是这样的:

import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_file, output_dir, class_names): tree = ET.parse(xml_file) root = tree.getroot() size = root.find('size') img_w = int(size.find('width').text) img_h = int(size.find('height').text) base_name = os.path.splitext(os.path.basename(xml_file))[0] txt_file = os.path.join(output_dir, base_name + '.txt') lines = [] for obj in root.iter('object'): cls_name = obj.find('name').text if cls_name not in class_names: continue cls_id = class_names.index(cls_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) 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(txt_file, 'w') as f: f.write('\n'.join(lines)) class_names = ['apple_scab', 'apple_rust', 'brown_spot', 'mosaic', 'anthracnose'] xml_dir = 'annotations/train' out_dir = 'labels/train' os.makedirs(out_dir, exist_ok=True) for xml_name in os.listdir(xml_dir): if xml_name.endswith('.xml'): voc_to_yolo(os.path.join(xml_dir, xml_name), out_dir, class_names)

注意两个细节:

  • 类别列表的顺序必须和classes.txt完全一致,否则索引错位。
  • 转换前要检查图片尺寸是否和<size>节点一致。有些爬虫下来的图片被重采样过,但XML里的尺寸没更新,转换出来的坐标就会整体偏移。

反向转换(YOLO转VOC)麻烦一点,因为TXT里没有图片尺寸信息,必须去读对应图片的宽高。这就是为什么数据集的目录结构里图片和标签要成对放,光有TXT没有图片,转换的时候会直接卡住。

3. 训练前的数据体检:统计、清洗与目录整理实操

3.1 先统计类别数量与分布,别急着训练

我自己拿到一个数据集的第一件事,不是直接丢进训练脚本,而是先做统计。用下面的代码可以快速看每个类别的目标数量:

import os label_dirs = ['labels/train', 'labels/val', 'labels/test'] class_counts = {} total_boxes = 0 for label_dir in label_dirs: for txt_name in os.listdir(label_dir): with open(os.path.join(label_dir, txt_name), 'r') as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue cls_id = int(parts[0]) class_counts[cls_id] = class_counts.get(cls_id, 0) + 1 total_boxes += 1 print(f"总标注框数: {total_boxes}") for cls_id in sorted(class_counts.keys()): print(f"类别 {cls_id}: {class_counts[cls_id]} 个框")

统计结果出来后,重点关注两方面:类别间是否严重不均衡、训练集和验证集的类别分布是否一致。如果某一类的样本量只有另一类的十分之一,模型很容易偏向多数的类别,小类别的召回率会很低。这个数据集平均下来每个类别有4000多张图,算是比较均衡的,但个别类别因为天然难以表现(比如花叶病典型斑驳需要特定光照),数量偏少也是可能的。

3.2 检查标注异常:越界、空标签、文件名对不上

标注文件里的坑,最常见的几种:

  • 边界框越界:坐标值超出图片宽高范围,比如xmax大于图片宽度。这种框训练时会损失一部分梯度信息,甚至导致某些增强操作(如Mosaic)报错。
  • 空标签:图片有文件名,但TXT或XML里没有目标,或者TXT文件内容为空。这种情况在训练时会被当成背景图片,如果混入了验证集,指标计算会出偏差。
  • 文件名对不上:图片存在但没有标签文件,或反过来。数据集搬运、重命名过程中经常发生。

检查脚本可以这样写:

import os from PIL import Image img_dir = 'images/train' label_dir = 'labels/train' img_files = os.listdir(img_dir) label_files = os.listdir(label_dir) img_names = {os.path.splitext(f)[0] for f in img_files} label_names = {os.path.splitext(f)[0] for f in label_files} print("有图无标签:", len(img_names - label_names)) print("有标签无图:", len(label_names - img_names)) # 检查越界 for txt_name in label_files: base = os.path.splitext(txt_name)[0] img_path = os.path.join(img_dir, base + '.jpg') if not os.path.exists(img_path): continue with Image.open(img_path) as im: w, h = im.size with open(os.path.join(label_dir, txt_name), 'r') as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue _, x_center, y_center, box_w, box_h = map(float, parts) xmin = (x_center - box_w / 2) * w xmax = (x_center + box_w / 2) * w ymin = (y_center - box_h / 2) * h ymax = (y_center + box_h / 2) * h if xmin < 0 or ymin < 0 or xmax > w or ymax > h: print(f"{txt_name} 中越界框: {line.strip()}") break

注意:边界框轻微越界(比如0.5个像素以内)通常问题不大,YOLO训练时会自动裁剪。但明显超出图像范围的框,一定得处理,否则会在数据增强时暴露出大量空白区域,干扰模型学习。

3.3 data.yaml配置与数据划分

YOLOv8训练时需要一个data.yaml,配置训练集和验证集路径、类别数量、类别名称:

train: /path/to/dataset/images/train val: /path/to/dataset/images/val test: /path/to/dataset/images/test nc: 5 names: ['apple_scab', 'apple_rust', 'brown_spot', 'mosaic', 'anthracnose']

如果你只有一份train.txtval.txt路径列表,也可以直接用YOLO自带的工具划分:

python -c "from ultralytics.data.utils import check_det_dataset; check_det_dataset('data.yaml')"

划分时建议训练:验证:测试按8:1:1,如果图片数量少可以提高到9:0.5:0.5,但验证集数量太少会导致评估指标波动很大,不推荐低于200张。

4. 基于YOLOv8的完整训练流程与参数调整

4.1 环境准备与数据集放置方式

训练YOLOv8需要安装ultralytics库,Python 3.8以上,PyTorch 1.8以上:

pip install ultralytics

数据集的放置方式,我建议直接把imageslabels放到同一个父目录下,并且保持训练集、验证集的子目录名称一致。YOLOv8默认的逻辑是:如果train是图片目录路径,那么它会在同级的labels目录中找对应的TXT文件。目录结构如下:

dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml

这种结构是YOLOv8最常见的读取方式,强烈建议不要改动。有些同学把labels和images分开放在不同磁盘或不同命名空间里,后面所有脚本都要手动指定路径,徒增烦恼。

4.2 训练参数设置:batch、epochs、imgsz怎么定

官方推荐的基础训练命令:

yolo detect train data=/path/to/data.yaml model=yolov8s.pt epochs=100 batch=16 imgsz=640 device=0

参数选择的实际经验:

  • model:建议从yolov8s.pt开始。s版本比n版本精度高不少,比m版本快很多,在农业这种通用物体检测场景下性价比最高。如果先试跑验证流程,用yolov8n.pt更快。
  • batch:显存允许范围内尽量大。8G显存跑640分辨率,yolov8s大概能开batch=16;如果开到16报OOM,调到8,不要硬撑。batch太小(比如2、4)容易导致BN层统计量不稳定,收敛慢。
  • epochs:建议先跑50个epoch观察曲线,如果验证集损失还在下降,继续跑到100或150。迁移学习场景下100个epoch基本够用;从零训练则需要200以上。
  • imgsz:训练分辨率。640是速度和精度的平衡点。如果你要检测的病害斑块面积很小,可以尝试imgsz=960,但显存占用会大幅增加。用前先确认原始图片的分辨率分布,如果大部分图只有800x600,强行上960只会把图像放大,并不会增加有效信息。

一个经验公式:假设你的显卡是NVIDIA RTX 3060 12G,训练yolov8s、640分辨率、batch=16,大约每小时能完成4000到6000张图的epoch训练。21119张图,跑100个epoch大约需要3到5小时,这个时间成本是可以接受的。

4.3 训练过程的监控与效果评估

训练启动后,终端会实时输出每个epoch的loss、mAP50、mAP50-95、precision、recall等信息。

训练结束后,重点看这三个指标:

  • mAP50:IoU阈值0.5下的平均精度,衡量“框大致准不准”。农业病害检测场景,mAP50到0.85以上算不错。
  • mAP50-95:IoU阈值从0.5到0.95逐步提升的平均精度,更严格。这个值在0.6以上说明模型定位精度和分类能力都过硬。
  • F1-conf曲线:综合precision和recall的指标,用来帮你挑置信度阈值。不同类别的最佳阈值可能不一样,预测时可以按类别分别设阈值。

训练完成后,验证集预测结果可视化文件在runs/detect/train/val_batchX_pred.jpg,可以直接查看预测框和真实框的对比。如果预测框有严重的重复框或漏检,说明NMS阈值(confiou)需要调。

4.4 训练结果推理测试

训练完成后,对单张图片推理:

yolo detect predict model=runs/detect/train/weights/best.pt source=/path/to/test/image.jpg conf=0.25

conf=0.25是默认置信度阈值。实际测试时可以在0.1到0.5之间多试几次,找到视觉效果最好的阈值。如果模型对某类病害漏检严重,可以单独注意该类别的置信度分数,适当调低阈值。

对测试集批量推理并计算指标:

yolo detect val model=runs/detect/train/weights/best.pt data=/path/to/data.yaml split=test

这里有个容易被忽略的点:split=test必须指定,否则默认只跑验证集。如果你的data.yaml里没有定义test字段,这一步会直接报错。

5. 训练过程中最常见的5个坑与排查方法

5.1 无法加载标签文件,报Error loading label

这个提示是YOLO系列的经典报错。通常原因:

  • TXT文件里某行不是5个字段,或者坐标值不是0到1之间的数字。
  • 类别索引超出nc范围,比如nc=5但TXT中出现了cls_id=5
  • 图片和TXT文件名不一致。

排查方式很简单,看报错信息里具体是哪个文件,打开检查一下。如果数量不多,直接手动修正;如果整个数据集大面积出错,回到第3节的检查脚本去批量处理。

5.2 训练集损失下降,验证集指标却很低

典型的过拟合信号。数据集只有几千张图时很常见。应对手段有两个方向:

  • 数据增强:YOLOv8默认开启Mosaic、HSV扰动、翻转等增强。可以适当调高hsv_hhsv_shsv_v的数值,模拟不同光照条件下苹果叶片的颜色变化。
  • 正则化:调低dropout或用weight_decay控制权重衰减。YOLOv8里增加weight_decay=0.0005通常能压一压过拟合。

另外要注意:如果验证集的病害形态和训练集相差太大(比如训练集全是晴天拍摄,验证集全是阴天拍摄),那模型泛化能力不足的本质不是过拟合,是数据分布差异太大。这种情况需要重新划分数据集或者补充更多样化的图片。

5.3 叶片上的小病斑总是漏检

苹果黑星病和炭疽病的斑点在早期只有几个像素大小,在640分辨率的特征图上很容易被下采样丢掉。解决办法:

  • 提高imgsz到960或1280,直接放大输入图像,小目标对应更多的特征像素。
  • 使用SAHI(Slicing Aided Hyper Inference)方案,把大图切块后分别推理,再合并结果。这个方案对小目标检测提升非常明显,代价是推理时间增加。
  • 如果只是个别类别漏检,检查一下该类别的标注框是否过小。YOLO默认的anchor偏好中等偏大的目标,极小目标需要专门调。

5.4 数据增强策略对植物病害检测的影响

注意一个农业场景的细节:植物叶片病害的识别,很多特征依赖真实的颜色和纹理。YOLOv8默认的hsv_h=0.015hsv_s=0.7hsv_v=0.4,对于颜色敏感的病害检测,HSV饱和度扰动到0.7可能有点过了。比如苹果锈病的橙色病斑,颜色被过度增强后可能变得不典型,模型学到的颜色特征会偏。

我一般会把hsv_s调到0.3左右,hsv_v调到0.2,既能模拟不同光照,又不会让颜色失真太严重。这个参数调整没有绝对标准,最好拿着验证集效果来回试。

5.5 显存不足与训练速度优化

跑大图高分辨率时显存不够,几个实用的招:

  • 调小batch,但不要低于8。
  • 开启amp(混合精度训练),YOLOv8默认开启,能省约30%显存,速度还更快。
  • 使用梯度累积,比如batch=8配合accumulate=4,等效于batch=32的效果,但训练时间会变长。
  • 在数据加载瓶颈明显时,调大workers(DataLoader线程数)到CPU核心数的2倍以内,能让GPU利用率明显提升。Windows上workers=0有时反而更快,因为多进程开销大。

我实际遇到过一种情况:workers=8时CPU占用直接拉满,GPU利用率掉到60%左右,把workers降到4之后反而稳定在95%以上。所以这个参数真的要结合实际机器来调,别盲信默认值。

6. 从模型到落地:苹果叶病害检测的扩展与实用建议

6.1 模型部署到移动端与边缘设备

如果你要把训练好的模型部署到果园现场的边缘设备(比如树莓派、Jetson Nano、手机端),YOLOv8提供了导出功能:

yolo export model=best.pt format=onnx imgsz=640

导出ONNX后可以再转成TensorRT(NVIDIA设备)或NCNN(移动端),推理速度能提升数倍。需要注意:导出时imgsz必须和训练时一致,否则输出层的anchor尺寸会不匹配,推理结果会错乱。

边缘设备上的推理,建议把输入图像先做等比例缩放,然后在上下或左右补灰边到640x640,而不是直接拉伸。直接拉伸会让病斑的形状比例失真,检测框位置也会偏移。

另外,模型量化到INT8可以进一步减小体积和加速,但精度会有一定损失。对苹果病害检测这种任务来说,少量精度损失通常可以接受,前提是你验证集上的mAP50还能保持在0.8以上。

6.2 后续如何扩充数据集,让模型更鲁棒

这个数据集虽然有两万多张图,但如果你要把模型用到不同地区、不同品种的果园,现场数据分布大概率会偏移。我建议按以下优先级补充数据:

  • 不同光照和天气条件下的叶片图(正光、逆光、阴天、雨天)。
  • 不同生长阶段的叶片(嫩叶、老叶对病害的呈现差异很明显)。
  • 不同拍摄设备(手机、无人机、固定摄像头)的分辨率和色彩风格差异。
  • 叶片互相遮挡、多种病害混合发生的场景,这种最难标,但对落地最有用。

补充数据后的标注,可以先用现有模型做自动预标注,生成YOLO格式的TXT,再用标注工具人工修正。这个流程能把标注时间缩短一半以上。我自己试下来,模型越训越好的关键不是单次训练调到多完美,而是建立“收集数据-预标注-人工校正-增量训练”的循环,每次训练都比上一次多覆盖一些真实场景。

在实际操作中我还有个体会:训练结果好坏,很多时候不取决于模型结构,而取决于训练前花多少时间做数据检查。有些数据集拿到手,你以为标签是好的,实际一查一堆越界框和错误类别,训练出来的模型外行看着还行,内行一看PR曲线就知道有问题。花一个小时跑一遍体检脚本,后面能省一整周调试的时间,这笔账怎么算都划算。

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

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

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

立即咨询