甘蔗病害图像分类数据集:19,000张图片的迁移学习实战拆解
发布时间:2026/9/26 2:12:07 作者:尧图编辑部 阅读量:1,286

简介面向计算机视觉学习者的甘蔗植物病害图像分类数据集整体规模约一万九千张已标注图片覆盖红腐病、锈病、健康、枯萎病等六个常见类别具体标签映射以配套的JSON标注文件为准。压缩包内共两千个文件其中绝大多数为JPG格式图像已按训练集与测试集分目录存放另附一个Python可视化脚本和一个JSON标注文件整体压缩包大小约四百二十五兆字节。从文件名可以看出部分图像经过了水平翻转、垂直翻转等数据增强预处理能够有效扩充样本多样性对提升模型泛化能力有直接帮助运行可视化脚本可以快速查看任意类别的图片样本便于直观对比不同病害的表观特征也方便检查数据划分是否合理。目前该资源已被一百九十六人浏览学习适合正在从事植物病害识别、图像分类课程设计或毕业设计的同学也适用于需要标准数据集来调优CNN、ResNet等模型性能的开发者。通过这套数据用户可以专注于模型结构设计与训练调参减少在数据采集、清洗和标注上的重复投入。1. 甘蔗植物病害图像分类数据集约 19,000 张已标注图片图像分类最稳的练手基底做农业视觉的人都知道甘蔗病害识别的难点从来不在模型而在数据。市面上公开的植物病害集大多是水稻、小麦、番茄甘蔗的要么数量少得可怜要么标注混乱得没法用。这份约 19,000 张的已标注甘蔗病害图像分类数据集补的正是这个缺口——类别覆盖甘蔗生产上常见的赤腐病、锈病、花叶病、叶斑病等图片是实际田间拍摄带自然光照、背景遮挡、叶片重叠这些真实噪声拿来训练图像分类模型落地感比标准学术数据集强不少。适合三类人做农业视觉方向毕设的学生想快速验证分类思路的算法工程师以及要给甘蔗病害检测系统做预分类前置任务的人。它不是学术 benchmark是能直接进训练管线的工程数据。2. 数据集解包与标注格式解读先摸清底细再谈训练拿到数据集的第一件事不是急着训练而是把文件组织和标注格式彻底搞清楚。图像分类数据集的坑有八成在加载阶段就埋下了。2.1 目录结构按类别分文件夹还是统一 CSV 映射常见的图像分类数据集有两种组织方式。第一种是 ImageNet 风格的按类别分目录形如train/rust/、train/mosaic/每个子目录名就是类别标签。第二种是扁平存放的图片文件外加一份labels.csvcsv 里至少有两列一列是文件名一列是类别名。这份数据集大概率不脱离这两种形态解压后先跑一遍目录扫描确认属于哪一种再决定怎么写读数据代码。import os from collections import Counter data_root sugarcane_disease # 扫描第一层目录判断是按类别分目录还是扁平标签文件 for entry in os.listdir(data_root): full os.path.join(data_root, entry) if os.path.isdir(full): print(f目录: {entry}, 包含文件数: {len(os.listdir(full))}) elif entry.endswith(.csv): print(f发现标签文件: {entry}) # 统计标签分布如果用的是CSV方案 import pandas as pd if os.path.exists(os.path.join(data_root, labels.csv)): df pd.read_csv(os.path.join(data_root, labels.csv)) print(df.head()) print(Counter(df[label]))代码逻辑是先用os.listdir探明顶层结构再根据结果决定后续解析方式。如果是按类分目录类别名直接由子目录名给出如果是 CSV则需要额外读一次文件构建映射表。这里有一步容易被忽略无论哪种组织方式都要确认图片文件的扩展名是否统一.jpg、.jpeg、.png混用很常见加载时统一筛一遍避免后续解码报错。建议按“眼睛过一遍当年的实际拍摄时间与地块拆分验证集”的原则去划分同一块田里连续拍的叶片前 80% 进训练集、后 20% 进验证集模型会记得纹理背景这在田间数据集里几乎是必然的。这份数据集如果按目录分好了 train/val先信它没分好的话用train_test_split分层采样每个类别的比例都保持住。2.2 类别分布与标注质量这两件事决定训练策略甘蔗病害分类的类别分布天然不均衡。比如锈病和赤腐病在产区高发样本可能上千张某些冷门病害或生理性损伤图片可能只有一两百张。先用Counter统计全量分布看看最大类和最小类的比值。如果超过 10 倍后续必须做类别重加权或者增强补偿如果在 3 倍以内常规训练问题不大。另一个要检查的是标注质量。分类数据集的错误标注不像目标检测那样显眼错标一两张不会让 loss 爆炸但会在混淆矩阵里以“异常单类错分”的形式出现。挑几个样本数少的类别人工过一遍缩略图确认标签没有系统性错误——比如锈病早期症状和叶斑病很容易互相标错。# 按类别统计图像数量 from pathlib import Path image_exts {.jpg, .jpeg, .png, .bmp} def count_by_folder(root): folder_counts {} for sub in Path(root).iterdir(): if sub.is_dir(): n sum(1 for p in sub.rglob(*) if p.suffix.lower() in image_exts) folder_counts[sub.name] n return folder_counts counts count_by_folder(os.path.join(data_root, train)) for k, v in sorted(counts.items(), keylambda x: x[1], reverseTrue): print(f{k}: {v})这段输出能直接指导训练超参对于样本少的类别我在训练时会调高它的采样权重或者用更强的数据增强。对于样本多的类别则要留意模型会不会在它上面过拟合到背景纹理。标注质量的抽检尽量用原图看不要看缩放后的缩略图——病害的细微差别在缩小后很难分辨抽检时看原图局部放大比看整图缩略图可靠得多。2.3 训练集与验证集的划分别让数据泄漏毁掉可信度数据泄漏是图像分类里最隐蔽的问题。甘蔗病害数据集的泄漏主要来自同图重复和同源近邻图。田间拍摄时常常连拍同一个叶片出现在多张图里只是轻微变换角度模型在训练里见过验证时就“开卷考试”准确率虚高五六个点都算少的。from sklearn.model_selection import train_test_split # 假设你已经拿到了所有图片路径和对应标签 paths, labels get_all_samples() # 自行实现返回 (图片路径列表, 标签列表) train_paths, val_paths, train_labels, val_labels train_test_split( paths, labels, test_size0.2, stratifylabels, # 分层保证验证集里每个类别比例与全集一致 random_state42 # 固定随机种子复现结果 )stratify参数是必须的。不分层的话冷门病害类别可能在验证集里恰好一张都没有评估结果完全失真。划分完之后额外做一步查重——用很简单的方式对每张图取一个缩略图再算感知哈希两两比对把重复度极高的图归到同一个视频序列或同源批次里确保同一个来源的图不会同时出现在训练和验证两个集合。查重脚本会多花几分钟跑换来的模型可信度绝对值这时间。我一般还会把源目录结构保存成训练时传入的路径信息这样溯源排错的时候能定位到具体是哪块田的数据导致了某个诡异 loss 曲线。3. 构建训练管线从 ResNet50 起步的迁移学习基线数据准备好了接下来就是模型。我建议第一版基线从 ResNet50 起步而不是直接上 ViT。原因很简单19,000 张数据对纯监督训练的 ResNet50 来说刚好能吃饱而对从头训练的 ViT 来说远远不够必须配大规模预训练权重才能压住过拟合。农业图像分类的第一要务是稳定跑通先把基线分数拿到手再谈换模型结构。3.1 为什么是 ResNet50 而不是更深的网络ResNet50 有约 2500 万参数ImageNet 预训练权重随处可得在中等规模数据集上微调是性价比最高的选择。ResNet101 参数多了近一倍在 19,000 张数据上带来的提升通常不超过 1 个点训练时长却多出 60% 以上ViT 和 Swin 这类 Transformer 模型在数据量不足时会表现得比 CNN 更敏感需要更精细的调参技巧。这不是说 CNN 比 Transformer 好而是对“甘蔗病害分类”这个具体任务数据和算力约束下 ResNet50 是容错率最高的起点。后续如果要冲更高精度再替换成 EfficientNet 或 ConvNeXt它会作为 baseline 模型负责给出对照分数。3.2 数据加载与增强管线田间图像的预处理细节甘蔗叶片图像和 ImageNet 的标准图片差异不小叶片占画面比例不确定背景有泥土、天空、其他叶片光照强度从正午强光到早晚弱光都有。数据增强要做足但也不能盲目堆强度尤其是随机擦除和 CutMix 这类操作对病害纹理的破坏会让模型学不到关键特征。import torch from torch.utils.data import Dataset, DataLoader from torchvision import transforms from PIL import Image class SugarcaneDiseaseDataset(Dataset): def __init__(self, path_list, label_list, transformNone): self.path_list path_list self.label_list label_list self.transform transform def __len__(self): return len(self.path_list) def __getitem__(self, idx): img Image.open(self.path_list[idx]).convert(RGB) label self.label_list[idx] if self.transform: img self.transform(img) return img, label train_transform transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomResizedCrop(224, scale(0.6, 1.0)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomVerticalFlip(p0.3), transforms.RandomRotation(15), transforms.ColorJitter(brightness0.3, contrast0.3, saturation0.3), 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]) ])这里的细节在RandomResizedCrop的scale(0.6, 1.0)这个参数控制裁剪面积占原图的比例。病害叶片图像中特征区域可能只占叶片的一小部分裁剪比例太狠会把病害区域裁掉0.6 的下限是兼顾增强强度和特征保留的经验值。RandomVerticalFlip我开了但概率压到 0.3因为甘蔗叶片竖直生长的比例高垂直翻转过度会让图像的物理含义失真。验证集不要加随机的几何变换只用Resize加CenterCrop否则评估结果会带上增强噪声波动变大不利于对比不同实验。3.3 训练脚本优化器、学习率调度与微调策略训练阶段的配置直接影响收敛速度。我用的配置默认是 AdamW初始学习率 3e-4配合 CosineAnnealing 调度和一个免费的 warmup。迁移学习的经验是预训练模型的前几层是通用特征冻结它对防止小数据集过拟合很有帮助。但在这份甘蔗数据集上我的建议是只冻结第一个卷积阶段layer1其余全部解冻因为叶片纹理与 ImageNet 的自然图像差异已经不算小冻结太多层会导致模型学不到甘蔗特有的表面质感。from torchvision import models import torch.nn as nn device torch.device(cuda if torch.cuda.is_available() else cpu) model models.resnet50(pretrainedTrue) model.fc nn.Linear(model.fc.in_features, num_classes) # 冻结第一个残差阶段其余参与微调 for name, param in model.named_parameters(): if name.startswith(layer1): param.requires_grad False model.to(device) optimizer torch.optim.AdamW( model.parameters(), lr3e-4, weight_decay1e-4 ) scheduler torch.optim.lr_scheduler.CosineAnnealingLR( optimizer, T_max30, # 总训练轮数减掉 warmup 轮数 eta_min1e-6 )weight_decay1e-4是分类任务的稳妥默认值太大会压制模型表达能力太小则起不到正则作用。余弦退火的eta_min设到 1e-6 是为了训练后期把学习率降到很低在损失面最底部做精细搜索。这里有个微小但重要的点如果用了 BatchNorm冻结 layer1 的同时BN 层的统计量也应该一起冻结否则 layer1 里 BN 的均值方差还在更新效果就乱套了。PyTorch 中设置requires_gradFalse并不会自动停止 BN 统计量的更新需要额外把对应层的track_running_stats关掉或者干脆只冻结 layer1 里带卷积和 BN 名字的参数。训练循环本身没有悬念前向、算 loss、反传、更新学习率。值得记录的是每个 epoch 结束后的三个数训练 loss、验证 loss、验证准确率。如果训练 loss 持续下降但验证 loss 在第 8 个 epoch 附近开始回升这就是过拟合的明确信号且回看此时的学习率位置通常还没走完余弦曲线说明当前模型容量对这份数据已经偏大下一版实验应该降模型规模或增强正则。3.4 评估指标不要只盯单个准确率分类任务的评估多设几个维度后面换模型对比时才不会被单一数字带偏。from sklearn.metrics import confusion_matrix, classification_report all_preds [] all_labels [] model.eval() with torch.no_grad(): for images, labels in val_loader: images images.to(device) outputs model(images) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().tolist()) all_labels.extend(labels.tolist()) print(classification_report(all_labels, all_preds, target_namesclass_names)) cm confusion_matrix(all_labels, all_preds)classification_report给到每个类别的 precision、recall、F1比整体准确率信息量大得多。类别多而样本不均衡时整体准确率会被大类别主导看起来 92% 好像不错但冷门病害类别的 recall 可能只有 55%这在农业场景意味着漏检大量已发病植株。混淆矩阵保存下来后续做错误分析要反复用它。4. 换主干与增强策略从“能跑”到“跑得准”基线模型稳了之后进阶方向有两条主线换更强的主干网络以及针对病害图像特性做数据增强重构。两条线可以并行试但每次只动一个变量否则实验结果没法归因。4.1 主干网络升级EfficientNet、ConvNeXt 与 ViT 的实测取舍在 19,000 张数据规模下主干网络的选择有几个容易踩的误区。EfficientNet-B4 在 ImageNet 上有不错的口碑它的输入分辨率是 380比 ResNet 的 224 大不少这意味着训练时间和显存都会涨一截而且在迁移到农业图像时输入分辨率带来的收益是否跑得过计算开销需要实际对比。ConvNeXt 的架构继承了 ResNet 的骨架和 Transformer 的训练技巧在中等数据规模下微调效果通常优于 ResNet但权重文件也更大。ViT 最好只在有大规模预训练权重的情况下使用DINOv2 或 CLIP 系列的 ViT 权重可以被迁移到农业图像。实际做法是先用 EfficientNet-B3 或 ConvNeXt-Base 替换 ResNet50保持其他设置完全不变跑完同一个训练计划对比验证集 F1。这一步能快速看清“换头”有没有带来实质收益。4.2 类别不平衡与病害特征增强病害分类最大的威胁其实不是类别不平衡而是“类间特征相似”和“类内特征差异大”。锈病的早期孢子堆和叶斑病的初期圆斑在 224 分辨率下差异可能只有几个像素同一病害在不同甘蔗品种上的表现颜色深浅各不相同。所以增强策略的重心应该放在保留细微纹理差异的变换上。import albumentations as A train_aug A.Compose([ A.Resize(256, 256), A.RandomResizedCrop(224, 224, scale(0.6, 1.0)), A.HorizontalFlip(p0.5), A.RandomBrightnessContrast(brightness_limit0.2, contrast_limit0.2, p0.5), A.HueSaturationValue(hue_shift_limit10, sat_shift_limit20, val_shift_limit20, p0.5), A.GaussNoise(var_limit(10.0, 30.0), p0.3), A.CLAHE(clip_limit2.0, tile_grid_size(8, 8), p0.5), ])Albumentations 比 torchvision 的增强更适合这个场景原因在于它的CLAHE增强能自适应提升叶片局部对比度对锈病孢子堆、病斑边缘这类低对比度纹理非常有用。GaussNoise模拟田间拍摄时传感器噪点这在光照不足的阴天照片里是真实存在的。但注意HueSaturationValue的hue_shift_limit不要超过 10甘蔗叶片健康的绿色和病害后的黄化色之间有诊断意义色相偏移过大会让模型学到错误的颜色关联。代价是让模型对颜色变化过度敏感——在农业视觉里这属于宁可错过不可做错的权衡。类别不平衡的硬解法是采样策略。用WeightedRandomSampler让每个 epoch 从样本少的类里重复采样等价于人为拉平类别分布。import torch.utils.data as data_utils labels_array train_dataset.get_labels() # 假设数据集对象能直接给出全部标签 class_counts torch.bincount(torch.tensor(labels_array), minlengthnum_classes) class_weights 1.0 / class_counts.float() sample_weights class_weights[labels_array] sampler data_utils.WeightedRandomSampler( sample_weights, num_sampleslen(sample_weights), replacementTrue ) train_loader DataLoader(train_dataset, batch_size32, samplersampler)replacementTrue允许多次抽到同一张图这是重采样方案的标准做法。它的副作用是训练时长增加因为一个 epoch 会经过的样本数变多了。如果训练时间敏感可以换另一种方案保持原始采样不变在 loss 上按类别权重加权torch.nn.CrossEntropyLoss(weightclass_weights.cuda())两种方案效果接近区别在收敛稳定性和超参敏感度。WeightedRandomSampler 更直接但容易让小类别的模型过拟合到重复样本上loss 加权更平滑但权重比例需要小心调。4.3 各层学习率差异化微调阶段的最强细节主干网络的不同层应该有不同的学习率。预训练模型的前几层学习到的已经是很通用的边缘、纹理特征几乎不需要大改动最后一层全连接是随机初始化的需要较大学习率快速学习新类别。用分组参数设置来实现。backbone_params [] classifier_params [] for name, param in model.named_parameters(): if fc in name: classifier_params.append(param) else: backbone_params.append(param) diff_lr_optimizer torch.optim.AdamW([ {params: backbone_params, lr: 1e-4}, {params: classifier_params, lr: 1e-3} ], weight_decay1e-4)这里设置骨干网络学习率 1e-4新分类层学习率 1e-3差一个数量级让随机初始化的分类层快速拟合的同时避免破坏预训练特征。比单一学习率通常能稳定提升 1 到 2 个点。要注意每个 epoch 后给scheduler.step()时如果用的是CosineAnnealingLR它会同时缩放所有参数组的学习率保持这个倍率关系不变这样是合理的。如果某个类别的 F1 一直起不来优先怀疑标注质量其次怀疑增强强度最后才怀疑模型容量——顺序不能反反了就是浪费时间调不存在的参数。5. 避坑与常见问题排查甘蔗病害分类训练的五个血泪经验训练病害分类模型会遇到的坑很多是农业图像特有的。我按踩坑频率排了五条每一条都是现象、原因、解决三步走。5.1 验证集准确率高但田间实测拉跨现象模型在验证集上跑出 95% 的准确率拿到田间新拍的照片一试准确率直接掉到 80% 以下。原因典型的域偏移或者说数据泄漏的变种。训练和验证数据来自同一批采集批次拍摄设备、光照条件、地块背景高度一致模型学到的是“这批照片的风格”而不是“病害的本质特征”。田间新图片在设备型号、拍摄角度、叶片湿度上都不同模型自然失效。解决划验证集时必须按“采集批次”而不是按“单张图片”随机切分。同一时间、同一块地、同一台设备拍摄的图全部归入同一集合确保验证集来自模型没见过的采集批次。如果数据集的目录结构本身带有批次信息用这些信息分组后再划分别图省事直接全量随机切。5.2 训练 loss 降不下去卡在某个值附近震荡现象训练 loss 在前几个 epoch 正常下降到 0.6 附近开始来回震荡不再下降但准确率还在缓慢上升。原因八成出在标签噪声上尤其是病害早期症状的图片被错误标注。模型对错误标签的样本反复学习梯度方向一会对一会错loss 自然降不动。另一个元凶是学习率过高模型在损失面底部来回跳跃进不了更低的谷底。解决先降学习率到当前值的十分之一看三个 epoch 内 loss 是否重新开始下降。如果没变化抽查训练集中 loss 最高的几十个样本人眼复核标签。我常用一个技巧把训练 loss 最高的样本导出成一张拼接大图一眼扫过去就能发现错误标注集中在哪几个类别。5.3 混淆矩阵里两个类稳定互错现象锈病和叶斑病的图经常互相误判无论怎么调参这两个类的混淆始终存在。原因这两个病斑在早期阶段视觉上高度相似图像分辨率不够或者病斑面积太小时模型根本没有足够的像素做判别。数据集的原始拍摄距离决定了这个下限。解决先看训练集里这两个类的样本统计病斑占整图的大致比例。如果大部分样本里病斑只占整图不到 10%就要考虑先做一个病害区域检测或分割前置裁出病斑局部再做分类而不是直接端到端全图分类。如果病斑占比正常尝试把输入分辨率提高到 384 或更高看混淆比例是否下降。5.4 数据增强过强导致训练集 acc 都上不去现象加了 RandomErasing 或大幅旋转之后训练准确率迟迟不到 80%验证集当然也低。原因增强不是为了把图像变成模型不认识的样子。病害特征可能是局部几个像素的小斑点随机擦除把病斑擦掉训练时模型看到的图片失真学不到有效特征。解决把RandomErasing的擦除概率降到 0.1 以下旋转角度限制在 15 度以内裁切比例下限控制在 0.5 以上。如果仍然收敛慢检查增强策略里是否有破坏中心区域的变换在起反作用。记住增强是数据不足时的补偿不是越多越好。5.5 GPU 内存不足或训练中断后白跑现象batch size 设 64显存直接爆掉跑了 20 个 epoch 的进程因为网络抖动断了模型权重没保存全部白费。原因输入分辨率 224 的 ResNet50 用 32 的 batch size 是常规配置但迁移到 EfficientNet-B4 或 ViT 时显存占用翻倍不改 batch size 必爆。训练中断是云端机器上最让人血压升高的场景没有之一。解决显存不够时先减 batch size 到 16同时把学习率按比例调整——batch size 减半学习率大致减半。训练脚本里每个 epoch 结束保存一次 checkpoint保留最优和最近两个版本磁盘只占几百 MB换来的是随时可以续跑的后悔药。torch.save(model.state_dict(), fcheckpoint_epoch{epoch}.pth)这行代码值回所有训练时间。6. 落地验证的最后一公里混淆矩阵复盘与模型导出模型训练完成验证集准确率也满意了但距离真正能用的农业视觉工具还有两步错误样本的复盘分析和可部署格式的导出。这两个步骤解决的是“技术指标达标但实际场景没法用”的最后一公里问题。错误样本复盘的核心工具是混淆矩阵但我说的不是打印一张数字矩阵就完事而是按“每一个错误对”展开到具体图片。做法很简单收集验证集所有预测错误样本按真实类别预测类别分组每组随机抽 8 到 10 张图拼一张网格图肉眼观察。这一步消耗的时间不多收获却很大——你会发现错误标签里有一批是标注者本身把锈病早期和叶斑病混了这在农业数据采集里属于常见的系统性标注误差。发现这种情况之后常规做法是把这批样本先剔除或者重新标注再评估模型才有意义。另一个常被忽视的行为是统计每个类别的置信度分布置信度高但预测错误的样本比置信度低但预测正确的样本更有分析价值前者意味着特征学到了错误关联。模型导出分两步。第一步用torch.jit.trace或 ONNX 导出部署格式注意输入尺寸要与训练一致导出时把归一化操作直接编进计算图避免部署端和训练端的预处理逻辑产生偏差。第二步是做一次端到端的本地验证——用导出后的模型跑一遍训练时没见过的现场照片看的不是准确率而是输出的置信度分布是否符合直觉正常叶片应该置信度分散或偏低典型病斑应该集中到某个特定类别且置信度偏高。如果模型对正常叶片的最高置信度类别是“赤腐病”且分数超过 0.9多半是训练集中健康叶片的占比过低模型没有见过足够的负样本这时候要做的是回去补健康样本不是调模型阈值。从这份数据集的第一个 epoch 到现在我的习惯是每次训练结束都保留一套完整的实验记录数据划分的随机种子、增强策略的代码版本、训练超参、混淆矩阵和错误样本图。这套记录救了我不止一次——换主干网络后效果反而变差时翻出基线实验的配置逐项对比立刻能找到是哪一层改动引入了问题。从那以后每次拿到新的植物病害数据集我都会强制走一遍先做分布式扫描和同图查重、再定训练策略的流程。希望这篇拆解能帮你在甘蔗病害分类这个任务上少踩几个我踩过的坑。本文还有配套的精品资源点击获取