YOLO11cls实战:水稻叶病虫害15000张图像分类训练全流程
发布时间:2026/9/30 13:53:22来源:尧图网络
简介面向水稻叶病虫害识别与图像分类项目的完整数据集资源适合计算机视觉学习者、农业智能化开发者及算法训练人员使用。内容包含15000张真实场景高质量水稻叶片图像按细菌性叶枯病、褐斑病、健康叶片、叶瘟病、叶鞘腐病、窄褐斑病、穗颈瘟、稻飞虱、纹枯病、钨黄病毒病10个类别整理为对应分类文件夹标注质量高可直接用于YOLO11cls等图像分类模型的训练与验证。资源包为1个PDF文件大小2.32MB便于快速了解数据集结构、类别分布及获取方式。另附YOLO11cls一键训练脚本及博主训练结果日志有助于降低环境配置与训练门槛作为通用分类数据集补充亦适用。已有318人学习下载适合希望快速上手水稻叶病虫害分类任务的研究者与开发者参考使用。1. 水稻叶病虫害分类数据集15000 张图、分类文件夹和一键训练脚本的实际分量这套“水稻叶病虫害分类数据集”本质是把 15000 张已经按类别归好文件夹的叶片图像连同 YOLO11cls 的一键训练脚本一起交付给你。你要做的不是从零攒数据、写分类器而是把文件夹路径校好运行脚本然后拿到一个能直接对叶片图片做病害分类的模型。对刚入视觉方向的学生、想快速验证病虫害识别可行性的农业团队这是个性价比很高的起点。真正容易被忽略的反而是数据质量检查和脚本参数选择这两步直接决定模型落地时的可用程度而不是训练过程本身。2. 拆开 15000 张分类文件夹先做数据体检再谈训练2.1 分类文件夹的标准摆法类别目录、train/val 划分规则拿到数据集之后第一件事不是解压看热闹而是核对目录结构。这套数据集的原始形态应该是“分类文件夹”也就是每个类别一个文件夹里面直接放该类别的图片。绝大多数图像分类项目都采用 ImageNet 风格目录YOLO11cls 的分类模式同样认这套结构。常见做法是整理成下面这个样子rice_leaf_dataset/ ├── train/ │ ├── rice_blast/ # 稻瘟病 │ │ ├── img_0001.jpg │ │ ├── img_0002.jpg │ │ └── ... │ ├── rice_brown_spot/ # 胡麻叶斑病 │ ├── rice_bacterial_blight/ # 白叶枯病 │ ├── rice_sheath_blight/ # 纹枯病 │ └── healthy/ # 健康叶片 └── val/ ├── rice_blast/ ├── rice_brown_spot/ ├── rice_bacterial_blight/ ├── rice_sheath_blight/ └── healthy/train 和 val 下的类别子目录名字必须完全一致不能 train 里叫 rice_blast、val 里叫 Rice_Blast。大小写不一致、带空格、带中文名后续写 YAML 或者做推理时都会变成坑。2.2 用三段 Python 给数据集做体检每类数量、尺寸异常和损坏文件拿到数据集后第一件事不是解压看热闹而是核对目录结构。这套数据集应该是“分类文件夹”形式也就是每个类别一个文件夹直接放该类别图片。绝大多数图像分类项目都采用这个结构YOLO11cls 的分类模式同样认这套结构。常见做法是整理成 train/ 和 val/ 两个大目录各自下面再按类别分文件夹。但手头这份数据往往只给了“分类文件夹”并没有划分验证集后面要自己分层抽样。先写一段体检脚本确认三件事每个类有多少张图、有没有损坏文件、图片尺寸是否异常。用 Python 的 PIL 就能解决import os from collections import Counter from PIL import Image def inspect_dataset(root_dir): stats Counter() broken_files [] size_list [] for cls_name in os.listdir(root_dir): cls_path os.path.join(root_dir, cls_name) if not os.path.isdir(cls_path): continue for img_file in os.listdir(cls_path): if not img_file.lower().endswith((.jpg, .jpeg, .png)): continue img_path os.path.join(cls_path, img_file) stats[cls_name] 1 try: with Image.open(img_path) as im: size_list.append(im.size) # (宽, 高) except Exception as e: broken_files.append((img_path, str(e))) # 输出统计结果 for cls_name, count in stats.most_common(): print(f{cls_name}: {count} 张) print(f\n损坏图片: {len(broken_files)} 张) for path, err in broken_files[:10]: print(f {path} - {err}) # 检查尺寸是否统一 if size_list: widths [w for w, h in size_list] heights [h for w, h in size_list] print(f\n宽度范围: {min(widths)} ~ {max(widths)}) print(f高度范围: {min(heights)} ~ {max(heights)}) return stats, broken_files # 用法把原始分类文件夹的根目录传进来 stats, broken inspect_dataset(leaf_images)这段脚本的逻辑很简单遍历根目录下的每个子目录当作类别统计图片文件数量然后用 PIL 打开验证完整性。size_list能帮你看出图片尺寸是否差异很大比如混了一堆手机拍的 4000×3000 大图和网络爬虫抓的 200×200 小图。这两个文件修好后数据体的底子就算清楚了。提示如果损坏图片超过总数 1%先联系数据来源方确认别自行删图补齐容易破坏类别分布。2.3 容易被忽略的三类数据问题样本不均衡、背景雷同与病程混杂15000 张图听起来足够训练但分布往往并不均匀。常见的水稻叶部病害类别大概是稻瘟病、白叶枯病、胡麻叶斑病、纹枯病和健康叶片各类别典型症状差别较大类别名典型症状识别难度rice_blast稻瘟病梭形病斑边缘褐色、中央灰白中等rice_brown_spot胡麻叶斑病褐色近圆形小点边界清晰较低rice_bacterial_blight白叶枯病沿叶缘或叶尖的灰白色长条斑中等rice_sheath_blight纹枯病云纹状斑块病斑呈波纹扩散较高healthy健康叶片无病斑叶色均匀低体检脚本跑完之后你大概率会发现“健康叶片”这类特别多因为采集容易而“稻瘟病”这类依赖特定发病条件可能只有两三千张。这种不均衡如果不处理训练出的模型会对多数类过拟合少数类召回率很低。背景雷同是另一个隐蔽问题如果数据采集来自田间叶片背景是土壤和稻丛如果来自实验室背景可能是白纸或黑布。模型很容易学到“黑背景某种病害”这种相关性。一个快速验证方法是从每类随机抽 20 张图拼成网格图肉眼看一遍背景分布这一步能避免后面训练完才发现模型靠背景作弊。病程阶段混杂也值得注意。同一病害在不同发病阶段斑纹形态差异巨大初期的稻瘟病只是针尖大小的褐点后期才长出梭形大斑。如果数据集的分类文件夹里没有进一步区划发病阶段模型会学得很挣扎。这类问题没法靠脚本自动解决只能人工看一批图确认类别内形态是否一致。3. 把分类文件夹整理成 YOLO11cls 可训数据划分脚本与结构规范3.1 YOLO11cls 认哪种目录结构不认散装图片只认类别子目录YOLO11cls 是 Ultralytics YOLO11 系列里的分类模型训练数据既可以直接指向数据集根目录也可以指向一份 YAML 配置。但无论哪种方式最终读取的目录结构都必须是数据集根目录/train/类别名/*.jpg验证集则是数据集根目录/val/类别名/*.jpg。很多新手在这里被坑过手里数据是“训练集文件夹 测试集文件夹”但每一类图片是平铺存放必须在图片文件名里体现类别YOLO11cls 是不会解析这种命名的。它只认类别子目录文件名是什么完全无所谓。如果你的原始数据不是这种结构第一步就是把它改造成上面第 2.1 节展示的树形结构。如果原始数据文件夹就是按类别组织的改造起来很快验证集划分才需要多花点心思。3.2 训练/验证集划分脚本按类比例分层抽样避免数据泄漏划分验证集最常见的问题是偷懒用random.shuffle一次性洗牌然后把后 20% 当验证集。这样容易出现某个类别在验证集里全集中在特定拍摄批次训练集和验证集分布不一致。更好的做法是按类别分层抽样保证每个类别都均匀切出 20%。import os import random import shutil SRC_DIR leaf_images # 原始分类文件夹根目录 DST_DIR rice_leaf_dataset # 目标数据集根目录 VAL_RATIO 0.2 # 验证集占比 SEED 42 # 固定随机种子保证结果可复现 random.seed(SEED) def split_dataset(): for cls_name in os.listdir(SRC_DIR): cls_path os.path.join(SRC_DIR, cls_name) if not os.path.isdir(cls_path): continue # 只统计图片文件忽略隐藏文件和非图片格式 images [ f for f in os.listdir(cls_path) if f.lower().endswith((.jpg, .jpeg, .png)) ] if not images: print(f警告: 类别 {cls_name} 没有图片跳过) continue random.shuffle(images) val_count int(len(images) * VAL_RATIO) # 建立目标目录结构 train_dir os.path.join(DST_DIR, train, cls_name) val_dir os.path.join(DST_DIR, val, cls_name) os.makedirs(train_dir, exist_okTrue) os.makedirs(val_dir, exist_okTrue) # 复制而不是移动原始数据保留一份做后悔药 for img in images[val_count:]: src os.path.join(cls_path, img) dst os.path.join(train_dir, img) shutil.copy2(src, dst) for img in images[:val_count]: src os.path.join(cls_path, img) dst os.path.join(val_dir, img) shutil.copy2(src, dst) print(f{cls_name}: 共 {len(images)} 张 - 训练 {len(images) - val_count} 张 / 验证 {val_count} 张) if __name__ __main__: split_dataset()这里有两个细节值得说明。第一用shutil.copy2而不是shutil.move万一后面发现划分有问题还能重新来直接移动会破坏原始数据。第二固定SEED 42任何一次重新划分都产生完全相同的结果方便对比实验。3.3 用 datasets.yaml 把类别顺序钉死后续换模型也不会错位分类目录结构没问题了建议顺手写一份 YAML 配置。YOLO11cls 训练时直接传数据集根目录也能跑但 YAML 的好处是把类别顺序和路径显式记录成配置后续你用同一个数据集换模型、换脚本、交给同事复现都不会因为目录排列顺序变化导致类别错位。# rice_leaf_dataset.yaml path: /data/rice_leaf_dataset # 数据集绝对路径或相对路径 train: train # 训练集目录相对 path val: val # 验证集目录相对 path names: 0: rice_blast 1: rice_brown_spot 2: rice_bacterial_blight 3: rice_sheath_blight 4: healthyclasses 顺序一旦定下来就不要随意增删。如果要调整类别必须重新划分数据、重新训练否则旧模型的输出对应关系和新配置对不上。实际项目里见过有人为了测试新类别把 YAML 中间的0: rice_blast换成0: healthy训练完推理结果全部张冠李戴——这类问题没有任何报错提示只能从输出里慢慢发现。提示YAML 里的path建议写绝对路径省得后续改工作目录时找不到数据。4. 一键训练脚本实操YOLO11cls 从环境到跑出权重4.1 装环境与预训练权重ultralytics 安装和首次下载训练脚本依赖ultralytics包装一次即可。Python 建议 3.9 或 3.10PyTorch 版本根据显卡驱动选择。纯 CPU 也能跑小数据集但 15000 张图规模下速度会很难看优先配一张 CUDA 显卡。# 创建虚拟环境 conda create -n yolo11 python3.10 conda activate yolo11 # 安装 ultralytics自动拉取依赖的 torch 和 torchvision pip install ultralytics首次执行训练时YOLO11cls 会从 Ultralytics 官方权重地址下载预训练模型比如yolo11n-cls.pt。如果网络环境拉不动去模型仓库手动下载.pt文件放到当前目录程序检测到本地文件就不会重复下载。验证安装是否正常用一行代码from ultralytics import YOLO model YOLO(yolo11n-cls.pt) # 加载分类预训练模型 print(model.model)能正常打印网络结构就是一个好征兆。如果报找不到yolo11n-cls.pt先确认文件确实在当前目录且文件名没被改动。4.2 Python 训练脚本逐行拆解epochs、imgsz、batch、lr0 怎么设写训练脚本的核心思路是把可调参数都集中在文件顶部而不是散落在训练参数里。下面这份脚本加上注释基本可以当模板复用了from ultralytics import YOLO # 配置区 DATA_YAML rice_leaf_dataset.yaml # 数据集配置 PRETRAINED yolo11n-cls.pt # 预训练权重 EPOCHS 80 # 训练轮数 IMGSZ 224 # 分类输入分辨率 BATCH 32 # 批次大小 LR0 0.01 # 初始学习率 DEVICE 0 # GPU 编号 PROJECT runs/rice_cls # 输出项目目录 NAME yolo11n_baseline # 本次实验名称 PATIENCE 20 # 早停耐心轮数 # model YOLO(PRETRAINED) results model.train( dataDATA_YAML, epochsEPOCHS, imgszIMGSZ, batchBATCH, lr0LR0, deviceDEVICE, projectPROJECT, nameNAME, patiencePATIENCE, )参数选择没有什么玄学但有几条靠谱经验。imgsz224是分类任务的默认分辨率不需要沿用目标检测习惯开 640病斑细节在小分辨率下照样能保留特征。batch的大小受显存约束明显一张 8GB 显存卡跑batch32在imgsz224下基本到顶12GB 以上可以开到 64但这取决于你显卡型号。lr00.01是 Ultralytics 默认初始学习率配合 Adam 优化器在大多数分类数据集上都有稳定表现。patience20表示验证集指标连续 20 轮没有提升就自动终止训练防止无效空转。如果你更习惯命令行方式等价写法是yolo classify train datarice_leaf_dataset.yaml modelyolo11n-cls.pt epochs80 imgsz224 batch32 lr00.01 device0 projectruns/rice_cls nameyolo11n_baseline patience20两种方式最终生成的产物完全一致按团队协作习惯选一种就好。需要说明的是yolo11n-cls.pt是 nano 版本参数量最小后续想提精度可以换成yolo11s-cls.pt或yolo11m-cls.pt代价是训练时间和显存占用成倍上涨。4.3 训练日志读法loss 曲线、top1 acc 和混淆矩阵看哪几个点训练开始后终端会逐轮打印指标同时在runs/rice_cls/yolo11n_baseline/下生成 CSV 和图表。你需要关注四个东西训练损失train_loss和验证损失val_loss的走势是最基础的判断依据。训练损失持续下降、验证损失也开始下降说明模型在正常学习特征。如果训练损失下降而验证损失先降后升就是过拟合信号需要增加数据增强或减小模型容量。top1 acc 和 top5 acc 是分类任务的直接指标这里看两个值验证集 top1 acc 是否稳定以及最终轮的 top1 和 top5 差距大不大差距过大说明模型对部分类别置信度不足。训练结束会自动生成混淆矩阵图confusion_matrix.png这个图比任何指标都直观。它展示每一类真实标签和预测标签的对应关系。对角线越亮越好如果某个类别除了对角线之外还有一条亮线说明模型老是把它误判成另一个类别。这个信息在后面调整类别权重时非常有用。5. YOLO11cls 训练避坑记录五个从数据到显存的真实问题5.1 训练集和验证集重叠导致精度虚高训练完看到验证集 top1 acc 高达 0.97我心里第一反应不是高兴而是怀疑数据泄漏。拿着模型去推理新拍的叶片图预测结果明显比训练时差一截翻车点在于数据划分脚本有 bug同一张图的副本同时出现在训练集和验证集里模型已经见过验证集的“标准答案”了。原因通常有两个。一是划分前没有给所有图片统一打乱某些类别按目录顺序切分时同批采集的图像天然聚集在相邻区间前 80% 和后 20% 之间有重叠。二是原始数据文件夹本身有重复图片同一张图复制改名后存在多个类别里划分时没有去重。解决方法是划分脚本里增加一步全局查重对每张图计算文件哈希重复文件只保留一份参与划分。做法是结合第 3.2 节的脚本在random.shuffle之前先跑一次哈希去重import hashlib def file_hash(path): with open(path, rb) as f: return hashlib.md5(f.read()).hexdigest() # 在遍历类别、收集 images 时按哈希去重 seen set() unique_images [] for img in images: h file_hash(os.path.join(cls_path, img)) if h in seen: print(f删除重复图片: {img}) continue seen.add(h) unique_images.append(img) images unique_images5.2 显存不足训练中断第一次跑训练脚本batch 设成 64显卡是 8GB 显存第一轮还没结束就报 CUDA out of memory训练直接中断。这类情况大概率是 batch 设置得过于乐观或者是开了cacheTrue把全部图片载入内存。最简单也最稳的方法是让 Ultralytics 自动检测可用显存来分配 batch 大小。直接把batch64改成batch-1程序会跑一个快速基准测试自动设置一个不爆显存的批次大小。缺点是基准检测本身也要占用显存所以更推荐手动调低。解决方式很直接8GB 显存、imgsz224 的分类任务batch 设 16 或者 32 都能跑。再有就是检查训练命令里有没有加cacheTrue这个参数会预加载数据到内存显存不足时经常触发额外开销在分类任务上没必要。5.3 类别顺序错位导致推理结果张冠李戴训练完成之后把best.pt部署到推理脚本里发现给定的稻瘟病叶片图片被识别成健康叶片而且连续几张结果都偏移一个位置。排查之后发现是数据集 YAML 和训练脚本的类别顺序不一致。原因不复杂训练时用的rice_leaf_dataset.yaml类别顺序是0: rice_blast, 1: rice_brown_spot但后来为了验证新类别有人直接改动 YAML 把healthy放到了第 0 位而训练脚本没有同步更新模型训练还是按旧目录结构走的最终推理时的类别映射关系就全乱了。解决方法是把 YAML 当作唯一的类别映射依据训练前打印模型类别列表核对一次。训练完成后执行一次快速自检model YOLO(runs/rice_cls/yolo11n_baseline/weights/best.pt) print(model.names)输出的字典键值必须和rice_leaf_dataset.yaml里的 names 完全一致包括顺序和拼写。5.4 图像预处理把病斑细节拉变形训练集 top1 到了 0.9但放到真实农田拍回来的照片上效果差得离谱。原始数据里很多叶片是小尺寸病斑图片本身是高分辨率大图而训练时直接把整张图resize到 224病斑细节被压缩成了几个像素点特征基本丢光。这个坑的根子不在 YOLO11cls在数据准备阶段。分类任务里标准做法是保证送入训练前的主要目标物这里就是叶片已经占据画面的主体。如果原始图里叶片只占三分之一先人工或简单脚本裁切一次把叶片区域框出来再进训练管线。另外要注意resize的拉伸问题。Ultralytics 分类模式默认做等比缩放加 padding不会强行拉伸变形。如果你自己写预处理函数时用Image.resize((224, 224))直接拉宽高比就会改变病斑形态也随之扭曲。我的习惯是拍摄数据尽量统一长宽比实在不统一就在脚本里明确等比缩放加灰边。5.5 早停生效太快还没收敛就停了训练只跑了几十轮就自动停止top1 acc 停在 0.84 左右看 loss 曲线还有明显的下降趋势这就是patience设太小导致的早停误伤。patience的含义是验证集指标连续 N 轮没有提升就终止训练。设成 10 或 15在训练初期模型连续几轮平坦期就直接停了完全来不及跳到后面的更优区域。这种问题在自定义数据集上尤其常见因为 loss 曲线的收敛节奏和预训练模型差异大早停设置不能直接照抄官方 examples。解决方法是把patience调大到30~50或者干脆关掉早停设成 0用固定轮数跑完再人工判断。还有一种做法是开启cos_lrTrue让学习率按余弦曲线衰减后期自动缩小步长去逼近最优解配合大一点的patience通常能让收敛更充分。6. 验证模型与进阶提点混淆矩阵、单张推理和三个提点技巧训练完不等于交付验证这一步决定模型能不能真拿去用。最靠谱的验证不是只看训练日志里的 top1 acc而是亲手复算验证集指标再对单张图做推理确认。from ultralytics import YOLO model YOLO(runs/rice_cls/yolo11n_baseline/weights/best.pt) metrics model.val(datarice_leaf_dataset.yaml, splitval) print(Top1 准确率:, metrics.top1) print(Top5 准确率:, metrics.top5)单张推理同理建议同时输出 top1 和 top5 两个结果result model.predict(sourcetest_leaf.jpg)[0] top1_idx result.probs.top1 print(Top1 预测:, result.names[top1_idx], 置信度:, result.probs.top1conf.item()) top5_indices result.probs.top5 for i in top5_indices: print(f Top5 候选: {result.names[i]} ({result.probs.top1conf.item():.4f}))阅读混淆矩阵时有一个习惯我一直沿用别只看对角线专门找矩阵里对角线之外的亮块。比如 rice_brown_spot 大量被预测成 rice_sheath_blight说明这两类特征本身相似度偏高后续要么补数据要么考虑将这两个类别合并成“病斑类”去掉歧义。最后说三个投入产出比最高的提点手段。第一把yolo11n-cls换成yolo11s-cls或yolo11m-cls参数量上去了特征提取能力自然更强代价只是训练时间翻倍。第二对样本不均衡的类别做加权处理在model.train()里调整每个类别的 loss 权重让少数类被足够重视。第三训练时把fraction参数从默认全量数据改为 0.8 做几次快速实验先验证模型方向和参数设置在少量数据上是否合理再投入全部数据跑完整训练能省下不少试错成本。回到最初的问题这套“15000 张图 分类文件夹 YOLO11cls 一键训练脚本”值不值得投入我的答案始终是值得但前提是你愿意在跑训练之前花半天时间做数据体检、修好验证集划分并在训练过程中盯着混淆矩阵而不是只看准确率数字。我第一版模型正是因为跳过了数据体检拿了个虚高的 acc 当成果汇报后来才发现模型记住的是背景而不是病斑。现在每次开工我都会把体检脚本排在训练脚本前面固定跑一遍这个顺序反过来省下的时间远多于多花的几分钟。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网