简介这份番茄叶子缺陷图像分类数据集面向从事图像分类、农业病害识别与深度学习实践的开发者及学生提供约3000张已标注的番茄叶片图像覆盖细菌斑点、早疫病、健康、Septoria_spot等7个类别可直接作为分类网络输入省去繁琐的采集与标注环节。资源包共2000个文件以1998张jpg图像为主体另含1个py可视化脚本与1个json类别说明文件压缩包约161.45MB并已划分训练集与测试集同类图片集中存放便于按目录直接加载。运行包内show脚本即可快速预览样本分布与图像质量适合用于模型训练、迁移学习对比及数据增强实验。目前已有63人学习下载可作为番茄病害识别任务的可靠起点帮助读者把精力集中在网络结构改进与调参上。1. 番茄叶子缺陷分类数据集7 类、约 3000 张、已划分训练测试的落地资源拿到一个植物病害分类任务最耗时的往往不是搭网络而是找一批干净、已标注、类别均衡的图。这份番茄叶子缺陷图像分类数据集就是冲着这个痛点来的约 3000 张已标注图片覆盖细菌斑点、早疫病、健康、Septoria_spot 等 7 个类别并且已经按训练集、测试集分好目录同一类图片放在一起。它适合做图像分类算法验证、迁移学习微调、以及 yolov8 训练自己的数据集这类流程里的分类分支预研。资源里还带了一个 show 脚本用来快速可视化数据集分布省得自己写统计代码。下面按「这是什么 → 怎么用 → 坑在哪」的顺序拆开讲。2. 数据集结构与类别体系先看清目录再动手2.1 目录组织与文件命名规律从项目正文给出的文件名能反推出这套数据的组织方式。文件名前缀基本对应类别缩写EB_是 Early Blight早疫病SS_是 Septoria Spot斑枯病MV_是 Mosaic Virus花叶病毒BM_是 Bacterial Spot细菌斑点一类还有Early_blight_on_tomato_leaves_(7871930010).jpg这种带原始来源编号的长名。这说明数据来源是混合的——一部分是重命名规整过的一部分保留了原始采集名。常见做法是根目录下分train/和test/每个子目录再按类别建文件夹tomato_leaf_dataset/ ├── train/ │ ├── Bacterial_spot/ │ ├── Early_blight/ │ ├── Healthy/ │ ├── Septoria_spot/ │ ├── Mosaic_virus/ │ ├── Leaf_mold/ │ └── Target_spot/ └── test/ ├── Bacterial_spot/ ├── Early_blight/ └── ...这种ImageFolder风格的结构是 PyTorchtorchvision.datasets.ImageFolder和 TensorFlowimage_dataset_from_directory都直接认的格式不需要额外写索引文件。类别数 7 个具体名称以资源里的 json 文件为准——摘要里明确说了「具体查看 json 文件」所以别凭文件名前缀硬猜先打开 json 核对类别标签和对应的文件夹名。2.2 类别数量与划分比例核对约 3000 张、7 类平均下来每类 400 多张。但实际分布通常不均匀早疫病和健康叶这两类往往偏多Septoria_spot 这类可能偏少。动手前先跑一段统计把每类数量、训练/测试比例摸清楚import os from collections import Counter root tomato_leaf_dataset for split in [train, test]: split_path os.path.join(root, split) counter Counter() for cls in os.listdir(split_path): cls_path os.path.join(split_path, cls) if os.path.isdir(cls_path): # 只统计图片文件过滤掉隐藏文件和说明文档 n len([f for f in os.listdir(cls_path) if f.lower().endswith((.jpg, .jpeg, .png))]) counter[cls] n print(split, dict(counter), total:, sum(counter.values()))这段代码遍历 train 和 test 两个目录按类别文件夹统计图片数量。endswith那行是关键——数据集里可能混着.txt或.json说明文件不过滤会把它们算进图片数导致统计虚高。跑完你会得到类似{Early_blight: 520, Healthy: 480, ...}的输出。如果发现某类测试集只有个位数那评估指标会非常不稳得考虑做分层抽样或交叉验证。提示统计结果和 json 里声明的类别数对不上时以实际文件夹为准json 可能是标注阶段的中间产物。3. 直接喂给分类网络预处理、加载与训练验证3.1 预处理与归一化参数怎么设摘要说「数据经过预处理可以直接作为分类网络输入使用」但这不等于可以跳过归一化。常见做法是统一 resize 到 224×224 或 256×256再用 ImageNet 的均值和标准差做归一化——因为绝大多数迁移学习 backboneResNet、EfficientNet、ViT都是在 ImageNet 上预训练的输入分布对齐能明显加快收敛。from torchvision import transforms train_tf transforms.Compose([ transforms.Resize((224, 224)), # 统一尺寸ViT 常用 224 transforms.RandomHorizontalFlip(), # 叶片左右翻转不改变类别 transforms.RandomRotation(15), # 小角度旋转增强泛化 transforms.ToTensor(), transforms.Normalize( # ImageNet 统计量 mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225] ), ]) val_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize( mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225] ), ])训练集加了翻转和旋转验证/测试集只做 resize 和归一化——这是标准做法验证阶段不能引入随机增强否则指标每次跑都不一样没法复现。RandomRotation(15)的 15 度是经验值叶片病害的旋转不变性比较强但角度太大可能把病斑转出画面边缘反而引入噪声。3.2 用 ImageFolder 加载并跑通一个 baseline结构规整的好处在这里体现三行代码就能建好 Dataset。from torchvision.datasets import ImageFolder from torch.utils.data import DataLoader train_ds ImageFolder(tomato_leaf_dataset/train, transformtrain_tf) test_ds ImageFolder(tomato_leaf_dataset/test, transformval_tf) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers4) test_loader DataLoader(test_ds, batch_size32, shuffleFalse, num_workers4) print(类别映射:, train_ds.class_to_idx) # 确认 7 类标签顺序 print(训练样本:, len(train_ds), 测试样本:, len(test_ds))class_to_idx打印出来是类别名到数字标签的映射务必核对它和 json 里声明的类别一致——如果文件夹名拼写有出入比如Septoria_spot写成SeptoriaSpotImageFolder 会当成两个不同类别类别数就变成 8 了。num_workers4在 Windows 上如果报错改成 0 先跑通这是 DataLoader 多进程在 Windows 下的老问题。3.3 训练循环与评估指标baseline 用 ResNet18 微调就够验证数据可用性import torch import torch.nn as nn from torchvision import models device cuda if torch.cuda.is_available() else cpu model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) model.fc nn.Linear(model.fc.in_features, 7) # 改成 7 类输出 model model.to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-4) for epoch in range(10): model.train() for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() loss criterion(model(imgs), labels) loss.backward() optimizer.step() # 每轮在测试集上算准确率 model.eval() correct total 0 with torch.no_grad(): for imgs, labels in test_loader: imgs, labels imgs.to(device), labels.to(device) pred model(imgs).argmax(1) correct (pred labels).sum().item() total labels.size(0) print(fepoch {epoch} acc {correct/total:.4f})model.fc换成 7 类线性层是迁移学习的关键一步backbone 权重保留只改分类头。lr1e-4比从头训练的 1e-3 小一个量级因为预训练权重已经很好了学习率太大会把特征打乱。评估只看准确率在类别不均衡时会骗人建议再补一个混淆矩阵看 Septoria_spot 这类是不是被大量误判成早疫病——这两种病斑在视觉上确实容易混。4. 可视化与数据质量排查show 脚本和常见坑4.1 用 show 脚本快速看分布资源自带的 show 脚本是省事的地方。它一般做两件事统计每类数量画柱状图以及随机抽几张图拼成网格看样本质量。如果你拿到的 show 脚本依赖 matplotlib 和 PIL直接跑python show.py --data_root tomato_leaf_dataset --split train参数--data_root指向数据集根目录--split选 train 或 test。跑完会弹出类别分布柱状图和样本缩略图。重点看两样一是柱状图有没有某类特别矮样本太少二是缩略图里有没有明显不是叶片的图混进来——混合来源的数据集最容易出现这种脏样本。4.2 数据质量排查清单在正式训练前我一般会强制过一遍这几项尺寸一致性identify -format %wx%h\n *.jpg | sort | uniq -c看有没有异常小图比如 32×32 的缩略图混入。损坏文件用 PIL 逐个Image.open().verify()损坏图会让训练中途崩。重复图算感知哈希pHash去重混合来源数据集常有重复采集。标签噪声抽每类 20 张人工看确认文件名前缀和实际病斑对得上。这几步花不了半小时但能避免训练到一半发现某类全是错标、白跑几轮的血泪经验。5. 避坑与常见问题排查5.1 类别数对不上文件夹名拼写不一致现象class_to_idx打印出 8 个或更多类别但 json 里写的是 7 类。原因不同来源的图片在重命名时把同一类写成了不同形式比如Septoria_spot和Septoria spotImageFolder 按文件夹名区分就多出一类。解决先跑 2.2 的统计脚本列出所有文件夹名和 json 逐一比对把拼写统一后再加载。5.2 训练准确率虚高训练测试集图片重复现象测试集准确率一上来就 99%但换一批真实图就崩。原因混合来源数据集在划分时可能把同一张原图的增强版本分到了训练和测试两边造成数据泄漏。解决对全量图算 pHash检查训练集和测试集之间有没有近似重复有的话从测试集剔除。5.3 Windows 下 DataLoader 报错或卡死现象num_workers大于 0 时抛RuntimeError或进程挂起。原因Windows 的多进程 spawn 机制和 Linux 的 fork 不同DataLoader 在 Windows 上对 worker 数敏感。解决把num_workers设为 0 先跑通或把训练代码放进if __name__ __main__:保护块里。5.4 某类样本过少导致指标失真现象整体准确率还行但混淆矩阵显示 Septoria_spot 几乎全错。原因该类样本数远少于其他类交叉熵损失被大类主导。解决用WeightedRandomSampler做重采样或在损失里传weight按类频率反比加权。5.5 归一化参数用错现象loss 下降极慢或震荡。原因自己随手设了 mean/std和预训练 backbone 的输入分布不匹配。解决用 ImageNet 的mean[0.485,0.456,0.406]、std[0.229,0.224,0.225]除非你用的是特定领域预训练权重。6. 进阶把这份数据接进 yolov8 分类流程与验证技巧这份数据虽然是分类格式但完全可以接进 yolov8 训练自己的数据集那条链路里的分类分支。Ultralytics 的yolo classify任务要求的目录结构和这里几乎一样只是根目录名有约定# 转成 ultralytics 分类任务期望的结构 dataset_cls/ ├── train/ │ ├── Bacterial_spot/ │ └── ... └── val/ # 把原 test 重命名为 val ├── Bacterial_spot/ └── ...然后写一个data.yaml指向它或者直接用命令行yolo classify train datadataset_cls modelyolov8n-cls.pt epochs30 imgsz224yolov8n-cls.pt是分类专用预训练权重imgsz224和前面预处理对齐。跑完在runs/classify/train/下会出混淆矩阵和每类 P/R 曲线比手写评估省事。验证数据可用性有个我常用的技巧先只用 10% 数据训 3 个 epoch如果准确率能明显超过随机猜测7 类随机约 14%说明数据和标签基本对齐如果死活卡在 14% 附近八成是标签映射错了或图片和标签没对上。这个快速冒烟测试能在投入完整训练前就暴露大问题。从那以后我每次拿到新数据集都强制先跑一遍「统计 冒烟训练 混淆矩阵」三件套再决定要不要花时间调参。这套流程帮我省下过好几次白跑一整晚的算力。希望帮到你。本文还有配套的精品资源点击获取