番茄叶子缺陷分类数据集:7类近3000张已标注图像,直接用于分类网络训练

📅 发布时间:2026/10/5 8:59:06
番茄叶子缺陷分类数据集:7类近3000张已标注图像,直接用于分类网络训练
简介这份番茄叶子缺陷图像分类数据集面向从事图像分类、农业病害识别与深度学习实践的开发者及学生解决番茄叶片病害样本获取难、标注成本高的问题。数据已完成预处理可直接作为分类网络输入共划分7个类别包括细菌斑点、早疫病、健康、Septoria_spot等具体类别可查看包内json文件。资源按训练集与测试集分别存放同类图片便于直接开展模型训练与评估并附带show脚本用于数据集可视化方便快速检查样本分布与图像质量。压缩包为7z格式共约2000个文件其中1998个jpg图像、1个py脚本和1个json标注文件整体约161.45MB结构清晰、开箱即用。目前已有63人学习下载适合用于图像分类网络改进、分割任务预训练及计算机视觉项目实战帮助读者省去数据采集与清洗环节把精力集中在模型设计与调参上。1. 番茄叶子缺陷分类数据集7 类近 3000 张已标注图拿到手就能喂分类网络如果你正在做农业视觉相关的图像分类项目大概率绕不开一个现实问题公开的植物病害数据集要么类别太少要么标注质量参差要么图片数量不够撑起一个像样的 baseline。这个番茄叶子缺陷图像分类数据集约 3000 张已标注图片覆盖 7 个类别——细菌斑点、早疫病、健康、Septoria_spot 等具体类别以资源里的 json 文件为准。图片已经过预处理训练集和测试集按同一类数据分目录存放直接就能作为分类网络的输入。它还附带了一个 show 脚本方便你先可视化确认数据分布再动手训练。适合做图像分类算法验证、迁移学习实验或者拿它当农业场景的入门数据集来跑通完整流程的人。2. 数据集结构与类别体系先搞清楚 7 个类怎么分、目录怎么摆2.1 类别定义与 json 标注文件的作用这个数据集的核心信息不在图片文件名里而在随资源附带的 json 文件中。7 个类别分别是细菌斑点Bacterial_spot、早疫病Early_blight、健康Healthy、Septoria_spot以及另外几类番茄叶部常见缺陷。为什么强调先看 json因为图片文件名里出现的 EB_、SS_、MV_、BM_ 这类前缀是缩写不同批次的命名习惯可能不一致json 才是类别到索引的权威映射。常见做法是拿到数据集后第一件事不是写训练脚本而是把 json 读出来打印类别列表和每类对应的图片数量。这一步能帮你快速判断是否存在类别不均衡——农业病害数据集里健康样本往往偏多病害样本偏少如果不提前知道训练时 accuracy 看着很高实际对病害类的召回率可能惨不忍睹。import json import os from collections import Counter # 读取标注文件确认类别映射关系 with open(dataset/annotations.json, r, encodingutf-8) as f: meta json.load(f) # 打印类别列表和索引 classes meta.get(classes, []) print(类别总数:, len(classes)) for idx, name in enumerate(classes): print(f 索引 {idx}: {name}) # 统计每个类别的图片数量检查是否均衡 label_counter Counter() for item in meta.get(images, []): label_counter[item[label]] 1 print(\n各类别样本数:) for cls_name, count in label_counter.items(): print(f {cls_name}: {count})这段代码的逻辑很直接先加载 json拿到类别列表再遍历 images 字段统计每类数量。参数上唯一需要注意的是 json 的字段名——不同标注工具导出的结构不一样有的用classes有的用categories有的把标签直接写在每个 image 对象的label字段里。如果运行报 KeyError先 print 一下 json 的顶层 key 再调整。2.2 训练集/测试集目录结构与加载方式数据集已经划分好训练集和测试集存放方式是「各自的同一类数据图片放在同一个目录下」。这意味着目录结构大概率长这样dataset/ ├── train/ │ ├── Bacterial_spot/ │ ├── Early_blight/ │ ├── Healthy/ │ ├── Septoria_spot/ │ └── ... ├── test/ │ ├── Bacterial_spot/ │ ├── Early_blight/ │ └── ... └── annotations.json这种按类分目录的结构对 PyTorch 的ImageFolder和 TensorFlow 的image_dataset_from_directory都是原生支持的不需要额外写解析逻辑。我一般会先用一个最小脚本验证目录能不能被正确读取类别数和图片数对不对得上再进入正式训练。import os data_root dataset for split in [train, test]: split_path os.path.join(data_root, split) if not os.path.exists(split_path): print(f[警告] {split} 目录不存在检查路径) continue print(f\n {split} ) total 0 for cls_name in sorted(os.listdir(split_path)): cls_path os.path.join(split_path, cls_name) if os.path.isdir(cls_path): n len([f for f in os.listdir(cls_path) if f.lower().endswith((.jpg, .jpeg, .png))]) total n print(f {cls_name}: {n} 张) print(f 合计: {total} 张)这段脚本的作用是「体检」确认 train 和 test 下每个类目录存在、图片格式可识别、数量合理。参数上图片扩展名我列了 jpg/jpeg/png 三种因为原始文件名里出现了.jpg但预处理后有可能统一转了格式实际以你解压后看到的为准。如果某个类显示 0 张要么是目录名拼写不一致要么是图片被放在了子目录里。提示先跑通目录检查再写训练代码比训练到一半发现某个类没加载上要省时间得多。3. 用 show 脚本可视化数据集训练前先确认图片没坏、类别没串3.1 show 脚本的典型用法与参数调整资源里附带了一个 show 脚本用途是可视化数据集。这类脚本通常做三件事随机抽样若干张图片、按类别网格排列、显示图片和对应标签。不同作者写的 show 脚本参数名不一样但核心逻辑大同小异。我一般会先打开脚本看一眼它读的是哪个目录、有没有硬编码路径再决定是直接跑还是改两行。假设 show 脚本基于 matplotlib 实现典型调用方式是# 常见调用形式具体参数以脚本内 argparse 定义为准 python show.py --data_root dataset/train --num_per_class 5 --cols 7如果脚本没有 argparse而是直接写死了路径那就手动改data_root变量指向你的实际目录。num_per_class控制每个类抽几张cols控制网格列数。7 个类的话每类抽 5 张、7 列排列正好是一张 5 行 7 列的网格图一眼就能看出类间差异。3.2 从可视化结果判断数据质量可视化不只是「看看图」它能帮你发现几类问题。第一图片是否损坏——有些数据集里混着截断的 jpg训练时才会报错提前看到就能剔除。第二类别是否串了——比如 Early_blight 目录里混进了 Healthy 的图这种标注错误在农业数据集里并不罕见。第三图片尺寸和色调是否一致——如果预处理做得不到位有的图偏暗、有的偏亮训练时归一化参数就得仔细调。我一般会重点看早疫病和细菌斑点这两类因为它们在外观上有时比较接近早期症状都是叶面斑点。如果可视化时发现这两类肉眼都难分那模型学起来也会吃力可能需要考虑更强的数据增强或者换用更高分辨率的输入。import matplotlib.pyplot as plt from PIL import Image import os import random def visualize_samples(data_root, classes, num_per_class5): fig, axes plt.subplots(len(classes), num_per_class, figsize(num_per_class * 2.5, len(classes) * 2.5)) for i, cls in enumerate(classes): cls_dir os.path.join(data_root, cls) if not os.path.isdir(cls_dir): continue imgs [f for f in os.listdir(cls_dir) if f.lower().endswith((.jpg, .png, .jpeg))] samples random.sample(imgs, min(num_per_class, len(imgs))) for j, img_name in enumerate(samples): img Image.open(os.path.join(cls_dir, img_name)) axes[i][j].imshow(img) axes[i][j].set_title(cls, fontsize8) axes[i][j].axis(off) plt.tight_layout() plt.savefig(dataset_preview.png, dpi150) plt.show() classes [Bacterial_spot, Early_blight, Healthy, Septoria_spot] visualize_samples(dataset/train, classes)这段代码是我自己常用的替代方案如果原 show 脚本跑不通可以直接用。逻辑是按类别逐行抽样每类抽num_per_class张拼成网格图保存。参数上figsize控制整体图幅类别多的时候要相应调大否则标题会重叠。random.sample保证每次抽的图不一样多跑几次能看到更多样本。注意如果可视化时发现某类图片明显偏少或偏多先别急着训练回到 2.1 的统计脚本确认数量再决定是否要做重采样或类别权重调整。4. 接分类网络训练从 ImageFolder 到第一个 baseline4.1 数据加载与预处理参数怎么设这个数据集已经预处理过可以直接作为分类网络输入。但「可以直接输入」不等于「不需要做任何变换」。常见做法是在训练时加一层ResizeToTensorNormalize把图片统一到网络期望的尺寸和数值范围。如果你用的是 ImageNet 预训练权重归一化参数就用 ImageNet 的均值和标准差如果是从头训练可以用数据集自身的统计量。import torch from torchvision import datasets, transforms from torch.utils.data import DataLoader # 训练集变换轻度增强 标准化 train_tf transforms.Compose([ transforms.Resize((224, 224)), # 统一到 224适配主流骨干 transforms.RandomHorizontalFlip(p0.5), # 水平翻转农业图像通常安全 transforms.RandomRotation(15), # 小角度旋转模拟拍摄角度变化 transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) # 测试集只做 Resize 和标准化不做增强 test_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]) ]) train_ds datasets.ImageFolder(dataset/train, transformtrain_tf) test_ds datasets.ImageFolder(dataset/test, transformtest_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) print(训练集样本数:, len(train_ds)) print(测试集样本数:, len(test_ds))参数说明Resize((224, 224))是最通用的选择ResNet、EfficientNet、ViT 都能吃。RandomHorizontalFlip对叶片图像基本无害因为叶子左右翻转后语义不变。RandomRotation(15)模拟拍摄时的轻微角度偏差角度别设太大超过 30 度可能把病斑转到不合理的位置。batch_size32在 3000 张量级下比较稳显存不够就降到 16。num_workers按机器 CPU 核数调Windows 下如果报错就设成 0。4.2 选一个骨干网络跑通训练循环数据集规模不大3000 张 7 类直接用 ResNet18 或 EfficientNet-B0 这类轻量骨干就够。我一般先用 ResNet18 跑一个 baseline确认流程通、指标合理再考虑换更大的模型或做改进。import torch.nn as nn import torch.optim as optim from torchvision import models device torch.device(cuda if torch.cuda.is_available() else cpu) # 加载预训练 ResNet18替换最后一层适配 7 分类 model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) model.fc nn.Linear(model.fc.in_features, 7) model model.to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-3) for epoch in range(10): model.train() running_loss 0.0 for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() outputs model(imgs) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() print(fEpoch {epoch1}, Loss: {running_loss/len(train_loader):.4f})逻辑说明用 ImageNet 预训练权重初始化只替换全连接层这是小数据集上最稳妥的做法。lr1e-3对 Adam 来说偏大但只训练 fc 层时问题不大如果你解冻了所有层做微调学习率要降到 1e-4 甚至更低。CrossEntropyLoss是分类任务标配如果类别不均衡可以加weight参数传入每类权重。4.3 评估与混淆矩阵别只看 accuracy训练完必须看混淆矩阵尤其是早疫病和细菌斑点这两类容易混。accuracy 高不代表模型真的学到了区分性特征可能只是把健康样本全分对了。from sklearn.metrics import confusion_matrix, classification_report import numpy as np model.eval() all_preds, all_labels [], [] with torch.no_grad(): for imgs, labels in test_loader: imgs imgs.to(device) outputs model(imgs) preds outputs.argmax(dim1).cpu().numpy() all_preds.extend(preds) all_labels.extend(labels.numpy()) print(classification_report(all_labels, all_preds, target_namestrain_ds.classes)) print(\n混淆矩阵:) print(confusion_matrix(all_labels, all_preds))classification_report会给出每类的 precision、recall、f1-score比整体 accuracy 信息量大得多。混淆矩阵则直接告诉你哪两类在互相误判。如果 Early_blight 和 Bacterial_spot 之间的误判特别多说明模型提取的特征对这两类的区分度不够可以考虑加注意力模块、换更高分辨率输入或者针对这两类做额外增强。5. 避坑与排查标注、路径、显存这三处最容易翻车5.1 类别索引与 json 不一致导致标签错位现象训练时 loss 正常下降但测试集 accuracy 始终在 1/7 附近等于随机猜。 原因ImageFolder按目录名字母序生成class_to_idx而 json 里的类别索引可能是另一套顺序。如果你用 json 的索引去解释模型输出标签就全错位了。 解决训练前打印train_ds.class_to_idx和 json 里的类别列表逐一对照。如果顺序不一致要么重命名目录加数字前缀强制排序要么在评估时用class_to_idx做映射别直接用 json 的索引。5.2 图片扩展名大小写导致漏读现象目录里明明有图但ImageFolder报FileNotFoundError或者某类样本数为 0。 原因部分图片扩展名是.JPG或.JPEG而加载逻辑只匹配小写。 解决在统计和加载前统一转小写或者用PIL的Image.open做容错。我一般会在数据准备阶段批量把扩展名改成小写省得后面反复排查。5.3 训练集和测试集类别数不一致现象测试时模型输出 7 类但测试集某个类目录不存在评估时报维度错误。 原因划分时可能把某个类的全部样本都放进了训练集测试集里没有这个类。 解决跑 2.2 的目录检查脚本确认 train 和 test 下类别目录完全一致。如果测试集缺类要么从训练集里匀一部分出来要么在评估时跳过缺失类。5.4 显存不足导致 batch_size 被迫降到 1现象CUDA out of memory只能把 batch_size 设成 1训练慢且梯度不稳。 原因图片 Resize 到 224 后ResNet18 的中间激活值在 batch_size32 时可能超出小显存卡的容量。 解决先降 batch_size 到 16 或 8再开混合精度训练torch.cuda.amp能省将近一半显存。如果还不够把输入降到 128 或 160但要注意小分辨率可能损失病斑细节。5.5 show 脚本硬编码路径导致跑不通现象运行 show 脚本报路径不存在但你的数据明明放在那儿。 原因脚本里写死了作者本机的绝对路径比如/home/user/data/tomato/。 解决打开脚本把data_root改成相对路径或你的实际路径。如果脚本没有参数入口就在开头加一个argparse方便后续复用。6. 进阶玩法用这个数据集验证分类网络改进是否真的有效这个数据集真正的价值不只是跑一个 baseline而是拿它当「试金石」来验证你手头的分类网络改进到底有没有用。3000 张、7 类、已划分规模刚好——大到能看出方法差异小到一次实验能在单卡上跑完。我一般会固定三件事固定随机种子、固定训练轮数、固定评估指标然后只改网络结构或损失函数看混淆矩阵里那几对易混类别的误判数有没有下降。具体做法是先跑一个 ResNet18 baseline记录每类的 f1-score 和混淆矩阵然后换上你改进的模块比如在 backbone 后面加一个轻量注意力、或者换用标签平滑的损失函数其他条件不变再跑一次。对比时重点看早疫病和细菌斑点这两类的 f1 有没有提升因为这两类最容易混改进如果有效最先体现在这里。# 固定随机种子保证两次实验可比 import torch, random, numpy as np def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark False set_seed(42)参数上cudnn.deterministicTrue会稍微拖慢训练速度但能保证结果可复现。如果你更在意速度、不在意完全复现可以设成 False。benchmarkFalse同理关了之后卷积算法选择更稳定。另一个进阶方向是拿这个数据集做迁移学习的消融实验分别冻结不同数量的层看哪一层开始解冻时指标提升最明显。农业图像和 ImageNet 的分布差异不小通常解冻最后两个 stage 就能拿到大部分收益全部解冻反而可能过拟合。验证改进是否有效还有一个容易被忽略的点测试集不能反复用来调参。我一般会从训练集里再切一个小验证集用验证集选超参测试集只在最后跑一次。这个数据集已经分好了 train/test你可以从 train 里再切 10% 出来当 val别动 test。从那以后我每次拿到新数据集都强制先跑一遍目录检查和可视化确认类别映射、图片数量、样本质量三件事没问题再写训练代码。这个习惯帮我省下了至少两次「训练三天后发现标签全错」的后悔药。希望帮到你。本文还有配套的精品资源点击获取