简介:这是一份面向图像分类学习者的庄稼害虫识别数据集,覆盖蛀虫、健康无虫、螨虫等4个类别,训练集与验证集按目录分开存放,使用ImageFolder即可直接加载,也可作为YOLOv5分类任务的数据输入。压缩包共676个文件,以673张jpg图片为主,另含1个json分类字典、1个可直接运行的可视化脚本和1张示例效果图,整体约53.89MB,无需额外清洗即可开始实验。数据规模为训练集620张、验证集53张,采用data目录下train与test的清晰布局,同一类别图片集中存放,方便直接统计、读取和样本划分。原始素材命名字段保留了拍摄编号与图片来源,适合图像分类入门、模型效果验证、课程设计以及害虫识别竞赛练手。当前已有277人学习浏览,配套脚本会随机读取4张图片并生成展示结果,可快速直观检查分类效果与标注质量,方便后续训练调参。
1. 庄稼害虫图像分类数据集:拿到手先看这三样再动手
做农业视觉项目的人应该都有体会,数据集质量往往比模型结构更决定上限。这套 4 类庄稼害虫分类数据,train 620 张、test 53 张,已经按文件夹分好类,用 PyTorch 的 ImageFolder 直接能打开,还附带一个 json 分类字典和可视化脚本。它最大的价值不是数据量有多大,而是帮你把「数据组织」这一步省掉了——不用自己写 Dataset、不用手动划分文件夹、连图片预处理都省了,解压就能开始跑基线。适合刚接触图像分类想练手的新手,也适合需要快速验证模型改动的熟手。这篇文章我会从目录结构、加载方式、训练命令、数据增强到避坑排查,把这份资源完整过一遍。
2. 数据集目录结构解析:读懂 train/test 划分与 json 字典文件
2.1 目录树与文件命名规则:训练集 620、验证集 53 意味着什么
先把资源解压,正常情况下你会看到一个data目录,里面分train和test两个子目录,每个子目录下再按类别建文件夹。命名大概长这样:
data/ ├── train/ │ ├── 蛀虫/ │ │ ├── 23327_jpg.rf.809a1904da5ba707827adc7343253f26.jpg │ │ ├── 25645_jpg.rf.1b2457dc99d7b774a6eeb537efb2a253.jpg │ │ └── ... │ ├── 健康无虫/ │ │ └── ... │ ├── 螨虫/ │ │ └── ... │ └── ... ├── test/ │ ├── 蛀虫/ │ ├── 健康无虫/ │ └── ... └── class_dict.json文件命名里那串jpg.rf.加哈希值,是 Roboflow 导出数据的典型痕迹,说明这些图片可能是用标注工具整理后导出的。rf前缀不影响加载,PyTorch 只看扩展名和文件内容,不用管这个。真正需要注意的是类别文件夹名字,如果里面是中文名,某些老版本 PyTorch 在 Windows 上读取会踩编码坑,后面我会专门说这个。
train 总数 620、test 总数 53,这个比例约 92:8。对深度学习来说测试集略少,但作物害虫这种细分场景,样本采集成本本来就高,53 张验证图已经能反映模型泛化趋势。更关键的是,620 张训练图要分给 4 个类,平均每类只有 155 张——这个体量提醒你两点:第一,必须做数据增强,否则很容易过拟合;第二,迁移学习基本是必选项,从头训练一个 CNN 在这个规模下翻车概率极大。
2.2 json 字典文件与可视化脚本:动手前先看分布
资源里附带了一个 json 文件,内容是 4 个分类的字典映射。我的习惯是先打印出来看看,确认类别顺序和文件夹名完全对应。用 Python 加载 json 只需要几行:
import json with open('data/class_dict.json', 'r', encoding='utf-8') as f: class_dict = json.load(f) print(class_dict) print('类别数量:', len(class_dict))这段代码先按 UTF-8 编码读取 json,再打印字典内容和类别数。为什么要强调encoding='utf-8'?因为有些数据集导出时用的是 GBK 编码,不指定编码在 Windows 上会直接抛UnicodeDecodeError。类别数打印出来应该是 4,如果少于 4,说明 json 和数据集版本不匹配,后面训练时 num_classes 就会算错。
另外,资源里还带了一个可视化 py 文件,功能是随机取 4 张图展示并保存到当前目录。脚本无需改参数直接可跑。这个工具虽然小,但很实用——我通常会在训练前跑一遍,用肉眼看每个类别的图片质量:有没有模糊图、有没有误标样本、有没有背景差异过大的图。很多时候模型准确率上不去,根因是原始数据里混了错误标注,可视化是排查这类问题最快的方式。
2.3 ImageFolder 加载方式:不用写自定义 Dataset 的理由
用 PyTorch 加载这个数据集,核心就是一句话:ImageFolder按文件夹名自动映射类别,图片无需额外操作就可以用于训练。加载代码非常简单:
from torchvision import datasets, transforms data_transforms = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) train_dataset = datasets.ImageFolder( root='data/train', transform=data_transforms ) test_dataset = datasets.ImageFolder( root='data/test', transform=data_transforms ) print('训练集类别:', train_dataset.classes) print('训练集样本数:', len(train_dataset)) print('验证集样本数:', len(test_dataset))这段代码里有两个容易忽略的点。第一,Normalize用的均值方差是 ImageNet 预训练模型的统计值,不是这个数据集自己的统计值——如果你打算加载预训练权重做迁移学习,就必须用这套值;如果从头训练,其实可以不用 Normalize 或者用自己算的统计值。第二,train_dataset.classes会按文件夹名的字母序或 Unicode 码点排序,打印出来确认一下顺序,尤其是中文文件夹名,排序结果可能和你预期不一致,而类别索引是训练时计算 loss 的重要依据,搞错了模型学的东西就是错的。
ImageFolder 能直接加载,省掉的不只是 Dataset 代码,还有标签编码逻辑。它是类别路径下的文件名自动按文件夹分桶,底层返回(img_tensor, label_index),其中 label_index 是 classes 列表里的整数序号。DataLoader 再配合shuffle=True做批次采样,这就是一个标准分类流程。
3. 用 YOLOv5 分类模式跑通训练:模型选型与命令详解
3.1 为什么优先选 YOLOv5 分类而不是从头训 ResNet
这个数据集可以用作 YOLOv5 的分类数据集,这是它自身描述里明确写的。我建议新手直接走这条路,而不是自己去写 ResNet 训练脚本。理由有三层:第一,YOLOv5 分类模式已经封装好了数据加载、增强、训练循环、学习率调度和评估,你不用去管CrossEntropyLoss内部实现;第二,它默认用 ImageNet 预训练权重初始化,在这个 620 张的小数据集上是保命级别的操作,从头训一个 ResNet18 大概率收敛慢且泛化差;第三,YOLOv5 自带val.py评估脚本,会输出准确率、召回率、混淆矩阵,省去你自己画图的功夫。
我见过不少人在小数据集上自己搭 CNN,折腾两三天,准确率卡在 85% 上不去,换成迁移学习一个上午就跑到 94%。这不是模型结构的问题,是数据量撑不起从头学习复杂特征。YOLOv5 分类模式本质上就是微调预训练分类头,你只需要关心数据路径和超参数。
3.2 数据目录改造成 YOLO 分类格式:软链接方式最省事
YOLOv5 分类模式对数据目录的要求和 ImageFolder 略有不同:它需要你提供数据集根路径,根路径下要有train和val两个文件夹——注意,它默认检查的是val,不是test。而这份资源里划分的是train和test,所以直接跑会报找不到val目录。处理方法有两个,一个是把test重命名为val,另一个是建一个软链接。
# 方法一:直接改名 mv data/test data/val # 方法二:软链接,保留原目录不动 ln -s data/test data/val我倾向于用软链接,因为这两个目录名在 PyTorch 和 YOLO 里指向不同含义:PyTorch 的 ImageFolder 用test只是普通加载目录,而 YOLOv5 会把val目录在训练过程中每轮验证一次。如果你把test完全改名成val,以后再用 PyTorch 脚本加载时还得改回来,软链接能两边同时满足。另外注意,YOLOv5 分类模式还要求 train 和 val 目录下的类别文件夹一一对应,也就是说 val 里必须有和 train 完全一样的 4 个类别目录,缺一个都会在加载时报错。
3.3 训练命令关键参数:图片尺寸、epoch、batch 怎么设
YOLOv5 训练分类模型的命令行入口是classify/train.py,核心参数可以通过命令行覆盖。下面是针对这套数据的一套合理起始配置:
python classify/train.py \ --model yolov5s-cls.pt \ --data data \ --epochs 50 \ --img 224 \ --batch 32 \ --name pest_cls这条命令做了四件事:加载 YOLOv5s 分类预训练权重、指定数据集根目录、训练 50 轮、图片分辨率 224、批次大小 32。--data data指向的目录下必须包含train和val两个子目录,YOLOv5 会自动读取里面的类别文件夹并构建标签映射。
参数设置上,--img 224是个平衡点:这个数据集的图片大多来自田间拍摄,原图分辨率可能参差不齐,224 是 ImageNet 预训练常用尺寸,能直接复用预训练统计特征。如果你觉得 224 损失细节,可以试 320,但显存占用会明显上升。--batch 32在单卡 8G 显存下基本安全,如果显存不够先降到 16,不要直接调小图片。--epochs 50对小数据集够用,我建议训练时开启--cos-lr余弦退火,收敛会更稳定。
还有一个重要参数是--pretrained,默认情况下 YOLOv5 会加载--model指定权重里的预训练参数,如果你想从头训练,要显式加--pretrained为空路径或者用--weights ''。我强烈不建议从头训,除非你想对比迁移学习的效果。
4. 数据增强与类别不均衡处理:把 620 张小数据集用出上限
4.1 类别统计先行:看清四种害虫的样本分布
数据增强怎么做,取决于类别分布长什么样。先在训练前统计每个类别的样本数,用 Python 一行就能完成:
import os from collections import Counter train_dir = 'data/train' counts = Counter() for cls_name in os.listdir(train_dir): cls_path = os.path.join(train_dir, cls_name) if os.path.isdir(cls_path): counts[cls_name] = len(os.listdir(cls_path)) for cls, cnt in counts.most_common(): print(f'{cls}: {cnt}')这段代码遍历 train 目录下的子文件夹,统计每个文件夹里图片数量。跑完后你会看到 4 个类别的具体分布。如果某一类比其他类少很多,比如螨虫只有 80 张,其他类有 180 张,那就需要针对性处理。620 张分 4 类,最坏情况下某类可能不足 100 张,这个规模下模型很容易把少样本类别忽略掉,因为多数类别的梯度主导了更新方向。
YOLOv5 分类模式的增强策略是内置的,默认开启随机翻转、缩放、色彩抖动等在线增强,但你也可以手动控制。如果是自己写 PyTorch 训练脚本,需要在transforms里显式添加增强操作,下面这段是常用组合。
4.2 在线增强:torchvision transforms 的常用组合
在线增强的意思是训练时每轮都随机变换图片,相当于每张原图产生了多个变体,模型永远看不到完全相同的两张图。常用组合如下:
train_transforms = transforms.Compose([ transforms.RandomResizedCrop(224, scale=(0.7, 1.0)), transforms.RandomHorizontalFlip(p=0.5), transforms.RandomRotation(15), transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ])这里每个操作都有明确目的。RandomResizedCrop随机裁剪图片的一部分并缩放到 224,模拟害虫在画面中位置和大小不固定的情况,scale 0.7 到 1.0 的意思是裁剪面积占原图的 70% 到 100%,不会裁掉主体。RandomHorizontalFlip对害虫识别是安全的,因为庄稼害虫不存在左右语义差异。RandomRotation(15)限定了旋转角度在正负 15 度内,转太多会引入无意义的背景干扰。ColorJitter调节亮度对比度饱和度,模拟不同时间、不同天气下田间拍摄的光照差异。
验证集的 transform 就不要加任何随机操作了,只做 Resize 和 Normalize,否则验证准确率会波动很大,无法稳定判断模型是否收敛。这是新手最容易犯的错误,训练集验证集用同一套 transform,最后看 loss 曲线总觉得不对劲。
4.3 离线补样本:简单翻转与裁剪扩增的取舍
如果统计完发现某个类别样本实在太少,比如不足 100 张,光靠在线增强可能还不够。这时候可以考虑离线扩增,即在训练前把图片复制并做变换,写入磁盘。对农业害虫这种类内差异较大的场景,我一般只做两种离线扩增:水平翻转和垂直翻转。旋转 90 度这种操作在害虫识别里要谨慎——有些害虫有朝向特征,翻了 90 度可能就不像真实样本了。
from PIL import Image import os def augment_folder(src_dir, dst_dir, flip_modes=['FLIP_LEFT_RIGHT', 'FLIP_TOP_BOTTOM']): os.makedirs(dst_dir, exist_ok=True) for fname in os.listdir(src_dir): img_path = os.path.join(src_dir, fname) img = Image.open(img_path).convert('RGB') for mode in flip_modes: flipped = img.transpose( Image.FLIP_LEFT_RIGHT if mode == 'FLIP_LEFT_RIGHT' else Image.FLIP_TOP_BOTTOM ) base, ext = os.path.splitext(fname) flipped.save(os.path.join(dst_dir, f'{base}_{mode}{ext}'))注意这段代码假设图片本身方向正确。如果原图已经旋转过(比如手机拍摄带了 EXIF 方向信息),PIL 在打开时会自动处理 EXIF,但要确保保存时用的是处理后的图像对象而不是原始字节。另一个坑是:离线扩增后的图片文件名不能与原有文件重名,否则会覆盖原图。用_FLIP_LEFT_RIGHT这种后缀能避免冲突。
离线扩增不要贪多。小数据集上过度扩增会让验证集失去意义——如果验证集里出现了训练集的增强变体,评估准确率会虚高。我一般只在训练集做离线增强,验证集保持原始图片。
5. 常见问题与避坑排查:打开、训练、验证三个环节的坑
5.1 现象:ImageFolder 报错「Found 0 files in subfolders」
用ImageFolder加载时报Found 0 files in subfolders of ...,训练集图片一张都没读进来。
原因有两个:第一,路径指向错误,train_dir写成了data而不是data/train;第二,目录下的图片扩展名不被ImageFolder识别,比如图片是.jpeg但实际是损坏文件,或者扩展名是大写.JPG。在部分 Linux 系统里,ImageFolder默认的扩展名列表是('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif', '.tiff', '.webp'),没有.JPG。
解决方案:检查目录层级,用os.listdir(train_dir)打印前几个文件名看看扩展名;如果是大小写问题,批量重命名。
# 把大写扩展名改成小写 for f in */*.JPG; do mv "$f" "${f%.JPG}.jpg"; done这个命令逐一把.JPG后缀改成.jpg,但注意类别文件夹也要能读,文件夹名不要带点号,否则系统可能误判。
5.2 现象:验证集准确率高但实际识别效果差
在 test 集上准确率达到 95%,拿到田间新拍的图片一测,识别结果完全随机。
原因几乎是「过拟合加数据集分布太窄」。620 张训练图如果都来自同一块地、同一台相机,模型学到的是这块地的背景纹理而不是害虫本身。验证集 53 张也来自同一分布,所以验证准确率虚高。这是小数据集最常见的问题,不是模型 bug。
解决方案是交叉验证式的判断:把 train 里的每一类抽出 10% 作为额外验证集,再跑一轮,对比两轮准确率差异。差异超过 5 个点就说明过拟合严重。此时要做三件事:增强从在线增强换成更强的随机裁剪加色彩抖动、冻结 more layers、加 dropout。YOLOv5 里 dropout 默认是 0,可以调--dropout 0.2。
5.3 现象:train 和 test 目录都有数据,但 YOLOv5 提示 No labels found
跑classify/train.py时报错No labels found in data/train,但图片明明都在。
原因是 YOLOv5 分类模式可能在做标签缓存,它去data/train下找labels文件或.txt标注,找不到就报错。分类模式其实不需要目标框标注,但某些版本对目录结构检查严格。
解决方案:确认--data指向的目录下是train和val,而不是train和test,并且 val 目录存在且非空。如果还是报错,手动建一个空labels文件夹放进去,有些版本会校验这个目录存在才继续。
mkdir -p data/labels注意val目录内的类别文件夹名称必须和train里的完全一样,连顺序都要一致。如果类别文件夹名带空格或特殊符号,建议都改成英文或简短中文名,比如pest_boring、healthy,这能省掉不少加载阶段的编码麻烦。
5.4 现象:训练中途 Loss 变成 NaN
训练到第 20 轮左右,loss 突然变成 NaN,然后准确率直接归零。
原因是学习率过大导致梯度爆炸,或者数据里有像素异常值。小数据集容易出这个问题,因为每批次的梯度方差大,学习率稍微激进一点就发散。日志里能看到 loss 在一段正常下降后突然跳到一个极大值,然后全部变成 nan。
解决方案:把初始学习率调小一个数量级,YOLOv5 分类模式默认 lr 是 0.01,小数据集我建议开到 0.001,并开启--cos-lr让学习率平滑下降。另外,检查数据里有没有全黑或全白图片,像素值全是 0 或 255 的图经过归一化后会出现极端值,间接导致梯度异常。用 PIL 遍历一遍所有图片,算一下每张图的像素标准差,把标准差接近 0 的图挑出来删掉或替换。
6. 混淆矩阵与单张图片推理自检:训练完一定手动过一遍
训练完成后,不要只看 test 准确率。准确率会掩盖很多问题,尤其是类别不均衡时——如果螨虫占了一半训练集,模型全猜螨虫也能有 50% 准确率。我每次训练完必做两件事:看混淆矩阵,做单张推理。
YOLOv5 的classify/val.py在验证结束后会自动生成混淆矩阵图,保存在runs/val-cls/exp/confusion_matrix.png。拿到这张图先看对角线,四类害虫的对应格子里数字应当明显高于其他格子。如果发现两个类别互相混淆严重,比如健康无虫经常被识别成蛀虫,说明这两个类在视觉上重叠度太高,或者训练样本里这两类背景相似。此时优先检查原始图片,而不是急着换模型。
单张图片推理用classify/predict.py即可:
python classify/predict.py \ --weights runs/train-cls/exp/weights/best.pt \ --source data/test/蛀虫/23327_jpg.rf.809a1904da5ba707827adc7343253f26.jpg这个命令会输出这张图属于每个类别的概率。我习惯把验证集里预测错的几张图单独挑出来,逐个看原图:有些是图片本身模糊,有些是背景里有其他昆虫。我对这个数据集印象最深的一次踩坑,是发现验证集里有一张「健康无虫」的图片实际拍的是土壤特写,模型自然无法识别——数据清理永远比调参有效。从那以后我每次拿到新数据集,都会在第一天把可视化脚本跑一遍,强制自己看完全部类别的样本再动手训练。希望你也能养成这个习惯,训练之前先看清数据长什么样,能帮你省掉后面好几轮返工的力气,希望帮到你。
本文还有配套的精品资源,点击获取