尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

大豆叶片病害图像分类数据集:3600张样本的实战指南

大豆叶片病害图像分类数据集:3600张样本的实战指南 简介图像分类是计算机视觉的基础任务其效果高度依赖数据质量与数量。在实际工程中带标注的数据集往往稀缺尤其农业病害场景受季节与地域限制高质量样本收集难度大。本文围绕一个约3600张已标注的大豆叶片病害图像数据集探讨小样本条件下如何开展图像分类实践。通过迁移学习利用预训练模型结合针对性的数据增强策略与合理的模型选型能够在有限数据下训练出可用的分类模型。该数据集适用于农业视觉研究、植保识别原型验证及图像分类算法入门。文章从数据集校验、模型对比到训练调参提供了可落地的完整方案为处理同类小规模图像分类问题提供参考。 数据集的坑我在各类图像分类项目里踩过不少这次专门说一份最近在整理实验时反复用到的大豆叶片病害图像分类数据集已标注约3,600张。干这行的人都清楚图像分类项目最耗时间的往往不是调模型而是搞数据采集、清洗、标注、校验一环扣一环。这份数据集的好处是省掉了最脏最累的标注环节拿到手可以直接进训练管线非常适合做农业视觉方向的研究、植保识别App的原型验证或者拿来做图像分类算法的入门练手。先说清楚它是什么这是一批大豆叶片图像的集合每一张图都对应一个明确的病害类别标签约3,600张。数字听起来不算大对深度学习来说属于典型的小规模数据集但农业病害场景的实际项目里这反而是常态——地里能采集到的病害样本有限病害爆发有季节性很多病害只在特定生长阶段出现想凑几万张高质量病害图非常难。所以理解这份数据集的关键不是3600张太少而是怎么在有限样本下把分类模型训练到能用。这篇文章我会从数据集定位、标注质量校验、模型选型、训练参数、常见坑点五个方面展开最后给一套可以直接照着跑的方案。适合的人群包括正在做植物病害识别课题的学生、想快速搭建农业识别Demo的算法工程师、以及单纯想找一份带标注数据练手图像分类的新人。1. 数据集定位与基础认知1.1 3,600张数据意味着什么先算一笔账。假设这份数据集包含8个类别包括7种常见大豆叶片病害加健康叶片平均每个类别大约450张。这个规模在图像分类里是一个非常典型的小样本中等类别场景。训练集大概2,100到2,500张验证集700到800张测试集300到400张如果按7:2:1划分每个类别训练样本约260到310张如果不用任何预训练模型从头训练一个ResNet50大概率会严重过拟合用预训练模型做迁移学习微调这个规模是够用的前提是数据质量没问题。很多人拿到数据先急着训练我建议先花十分钟想清楚任务性质。大豆叶片病害分类是一个细粒度图像分类问题它和ImageNet那种猫狗鸟分类最大的区别在于不同病害之间的视觉差异可能非常小比如褐斑病和灰斑病早期症状都是叶片上出现小斑点颜色、形状都相近而同一病害在不同发病阶段、不同光照条件下外观差异又可能很大。这意味着模型需要真正学到病斑的纹理、分布、边缘特征而不是靠颜色深浅或者背景纹理偷懒。1.2 这组数据适合做什么明确了数据规模和应用场景再谈技术选型才不会跑偏。这份数据集可以支撑三类典型任务第一图像分类模型的Baseline对比。拿ResNet、EfficientNet、MobileNet、ViT等模型在同样划分下跑一遍对比准确率、参数量、推理速度这是最省事的用法。第二农业病害识别原型验证。做植保类App或者硬件识别设备先用这份数据验证算法的可行性和精度上限证明用摄像头拍叶片能在手机上完成病害识别这个流程跑得通然后再去采集更大规模的真实数据。第三数据增强和半监督学习的研究。因为数据量偏小天然适合测试不同数据增强策略、Mixup、CutMix、伪标签、半监督训练等方法对小样本分类的增益效果。2. 动手前先做数据体检2.1 看清标注形态再写代码拿到数据集后第一步不是训练而是确认标注的组织方式。图像分类数据集的标注质量往往比想象中更容易出问题常见的组织方式有两种一种是文件夹即标签典型的ImageFolder结构dataset/ ├── train/ │ ├── healthy/ │ ├── brown_spot/ │ ├── frog_eye_spot/ │ └── ... ├── val/ └── test/另一种是文件名加标注文件比如CSV或JSON记录每张图片的路径和类别ID。不同的组织方式对应不同的数据加载代码写法。ImageFolder可以直接用PyTorch的torchvision.datasets.ImageFolder加载而CSV标注需要自己写Dataset类。先写一段简单脚本统计各类别样本数量import os from collections import Counter data_root dataset/train counts Counter() for class_name in os.listdir(data_root): class_path os.path.join(data_root, class_name) if os.path.isdir(class_path): counts[class_name] len(os.listdir(class_path)) for cls, cnt in counts.most_common(): print(f{cls}: {cnt} 张) print(总样本数:, sum(counts.values()))这段代码跑完你就知道各类别分布是否均衡哪些类样本少哪个类可能缺失。类别不均衡在病害数据集里非常常见因为不同病害的流行程度差异大。2.2 标注质量抽检别盲信已标注已标注不等于标注正确。我见过不少标注集里叶片病害图混入了杂草图、背景图、甚至重复图也见过同一症状在不同类别之间标串的情况。盲信标注等于把模型的性能上限交给未知风险。推荐一个简单的抽检流程每个类别随机抽20到30张图拼成一张网格图人工过一遍。import matplotlib.pyplot as plt from torchvision.datasets import ImageFolder from torchvision.utils import make_grid import torch dataset ImageFolder(dataset/train) # 每个类别取前24张做拼接预览 for cls_idx, cls_name in enumerate(dataset.classes): indices [i for i, (_, label) in enumerate(dataset.samples) if label cls_idx][:24] imgs [dataset[i][0] for i in indices] grid make_grid(imgs, nrow6, normalizeTrue) plt.figure(figsize(12, 8)) plt.imshow(grid.permute(1, 2, 0).numpy()) plt.title(cls_name) plt.axis(off) plt.show()实际经验是如果一眼扫过去发现某几张图明显不属于该类优先确认是采集噪声还是标注错误。标注错误需要修正采集噪声可以选择剔除或保留做鲁棒性训练两种处理方式取决于你最终的使用场景。2.3 图片质量筛选除了标注检查还要做图片质量过滤。大豆叶片图像的采集环境复杂常见问题包括过曝或欠曝叶片纹理完全看不清严重失焦模糊无法分辨病斑细节叶片占比过小背景占据大部分画面同一叶片的多张重复或近似重复图会导致训练集和验证集信息泄漏。重复图这个问题值得展开说。如果同一病叶的不同角度照片被同时分到训练集和验证集验证集评估出来的指标会虚高因为模型见过几乎相同的图像。为了尽量避免这种情况可以在划分数据集前做一次感知哈希去重把哈希值接近的图片归为一组确保它们划分到同一个集合。import imagehash from PIL import Image import os def compute_phash(path): with Image.open(path) as img: img img.convert(RGB).resize((256, 256)) return imagehash.phash(img) # 对每个类别计算phash若两张图哈希距离小于阈值视为近重复这个步骤在3600张的规模下很快却能避免一个很隐蔽的评估陷阱。3. 模型选型与训练策略3.1 为什么一定要用迁移学习放到真实场景里3600张图像从头训练一个深层卷积网络几乎注定失败。原因在于深度学习模型的泛化能力依赖大量数据而病害叶片识别表面上只有几个类别但病斑纹理、位置、形态的组合空间非常大3,600张覆盖不完。迁移学习的思路是先用ImageNet这种亿级数据集的预训练权重作为初始参数模型在前几层已经学会了边缘、纹理、颜色分布这些通用视觉特征我们只需要在最后几层做领域切换适应大豆叶片病害的专属特征。这相当于一个刚毕业的学生已经具备通用的基础知识到岗后只需要接受半个月业务培训就能干活而不是从零开始学说话写字。实际效果差别有多大以我的经验在3600张的规模下用预训练ResNet50微调测试准确率能到85%到92%从头训练同样的网络能上70%就算不错了。差距是数量级的。3.2 主流模型对比与选择考虑到数据规模偏小模型结构不宜过于复杂参数不宜过多否则即使有预训练权重也容易过拟合。下表是我在类似农业数据集上反复对比后的参考结论模型参数量ImageNet Top-1训练速度小数据适配性适用场景ResNet5025.6M76.2%快强最稳妥的BaselineEfficientNet-B312.3M81.7%中中精度优先且算力充足MobileNetV3-Large5.5M75.2%很快中边缘设备部署ViT-Tiny5.7M72.2%慢弱数据充足时才能发挥ConvNeXt-Tiny28.0M82.1%中较强精度和效率均衡这里说明一下ViT在小数据集上表现不佳直接使用预训练权重也不太理想——视觉Transformer依赖大量数据学习空间关系3600张连热身都不够。如果一定要用Transformer结构建议选Swin-T或者ConvNeXt这类带层级归纳偏置的变体或者用蒸馏出来的小ViT。我的建议是图省心选ResNet50图精度选EfficientNet-B3图部署选MobileNetV3。农业生产场景通常跑在手机或无人机盒子上MobileNetV3这类轻量模型在精度可接受的前提下有更低的延迟。3.3 数据增强策略农业场景有自己的配方数据增强是小样本图像分类的救命稻草但很多人直接用ImageNet上常见的增强策略没有针对农业图像做调整。这会导致增强后的图像和真实场景脱节模型在测试集上表现不错一到实地识别就翻车。大豆叶片病害图像有其特殊性病斑的纹理细节是诊断关键颜色也有参考价值比如锈病的疱斑颜色偏红褐但叶片本身的整体颜色可能受光照、品种影响。我的增强策略分为三个梯队第一梯队基本几何变换可以安全使用随机水平翻转、随机旋转、随机裁剪缩放。这类增强不破坏病斑的纹理和颜色信息泛化能力提升最明显。第二梯队颜色扰动需要慎重随机的亮度、对比度、饱和度调整对小样本训练帮助很大但幅度要控制。如果测试环境的光照变化不大颜色抖动幅度过大会让模型学到不稳定的颜色特征。建议亮度、对比度扰动范围控制在正负15%以内。第三梯队结构性强增强Mixup、CutMix、随机擦除。这类方法能强制模型学习更鲁棒的特征在病害分类里效果不错。随机擦除尤其适合模拟叶片被遮挡、病斑被叶片脉络遮挡的真实情况。推荐一套组合配置from torchvision import transforms train_transform transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.6, 1.0)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees15), transforms.ColorJitter(brightness0.15, contrast0.15, saturation0.15, hue0.05), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_transform transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])注意验证集或测试集绝对不要用随机增强只用固定尺寸的Resize加CenterCrop否则指标没有可比性。4. 训练全流程实操4.1 数据划分与超参数设置按照之前说的假设数据集有8个类别共3600张。我通常按7:2:1划分为训练集2520张、验证集720张、测试集360张。为了保证各类别比例一致使用分层采样的方式划分不要简单随机切否则小类别可能在验证集或测试集中消失。from sklearn.model_selection import train_test_split import shutil, os # 假设labels是[(path, class_id), ...] train_paths, temp_paths train_test_split( samples, test_size0.3, stratify[s[1] for s in samples], random_state42 ) val_paths, test_paths train_test_split( temp_paths, test_size1/3, stratify[s[1] for s in temp_paths], random_state42 )训练超参数按经验配置如下超参数推荐值说明输入尺寸224x224ResNet/EfficientNet标准输入Batch Size32 或 64视显存而定越小震荡越大优化器AdamW比SGD收敛更快泛化不输初始学习率1e-4迁移学习微调不宜过大权重衰减1e-4抑制过拟合训练轮数30-50配合早停在验证集最优时保存学习率调度Cosine Annealing后期更平稳逼近最优一个关键点微调时初始学习率不能照搬从头训练的经验0.1或0.01因为预训练权重已经很接近局部最优学习率太大会把学好的通用特征冲坏。1e-4是安全起点如果损失下降太慢再考虑提到3e-4。4.2 完整训练代码骨架这里给一套基于PyTorch的完整可运行训练代码骨架重点部分加了注释。这里以ResNet50为例import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import datasets, models, transforms from tqdm import tqdm # 数据加载 train_dataset datasets.ImageFolder(dataset/train, transformtrain_transform) val_dataset datasets.ImageFolder(dataset/val, transformval_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers4, pin_memoryTrue) # 加载预训练模型替换分类头 model models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V1) num_features model.fc.in_features model.fc nn.Linear(num_features, len(train_dataset.classes)) # 冻结骨干网络的前几层只微调后三层和分类头 for name, param in model.named_parameters(): if not name.startswith(layer4) and fc not in name: param.requires_grad False model.cuda() criterion nn.CrossEntropyLoss() optimizer optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr1e-4) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max30) # 训练循环只写关键部分 best_acc 0.0 for epoch in range(30): model.train() running_loss 0.0 for inputs, labels in tqdm(train_loader): inputs, labels inputs.cuda(), labels.cuda() optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() # 验证 model.eval() correct, total 0, 0 with torch.no_grad(): for inputs, labels in val_loader: inputs, labels inputs.cuda(), labels.cuda() outputs model(inputs) _, preds torch.max(outputs, 1) correct (preds labels).sum().item() total labels.size(0) val_acc correct / total print(fEpoch {epoch1}: loss{running_loss/len(train_loader):.4f}, val_acc{val_acc:.4f}) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_model.pth) scheduler.step()这段代码有两点值得说明。一是冻结策略初始化时只让最后几层可训练前几层保持预训练参数不动这样做的好处是减少过拟合并加快训练速度训练几个epoch后可以解冻全部层做精细化微调。二是模型保存的时机要以验证集精度为准而不是等训练全部结束才保存。4.3 评估指标准确率不够要看混淆矩阵训练完之后很多人只看测试集准确率就完了。在病害分类这种类别易混淆的任务里准确率会掩盖很多问题。打个比方假设锈病占比30%褐斑病占比5%如果模型把所有样本都判成锈病准确率也有30%但褐斑病识别完全失败。这在农业场景是不可接受的——漏检一种病害可能导致防治方案错误损失远超整体准确率体现的水平。所以要额外输出三样东西分类报告、混淆矩阵、单类别的精确率和召回率。from sklearn.metrics import classification_report, confusion_matrix import numpy as np model.load_state_dict(torch.load(best_model.pth)) model.eval() all_preds, all_labels [], [] with torch.no_grad(): for inputs, labels in test_loader: inputs inputs.cuda() outputs model(inputs) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.numpy()) print(classification_report(all_labels, all_preds, target_namestrain_dataset.classes)) print(confusion_matrix(all_labels, all_preds))实际项目中重点关注两件事一是哪些类别之间互相混淆二是混淆模式是否与人类专家的判断一致。比如褐斑病和灰斑病混淆说明模型捕捉到了两者共同的斑点特征但没区分细粒度差异此时可以增加该类别的样本或者针对易混淆pair做专门的难例挖掘。5. 常见问题排查与避坑实录5.1 训练准确率高、验证准确率低过拟合这是小数据集上最常见的问题。表现是训练集准确率一路飙升到98%以上验证准确率卡在70%左右上不去。对策优先级从高到低排列加入/加强数据增强特别是随机擦除和Mixup降低模型复杂度把ResNet50换成MobileNetV3或EfficientNet-B0增大权重衰减系数从1e-4调到5e-4在分类头之前加Dropout层概率设0.3到0.5减少微调轮数冻结更多骨干层。我实测下来数据增强和模型轻量化的效果最明显调整Dropout收益相对有限。5.2 验证集准确率震荡剧烈忽高忽低这种问题通常和两个因素有关Batch Size太小导致梯度估计噪声大或者学习率设置偏高。在3600张的小数据集上Batch Size设为16时loss曲线经常大幅抖动设为32就平稳很多。另外优化器从AdamW换到带动量的SGD加上Cosine退火调度也能缓解后期震荡。如果已经训练到后期可以把学习率直接降到1e-5做最后几个epoch的收敛。5.3 某几个类别识别效果特别差用混淆矩阵通常会暴露一两个困难户。处理思路是数据层面和算法层面并行数据层面可视化出错样本确认是不是标注错误或样本本身模糊。如果是数据问题造成的清洗数据后重训而不是硬调模型。算法层面使用类别加权损失函数给样本少的类别更高的权重class_counts torch.tensor([450, 400, 380, ...], dtypetorch.float).cuda() class_weights 1.0 / class_counts class_weights class_weights / class_weights.sum() criterion nn.CrossEntropyLoss(weightclass_weights)更进一步可以尝试Focal Loss它通过降低易分类样本的权重让模型更关注难分类样本。缺点是调参成本高gamma值要尝试。5.4 训练指标很好一到真实场景识别就翻车这是农业图像项目最经典也最难解决的坑。原因通常是训练数据和真实采集数据的分布差异包括拍摄设备、角度、光照、叶片品种、生育期的不同。在无法立刻扩充真实数据的前提下有几个立即能用的补救手段测试时增强推理的时候把同一张图做水平翻转、轻微旋转等多次预测平均概率作为最终结果多尺度推理用224和256两种尺寸分别预测再取均值收集少量真实场景图做验证哪怕只有50张也能评估模型的真实表现而不是自我感觉良好。我自己遇到过一个案例模型在测试集上准确率94%拿到田间拍的照片测准确率掉到78%。于是用真实田间照片做数据增强给训练集补充了模糊模拟、光照变化模拟之后田间效果才回到88%。5.5 问题速查表问题现象可能原因解决方案训练Loss不降学习率过小或数据标签错乱打印batch标签检查尝试lr增大10倍训练准确率100%验证准确率低严重过拟合增强数据、简化模型、增大weight decay验证Loss先降后升过拟合用早停回调在验证Loss最低点保存模型某类全预测错标注反了或样本太少可视化该类样本检查标注质量训练速度极慢num_workers0或CPU训练设置num_workers4以上使用GPU训练显存不足Batch Size过大降到16或8梯度累积补偿batch size6. 数据集的扩展玩法与实际经验6.1 从图像分类扩展到目标检测和分割这份数据集以图像分类为标注形式但如果你要部署到真实的田间监测场景经常会遇到一片叶子上同时有多个病斑、不同病害混合感染的情况。此时分类模型只能告诉你这株大豆有褐斑病但没法告诉你病斑在叶片的哪个位置、面积有多大。后者对精准施药很重要。方案有二。一是直接把分类任务改成多标签分类让模型同时输出叶片的多个病害标签。二是把分类数据集重标注为目标检测或分割格式——这需要重新标注工作量不小但3600张图的规模可控。如果做检测推荐用YOLOv8或MMDetection标注格式从文件夹分类转为YOLO的txt格式即可。转换思路很简单每张图按类别分组用LabelImg或CVAT标出病斑的bounding box导出YOLO格式。但要注意分类数据集的图像往往是一张图对应一个类别叶片可能占满整张图检测框的尺寸会很大且背景占比小检测任务其实和分类差不太多。如果原始图像是叶片平铺拍摄直接做检测可能增益有限如果是田间植株全景图检测就非常有价值。6.2 模型轻量化与部署训练完模型只是第一步真正要落地到手机或无人机边缘设备还需要考虑模型大小和推理速度。MobileNetV3、EfficientNet-Lite这些轻量模型是首选再配合量化压缩比如PyTorch的INT8量化或ONNX Runtime模型体积可以压缩3到4倍推理速度翻倍。一个值得提醒的点轻量模型在小数据集上训练难度更大参数量少导致拟合能力有限。建议先在ResNet50上调好数据增强和训练策略确认这个数据集的可学习上限再迁移到轻量模型上训练避免一上来就用小模型最后怀疑是自己的数据有问题。6.3 数据扩充的路线3600张有个天花板后续要进一步提升模型精度最有效的一定是扩充数据。三个方向一是按生长周期采集同一病叶的连续图像让模型学到病害发展过程的时序特征二是跨地域采集不同种植区域的品种、气候差异会让叶片表现差异很大三是做class-incremental学习把这份数据集作为历史数据后续新数据到来时做增量更新而不是全部重训。农业图像方向有个很现实的情况每年病害种类和流行程度都在变化数据集需要持续维护。我个人的维护策略是每次新数据入库先做类别分布统计再和旧数据集合并后做增量训练而不是只在旧数据上重训否则模型对新的病害表现会产生灾难性遗忘。最后说点我自己的体会。做了这么多年图像分类项目最深的感受是在农业病害这类小样本场景中数据集质量对模型性能的影响远大于模型结构的选择。拿到一份像这样已标注、约3600张的数据集先不要急着跑模型把数据检查、类别分析、划分策略这三步做扎实比换十个模型都更有价值。数据是土壤模型是种子土壤不肥种子再好也长不出好庄稼。这份数据集的价值取决于你怎么对待它。本文还有配套的精品资源点击获取
返回列表