ARTICLE DETAIL

资讯详情

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

水稻叶病虫害分类数据集整理与YOLO11cls训练避坑指南

水稻叶病虫害分类数据集整理与YOLO11cls训练避坑指南 简介面向水稻叶病虫害识别任务的中文分类数据集资源适合算法工程师与农业AI研究者用于模型训练与场景验证。数据集包含细菌性叶枯病、褐斑病、健康叶片、叶瘟病、叶鞘腐病、窄褐斑病、穗颈瘟、稻飞虱、纹枯病、钨黄病毒病共10个类别共15000张真实环境下的高质量水稻叶片图片按类别文件夹整理标注规范可直接用于YOLO11cls等分类算法的训练与评估。压缩包内仅含1个PDF文件大小2.32MB因原始数据体量较大托管在百度网盘PDF内附数据集基本情况说明及百度网盘获取方式。目前已有318人学习适合作为水稻病虫害实际项目落地或通用分类数据集扩充的参考。附赠YOLO11cls一键训练脚本及博主训练结果日志可帮助读者快速复现训练流程并观察各类别识别效果降低从数据到模型的搭建门槛。1. 水稻叶病虫害分类数据集配YOLO11cls15000张图真正要解决的是数据组织问题真正的瓶颈从来不是YOLO11cls模型本身而是你手头有一堆水稻叶病虫害照片时不知道哪些图该归到哪一类、病斑长相近的类别怎么和模型说清楚。目标分类任务看名字简单做起来却很现实15000张图如果只是堆在一个目录里YOLO11cls训练时就会把背景、光照、拍摄角度一起学进去准确率再高也不敢下田用。这套分类文件夹整理与YOLO11cls一键训练脚本的组合解决的正是从散装图片到可复用分类模型之间的脏活先把图按类别归好再用脚本完成划分、训练、验证。适合刚拿到水稻叶病虫害图像、准备训练第一个分类模型的工程师也适合用yolov5或yolov8训练过自己数据集、想转到YOLO11cls的从业者。2. 先弄懂分类文件夹整理的含义YOLO11cls需要的只是目录不是标注文件2.1 分类文件夹为什么比任何标注格式都省心检测任务要画框分割任务要画mask而目标分类任务只需要一个约定同一类别的图片放在同名文件夹里文件夹名就是标签。这个约定在深度学习里叫ImageNet风格目录结构YOLO11cls的classification模式原样继承了这个设计。你不需要写xml不需要写txt不需要标注框坐标分类任务的全部标注信息都藏在目录名里。数据格式选什么直接决定后面脚本的复杂程度。我见过不少从检测转过来的朋友第一反应是给图片写标注文件其实分类任务根本不需要。你只要保证数据集根目录下每个类别一个文件夹里面放好对应图片即可。15000张图听起来很多但如果按8个类别来分平均每个类别不到2000张这个量级做微调是够用的。真正要小心的不是总数而是类别之间有没有人肉眼都分不清的相近病斑这会在第五章验证部分细说。如果数据已经按类别分好文件夹最终要形成的标准目录结构是这样的rice_leaf_dataset_split/ ├── train/ │ ├── leaf_blast/ # 稻叶瘟 │ │ ├── leaf_blast_0001.jpg │ │ ├── leaf_blast_0002.jpg │ │ └── ... │ ├── leaf_blight/ # 白叶枯 │ ├── brown_spot/ # 胡麻叶斑病 │ ├── sheath_blight/ # 纹枯病 │ └── healthy/ # 健康叶 └── val/ ├── leaf_blast/ ├── leaf_blight/ ├── brown_spot/ ├── sheath_blight/ └── healthy/注意我们用的是train和val两层结构而不是把15000张图全部塞进类别文件夹里直接开训。Ultralytics的classify接口接收data参数时默认按train和val两个子目录去找图如果你的数据一开始没有划分脚本里就得自己切。这也解释了为什么对应分类文件夹整理这么重要——目录结构对了后面脚本能写得很薄目录结构不对脚本会写出一堆补丁逻辑来收拾数据。2.2 别把整理任务做成手工活用Python把散图归到类别文件夹手里图片的命名通常是不规律的常见有一批按采集时间命名一批按病害拼音缩写命名。最稳妥的做法是先写一个扫描脚本把图片预览和文件名输出成清单人工确认几轮再交给脚本去搬。单纯的复制粘贴在Windows资源管理器里做几千张还行做到上万张就容易漏而且无法复现。下面是一个整理脚本它假设你的原始图片已经按类别放了子目录类别子目录名就是要用的标签import os import shutil from collections import Counter src_root raw_images # 原始图片目录里面按类别分好了子目录 dst_root rice_leaf_dataset # 整理后的数据集根目录 class_labels sorted([ d for d in os.listdir(src_root) if os.path.isdir(os.path.join(src_root, d)) ]) for cls in class_labels: src_cls_dir os.path.join(src_root, cls) for img_name in os.listdir(src_cls_dir): if not img_name.lower().endswith((.jpg, .jpeg, .png)): continue dst_cls_dir os.path.join(dst_root, cls) os.makedirs(dst_cls_dir, exist_okTrue) shutil.copy2( os.path.join(src_cls_dir, img_name), os.path.join(dst_cls_dir, img_name), ) stat Counter() for cls in os.listdir(dst_root): cls_dir os.path.join(dst_root, cls) if os.path.isdir(cls_dir): stat[cls] len([ f for f in os.listdir(cls_dir) if f.lower().endswith((.jpg, .jpeg, .png)) ]) print(整理完成各类别图片数) for cls, n in stat.most_common(): print(f{cls}: {n})这段脚本的逻辑是先扫描原始目录下有哪些类别子目录类别名直接作为标签然后把每张图片复制到目标目录的对应类别文件夹里。复制而不是移动是为了保留原始素材万一类别分错了还能重来。最后用Counter统计每个类别的图片数量这一步非常重要类别不平衡会让模型看起来准确率很高、实际却在偷懒第四章会专门讲。有个高频翻车点文件名里带中文或空格。Ultralytics框架本身能处理中文路径但不同操作系统的路径编码不统一容易出现能找到文件但打不开的怪问题。我一般会在整理阶段就把文件名统一改成英文字母加数字例如leaf_blast_0001.jpg这样做的好处是后面换服务器、换操作系统都不会因为路径编码问题突然报错。2.3 数据体检损坏图片与重复图片的排查整理完目录后不要急着训练先跑一遍体检。最常见的雷是下载或拷贝过程中图片文件损坏PIL读不出来或者同一张图被保存了多个尺寸版本导致训练集和验证集之间出现近重复样本让验证指标虚高。from PIL import Image import os data_root rice_leaf_dataset bad_files [] for cls in os.listdir(data_root): cls_dir os.path.join(data_root, cls) if not os.path.isdir(cls_dir): continue for img_name in os.listdir(cls_dir): img_path os.path.join(cls_dir, img_name) try: with Image.open(img_path) as im: im.verify() # 只检查文件结构不加载完整像素 except Exception: bad_files.append(img_path) print(f发现 {len(bad_files)} 个损坏文件) for p in bad_files[:20]: print(p)这里有个小坑Image.verify()调用之后这张图的句柄就失效了如果你想继续对图像做处理需要重新open一次。所以上面代码里用with Image.open包住就是为了防止后续误用同一个句柄。重复图片的去重我一般不在这一步做因为分类任务对少量重复并不敏感。但需要注意如果某个类别的图大量来自同一块田、同一个拍摄角度就要警惕了。最粗暴的检查方法是把图片缩小到16x16再算哈希把相似度高的图挑出来。这个动作不是必须的但能防止验证集里混入训练图的影子后面判断模型是否真的泛化时会省很多力气。3. 拆解YOLO11cls一键训练脚本五个关键参数与一条能真正跑通的命令3.1 从一堆文件夹到开始训练脚本替你做了哪三步Ultralytics提供的YOLO11cls用法核心其实就一行把数据根目录传给train方法模型用预训练权重初始化。但真正的一键训练脚本不应该只是封装这一行。常见做法是脚本要先做三件事检查目录结构、按固定种子划分训练验证集、把模型和超参固定下来保证两次训练之间只改一个变量。用yolov5或yolov8训练过自己数据集的人对这套流程肯定不陌生先准备data.yaml再写train命令然后盯日志。YOLO11cls把data.yaml弱化了分类模式下直接给根目录内部按train和val两个子目录去读图。这也是为什么整理阶段的目录结构会直接影响脚本复杂度——目录对了脚本可以写得很薄目录不对脚本就得花一堆代码去补数据坑。下面这份是我常用的训练脚本骨架可以直接改成自己的数据集路径import argparse import os import random import shutil from ultralytics import YOLO def prepare_split(root: str, val_ratio: float 0.2, seed: int 42) - str: 确保数据目录是 train/val 结构返回可直接传给 YOLO 的目录。 entries os.listdir(root) if train in entries and val in entries: print(已存在 train/val 结构直接使用原目录) return root out_root root _split random.seed(seed) classes sorted([ d for d in entries if os.path.isdir(os.path.join(root, d)) ]) for cls in classes: cls_dir os.path.join(root, cls) images sorted([ f for f in os.listdir(cls_dir) if f.lower().endswith((.jpg, .jpeg, .png)) ]) random.shuffle(images) n_val max(1, int(len(images) * val_ratio)) for split_name, split_images in ( (train, images[n_val:]), (val, images[:n_val]), ): dst_dir os.path.join(out_root, split_name, cls) os.makedirs(dst_dir, exist_okTrue) for f in split_images: shutil.copy2( os.path.join(cls_dir, f), os.path.join(dst_dir, f), ) print(f已生成划分后的数据集目录: {out_root}) return out_root def parse_args(): parser argparse.ArgumentParser(descriptionYOLO11cls 水稻叶病虫害分类训练) parser.add_argument(--data, typestr, defaultrice_leaf_dataset, help数据集根目录包含 train/val 或直接是类别文件夹) parser.add_argument(--model, typestr, defaultyolo11cls.pt, help预训练权重支持本地路径或框架内置名称) parser.add_argument(--epochs, typeint, default100) parser.add_argument(--batch, typeint, default16) parser.add_argument(--imgsz, typeint, default224) parser.add_argument(--seed, typeint, default42) parser.add_argument(--val-ratio, typefloat, default0.2) parser.add_argument(--workers, typeint, default4) return parser.parse_args() def main(): args parse_args() data_dir prepare_split(args.data, args.val_ratio, args.seed) model YOLO(args.model) model.train( datadata_dir, epochsargs.epochs, batchargs.batch, imgszargs.imgsz, seedargs.seed, workersargs.workers, ) model.val() if __name__ __main__: main()这段代码里prepare_split函数是关键。它先检查数据根目录下是否已经有train和val两个子目录有就直接用没有就按类别文件夹自动切分。注意两点一是classes和images都做了sorted排序再在随机种子下shuffle避免不同操作系统里os.listdir返回顺序不一致导致划分结果对不上二是生成的新目录是root加上_split后缀不污染原始类别文件夹。为什么不直接在原目录里创建train和val因为原始类别文件夹还留在根目录里如果Ultralytics扫描根目录可能会把train、val之外的多余文件夹也当成类别。把划分结果放到独立的root_split目录从结构上避开这个坑。train部分直接调用YOLO(args.model)再train模型参数可以传本地权重路径也可以传框架预置的模型名。如果你不想让脚本每次下载权重就把权重文件放在脚本同级目录并把--model改成对应的路径。训练结束后model.val()会加载best.pt做验证省得手动换权重。3.2 五个必调参数epochs、batch、imgsz、model、seed理解和调整参数是脚本里真正值钱的部分。先看参数表参数常见取值范围影响备注modelyolo11cls.pt / yolo11s-cls.pt模型容量与速度的取舍数据量小选轻量版epochs50200训练轮次决定拟合程度看val曲线收敛再停batch864显存占用与梯度稳定性batch太小loss震荡imgsz224 / 256 / 320输入分辨率影响细粒度纹理病斑小的建议256起seed固定一个整数复现性不固定没法对比实验epochs不要无脑开大。水稻叶病虫害分类里类间差异往往集中在病斑的纹理和颜色上如果训练集只有一两千张一类跑200轮很容易过拟合。我一般先跑100轮看val的top1曲线在哪个epoch后不再上升再用早停或直接在该轮数附近重跑。Ultralytics本身不自动早停脚本里可以观察日志后手动决定也可以外部包一层early stopping。batch和imgsz之间是显存博弈。imgsz224时batch32大概需要6到8GB显存如果显卡只有8GB优先把batch降到16而不是把imgsz降到160——分辨率太低时水稻叶片上毫米级的病斑纹理细节会直接丢失。目标分类模型对输入分辨率比检测任务更敏感因为整张图只产出一个类别判断特征全在全局信息和纹理统计里。model参数看起来简单实际上决定了下限。选yolo11cls.pt作为预训练权重相当于把ImageNet上训练的通用视觉特征迁移过来对细粒度病害识别帮助很大。如果你的类别数和图量都不大不需要从随机权重开始练那会慢得让人怀疑人生。seed这个参数最容易被忽略。很多人用yolov8训练自己的数据集时发现同样代码跑两次accuracy差两三个点其实不是代码写错了而是数据划分、增强、权重初始化里的随机性没有被锁住。YOLO11cls的train方法支持seed参数脚本里务必把它暴露成命令行参数我的默认值是42至少保证同一份数据、同一个种子下结果可复现。3.3 训练中断与续训last.pt和best.pt的分工训练到一半断电或显存溢出是家常便饭。Ultralytics在训练过程中会同时保存两份权重last.pt用来续训best.pt用来做最终评估。续训命令很简单python train.py --model runs/classify/train/weights/last.pt --data rice_leaf_dataset --epochs 100这里有个容易误解的地方--epochs在续训时表示的是总轮次而不是剩余轮次。如果你上次跑了80轮这次想补20轮要把epochs设成100不是20否则相当于又从头跑了100轮。这个细节我在初用Ultralytics时踩过翻车的原因就是想当然地以为传剩余轮次就行。另一个常见问题是修改batch后继续训。加载last.pt继续训练时优化器的学习率状态会延续但如果把batch从16改成32学习率最好也按比例调整否则loss曲线可能突然跳变。分类任务对学习率没那么敏感但遇到loss反而升高的情况先检查是不是这个原因。4. 避坑水稻叶分类训练最常翻车的五个位置4.1 类别不平衡96%的准确率可能只是稻瘟病猜对了现象训练结束打印accuracy读到0.96很兴奋。打开混淆矩阵一看leaf_blast类别的recall接近1而brown_spot类别的recall只有0.3多数类把少数类完全淹没了。原因数据集中稻瘟病图特别多比如有7000张胡麻叶斑病只有500张。模型优化的是整体loss少数类贡献的梯度占比太小学到一定程度后模型就选择偷懒——把不确定的样本都猜成多数类。这就是为什么分类任务里单看accuracy会骗人。解决先看类别统计再决定要不要做平衡。常见做法有三种一是给少数类做离线增强旋转、裁剪、颜色扰动都可以但注意不要把增强样本直接复制粘贴进原始文件夹否则训练集和验证集可能混入近似重复图二是在脚本里给少数类加权YOLO11cls的分类头没有直接暴露class_weight参数我一般用采样策略三是硬性设定每个类别的最小训练张数不足的用同一病斑的多个角度补拍。和PlantDoc这类公开植物病害集不同水稻叶病虫害分类没有开箱即用的标准库自己采集整理仍然是主流这反而要求你在采集阶段就尽量均匀。4.2 背景噪声被当成病害特征换块田就失效现象训练集上accuracy很高但拿手机新拍几张不同背景的水稻叶照片去推理预测结果明显乱掉甚至把健康的叶子判成稻瘟病。原因拍摄时稻叶后面有土壤、水面、稻穗模型学到的是有土壤和水面的图等于稻瘟病而不是病斑本身。分类模型不会自动忽略背景它只会统计哪个区域和标签最相关。尤其水稻叶病害的早期病斑面积很小背景像素在整张图里占比极大模型很容易走捷径。解决整理阶段统一把叶片主体裁出来或者至少把背景面积控制在一小部分。裁剪时不要用固定框硬切因为水稻叶细长最好先做简单的颜色分割或直接人工框出叶片。比裁剪更省力的替代方案是加大imgsz让模型能看到更多纹理细节但背景干扰严重的场景里这只能缓解不能根治。我自己的血泪经验是采集时尽量用纯色垫板垫在叶子下面一张白纸都行数据集质量立刻上一个大台阶。4.3 验证集划分不固定两次实验的差异说不清来源现象只把batch从16改成32accuracy从0.92变成0.94你以为是batch的功劳。把batch改回去再跑accuracy却回不到0.92而是0.93。原因每次训练脚本都重新随机划分train/val验证集里具体包含哪些图在变结果根本没有可比性。这个坑在yolov8训练自己的数据集时特别常见因为很多人习惯了框架自动划分没意识到随机种子会变。解决固定seed只是第一步。更强硬的做法是把划分好的数据集目录固化下来比如第一次划分时把train和val作为独立目录保存之后所有实验都用这套划分不再重新切。第三章脚本里的prepare_split已经做了这件事——第一次运行时生成rice_leaf_dataset_split目录之后运行检测到train和val就跳过划分。注意看脚本输出里有没有已存在train/val结构这句话确认它确实在复用同一套划分。4.4 resize把细长叶片压变形病斑纹理被拉伸后不可信现象稻叶是典型的细长形状长宽比经常超过5比1而imgsz默认会把它直接缩放到224x224的正方形。原本长条状的病斑在图上被压扁或拉长模型学到的是变形后的纹理。训练时看不出来因为所有训练图都被同样地变形但下田识别时采集到的原始比例不同模型就懵了。原因Ultralytics分类模式的默认预处理是resize到正方形不会保持原始长宽比也没有像检测任务那样自动letterbox的逻辑。对方形输入而言细长叶片的空白区域会被强行拉伸病斑形态完全失真。解决有两种思路。一种是采集阶段尽量把叶片横过来或竖起来统一方向让长宽比差异变小另一种是在训练时用更大的imgsz比如320再用随机裁剪增强给模型机会学到局部病斑纹理而不是整片叶子的全局形状。如果病斑本身只有几毫米把图放大到256以上基本是必须的。4.5 训练集里混入水印与网络重复图指标高全是假象现象val的top1有0.95但你手动拿真实大田照片一测准确率掉到0.7以下。仔细翻训练集发现不少图来自搜索引擎角落还留着网站水印或截图边框。原因模型忠实学到了水印和边框的特征。这些特征在训练集里和标签强相关但在真实场景中不存在所以一落地就翻车。解决整理阶段做一次水印和边框巡检把带水印的图剔除或裁掉边缘。重复图的处理可以用感知哈希去重方法简单把每张图缩到8x8或16x16比较哈希距离距离小于阈值就删掉一张。这一步不需要特别精确目标只是防止同一张图同时出现在train和val里导致验证指标虚高。顺手检查有没有从第三方平台截图的图这类图通常自带界面元素模型会把按钮和进度条也当成特征。5. 训练完别急着下田用混淆矩阵、Top-1/Top-5和单图推理做三重验证5.1 混淆矩阵比accuracy诚实得多画出每个类别的真实表现训练日志里的accuracy是一个加权平均值看不出哪个类别在拖后腿。我每次做完训练都会先用val目录跑一遍推理直接出混淆矩阵和分类报告这一步能看出模型到底是会判断还是只会猜多数类。import os from ultralytics import YOLO from sklearn.metrics import classification_report model YOLO(runs/classify/train/weights/best.pt) data_root rice_leaf_dataset_split/val class_names sorted([ d for d in os.listdir(data_root) if os.path.isdir(os.path.join(data_root, d)) ]) y_true, y_pred [], [] for cls in class_names: cls_dir os.path.join(data_root, cls) for img_name in os.listdir(cls_dir): img_path os.path.join(cls_dir, img_name) result model.predict(img_path, verboseFalse)[0] pred_cls class_names[int(result.probs.argmax())] y_true.append(cls) y_pred.append(pred_cls) print(classification_report(y_true, y_pred, target_namesclass_names))这段逻辑是遍历验证目录下每个类别文件夹对每张图调用model.predict得到概率分布用probs.argmax()取概率最高的类别。注意pred_cls这里用class_names做下标映射而不是直接拿模型的names字段——虽然两者通常一致但如果训练时的类别顺序和当前目录的排序不一致就会发生错位。多数类准确率高、少数类召回率低的情况下classification_report里一眼就能看出来。给模型打包前我会保留这份报告和混淆矩阵图作为这一版权重的体检档案。5.2 Top-1和Top-5分开看相似病斑类别的容错设计水稻叶病虫害分类里有相当一部分类别人眼都容易混淆比如稻叶瘟的初期病斑和胡麻叶斑病的小病斑颜色和形状都很接近。这种场景下Top-1准确率会显得很苛刻模型其实已经给出了正确的候选只是把两个相近类别的概率排序搞反了。Ultralytics在验证输出里会计算Top-1和Top-5两个指标。Top-5的意思是只要正确类别出现在概率最高的5个预测中就认为正确。我建议把两个指标一起打印出来如果Top-5比Top-1高很多说明类别定义之间本身存在语义重叠此时可以考虑合并类别或者把细分类别改成疑似稻瘟或胡麻斑这类联合标签而不是急着堆更多训练数据。Top-5也有它的迷惑性当类别总数本身很少时比如只有4类Top-5几乎永远等于100%没有参考价值。水稻叶分类如果有8到12个类别Top-5才有意义。类别越多Top-5和Top-1之间的差值越能反映数据定义的模糊程度。5.3 单图推理的固定套路给新照片一个可信区间验证集的图虽然和训练图不同但拍摄环境与采集习惯一致。真要判断模型能不能下田得拿一批完全不在已知数据集里的实拍照片单图推理看概率分布。from ultralytics import YOLO model YOLO(runs/classify/train/weights/best.pt) img field_test/IMG_20250401_153000.jpg result model.predict(img, verboseFalse)[0] probs result.probs for i in range(len(probs)): class_name result.names[i] p probs[i].item() print(f{class_name}: {p:.3f})这段代码会把每个类别的概率都打出来。看概率分布时我关注两点一是最高概率是否明显超过第二高如果只差零点零几说明模型自己也不确定二是所有类别的概率排布是否集中如果模型输出的分布非常平坦说明这张图里根本没有它认识的特征不要硬猜。真实田间场景的照片光照复杂偶尔出现一张低置信度预测是正常的只要比例控制在可接受范围内就可以部署。6. 让YOLO11cls少翻车的三个习惯训练前自检与下田部署前的最后一轮验证6.1 训练前自检清单六件事五分钟核对完检查项操作方法通过标准类别数量运行2.3的统计脚本每类不低于300张背景一致性随机抽看20张图每类叶片主体居中背景不杂乱重复图感知哈希粗查一遍无同一图跨train和valtrain/val结构查看是否存在两个子目录已存在且不自动重切文件名规范全部英文字母和数字无空格、无中文seed固定脚本参数默认42二次训练指标一致这套清单是我每次训练前都会过一遍的固定动作。六项里花时间最多的是背景一致性和重复图排查其余都是脚本自动完成的。很多人在YOLO11cls上翻车翻的根本不是模型参数而是这些没被检查到的数据问题。6.2 部署前的大田模拟测试用陌生照片做一次冒烟测试正式交付之前我最看重的一步是拿一台手机去田间拍20到50张和训练集风格完全不同的照片。拍摄时不要刻意追求和训练集一致的构图就按实际使用习惯去拍背景可能带天空、带水面、带稻穗。之后用5.3段的单图推理脚本逐张过一遍记录每张图的置信度和Top-1命中情况。如果陌生照片上的准确率和val集差距过大先不要怀疑模型参数回头查训练集是不是太多来自同一块田、同一时段、同一设备的照片。这比盲目调超参数有用得多。分类模型的泛化能力很大程度在数据采集时就定了训练脚本只能帮你把这批数据的潜力发挥出来。6.3 三个让我少翻车的小习惯第一每次训练在runs目录外另存一份划分后的数据集路径文本文件里面记录seed、各类数量、数据来源时间。第二把best.pt和验证时的混淆矩阵、分类报告放进同一个文件夹命名带上日期。第三训练完成的权重不直接覆盖老版本保留最近两个版本方便回滚。这三个动作加起来不超过十分钟但能在几周后完全忘了当时怎么训练时把整个过程的来龙去脉拉回来。我自己在目标分类项目上翻过不少车最深刻的一条教训就是数据整理阶段省下的时间都会在验证和部署阶段加倍还回来。把目录整理、划分固定、数据体检这几件事做到位YOLO11cls的一键训练才能真正一键。希望帮到你。本文还有配套的精品资源点击获取
返回列表