
简介面向智慧农业病害识别、计算机视觉分类算法研究及深度学习者这份数据集围绕大白菜叶片病害检测场景构建包含约2800张已标注的叶片图像样本共划分为背蛾、潜叶虫、霉菌3个类别并已划分好训练集与测试集目录。包内文件总数为2000个核心部分是1998张jpg格式图像另外配有1个json标注文件用于保存类别映射关系、1个Python可视化脚本用于一键查看各类样本展示整体压缩包约350.83MB目录结构直观方便直接读取与训练。数据集自带预划分目录省去手工切分样本的流程配合常见的CNN分类项目或yolov5分类代码即可快速复现叶片病害识别链路json文件便于核对标签与图片对应情况运行show脚本还能直观查看各类别图片分布、检查数据质量。对于需要做数据增强、迁移学习或对比不同分类网络的读者这份样本集也可作为稳定的基础数据目前页面显示已有154人学习使用适合从入门到进阶的图像分类实践场景。1. 大白菜叶片病害图像识别数据集2800张标注图像到底能不能用做农作物病害图像识别的人迟早都会遇到一个很现实的问题手里能拿到的开源数据集要么是实验室环境下的单片叶子特写要么是Imagenet里那种“一棵白菜全身像”真正能直接丢给模型做训练、还带着完整标注框的农业场景数据少得可怜。大白菜叶片病害图像识别数据集就是冲着这个缺口来的——约2800张已标注图像覆盖大白菜叶部常见病害标注形式是按目标检测或实例分割的框/掩膜去做不是简单打一个“有病/没病”的图级别标签。它的价值在于你把这套数据拿去做YOLO、Faster R-CNN或者SSD的训练可以跳过自己下载原图、清洗、标注、格式转换这一整条又脏又累的流水线直接进入模型迭代环节。本文会从数据里有什么、标注到底怎么组织的讲起再给出用YOLOv8在这个数据集上训练的完整路径和参数建议最后把最容易让人翻车的几个坑摊开来讲。适合谁读准备做农业视觉落地项目的算法工程师、做毕业设计的学生以及想验证“小样本增强能不能撑起一个可靠检测模型”的研究者。如果你手里正好有一批大田或大棚里的白菜叶片图想快速搭一个病害检测原型这套已标注数据是性价比很高的起点。2. 病害类别体系与标注质量先搞清楚数据里有什么才不会白训2.1 大白菜常见叶部病害与数据集的类别映射大白菜叶部病害是一个很凌乱的视觉问题。田间常见的包括霜霉病、软腐病、黑斑病、白斑病、病毒病、炭疽病另外还有一类特别容易被误标成病害的——虫害啃食后的坏死斑它本质不是病但视觉上和黑斑病极度相似。拿到数据集的第一步不是急着写训练脚本而是把类别清单列出来对照真实农业植保知识做一次核对。因为不同的“病”在叶片上的表现形态差异非常大霜霉病在叶正面呈现褪绿斑块叶背有白色霉层软腐病往往从叶缘或伤口开始水渍状腐烂边界模糊黑斑病有同心轮纹边界相对清楚。这些形态差异会直接影响目标检测任务的难度——边界模糊的病斑天然比边界清晰的病斑难收敛。常见做法是把类别数控制在5到8个以内。如果这套数据集把“健康叶”也单独列成一类建议保留因为实际落地的检测器必须能区分“无病”和“有病”哪怕只是在推理端把健康叶的置信度阈值调高一些也比模型强行把所有叶片都框出来要好。如果你拿到的数据只有病斑正样本没有健康叶负样本训练出的模型很容易在真实场景里变成“宁可错杀一千”的扫描器这是农业视觉落地最常见的血泪教训之一。2.2 标注格式VOC、COCO还是YOLO txt直接影响后续流程这套数据集标注格式决定了你第一天的工作量。常见三种情况格式组织方式读取方式后续适配VOC XML每个图像对应一个XML文件框坐标为xmin/ymin/xmax/ymaxxml.etree.ElementTree解析需要自己转YOLO格式或用labelImg直接打开COCO JSON单/多个JSON文件含images、annotations、categories三段结构pycocotools读取训练检测器较方便但转换也容易漏掉segmentation字段YOLO txt每张图一个txt每行“class x_center y_center width height”直接读取YOLO系训练可直接引用但对多边形分割不友好如果你拿到的是VOC或COCO而你又打算用YOLO系模型第一步就是写转换脚本。很多人在这一步翻车原因只有一个坐标系的归一化方式搞错了。VOC里是绝对像素坐标YOLO里是相对图像宽高的归一化坐标而且YOLO的框中心点坐标是相对于左上角的。转换脚本的核心就三行import xml.etree.ElementTree as ET tree ET.parse(Annotations/xxx.xml) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) for obj in root.iter(object): cls obj.find(name).text box obj.find(bndbox) x1, y1, x2, y2 [float(box.find(t).text) for t in (xmin, ymin, xmax, ymax)] x_center, y_center (x1 x2) / 2 / img_w, (y1 y2) / 2 / img_h w, h (x2 - x1) / img_w, (y2 - y1) / img_h print(cls, x_center, y_center, w, h)这段代码的逻辑是先从XML里拿到图像宽高然后对每个标注对象用框的绝对坐标换算成归一化中心点和宽高。注意x_center的计算是(x1 x2) / 2 / img_w而不是(x2 - x1) / 2 / img_w后者算出来的是半宽不是中心点。这个错误极隐蔽但一旦写错训练出的模型所有预测框都会偏移。转换完成后用下面这段代码随机抽几张图把坐标还原画回图上核验这一步别省import cv2 img cv2.imread(JPEGImages/xxx.jpg) h, w img.shape[:2] with open(labels/xxx.txt) as f: for line in f.readlines(): cls, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check.jpg, img)这步的意义是建立“标注文件 → 像素坐标 → 视觉可读”的闭环。通常我会抽30到50张图做人工目检重点看大白菜这种叶片边缘卷曲、层叠的物体标框是不是经常把叶缘切掉或框进别的叶片。这类错误在自动转换时完全发现不了。2.3 图像尺寸、拍摄环境与数据分布读图脚本与统计方法标注质量之外图像本身的分辨率分布、光照条件、拍摄角度决定了你后面要不要加额外的预处理。大白菜叶片病害数据集里图像来源可能是无人机俯拍、手机近距拍摄、或者大棚监控截图三种来源的成像尺度差异非常大。俯拍图里一片叶子的病害区域可能只有几十个像素手机近距拍摄的图里病斑占满整个画面。这会让模型在推理时对“尺度”极其敏感。写一个快速统计脚本跑一遍全量图像拿到宽高分布、平均尺寸、以及标注框相对图像面积的占比分布import os import cv2 import numpy as np img_dir images/ areas [] sizes [] for fname in os.listdir(img_dir): img cv2.imread(os.path.join(img_dir, fname)) h, w img.shape[:2] sizes.append((w, h)) areas.append(w * h) sizes np.array(sizes) areas np.array(areas) print(图像数量:, len(sizes)) print(宽度范围:, sizes[:, 0].min(), -, sizes[:, 0].max()) print(高度范围:, sizes[:, 1].min(), -, sizes[:, 1].max()) print(面积中位数:, np.median(areas))跑完之后你会得到一张分布表。如果发现图像宽高比剧烈变化比如同时存在 4:3 的近距图和 16:9 的监控截图建议统一做 letterbox 处理再进网络而不是直接 resize 成方形——直接拉伸会改变病斑的形态比例对霜霉病这种靠纹理特征区分的病害伤害很大。图像尺寸小于 640×640 的样本要格外留意YOLOv8 默认输入是 640小图会被强行放大本来就不明显的病斑纹理会被插值糊掉。3. 用YOLOv8在大白菜叶片病害数据集上训练检测模型从目录结构到第一个模型3.1 数据目录组织与训练/验证/测试集划分YOLOv8 是目前做这种中小规模目标检测最顺手的框架训练命令简洁、显存要求不高、导出部署方便。2800张图像的数据量用 YOLOv8s 或 YOLOv8m 在单张 RTX 3060 上就能跑起来不需要上多卡。先把目录组织好YOLOv8 的 data.yaml 对目录结构有硬性要求稳妥的格式是dataset/ ├── images/ │ ├── train/ (约1960张) │ ├── val/ (约560张) │ └── test/ (约280张) └── labels/ ├── train/ ├── val/ └── test/划分时用随机种子固定结果别用 Python 的 random.shuffle 裸跑后面复现实验时会发现每次划分都不同所有对比实验都失去意义。更关键的是按图划分而不是按标注框划分——一张图里可能同时有霜霉病和黑斑病如果把同一张图的不同框分进 train 和 val就是典型的数据泄漏val 指标会虚假偏高。import os import random import shutil random.seed(42) img_files os.listdir(images/) random.shuffle(img_files) n len(img_files) train_cut int(n * 0.7) val_cut int(n * 0.9) splits { train: img_files[:train_cut], val: img_files[train_cut:val_cut], test: img_files[val_cut:] } for split, files in splits.items(): os.makedirs(fimages/{split}, exist_okTrue) os.makedirs(flabels/{split}, exist_okTrue) for f in files: shutil.copy(fimages/{f}, fimages/{split}/{f}) label f.replace(.jpg, .txt).replace(.png, .txt) if os.path.exists(flabels/{label}): shutil.copy(flabels/{label}, flabels/{split}/{label})注意这里的假设图像扩展名是 jpg 或 png标注文件名与图像名完全对应。拿到数据集后建议先确认这两点有些数据的标注文件命名是带后缀的比如img_001.jpg对应img_001_bbox.txt不统一的话上面脚本会漏掉一大部分标注。3.2 用YOLOv8训练训练命令与关键超参数目录结构就绪后data.yaml 是第一个要手写的文件。类别数量必须和标注文件里的 class id 最大数对齐不要出现标注里有 id7 但 categories 只写了 7 类这种错位训练时不会报错但验证集 mAP 会一直异常低。# data.yaml train: dataset/images/train val: dataset/images/val nc: 6 names: [霜霉病, 软腐病, 黑斑病, 白斑病, 炭疽病, 虫咬斑]训练命令yolo detect train \ modelyolov8s.pt \ datadata.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ momentum0.937 \ weight_decay0.0005 \ projectrun_rgb \ nameexp1参数说明lr0是初始学习率0.01 对从头训练比较激进但如果是基于 COCO 预训练权重微调0.01 是常见起点一般会在前几个 epoch 看到 loss 从 1.x 快速掉到 0.5 以下。lrf是最终学习率相对初始学习率的比例0.01 表示余弦退火后最后学习率是初始的百分之一。imgsz640对这套数据够用除非你确认大量病斑目标小于 16×16 像素才需要考虑用 960 或 1280代价是显存占用几乎翻倍。weight_decay别调到 0.001 以上农业图像背景杂乱正则过强会压制模型对病斑纹理的拟合。训练过程要盯三个指标box_loss、cls_loss、以及验证集上的mAP50。2800张数据里如果 mAP50 在 40 个 epoch 后还没到 0.6先不要盲目加 epoch回到第2章检查标注质量和类别分布。3.3 类别不平衡与复杂背景干扰数据增强参数怎么设大白菜叶片病害天然有不平衡问题。霜霉病在潮湿季节高发样本可能占总量40%白斑病相对少见可能只有5%。类别不平衡在目标检测里的影响是多数类把训练主导了少数类的 recall 会特别低。解决方式优先级依次是换评估指标看每个类别的 per-class AP别只看整体 mAP给少数类复制几张做基础增强调整 loss 里的 cls 权重。YOLOv8 在训练时默认开启 Mosaic 增强但 Mosaic 对某些病害识别反而有反效果。霜霉病的特征是叶背霉层Mosaic 把四张图拼在一起跨图边界的叶片纹理被切断模型学到的是“拼贴感”而不是病斑本身。如果你发现训练集的 loss 掉得很快、验证集 mAP 却停滞不前第一件事就是把 Mosaic 概率从默认 1.0 降到 0.5甚至关掉再对比一轮。更好的做法是在训练脚本里显式控制增强组合。常见支持的做法是用 ultralytics 的配置参数调整 hsv 扰动和几何增强的幅度from ultralytics import YOLO model YOLO(yolov8s.pt) model.train( datadata.yaml, epochs100, imgsz640, batch16, hsv_h0.015, # 色调扰动幅度别超过0.02大白菜叶色偏绿过度扰动会改变病斑颜色 hsv_s0.5, # 饱和度扰动 hsv_v0.3, # 明度扰动 degrees10.0, # 旋转角度正负10度 translate0.1, # 平移比例 scale0.5, # 缩放范围0.5-1.5 mosaic0.5, # Mosaic开启概率减半 fliplr0.5 # 水平翻转概率 )参数设定的逻辑hsv_h0.015看似很小但大白菜从外叶到内叶的颜色差异本身就大色调扰动太大会让“黄化叶片”和“病斑褪绿”无法区分。degrees10.0是因为田间拍摄时叶片角度随机但旋转超过15度后叶片形态严重变形模型会把“叶形”当成判别特征。scale0.5意味着模型每轮会随机看到原图 0.5 到 1.5 倍大小的目标这比直接改输入分辨率更有效地提升尺度鲁棒性。4. 把2800张数据榨出接近上万张的效果数据增强与样本清理4.1 基础增强组合翻转到色彩扰动数据增强的本质是在不改变语义标签的前提下扩展样本分布。对农作物叶片病害来说最重要的增强维度是颜色不是几何。原因很简单同一片病叶在不同光照下拍出来的颜色差距比不同病害类别之间的颜色差距还大。CAB白斑病的灰白色坏死斑在阴影下拍可能偏暗绿这时候模型很容易把它错认成霜霉病的褪绿斑。颜色类增强要小心“增强过头导致标签失真”的坑。对比实验里把饱和度扰动开到 0.8 以上时霜霉病叶背的白色霉层会变蓝紫色模型学到的颜色特征完全偏离真实分布。落地项目里我一般把饱和度扰动控制在 0.3 到 0.5 之间明度扰动控制在 0.3 左右。几何层面水平翻转是安全的垂直翻转要谨慎——大田拍摄的叶片不一定是正立的病斑形态没有方向性垂直翻转理论无害但如果数据里混入了少量带果柄或叶柄的样本翻转后模型可能学到错误的结构特征。4.2 Mosaic、Copy-Paste与AutoAugment要不要用Mosaic 增强的原理是把四张图拼成一张相当于变相增大 batch size、增强对小目标的覆盖。但它有一个反模式当你的数据集目标很大病斑占图面积比高且每张图里病灶密集时Mosaic 拼接会造成大量目标被截断。YOLOv8 训练时如果出现found truncated boxes警告你要意识到这些截断框不是标注错了是 Mosaic 拼接导致目标跨出图边界模型被迫学习不完整的形态。所以很多人看到 Mosaic 让 mAP 反而下降就开始怀疑标注其实方向找错了——先把 Mosaic 关掉对比一轮。如果 Mosaic 确实不适合你的数据替代方案是 Copy-Paste 增强把一张图中的病斑区域裁下来粘贴到另一张健康叶片区域上。这对大白菜叶片病害极其有效因为田间最大的问题不是“病斑长什么样”而是“病斑在复杂背景下能不能被识别出来”。Copy-Paste 相当于人为制造大量“健康叶片病斑”的组合逼模型把注意力放在病斑本身而不是整张图的色调。import cv2 import numpy as np def copy_paste_batch(bg_img, fg_img, fg_mask, max_paste3): out bg_img.copy() h, w bg_img.shape[:2] for _ in range(max_paste): fg_h, fg_w fg_img.shape[:2] scale np.random.uniform(0.6, 1.2) new_w, new_h int(fg_w * scale), int(fg_h * scale) fg_resized cv2.resize(fg_img, (new_w, new_h)) mask_resized cv2.resize(fg_mask, (new_w, new_h)) y_start np.random.randint(0, max(1, h - new_h)) x_start np.random.randint(0, max(1, w - new_w)) roi out[y_start:y_start new_h, x_start:x_start new_w] mask_inv cv2.bitwise_not(mask_resized) bg_roi cv2.bitwise_and(roi, roi, maskmask_inv) fg_roi cv2.bitwise_and(fg_resized, fg_resized, maskmask_resized) out[y_start:y_start new_h, x_start:x_start new_w] cv2.add(bg_roi, fg_roi) return out这段代码把病斑区域fg_mask 标出的部分随机缩放后贴到背景图健康叶片上max_paste控制一张图上最多贴几个病斑。粘贴时要注意病斑的缩放系数不要超过 1.2 倍否则粘贴区域会超出原始病斑叶片纹理被破坏形成视觉上不自然的色块。4.3 半自动标注回验不干净的数据集是模型的天花板2800张标注数据看着不多但标注的质量往往不被重视。常见问题是标注框没有紧贴病斑边界、小病斑被漏标、病斑重叠区域只标了一个框、以及把叶柄基部的坏死误标成病害。这些标注噪声在训练初期影响不明显——YOLOv8 前几十个 epoch 会先把大目标的粗粒度特征学会到后期 loss 降低越敏感的模型对标注误差越敏感你会发现 mAP 卡在 0.7 上不去怎么看都是模型欠拟合但实际是标注本身不对。一个可行的清理方法是用当前模型对训练集做一轮推理把每个预测框和 GT 的 IoU 小于 0.3 的样本提出来人工快速过一遍。这些样本大概率是漏标或者错标而不是模型不行。这个技巧在学术圈叫“learning from noisy labels”在工程上就是“标注清洗”。挑完之后标注不齐的框补一补误标的框删一删再训练一轮mAP 通常会有至少 3 到 5 个百分点的反弹。5. 大白菜叶片病害数据集训练避坑五个最容易翻车的环节5.1 现象Loss正常下降但mAP始终偏低训练时 box_loss 和 cls_loss 都稳步下降说明模型在拟合训练集但 val 集上 mAP50 卡在 0.4 上不去。先查两件事一是验证集里是否存在大量“迁移距离”过大的图像比如训练集全是手机近距拍摄验证集混入了俯拍图模型没见过俯拍角度泛化无从谈起二是看每个类别的 AP 分布用 results.csv 画 per-class 曲线几乎一定是某几个少数类把整体拉下来的。解决方式不是加轮次而是回到类别层面做均衡对少数类用 4.2 节的 Copy-Paste 增强或直接复制少数类样本 2 到 3 倍放进训练集优先于一切调参。5.2 现象验证集指标很好田间实拍效果明显变差这是农业视觉里最经典的场景迁移问题。数据集的图像是在某一特定光照时间、特定拍摄设备下采集的你去田间用手机拍光照角度变了、背景多了泥土和杂草、叶片上还有露水反光。模型在验证集上 mAP 0.75到了现场可能只有 0.3。缓解手段在数据层面很难解决因为模型见过多少种域就能在多少种域下工作。实操上我会在训练时把 MixUp 增强打开YOLOv8 默认带并把 mosaic 概率调高一点用于域混淆。另外对部署端做“输入标准化”训练时记下图像的均值和标准差推理时做同样的归一化不要训练和推理用两套预处理逻辑。5.3 现象小病斑目标漏检严重大白菜叶片上的霜霉病早期症状是 10×10 像素左右的小褪绿点YOLOv8s 在 640 分辨率下小目标的特征图只有 80×80意味着小于 8×8 像素的目标几乎不可见。解决办法按优先级排序一是把输入尺寸提高到 960这一步对小目标的收益最大二是换 YOLOv8m 或 l模型通道更宽对小目标的语义特征更敏感三是检查数据增强里是否开启了大规模随机缩放scale0.5 会导致一部分训练小目标被缩得更小。5.4 现象验证集划分后类别分布失衡如果你用固定随机种子划分数据但没考虑类别维度那么运气差的时候某类病斑的样本全部集中在验证集里训练集一类的 sample 数接近 0模型完全没见过这个类验证指标自然崩掉。正确做法是按类别做分层划分统计每张图的标注类别集合确保每个类别在训练集和验证集的比例大致等于整体比例。代码上其实不复杂from collections import Counter import numpy as np cls_per_image {} for f in os.listdir(labels/): with open(flabels/{f}) as fh: cls_ids set() for line in fh: cls_ids.add(int(line.split()[0])) cls_per_image[f.replace(.txt, .jpg)] cls_ids # 按类别集合做stratified split keys list(cls_per_image.keys()) random.seed(42) random.shuffle(keys) train_keys [k for k in keys[:int(len(keys)*0.8)]] val_keys [k for k in keys[int(len(keys)*0.8):]]5.5 现象标注框越过叶片边缘Loss来回震荡大白菜叶片边缘有波浪形皱褶当标注框是矩形时很难完全贴合叶片轮廓。如果标注框把相邻叶片的区域也框进来了模型实际上在学“一段叶缘一片邻叶”的混合特征。病斑靠近叶缘时框会同时包含叶片背景和背景泥土训练时 loss 会反复震荡尤其当这类样本占比高且同时出现在训练和验证集时。解决办法只有两个方向一是对这类样本做框的收缩处理把边缘坐标向内缩 3 到 5 个像素二是换用分割模型YOLOv8-seg直接用多边形标注贴合叶片轮廓代价是标注工作量和训练显存都会上一个台阶。6. 部署与迭代技巧用TensorRT把训练好的大白菜病害模型压到端侧训练和验证通过后真正的挑战是把模型放到实际使用环境里。大白菜病害识别的落地场景通常是农民或植保人员用手机拍一张叶片应用返回病害类型和置信度或田间固定摄像头每隔几秒采集一张画面后台自动告警。这两种场景都要求模型在 Jetson 或手机端实时跑。YOLOv8 训练得到的 .pt 文件不能直接用必须先导出成推理框架支持的格式。常见流程是把 PyTorch 模型导出为 ONNX再在部署端转成 TensorRT 引擎。导出命令如下from ultralytics import YOLO model YOLO(run_rgb/exp1/weights/best.pt) model.export(formatonnx, dynamicFalse, imgsz640, simplifyTrue, opset12)导出的 ONNX 文件可以直接用trtexec构建 TensorRT enginetrtexec --onnxbest.onnx --saveEnginebest.trt --fp16 --workspace2048--fp16会让精度略降但对农业视觉任务来说mAP 损失一般不超过 2 个百分点换来的是 Jetson Orin Nano 上推理速度从 45ms 降到 12ms 左右。如果目标是手机端ONNX 还可以转成 NCNN 或 MNN但针对大白菜这种色彩特征强的任务fp16 的 TensorRT 表现足够稳定没必要上 INT8 量化——INT8 对颜色的量化误差会在病斑边界上表现得特别明显霜霉病霉层的淡白色区域最容易出现带状伪影。部署后的验证我建议设计一个“田间模拟集”统计你实际场景中的光照条件在晴天上午、阴天、以及傍晚各拍 20 张真实叶片图人工标注后单独作为测试集。这个测试集不参与训练只用于推理验证。迭代的节奏是遇到模型误检了某一类病斑就把这个样本加入训练集做一次增量训练。增量训练别直接拿 best.pt 当预训练权重继续跑——大白菜叶片数据集内部的一致性较强用上次训练最后的权重接着训练 30 个 epoch比重新从 COCO 预训练权重启动收敛快得多效果也更稳。我自己的习惯是给每轮实验建立一个带日期的目录记录 data.yaml、增强参数、训练命令、以及最后跑出来每类的 AP 列表。这样三个星期后有人问你“这个模型是怎么训出来的”你拿得出完整的参数和复现命令而不是靠记忆。处理好上面这些细节2800张已标注数据的价值才能最大化发挥出来。希望帮到你。本文还有配套的精品资源点击获取