YOLOV5图像分类实战:小麦叶病害识别从数据整理到模型部署
2026/9/14 9:28:30 网站建设 项目流程

简介:面向小麦叶片病害识别场景的YOLOV5分类实战资源包,包含完整可运行代码、标注数据集与训练好的权重参数,适合有一定深度学习基础、希望快速落地YOLOV5分类任务的学习者,也可用于农业病害检测项目的前期验证。整个压缩包为7z格式,整体265.15MB,解压后共2000个文件,核心是1866张jpg病害样本图像,另有51个yaml配置、51个py训练/推理脚本、sh启动脚本及ipynb示例,按datasets目录摆放即可开始训练。预训练模型仅10个epoch,top1准确率已达0.93以上,说明网络尚未收敛,加大轮次可进一步提升性能;配套的权重和脚本让用户无需从零调参即可复现并继续优化。目前已有198人学习下载,素材组织清晰,适合课程设计、毕业设计或实际农业场景的快速原型开发。

1. 7种小麦叶病害,YOLOV5分类模式为什么够用

小麦叶片病害识别,不少方案一上来就套检测框,这份资源里走的是另一条路:用 YOLOV5 自带的分类模式,输入整张叶片图,网络直接输出 7 个类别的概率,取 top1 作为识别结果。实测只训练 10 个 epoch,top1 准确率就过了 0.93,而且这个数字是在学习率还没走完 OneCycle 升温段、网络远未收敛时拿到的,可见 ImageNet 预训练主干对叶片纹理的迁移收益相当大。项目总大小 300MB,代码、数据集、训练好的权重都齐了,里面还附了 tutorial.ipynb 教程文件,拿到手能直接跑。它适合大田巡检初筛、农业系统图片归档、检测任务前置分类这三类场景。与检测模式不同,分类模式不需要一个一个画框,数据组织就是「一个类别一个文件夹」,上手成本低很多,但真要把 0.93 的结果稳定复现出来,数据集目录怎么摆、hyp 里动哪几个参数、评估时看哪些指标,还是有不少细节值得抠。

2. 从散图到 ImageFolder:datasets 目录的摆法

2.1 分类模式只认目录,不认标注文件

YOLOV5 的分类训练入口是 classify/train.py,它和检测模式最大的差异在于:不使用 YOLO 格式的 .txt 标注文件。数据加载走的是 ImageFolder 约定,根目录下按 train、valid、test 三个子目录组织,每个子目录内再按类别建文件夹,图片就放在类别文件夹下。对这份资源来说,拿到手的是 Roboflow 导出的一批散图,文件名是下面的风格:

datasets/wheat_leaf/ ├── train/ │ ├── hispa/ # 文件名第一段作为类别 │ ├── shape/ │ └── flower/ ├── valid/ └── test/

三个目录在分类任务里的分工比检测更明确:train 负责更新权重,valid 参与每个 epoch 的评估并决定 best.pt,test 只在全部训练结束后跑一次。表里列了常见的错法。

目录作用常见的错
trainImageFolder 扫描类别并训练类别文件夹与 valid 不一致
valid每轮评估,输出 top1/top5把 test 的图片混进 valid
test训练结束后做最终评估用训练日志当最终指标

这份资源的图片命名是这种风格:Hispa-112-_jpg.rf.9262afd3...jpgshape-433-_jpg.rf.56390ea7...jpgflower_0022_jpg.rf...jpg。看到_jpg.rf.基本可以断定是 Roboflow 导出时生成的,类别信息就藏在最前面的第一段。写一个一次性整理脚本,先把散图按类别复制到photos/类别名/下:

import re, shutil from pathlib import Path src = Path('raw_images') # 原始散图目录,按你的解压路径改 dst = Path('datasets/wheat_leaf/photos') def extract_label(name: str) -> str: # 开头连续字母数字,遇到 - 或 _ 即截断 m = re.match(r'^([A-Za-z0-9]+)[-_]', name) return m.group(1) if m else 'unknown' for img in src.glob('*.jpg'): label = extract_label(img.name).lower() # 统一小写,避免 Linux 下分两类 out_dir = dst / label out_dir.mkdir(parents=True, exist_ok=True) shutil.copy(img, out_dir / img.name)

正则^([A-Za-z0-9]+)[-_]只取文件名最开头的连续字母数字段,Hispa-112-...取到 Hispa,flower_0022...取到 flower。这里做了lower()是因为 Linux 文件系统区分大小写,Hispa 和 hispa 会被识别成两个类,小麦病害所有类名统一小写最省事。脚本用 copy 而不是 move,先确认统计数量对得上,再删原始文件,避免整理出错后没法恢复。如果从 Roboflow 直接导出,可以选 Folder 格式让它自动分好 train/valid/test 和类别文件夹,这个脚本只在拿到散图时用。

2.2 按 8:1:1 划分并保持每个类别的分布

照片归好类别之后,要拆成 train、valid、test 三份。拆分时最容易犯的错是全局 shuffle 之后再切:如果某个类别只有 30 张图,全局乱序后 valid 里可能只剩 2 张,评估结果方差会很大。正确做法是逐类 shuffle,再按比例切分:

import random, shutil from pathlib import Path random.seed(42) root = Path('datasets/wheat_leaf/photos') out = Path('datasets/wheat_leaf') ratios = (0.8, 0.1, 0.1) for cls_dir in root.iterdir(): if not cls_dir.is_dir(): continue images = list(cls_dir.glob('*.jpg')) random.shuffle(images) n1 = int(len(images) * ratios[0]) n2 = int(len(images) * (ratios[0] + ratios[1])) for split, chunk in zip( ('train', 'valid', 'test'), (images[:n1], images[n1:n2], images[n2:])): target = out / split / cls_dir.name target.mkdir(parents=True, exist_ok=True) for f in chunk: shutil.copy(f, target / f.name)

random.seed(42)是为了复现同一份划分,后面调参时如果发现某个类别一直表现差,先确认训练集里它的样本量,而不是怀疑网络结构。检查各类数量一条命令就行:

find datasets/wheat_leaf/train -mindepth 2 -type f | awk -F/ '{print $(NF-1)}' | sort | uniq -c

看到某个类别只有个位数时,优先做类别加权采样,或者给这个类单独补离线增强,比如水平翻转、随机裁剪。分类模式下 YOLOV5 对每类样本量的要求不算高,但 10 个 epoch 的短训练尤其怕冷门类样本太少,预训练先验容易把这一类带偏。

3. 训练命令与超参数:10 个 epoch 怎么稳定复现 0.93+

3.1 最小可复现命令

数据集摆好后,用 YOLOV5 训练自己的数据集,核心动作就一条命令。环境上按官方 requirements.txt 装好 PyTorch 和 torchvision 就够,classify 入口不需要额外依赖:

python classify/train.py \ --model yolov5s-cls.pt \ --data datasets/wheat_leaf \ --epochs 10 \ --img-size 224 \ --batch-size 32 \ --name wheat_leaf_exp1

参数里最关键的是--model yolov5s-cls.pt,这是官方在 ImageNet 上预训练过的分类权重,第一次运行会自动下载。没有它,或者下载失败时换成yolov5s-cls.yaml从零开始,但 10 个 epoch 远拿不到 0.93,因为叶片病斑的纹理先验全丢了。--data指向上一章搭的 datasets/wheat_leaf 根目录,脚本会自动找 train、valid 子目录。--img-size用 224,这是分类模式默认分辨率,原图很大时提高 288 会有收益,但 10 个 epoch 的短训里,把精力花在分辨率上不如多跑几个 epoch。

训练产物在runs/train-cls/wheat_leaf_exp1/,weights 目录里存 best.pt 和 last.pt,results.csv 是逐 epoch 的指标。默认学习率 0.01,配合 OneCycle 调度,前几个 epoch 实际上还在预热阶段,所以 top1 到第 8~10 个 epoch 还在涨是正常现象,不代表代码有问题。

3.2 超参数表:这次真正值得动的几项

classify/train.py 默认读data/hyps/hyp.scratch-low.yaml,里面很多字段是检测头用的,分类任务真正敏感的项不多,整理成表:

参数默认值作用10 轮短训建议
lr00.01初始学习率0.005~0.01,图少时降半
lrf0.1终点学习率比例0.1 保持
warmup_epochs3.0预热轮数1.0,10 轮里预热 3 轮太奢侈
weight_decay0.0005权重衰减0.0005 保持
hsv_h0.015色调扰动保持,叶片颜色不是强特征
fliplr / flipud0.5 / 0.0翻转概率保持,病害方向敏感别开 flipud

最容易踩的是 warmup_epochs。官方默认 3.0 是给 100~300 轮长训练设计的,10 轮短训里它占掉近三分之一,真正高速学习只剩后面几轮。改成 1.0 后,同样的 10 个 epoch,top1 通常能再涨 1~2 个点。改超参的常规做法是复制一个自己的 hyp 文件,只动这几项,再通过--hyp传进去:

python classify/train.py \ --model yolov5s-cls.pt \ --data datasets/wheat_leaf \ --epochs 10 --batch-size 32 \ --hyp hyp.wheat_leaf.yaml \ --label-smoothing 0.1 \ --name wheat_leaf_exp2

分类损失函数默认走带 label_smoothing 的交叉熵,0.1 的平滑系数在 7 类小数据集上建议保留,它把 one-hot 目标换成软标签,能明显抑制对训练集的过度自信。注意 YOLOV5 的 classify 入口和检测入口参数不完全一样,与其在命令行反复试覆盖项,不如直接改 hyp 文件,保证每次实验命令一致。

提示:10 轮短训性价比最高的一步是把 warmup_epochs 从 3.0 降到 1.0,其余超参先保持默认。

3.3 显存边界和收敛边界

224 分辨率下 batch-size 32 大约占 6~8 GB 显存。显存小于 6 GB 就把 batch-size 降到 16,同时把数据读取线程--workers调到 4 以内,否则 CPU 读图会成为瓶颈。另一个边界是类别数:classify 要求至少两个类别文件夹,如果--data指向了 datasets 根目录而不是 wheat_leaf 子目录,报错信息里会出现 train 目录不存在,顺着路径检查就能定位。

4. Top-1 0.93 背后的评估逻辑:先看混淆矩阵再看收敛

4.1 训练产物里先看哪几个文件

训练结束后第一件事不是打开权重,而是看runs/train-cls/wheat_leaf_exp1/results.csv。每一行是一个 epoch,包含 train_loss、test_loss、metrics/accuracy_top1、metrics/accuracy_top5 几列。注意这里的 test_loss 实际是 valid 集合上的结果。如果出现 train 的 top1 很高、valid 明显跟不上的情况,说明预训练先验还撑着,但模型已经开始记忆训练集,此时加数据增强比改结构更有效。

用官方评估脚本跑一遍 valid:

python classify/val.py \ --weights runs/train-cls/wheat_leaf_exp1/weights/best.pt \ --data datasets/wheat_leaf \ --img-size 224 \ --name val_exp1

输出会打印每个类别的 top1 和总体 top1,并把混淆矩阵图写到runs/val-cls/val_exp1/confusion_matrix.png。只看总体 top1 容易被多数类掩盖问题,比如某个冷门类只有十几张图,per-class top1 掉到 0.6,但对总体 0.93 几乎没影响。

4.2 混淆矩阵怎么看:先找对角线的缺口

打开 confusion_matrix.png 先看对角线:哪个类别的值明显低于其他类别,哪个就是当前模型的短板。再看这个类别的行方向,概率落到了谁头上,两类大概率视觉上很像。叶部病害里最常见的是同一叶片上早期和晚期病斑并存,模型倾向于把晚期样本归到早期类,这种混淆靠调学习率解决不了,要么补数据,要么考虑合并类别。可以按下面的顺序检查:

检查项看什么典型结论
对角线值各类别正确率某类明显低,先补该类数据
行方向分布错误集中到哪类两类互混,考虑合并或补差异样本
最后几个 epoch 曲线top1 是否仍在上涨在涨就加 epoch,不要动结构

如果觉得内置矩阵图不够细,classify/val.py 还会把标签和预测的 tensor 写到输出目录,直接用 sklearn 出逐类 report:

import torch from sklearn.metrics import classification_report labels = torch.load('runs/val-cls/val_exp1/labels.pt') preds = torch.load('runs/val-cls/val_exp1/preds.pt') class_names = [...] # 与 train 目录类名字母序一致 print(classification_report(labels.numpy(), preds.numpy(), target_names=class_names))

这里唯一的坑是类别编号:ImageFolder 按类名字母序编号,跟数据目录里的展示顺序不一定一致,class_names 要以训练日志或数据集的 classes 属性为准。report 里单个类别的 precision、recall、f1 比 top1 更能说明问题:recall 低是漏检,precision 低是误报,业务上两个的代价完全不同。

4.3 top1 0.93 够不够用取决于错误长在哪

素材里说「10 个 epoch 网络还没收敛」,对应的现象就是 results.csv 最后几行 top1 还在往上走,test_loss 也没有进入平台期。这时候继续加 epoch 就好,不用调结构。但 0.93 的 top1 意味着大约每 15 张错 1 张,拿去做施药决策显然不够,做图片归档和初筛则可以接受。关键是把那 7% 的错误集中在哪几个类别上搞清楚,而不是盯着平均值自我安抚。

5. 批量推理与导出的排错顺序

5.1 官方 predict 与类别映射

分类权重跑批量预测,一条命令:

python classify/predict.py \ --weights runs/train-cls/wheat_leaf_exp1/weights/best.pt \ --source data/leaf_images \ --img-size 224 \ --name batch_infer

结果写到runs/predict-cls/batch_infer,每张图带预测标签输出。source 传目录会递归子目录,传单图只处理一张。批量跑之前先挑 20 张肉眼核对,同时把类别 id 和名称映射存成 JSON 放在权重旁边:

{"0": "hisa", "1": "shape", "2": "flower"}

业务系统直接读这份映射,id 顺序与训练时类名字母序一致,导出前后都不要改目录结构。

5.2 导出 ONNX 并用 onnxruntime 推理

分类权重脱离 PyTorch 部署时先导出:

python export.py \ --weights runs/train-cls/wheat_leaf_exp1/weights/best.pt \ --include onnx \ --img-size 224

onnx 输入是 1×3×224×224、归一化到 [0,1] 的 RGB 张量,输出 1×7 logits:

import cv2, numpy as np, onnxruntime as ort names = ["hisa", "shape", "flower"] # 从 json 读,别手写 img = cv2.imread("leaf.jpg") img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (224, 224)).astype(np.float32) / 255.0 img = np.transpose(img, (2, 0, 1))[None] sess = ort.InferenceSession("best.onnx") out = sess.run(None, {sess.get_inputs()[0].name: img})[0] idx = int(out[0].argmax()) print(names[idx], float(out[0][idx]))

出问题按三个顺序查:先查 RGB/BGR 顺序,反了会导致结果集中到少数类别;再查归一化,YOLOV5 分类导出默认 0-1,不是 ImageNet 的 mean/std;最后查类别顺序,onnx 输出下标与训练时类名字母序对齐。上线后可以把这套权重当检测前置:先筛掉健康叶片,疑似病叶再送检测模型,在 Jetson 这类边缘设备上能省掉大量无效推理。

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

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

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

立即咨询