简介:面向植物病理与计算机视觉交叉领域研究者、农业智能装备开发者及图像分类入门学习者,这是一份带标注的大豆叶片病害图像分类数据集。资源共约6000张真实叶片图像,划分为Diabrotica speciosa(叶甲危害)、健康、Caterpillar(毛虫危害)三个类别,并已按训练集、测试集分目录存放,方便直接用于深度学习模型训练与评估。压缩包共含2000个文件,主体为1998张jpg图像,另附1个json类别配置文件与1个Python可视化脚本,可快速查看各类样本分布与标注情况,整体约808.63MB。目前已有135人学习下载,适合用于病害识别算法验证、迁移学习实验或农业智能诊断系统原型开发。通过该数据集可自行训练分类模型,验证不同网络结构在大豆叶片病害识别上的表现,也可结合图像增强、数据划分等技巧完成完整实验流程,为后续农业病害自动检测提供可靠实验基础。
1. 大豆叶片病害图像分类数据集:拿回来先做三件事
做图像分类,最耗时间的从来不是写模型,而是找一份能直接训练的数据集。这份大豆叶片病害图像分类数据集约6000张已标注图像,一共三类:Diabrotica speciosa(叶甲类虫害)、健康(healthy)、Caterpillar(毛毛虫),训练集和测试集已经按类别目录划分好,附带JSON标签文件和show可视化脚本。对刚接触图像分类、正在做网络改进对比实验、或者需要快速验证迁移学习效果的从业者来说,最合适的用法就是把数据流一次跑通,把精力省给模型本身。不过拿到手别急着开训,目录结构、标签顺序、图片完整性三件事先确认,能少踩一半的坑。
2. 目录与标签体系:JSON里藏着类别的最终答案
2.1 目录结构:训练集和测试集怎么组织的
这份数据集是按图像分类最常见的组织方式存放的:顶层分train和test两个目录,各自再按类别建子目录,每个目录里放同一类的图片。文件名类似healthy (871).jpg这种格式,括号里的编号只是原始采集顺序,和标签没有关系。目录结构大致如下:
dataset/ ├── train/ │ ├── Diabrotica speciosa/ │ ├── healthy/ │ └── Caterpillar/ ├── test/ │ ├── Diabrotica speciosa/ │ ├── healthy/ │ └── Caterpillar/ ├── dataset.json └── show.py这种组织方式的直接好处是PyTorch的ImageFolder可以零改动读取。要快速统计每个类别的图片数量,用一行shell命令就能完成:
# 统计训练集里每个类别的图片数量 find train -type f -name "*.jpg" | awk -F'/' '{print $2}' | sort | uniq -c-F'/'指定以斜杠作为分隔符,$2取的是路径第二段,也就是类别子目录名,最后uniq -c逐类计数。跑完你就能看到三个类的分布。注意一个容易忽略的细节:类名中间带空格,例如Diabrotica speciosa,在bash脚本或批处理里如果直接用路径拼接,必须给路径加引号,否则空格会被当成参数分隔符,这也是用find而不用手写路径的原因。
2.2 JSON标签文件:先打印keys再决定怎么解析
摘要里明确写了"具体查看json文件",说明类别的规范定义在JSON里,而不是靠目录名猜。目录名是给人看的,JSON里的映射是给代码用的。拿到JSON第一件事不是直接解析,而是打印它的结构:
import json with open("dataset.json", "r", encoding="utf-8") as f: data = json.load(f) print(data.keys()) # 常见输出: dict_keys(['classes', 'train', 'test']) 或 dict_keys(['labels'])encoding="utf-8"这一步在Windows上特别重要,很多标注工具导出的JSON默认带中文注释或用UTF-8编码,不指定编码容易直接抛UnicodeDecodeError。打印出顶层keys之后,再进一步看类别字段具体是什么结构。常见做法是classes或labels是一个列表,列表里每个元素是类别名字符串,顺序就是模型训练时数字标签的顺序。我一般会顺手把类别到索引的映射打印出来:
class_names = data.get("classes", data.get("labels")) for idx, name in enumerate(class_names): print(idx, name)这里关键要看两个地方:第一,类别的顺序和目录里子文件夹的字母序是否一致;第二,数字标签是从0开始还是从1开始。这两个问题如果没对齐,后面训练出来的模型会出现"验证集准确率很高、实际推理全错"的典型翻车现场。
2.3 show脚本:九宫格抽检比看准确率更直观
数据集配套的show脚本是用来可视化图像的,它解决的问题是:标注信息对不对,一眼就能看出来。通常这类脚本会随机抽一批图像,把原图和对应的标签名同时显示在画布上。运行方式最常见的是直接执行:
python show.py --data_dir train --num_samples 9--data_dir指向你想抽查的目录,--num_samples控制抽样数量,9张会排成3×3网格,方便快速浏览。如果脚本里没做参数解析,那就直接python show.py跑默认配置。建议在三个类别里各抽9张看一遍,重点确认叶甲和毛毛虫的标注有没有混——因为虫害早期叶片症状相似,人工标注时很容易把同一片叶子标成不同类。这一步花五分钟,后面省下来的是反复调模型的几天。
3. 从文件路径到DataLoader:一次跑通训练数据流
3.1 选型:ImageFolder还是自定义Dataset
对这个数据集来说,优先用torchvision.datasets.ImageFolder,因为它要求的就是"根目录下按类别分子目录"的格式,train和test两个目录都是天然符合的。ImageFolder会自动扫描子目录名并生成class_to_idx映射,这比手写自定义Dataset快得多,代码量少一半以上。但前提是你确认过2.2里JSON的类别顺序和目录字母序一致,如果不一致,就得在读取后手动重置标签映射。
只有一种场景建议换成自定义Dataset:你要在这个数据集基础上继续做病害分割、或者要同时读取掩膜图。纯分类任务用ImageFolder是务实的选择。我一般先用ImageFolder把完整流程跑通,确认模型能正常收敛,再按需改造成自定义的数据流水线。
3.2 完整的数据加载与预处理代码
图像分类数据集的加载链路是固定的:读图 → 变换 → 打包成batch → 喂给模型。下面这段代码可以直接替换到你的训练脚本里,预处理参数针对这个数据集的实际尺寸做了适配,同时也兼顾了迁移学习的通用要求:
import torch from torchvision import datasets, transforms from torch.utils.data import DataLoader import json # 1. 先读JSON,打印类别映射确认顺序 with open("dataset.json", "r", encoding="utf-8") as f: meta = json.load(f) class_names = meta.get("classes", meta.get("labels")) print("JSON类别顺序:", {i: name for i, name in enumerate(class_names)}) # 2. 训练集预处理:随机翻转+颜色抖动,抑制过拟合 transform_train = transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness=0.2, contrast=0.2), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) # 3. 测试集预处理:只resize和归一化,不做数据增强 transform_test = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) # 4. ImageFolder根据目录自动映射标签 train_set = datasets.ImageFolder("train", transform=transform_train) test_set = datasets.ImageFolder("test", transform=transform_test) tarin_loader = DataLoader(train_set, batch_size=32, shuffle=True, num_workers=4) test_loader = DataLoader(test_set, batch_size=32, shuffle=False, num_workers=4)Resize((224, 224))是这一系列配置里的关键参数:ResNet、ViT等主流分类网络默认输入尺寸都是224,且ImageFolder读进来的图片尺寸各不相同,不统一size会在DataLoader打包batch时报维度不一致错误。Normalize的mean和std用的是ImageNet数据集的统计值,这套值在迁移学习场景下是标准配置,不要随意改成0.5或你自己算的均值。shuffle=True只在训练集开,测试集保持False,这样才能保证评估时batch顺序稳定,保存的预测结果能和文件名一一对应。
3.3 类别不均衡:加权采样比换模型更直接
这类实地采集的植物病害数据集,天然存在类别不均衡问题——健康叶片好采集,虫害叶片要等虫情爆发期才拍得到。先看分布再决定要不要处理,处理手段优先选WeightedRandomSampler:
from torch.utils.data import WeightedRandomSampler # 统计每个类别的样本数,权重取倒数 counts = [len(train_set.targets[train_set.targets == i]) for i in range(3)] weights = [1.0 / c for c in counts] sample_weights = [weights[t] for t in train_set.targets] sampler = WeightedRandomSampler(sample_weights, num_samples=len(train_set), replacement=True) train_loader = DataLoader(train_set, batch_size=32, sampler=sampler, num_workers=4)sample_weights是一个和训练集长度相同的列表,每张图被抽到的概率和它的类别样本数成反比:少数类(通常是有虫害的类)每张图被抽中的权重更高,replacement=True允许多次重复采样同一张图。这样每个epoch里少数类参与的梯度更新次数明显变多。注意改了采样器之后,shuffle参数必须保持False,二者互斥。评估阶段不要用准确率当唯一指标,重点看少数类的recall和F1-score,否则模型只要全部预测健康叶片,准确率就能轻松超过80%。
4. 常见问题与避坑排查:五条实战记录与修复
4.1 高发问题清单与修复
下面五条是这个数据集和同类标注数据集的典型踩坑记录,按"现象 → 原因 → 解决"整理,遇到了可以直接照着处理。
问题一:JSON类别顺序和ImageFolder的标签映射对不上,模型训练异常。
现象:训练loss正常下降,验证集准确率波动,但保存模型后推理结果完全错乱,比如把健康叶片全部识别成毛毛虫。
原因:ImageFolder按子目录名字母序生成class_to_idx,而JSON里的classes数组顺序不一定是字母序,两边数字标签错位。
解决:打印对比两边映射,按JSON顺序重建索引。
# 用JSON里的类别顺序覆盖ImageFolder的自动排序 train_set.class_to_idx = {name: i for i, name in enumerate(class_names)} train_set.targets = [train_set.class_to_idx[name] for name in train_set.samples]class_to_idx是ImageFolder读取目录时自动生成的字典,手动覆盖后targets也要同步更新,因为samples里存的还是旧的类别名。这一步做完再打印几个样本确认标签值,能过滤掉大部分"推理时错位"的诡异问题。
问题二:Windows下跑show脚本报FileNotFoundError。
现象:在Windows命令行运行python show.py --data_dir train,报错提示找不到train/healthy目录,但用资源管理器明明看得到。
原因:类名Diabrotica speciosa中间有空格,命令行把路径截断了。
解决:路径加双引号,或者临时把类别目录里的空格替换成下划线后建立软链接。
python show.py --data_dir "train/Diabrotica speciosa" --num_samples 9Linux和macOS上空格问题不那么致命,但Windows的cmd和PowerShell会把空格当成参数分隔符。封装成Python脚本时建议内部用os.path.join拼接路径,避免手写带空格的完整走。
问题三:DataLoader在第一个batch就报维度不匹配。
现象:RuntimeError: stack expects each tensor to be equal size,报错位置在DataLoader内部。
原因:数据集里的图像分辨率不一致,有的可能是手机拍的4032×3024,有的是设备截图几百像素,Resize没有生效或没套在对应分支上。
解决:确认预处理在加载路径上,且统一Resize目标尺寸。
# 快速检查所有图像的原始尺寸分布 from PIL import Image import glob sizes = set() for path in glob.glob("train/**/*.jpg", recursive=True): with Image.open(path) as im: sizes.add(im.size) print("图像尺寸种类:", sizes)输出结果应该是单一尺寸,比如{(224, 224)},如果出现多个不同尺寸,说明有路径写错导致跳过Resize,直接检查transform是否应用到了所有split上。
问题四:训练准确率很高但实际效果差,怀疑标注有噪声。
现象:验证集准确率98%,但模型挑出来的虫害叶片里混了大量健康叶片边缘。
原因:虫害早期和健康叶片的纹理差异很小,标注阶段容易把轻微失绿的叶片标错。
解决:用show脚本随机抽100张做人工复核:python show.py --num_samples 100,按类别统计疑似错标比例。超过5%就建议用清洗策略过滤——把置信度低的样本挑出来让人复核,而不是直接从训练集删掉,因为少数类的样本本来就少。
问题五:迁移学习模型改完分类头,特征提取层参数被误冻结。
现象:用ResNet18做迁移学习,改了fc层输出为3,但训练几轮后loss纹丝不动。
原因:有的教程会教你for param in model.parameters(): param.requires_grad = False,但如果你忘了把最后一层设回True,整个网络包括新加的分类头都不更新。
解决:检查冻结逻辑,只冻结特征提取层,保持分类头可训练。
model = torchvision.models.resnet18(weights="IMAGENET1K_V1") for param in model.parameters(): param.requires_grad = False model.fc = torch.nn.Linear(512, 3) # 新层的requires_grad默认为True这里有一个细节:model.fc被新构造的nn.Linear替换后,新层参数的requires_grad默认就是True,不需要额外设置,但如果你在替换之前执行了循环冻结,替换后新层也不受影响。踩坑的人往往是用了model.load_state_dict之后分不清哪些层被覆盖了。
4.2 排查方法:三步定位标签错乱
遇到标签相关的问题,别急着调模型,先用下面三步定位。
第一步,打断点看映射。把class_to_idx和JSON的classes打印出来并排对比,确认数字标签含义。第二步,随机抽一批图像显示原图和预测结果,用第2章的show脚本做可视化。第三步,保存一个batch的真实标签和预测标签到CSV,逐行比对差异集中在哪些类别上。
import random from PIL import Image import matplotlib.pyplot as plt from torchvision import transforms # 随机抽查一张训练图,确认标签和图像内容是否一致 idx = random.randint(0, len(train_set) - 1) img, label = train_set[idx] inv_normalize = transforms.Normalize( mean=[-0.485 / 0.229, -0.456 / 0.224, -0.406 / 0.225], std=[1 / 0.229, 1 / 0.224, 1 / 0.225] ) plt.imshow(inv_normalize(img).permute(1, 2, 0).numpy()) plt.title(f"label={label}: {class_names[label]}") plt.show()inv_normalize的作用是把归一化后的像素值还原回原始RGB范围,否则显示出来的图像是偏色的。permute(1, 2, 0)把CHW的Tensor顺序转换成HWC——matplotlib的imshow只认这个排布。这一步跑通了,说明数据和标签的链路是通的,后面再出问题就可以放心往模型结构上排查。
5. 训练前最后一道检查:show脚本加两个统计习惯
最后一个技巧分享一个我每次处理标注数据集都会走的固定流程:计算类别分布、检查图像尺寸、跑一个完整epoch的冒烟测试,三步做完再正式开始训练。
类别分布和图像尺寸前面已经给了脚本,冒烟测试的配置有个参考写法:
import torchvision model = torchvision.models.resnet18(weights="IMAGENET1K_V1") model.fc = torch.nn.Linear(512, 3) # 替换分类头,适配3类weights="IMAGENET1K_V1"在最新版torchvision里是推荐写法,替代老版的pretrained=True,这样做的好处是权重来源明确可追溯。model.fc = torch.nn.Linear(512, 3)这一行的原理是:ResNet18的骨干输出512维特征,原来的全连接层是1000类(ImageNet),替换成3类输出。只需要改这一层,前面的特征提取能力全部保留,这是迁移学习在这个数据规模下最合理的做法——6000张图从头训练一个深度网络大概率过拟合,但微调预训练模型效果会很稳。
冒烟测试只跑一个epoch,观察两件事:loss有没有下降趋势、训练速度是否在可接受范围。如果loss一点没动,先查学习率和数据加载;如果速度太慢,考虑减小图像尺寸或降低num_workers。
从那以后,我每次拿到新数据集都强制走一遍这个流程:先看分布,再看尺寸,最后跑冒烟。数据集的坑大多集中在数据本身,模型反而很少出问题。这套习惯帮我挡掉了至少十次"训练一晚上、结果标签全错位"的翻车事故,希望帮到你。
本文还有配套的精品资源,点击获取