ARTICLE DETAIL

资讯详情

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

YOLO11cls水稻叶病虫害分类训练实战:从数据整理到模型落地

YOLO11cls水稻叶病虫害分类训练实战:从数据整理到模型落地 简介面向水稻叶病虫害分类与YOLO11cls图像分类实际项目这份PDF文档整理了15000张真实场景水稻叶片图像的数据集方案涵盖细菌性叶枯病、褐斑病、健康叶片、叶瘟病、叶鞘腐病、窄褐斑病、穗颈瘟、稻飞虱、纹枯病、钨黄病毒病等10个类别采用文件夹方式区分标注可直接用于YOLOCLS等分类算法训练。文档内附数据集基本情况介绍并给出百度网盘获取方式同时展示了博主提供的YOLO11cls一键训练脚本与训练结果日志可作为实际训练时的参数设定与流程参考。资源包共1个文件为PDF格式整体大小约2.32MB轻量便于查阅已有318人学习。适合正在开展农业视觉识别、水稻病害检测或需要补充通用分类数据集场景的研究者与算法工程师参考。1. 拿到15000张水稻叶病虫害图第一件事不是训练拿到“目标分类-水稻叶病虫害分类数据集”这种整理好的三件套很多人第一反应是直接拉训练脚本开跑。我的建议是先停十秒。15000张图听起来不少但放到一个8到10类的细粒度分类任务里每类平均也就一千多张属于中等偏小的规模真正决定模型能不能用的不是最后那行训练命令而是前面的目录结构、类别边界和验证集划分稳不稳。这个标题想解决的就是把从数据到模型的全过程压成三类东西按类别整理好的图片、规范的分类文件夹、以及YOLO11cls一键训练脚本。适合谁做植保AI、农业信息化或者拿水稻病害当课题的学生目标都是少折腾环境、多验证效果。2. 分类文件夹整理YOLO11cls 的数据集目录本身就是标签2.1 为什么分类任务不需要标注框文件夹名即类别很多人是从目标检测教程转过来的习惯性认为“数据集”一定长这样一个 images 目录放图片一个 labels 目录放同名的 txt 或 xml 标注框。YOLO11cls 不是这套玩法。它是图像分类分支模型输入是一整张叶片图输出是 softmax 概率分布所以它的标签根本不是标注框而是train/类别名/图片.jpg这种目录层级。类别名是谁就是文件夹名。这个设计对水稻叶病虫害非常合适。一张叶片上可能同时有稻瘟病斑和胡麻叶斑如果做检测你得为每一片病斑画框、标类别、处理重叠而做目标分类你只要回答“这张叶子属于哪一类”。它丢掉了位置信息换来了更低的标注成本和更稳的训练管线。15000张图的整理工作量集中在“把每张图放进正确的文件夹”而不是“给每张图画四五个框”。所以拿到这个数据集第一步不是看图片内容而是看目录结构对不对。下面是一个标准形态rice_pest_cls/ ├── train/ │ ├── rice_blast/ # 稻瘟病 │ ├── brown_spot/ # 胡麻叶斑病 │ ├── leaf_blight/ # 白叶枯病 │ └── healthy/ # 健康叶 └── val/ ├── rice_blast/ ├── brown_spot/ ├── leaf_blight/ └── healthy/train 和 val 平级data 参数传rice_pest_cls这个根目录。YOLO11cls 训练时会自动扫描子文件夹名生成类别列表不需要额外给类别清单。这个目录一旦搭错后面所有脚本都白搭。2.2 从散图到 train/val 目录整理脚本与关键字映射如果拿到手的不是已经分好类的文件夹而是一堆文件名带语义的散图常见做法是写一个按关键字归类的脚本。我一般会把映射表单独拎出来放顶部方便按实际数据集调整。真实的数据集命名往往混合了中文、英文简写和拍摄序号所以关键是映射表要足够全。# 把散落的叶片图按类别关键字归类到 YOLO11cls 目录结构 import random import shutil from pathlib import Path RAW_DIR Path(raw_pics) # 原始散图目录 OUT_DIR Path(rice_pest_cls) # 输出数据集根目录 SUBSETS [train, val] # 固定两个子集不要用 test 混进来 # 类别映射键是目标文件夹名最终类别名值是原始文件名里的关键字 CLASS_KEYWORDS { rice_blast: [blast, blast_, 稻瘟, neck_blast], brown_spot: [brown_spot, 胡麻, brownspot], leaf_blight: [blight, 白叶枯, leafblight], healthy: [healthy, 健康, normal], } random.seed(42) TRAIN_RATIO 0.85 # 先建好目录骨架避免后面 copy 时报父目录不存在 for subset in SUBSETS: for cls_name in CLASS_KEYWORDS: (OUT_DIR / subset / cls_name).mkdir(parentsTrue, exist_okTrue) # 扫描原始目录按关键字命中归类并按比例随机分到 train/val for img_path in RAW_DIR.iterdir(): if img_path.suffix.lower() not in {.jpg, .jpeg, .png, .bmp, .webp}: continue for cls_name, keywords in CLASS_KEYWORDS.items(): if any(kw in img_path.stem for kw in keywords): subset train if random.random() TRAIN_RATIO else val shutil.copy2(img_path, OUT_DIR / subset / cls_name / img_path.name) break这里有两个参数值得说。TRAIN_RATIO 0.85是训练集占比我一般用 0.8 到 0.9 之间分类任务对验证集的绝对数量比比例更敏感如果某个类总共只有 60 张图85% 训练意味着验证集只剩 9 张后面 4.1 节我会再讲分层处理。另一个是copy2而不是move我刻意保留原始目录不动整理错了还有后悔药原始图不大时优先复制磁盘紧张再改成shutil.move。脚本不做随机抽样修复同一个采集批次里连续拍摄的同病叶片可能被拆到两个子集里这在严谨场景下属于信息泄漏实际做的时候按采集批次或田块划分更合理。2.3 整理完别急着训练损坏图与类别分布自检整理完目录先别急着跑训练。我见过太多人在这一步跳过检查然后训练到一半 loss 突然变成 NaN最后定位到是某张截断的 JPEG。图像分类训练对脏数据的容忍度其实很低一张全黑的“伪图片”不会报错只会默默把梯度带偏。早期花五分钟做自检比训完再排查强得多。# 校验打印每个类的图片数量并检测损坏图片 from PIL import Image for subset in [train, val]: cls_counts {} for cls_dir in (OUT_DIR / subset).iterdir(): if not cls_dir.is_dir(): continue ok, bad 0, 0 for p in cls_dir.iterdir(): try: with Image.open(p) as im: im.verify() # 不真正解码像素但能发现截断/损坏头部 ok 1 except Exception: bad 1 print(f损坏图片: {p}) cls_counts[cls_dir.name] ok print(f[{subset}] 各类数量: {cls_counts})Image.verify()只校验文件头和数据完整性不会把整张图解码进内存速度很快15000张图一两分钟能跑完。看到各类数量输出后重点查两件事第一有没有类目数量为 0那说明关键字映射漏了第二train/val 比例对每个类是否大致一致如果某个类 train 有 500 张但 val 只有 3 张后面准确率波动会非常大。这一步做好数据集整理才算真正结束。3. YOLO11cls 一键训练脚本从装环境到跑通的最小路线3.1 环境安装与最小跑通先花 5 分钟验证管线环境安装没什么花活Ultralytics 已经把所有依赖打成一个包。不少人是顺着 yolov5 训练自己的数据集、yolov8 训练自己的数据集这类教程摸过来的到 YOLO11cls 这里最容易扑空的地方就是命令形态检测训练用yolo detect train分类训练是yolo classify train别看只差一个词数据路径逻辑完全不同。pip install ultralytics装完先确认一下版本避免装到太老的缓存包python -c import ultralytics; print(ultralytics.__version__)然后别一上来就跑完整训练先跑一个 5 个 epoch 的最小实验。这一步的唯一目的是验证“数据集目录 权重下载 模型前向”整条管线通不通yolo classify train datarice_pest_cls modelyolo11-cls.pt epochs5 imgsz224 batch16data 传数据集根目录model 传yolo11-cls.pt这个权重会在首次运行时自动下载。5 个 epoch 大概几分钟能正常跑完并且输出里出现每个类的 accuracy就说明目录结构没问题。这里频繁翻车的点是路径data 一定指向同时包含 train 和 val 的那个根目录指向 train 本身会报找不到验证集指向外层会扫描不到任何类。跑完这轮后面再考虑调参。3.2 一键训练脚本拆解核心参数与迁移学习设置最小命令验证通过后再上完整脚本。标题里的“一键训练”不是魔法是把合理默认值固化在一个脚本里。我一般会把配置区和训练逻辑分开参数集中在文件顶部方便每次实验只改一两行。from ultralytics import YOLO # 配置区只改这里 DATA_ROOT rice_pest_cls # 数据集根目录必须包含 train/val EPOCHS 80 # 总训练轮数 IMGSZ 224 # 输入分辨率显存充足再试 512 BATCH 32 # 批大小8G 显存建议 16 DEVICE 0 # GPU 编号无 GPU 改 cpu不建议 SEED 42 LR0 0.0005 # 迁移学习初期学习率别用默认值硬顶 model YOLO(yolo11-cls.pt) # 官方预训练分类权重 results model.train( dataDATA_ROOT, epochsEPOCHS, imgszIMGSZ, batchBATCH, deviceDEVICE, patience15, # 验证集 15 轮不涨就早停 seedSEED, lr0LR0, )参数选择上有几个值得展开的地方。modelyolo11-cls.pt是官方在 ImageNet 上预训练的分类权重15000张这种规模的数据集从零训练很难收敛迁移学习是性价比最高的路线。lr00.0005是我习惯的迁移学习初始学习率分类微调时默认学习率偏高容易在前几个 epoch 就把预训练特征冲坏如果训练脚本已经调好优先用脚本作者给的值。patience15对中小数据集很重要它让训练在验证集不再提升时自动停下来省掉盲跑完整 80 轮的算力。seed42是为了实验可复现不然每次训练结果都会飘。参数常见取值调整方向epochs80验证集还在涨就加到 120imgsz224病斑细纹理不明显时试 512batch16 / 32显存不足减半别硬怼lr00.0005 ~ 0.001早停太早说明 lr 偏高patience15 ~ 30数据噪声大时放宽到 303.3 训练产出解读results.csv 里盯哪几列训练完成后输出在runs/classify/train/目录下。weights/best.pt是验证集上表现最好的权重last.pt是最后一个 epoch 的权重日常推理优先用 best。results.csv里每一行是一个 epoch像财报一样把训练过程全摊开了。关键列只有四个train_loss、val_loss、accuracy_top1、accuracy_top5。accuracy_top1直接决定模型能不能用水稻叶这种细粒度分类top1 能到 80 以上就算能落地top5 只能当参考。如果train_loss一路下降但val_loss在中间开始反弹说明发生过拟合回到 4.3 节加增强或提前早停。如果训练中断了别从头再来Ultralytics 支持断点续训yolo classify train modelruns/classify/train/weights/last.pt datarice_pest_cls epochs80 resumeTrue这条命令会从上次中断的位置继续resumeTrue会自动读取训练状态算是个后悔药。4. 训练前把三件事做稳分层划分、imgsz 和增强参数4.1 分层抽样每个类必须留够验证样本如果拿到手的数据集已经划分好 train/val这一步可以跳过但如果想重新划分千万别用random.shuffle一把梭。水稻病害数据集天然就是不平衡的——稻瘟病图片多白叶枯图片少随机划分最容易出现的后果是某个类在验证集里只剩一两张准确率剧烈震荡。分层抽样的意思很直白按类别分别划分保证每个类在 train 和 val 里的比例一致并且 val 里每个类至少留下若干张。一个小脚本就能做import random from pathlib import Path import shutil SRC_DIR Path(rice_pest_cls_extracted) # 全量图按类放好 DST_DIR Path(rice_pest_cls) VAL_RATIO 0.2 random.seed(42) for cls_dir in SRC_DIR.iterdir(): if not cls_dir.is_dir(): continue imgs list(cls_dir.iterdir()) random.shuffle(imgs) val_cnt max(10, int(len(imgs) * VAL_RATIO)) # 每个类至少留 10 张做验证 for img in imgs[:val_cnt]: dst DST_DIR / val / cls_dir.name / img.name dst.parent.mkdir(parentsTrue, exist_okTrue) shutil.copy2(img, dst) for img in imgs[val_cnt:]: dst DST_DIR / train / cls_dir.name / img.name dst.parent.mkdir(parentsTrue, exist_okTrue) shutil.copy2(img, dst)关键在max(10, ...)这个下界。样本量少的类别验证集无论如何要保住 10 张左右否则一个 epoch 里验证集就几十张图acc 曲线会毛糙到没法判断早停。分类任务里验证集的质量比数量重要验证集里每类的构成也要和真实应用场景接近。另外划分完把类别数量和图片总数打印出来留档后面对比实验结果时能快速确认“这次跑的不是同一份数据”。4.2 imgsz 不是越大越好224 起步、512 精调水稻叶病虫害是典型的细粒度分类稻瘟病斑和胡麻叶斑在形态上接近白叶枯的条斑在低分辨率下很容易被模型当成叶片纹理。imgsz 决定了模型能看到多细的纹理但这不是免费午餐显存和时间按分辨率的平方增长。224 是 ImageNet 预训练的标准尺寸迁移学习时特征最匹配所以我一般先跑 224 把数据管线、增强、早停全部调顺再试 512 做精调对比。8GB 显存的显卡224 分辨率能跑 batch 32换成 512batch 基本要降到 8 甚至 4训练时间接近原来的 5 倍。判断要不要上 512 的标准很简单用 224 训练完看results.csv里 val_loss 的下降曲线如果模型明显欠拟合train_loss 和 val_loss 都没降下来才考虑加分辨率如果已经过拟合加分辨率只会让情况更糟。我做过不少分类项目很多情况下 224 加合理增强的效果胜过 512 硬跑——提升的那点准确率往往来自随机裁剪带来的等效数据增强而不是分辨率本身。4.3 增强怎么开hsv 三兄弟是细粒度分类的关键Ultralytics 在分类训练里内置了一套增强参数默认值对自然图像合理但直接套到水稻叶片上不一定合适。水稻叶片外观变化最大的维度不是形状是颜色缺素发黄、日灼发白、衰老发褐、湿度大时病斑颜色加深。这正好对应 hsv 三个通道的扰动做增强时我把重心放在颜色扰动上而不是几何变换。model.train( dataDATA_ROOT, epochsEPOCHS, imgszIMGSZ, batchBATCH, hsv_h0.015, # 色相扰动调大容易让叶片“变种” hsv_s0.4, # 饱和度扰动模拟清晨/正午光线差异 hsv_v0.3, # 亮度扰动模拟背光和阴影 degrees15, # 小角度旋转模拟田间拍摄角度 scale0.3, # 缩放扰动模拟不同拍摄距离 fliplr0.5, # 水平翻转对禾本科叶片安全 flipud0.0, # 垂直翻转我一般关掉 )hsv_h0.015意味着色相只做轻微扰动调太大会把绿色的健康叶变成紫色模型学到的是颜色偏移而不是病害特征。hsv_s和hsv_v才是主角它们模拟的是自然光线变化安全范围宽松。flipud0.0是我刻意关掉的——垂直翻转对水稻叶片这种有一定朝向性的物体不自然虽然模型通常也能扛住但没必要让它学这种不存在的数据分布。几何增强开猛了还有一个坏处模型会去记叶片轮廓而不是病斑纹理细粒度分类最怕这个。5. 避坑水稻叶病虫害分类训练中最常见的 5 个翻车现场翻车现场一训练报找不到类或数据集目录的断言错误。现象是脚本启动后立刻报错日志里出现类似 class not found 或 dataset not found 的信息。根因基本一致data 参数指向的目录层级不对。YOLO11cls 要求 data 指向的目录里直接包含 train 和 val而且 train 下面直接是类别文件夹如果 data 指向了 train 本身它会认为 train 里每个子目录都是“数据集根目录”扫描不到平级的 val直接报错。解决办法是确认目录结构严格按 2.1 节的形态来然后在跑训练前先执行ls rice_pest_cls看根目录下是不是只有 train 和 val 两个文件夹别把说明文档、统计表这类杂项放进去。翻车现场二训练集准确率很高田间实测全废。这是分类任务里最隐蔽的坑。现象是 val 上 top1 到了 85%拿手机对着真实田块拍几张一测全部分到健康叶。根因是训练集图片背景太“干净”——实验室拍照或网络扒图都是单叶、均匀背景而真实田间有泥土、水珠、阴影、其他叶片遮挡。模型实际学到的可能是背景特征而不是病斑纹理。解决办法分两步第一步整理数据时保留一部分带复杂背景的真实照片哪怕数量少也要放进训练集第二步在增强里把亮度扰动调大模拟阴天和逆光。如果数据集本身没有这类图宁可减少训练图数量也要从田间补拍一批这类困难样本对最终效果的影响远大于多几百张干净图。翻车现场三验证集 accuracy 卡在 60% 左右不动val_loss 也不降。把预测结果拉出来看大概率所有样本都被分到样本量最大的那个类最常见的是全员健康叶。根因是类别不均衡健康叶占了六七成模型找到的“捷径”是全猜健康叶因为这样 loss 最小。解决办法不是先去调模型而是先统计类别分布。如果某个类样本数不足多数类的四分之一先做抽稀和过采样对多数类随机抽一部分对少数类做轻度增强复制把比例压到 1:2 以内再训练。15000张图规模不算大靠 loss 权重这种偏门方案容易引入新问题宁可把数据配比调均衡也别指望模型自己扛。翻车现场四训练中途 loss 变成 NaN随后训练中断。现象很直接loss 一栏出现 nan然后训练卡死。常见原因有三个lr0 设置过高导致梯度爆炸、某张图片损坏全黑或文件截断、batch 里混入了异常分辨率图片。排查顺序先跑 2.3 节的校验脚本排除损坏图再把 lr0 降到 0.0001 试跑 10 个 epoch最后检查是不是 batch 过大导致显存溢出但报错被吞。我习惯在正式训练前先跑一遍 2.3 节的自检宁可多花五分钟也不想看着 20 个 epoch 的算力白费。翻车现场五Windows 下中文路径加中文类别名导致训练异常。现象是图片能打开目录也看起来没问题但训练时类别数对不上或者部分图片读取失败。根因是 Ultralytics 在底层做路径编码时对中文路径支持不稳定这类问题在 Linux 下不明显在 Windows 下很折磨人。解决办法是把数据集路径和所有类别文件夹名改成英文比如稻瘟病改成rice_blast同时在代码里用Path对象而不是字符串拼接路径避免\和/混用。类别英文名和中文名的映射关系单独存一份文件推理时再映射回中文显示完全不耽误使用。烟草病虫害数据集 yolo 这类作物病害项目也常见同样的坑不是水稻专属。6. 从 acc 到能落地混淆矩阵、错误样本回看与批量推理6.1 先跑 val 命令看混淆矩阵训练结束第一件事不是看 acc而是跑一次正式验证把混淆矩阵翻出来。yolo classify val modelruns/classify/train/weights/best.pt datarice_pest_cls跑完在runs/classify/val/下会生成confusion_matrix.png。对角线越高说明模型越干净重点看对角线之外哪些格子亮度高那是模型混淆的类。水稻叶任务里最常见的混淆是稻瘟病和胡麻叶斑互相认错因为两者都是斑点类病斑形态接近。发现混淆后回去各抽几十张图对比看是不是标注本身就模糊——比如一张叶片两种病斑都有当初归类时随便放了一边。这类“标签噪声”不会报错它只会咬着准确率不放。6.2 写个批量推理脚本把真实照片跑一遍验证集评估只是第一步真正要看的是模型在真实田块照片上的表现。我习惯把一批实拍图丢进一个目录批量推理后把 Top-1 类别和置信度落盘成文本人工快速扫一遍。from ultralytics import YOLO from pathlib import Path model YOLO(runs/classify/train/weights/best.pt) field_dir Path(field_shots) # 放实拍照片的目录 for img_path in sorted(field_dir.glob(*.jpg)): res model.predict(str(img_path))[0] top1_idx res.probs.top1 top1_name res.names[top1_idx] top1_conf res.probs.top1conf.item() print(f{img_path.name}: {top1_name} ({top1_conf:.3f}))res.probs.top1是最高概率类别在训练类别列表中的索引res.names把索引映射回类别名。置信度低于 0.6 的样本我会单独挪到一个need_confirm目录留给人复核不强行让模型表态。这套流程跑一遍比看十遍准确率曲线更能说明模型能不能用。6.3 再进一步换主干、TTA 与蒸馏如果 224 分辨率下 top1 到 85% 就很难涨了下一个增量来自换更大的主干yolo11l-cls.pt或yolo11x-cls.pt参数量更大对细粒度特征的拟合能力更强代价是训练和推理时间翻倍。15000张图规模训大模型容易过拟合要配合更重的增强和更早的早停。TTA测试时增强也有用推理时对图片做多尺度变换后取平均概率通常能稳定提升 1 到 2 个点但速度会慢不少不适合田间实时识别。我现在拿到新的分类任务习惯是先把训练跑通然后直接跳过 acc 看混淆矩阵和错误样本一遍遍把交错最严重的类拉出来对比。这个习惯帮我发现过不少标签标错的图也让我对“准确率数字”一直保持警惕。分类模型的准确率是个平均值平均值下面藏着的才是它真正不会的东西。希望帮到你。本文还有配套的精品资源点击获取
返回列表