资讯动态

蘑菇图像分类数据集详解:PyTorch与Keras加载训练实战

发布时间:2026/10/14 13:25:42 来源:尧图企业网站定制
简介面向图像分类与yolov5分类任务训练的12种蘑菇识别数据集类别涵盖姬松茸、阿曼妮塔、牛肝菌、Cortinarius等具体映射见随附的json类别字典使用前可先查看该字典确认各类别名称与数量。数据已按约8:2划分为训练集和测试集训练集9600张、测试集2400张文件夹按类别保存、目录结构清晰可直接作为yolov5分类数据集或CNN分类网络输入免去自行采集、清洗与划分的负担。整套资源共2000个文件包含1998张jpg原图、1个json类别字典和1个py样本可视化脚本压缩包约97.67MBshow.py脚本可快速预览各类别样本便于检查图像质量、类别分布与命名规范。目前已有1048人学习使用适合图像分类入门、蘑菇识别模型训练及分类网络课程实验也适合作为算法对比或毕业论文的基准数据集。1. 把蘑菇图像数据集拆开看划分好的文件夹才是这份数据最值钱的部分图像分类数据集有很多但当你拿到一份“12种蘑菇图像识别数据集”的时候真正值得先看的不是那 12 个类别而是它的组织方式。这份数据几乎把蘑菇识别项目起步阶段最麻烦的环节全部解决了图片已经按 train / val / test 三套目录切好图片按类别存放在对应文件夹里类别字典文件单独保存不需要再写分割脚本不需要手工整理标签。它适合刚把深度学习框架跑通、准备做第一个真实分类任务的新手也适合需要快速验证某个分类模型在自然图像上效果的开发者。很多做过蘑菇识别、叶片识别或昆虫识别的人都会有一个共同感受最后让模型翻车的往往不是网络结构而是数据划分和标签映射。这份数据集把这两件事提前做了。2. 拿到数据集先做三件事目录结构、字典映射、样本分布核对2.1 标准三段式目录结构train / val / test 各自长什么样这类数据集的通用排布方式非常稳定主目录下同时存在 train、val、test 三个文件夹每个文件夹内部按类别建子文件夹子文件夹名和类别字典里的 key 一一对应。有的版本会把 val 写成 valid有的把 test 写成 test本质没区别。常见划分比例大致在 7:2:1 或 8:1:1——train 负责拟合val 负责选模型和调超参test 只在最终评测时碰一次测试集参与训练是实验上的大忌。解压压缩包之后我建议先扫一眼目录结构确认外层和内层没有多套了一层目录这在实际下载的数据里非常常见像“mushroom_dataset / mushroom_dataset / train”这种嵌套经常出现。用命令行检查比用资源管理器直观得多也能顺便把目录层级关系看清楚cd mushroom_dataset find . -maxdepth 2 -type d | sort | head -50这段命令打印两层目录树。如果需要把每个类别目录下的图片数量也统计出来再补一步for cls in train/*/; do cls${cls%/} count$(find $cls -type f | wc -l) echo $cls : $count done脚本按字母顺序遍历 train 目录下的所有子目录统计每个类别文件夹中的文件数量并打印出来。这里有一个值得注意的细节${cls%/}是去掉路径末尾的斜杠避免输出中出现train/class_001/和class_001两种不一致的表示。2.2 类别字典文件的两个关键字段类名和数值标签怎么对上类别字典文件常见的两种形态分别是 JSON 和 CSV也可能是纯文本格式但核心内容是一致的。JSON 形态通常是“类名 → 显示名”的映射这是我说的最常见做法{ class_001: species_name_01, class_002: species_name_02, class_003: species_name_03, class_004: species_name_04, class_005: species_name_05, class_006: species_name_06, class_007: species_name_07, class_008: species_name_08, class_009: species_name_09, class_010: species_name_10, class_011: species_name_11, class_012: species_name_12 }这里只写出前三个字段示意实际文件里 12 个 key 一一对应 12 个类别文件夹。要注意的是JSON 里的 key 是稳定标识value 是方便人阅读的名字在训练阶段真正生效的是 key 到整数索引的映射关系。很多框架会按字母序给类别文件夹自动分配索引而不是按字典文件里的顺序所以拿到数据后别急着写死“class_001 等于标签 0”之类的假设。CSV 形态的字典就多一列常见格式是三列文件夹名、物种名、期望类别 ID。做训练前我更推荐先读 JSON 或 CSV 的文件夹名列然后和 os.listdir 的结果做一次比对保证两边完全一致。这个核对步骤看起来多余但能提前暴露“类名字段拼写不一致”“文件夹缺一个”“字典里混进了多余 key”三种问题。2.3 首轮核对脚本30 行代码摸清数据集的真实家底靠人工数文件夹不现实直接写一个简短的 Python 脚本把三件事一次做完校验字典和目录是否对齐、统计每个集合的图片数、找出空文件夹和异常文件。这是我每次拿到新数据集都会先跑的流程import os import json from collections import Counter # 路径按实际解压位置修改 BASE mushroom_dataset dict_path os.path.join(BASE, class_dict.json) with open(dict_path, r, encodingutf-8) as f: class_dict json.load(f) expected_keys set(class_dict.keys()) actual_dirs set(os.listdir(os.path.join(BASE, train))) # 1. 比对字典 key 与 train 目录下的子文件夹名 print(字典有但目录没有:, expected_keys - actual_dirs) print(目录有但字典没有:, actual_dirs - expected_keys) # 2. 统计 train / val / test 的图片数并按类别列出分布 for split in [train, val, test]: split_dir os.path.join(BASE, split) counts {} for cls in os.listdir(split_dir): cls_dir os.path.join(split_dir, cls) if not os.path.isdir(cls_dir): continue n len([f for f in os.listdir(cls_dir) if f.lower().endswith((.jpg, .jpeg, .png))]) counts[cls] n total sum(counts.values()) print(f{split}: 总图片数 {total}, 类别数 {len(counts)}) print(最少样本类别:, min(counts, keycounts.get), counts[min(counts, keycounts.get)])脚本前半段取字典的所有 key 与 train 下的子文件夹名求差集任何一边多出或缺少都会直接打印出来后半段分别统计三个集合的图片数。这里用列表推导加endswith过滤文件后缀是避免把 Thumbs.db、.DS_Store 这类非图片文件计入样本数。拿到输出之后重点看两个信息第一val 和 test 有没有出现字典之外的文件夹名第二有没有某个类别在 train 里有几百张在 val 或 test 里却只剩个位数。后者对后续训练影响非常大后面章节会说怎么处理。3. 把“文件夹”变成可训练数据PyTorch 和 Keras 两种加载路线3.1 PyTorch 路线ImageFolder 直接按文件夹名生成标签PyTorch 的torchvision.datasets.ImageFolder是为“按文件夹分好类的图片数据”量身设计的加载器。它做的事情本质上只有两件扫描根目录下每个子文件夹把子文件夹名当作类别名并自动生成类别到整数索引的映射按文件夹顺序读取图片路径在__getitem__返回图片张量和对应的整数标签。使用这套数据集的 PyTorch 加载代码非常简单import torch from torch.utils.data import DataLoader from torchvision import datasets, transforms # 定义预处理流程resize - 转张量 - 归一化 transform 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_dataset datasets.ImageFolder(mushroom_dataset/train, transformtransform) val_dataset datasets.ImageFolder(mushroom_dataset/val, transformtransform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers4)注意ImageFolder自动生成的类别索引是按文件夹名字母序排列的不是按class_dict.json里的顺序。也就是说train_dataset.classes里第一个名字会被映射成 0。如果你的验证代码或者评估脚本里手动把某个类名固定成了一个数字这里就会产生错位。打印验证这一步是值得养成的习惯print(train_dataset.classes) print(train_dataset.class_to_idx)classes是类别名列表class_to_idx是“类名 → 索引”的字典。两个字幕对照着看标签映射是否正常一清二楚。另一件要注意的事是train 用了shuffleTrueval 没有 shuffle。验证集必须保证每次评估看到的样本顺序稳定才能在两个模型之间做公平对比。3.2 Keras 路线flow_from_directory 的数据流方式和参数对照TensorFlow / Keras 侧的老牌加载方式是ImageDataGenerator配合flow_from_directory它的工作逻辑和 ImageFolder 基本相同给一个根目录按子文件夹自动生成标签。代码会比 PyTorch 那边更短from tensorflow.keras.preprocessing.image import ImageDataGenerator # 只做缩放不做增强 datagen ImageDataGenerator(rescale1.0 / 255.0) # train 和 val 各创建一个生成器 train_gen datagen.flow_from_directory( mushroom_dataset/train, target_size(224, 224), batch_size32, class_modecategorical, shuffleTrue, seed42 ) val_gen datagen.flow_from_directory( mushroom_dataset/val, target_size(224, 224), batch_size32, class_modecategorical, shuffleFalse, seed42 )target_size会直接把图片缩放成指定尺寸省去手动 Resizeclass_modecategorical表示标签输出成 one-hot 编码配合多分类网络最后一层 softmax 使用如果设置成sparse输出就是整数标签也完全可以。flow_from_directory的class_indices属性和 ImageFolder 的class_to_idx定位相同同样按字母序生效。这里要提醒一句如果你是第一次拿这套数据集跑 TensorFlow优先用flow_from_directory。虽然新版 TensorFlow 主推image_dataset_from_directory风格更接近 PyTorch 的返回 tensor但很多现成的老教程和迁移学习代码仍然依赖 ImageDataGenerator 的结构。两种方案都能跑通差别不大选一种熟悉的吃透就行。3.3 预处理参数选择为什么 224×224 和减均值是默认起点很多初学者会问为什么蘑菇图片那么大加载时非要 resize 成 224×224。答案和模型预训练生态绑定在一起绝大多数开源分类模型在训练时输入尺寸就是这个规格比如 ResNet 系列、EfficientNet 的 B0 到 B7 里也有很多默认用 224。直接用 224×224 意味着可以套用 ImageNet 上预训练好的权重迁移学习效果远好于从零训练。如果你用的是 Swin Transformer 这类模型它可能要求 224 或 256具体看模型卡上的说明。归一化参数 mean 和 std 直接用预训练权重的配套值这是最保险的做法。PyTorch 里最常见的几组数字就是[0.485, 0.456, 0.406]和[0.229, 0.224, 0.225]对应 ImageNet 统计出来的均值方差。如果换用另一种自己从零训练的模型mean 和 std 应该用训练集的统计值重新计算而不是套用预设。还有一个实际经验蘑菇的识别特征集中在菌盖纹理、菌褶和菌柄形态上如果原始图片分辨率很高而训练资源有限与其直接暴力 resize 到 224 丢掉细节不如先用中心裁剪或随机裁剪把主体区域裁出来再缩放。比如先随机裁0.875比例的区域再缩到 224对细节保真比单纯拉伸好很多。4. 从数据集到跑通训练在线增强、不均衡处理和收敛参数4.1 在线增强配置旋转、翻转之外的三种常用扰动图像数据增强做在线处理也就是训练时实时扰动、验证时不做这个选择本身有充分理由一方面避免了离线生成增大存储占用另一方面每次 epoch 看到的样本都不一样等效训练数据量被放大。蘑菇图像和一般自然图像差别不大常用的扰动方式也比较固定train_transform transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.8, 1.0)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees15), transforms.ColorJitter(brightness0.3, contrast0.3, saturation0.2, hue0.05), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])RandomResizedCrop会在图片上随机裁剪一块区域再缩放到 224用来模拟不同拍摄距离和构图差异scale(0.8, 1.0)表示裁剪区域占原图面积的 80% 到 100%留一部分空白让模型学会应对目标不在正中心的情况。RandomRotation只给了 15 度旋转角度不宜太大因为蘑菇本身有方向性菌盖的朝向过度旋转会引入不自然的样本。ColorJitter控制亮度、对比度、饱和度、色相扰动模拟不同光照和相机白平衡差异。在 Keras 侧这些增强基本都能在 ImageDataGenerator 里通过参数打开例如rotation_range15、width_shift_range0.1、brightness_range[0.8, 1.2]思路相同参数含义一致。增强不是越强越好训练初期可以先用较小的扰动观察 loss 是否正常下降后再逐步加大幅度否则模型可能花很长时间学不到真实特征。4.2 12 类样本不均衡时采样器优先还是 loss 加权优先先看训练集统计结果如果每个类的图片数量差距在 1.5 倍以内可以认为基本均衡不需要做额外处理。如果出现某个类别两三百张、另一个类别只有三五十张的情况则需要介入。处理类别不均衡我一般先改采样器不用急着动 loss因为采样器实现简单且不会改变梯度计算方式。PyTorch 的写法from torch.utils.data import WeightedRandomSampler # 为每个样本分配一个权重与类别样本数成反比 labels train_dataset.targets class_counts torch.bincount(torch.tensor(labels)) weights 1.0 / class_counts.float() sample_weights weights[labels] sampler WeightedRandomSampler( weightssample_weights, num_sampleslen(sample_weights), replacementTrue ) train_loader DataLoader(train_dataset, batch_size32, samplersampler, num_workers4)原理是样本少的类别里每个样本权重高sampler 在每次抽样时被抽到的几率更大训练时一个小类在每个 epoch 里出现的次数变多。replacementTrue允许同一个样本在一个 epoch 内被重复采样。如果用了采样器之后小类准确率仍然偏低再看 loss 加权。CrossEntropyLoss 本身就接收weight参数把每个类别的 weight 设成样本数的倒数或倒数开根号criterion torch.nn.CrossEntropyLoss( weight1.0 / class_counts.float() )采样器和 loss 加权不要同时上两个一起用容易把小类样本过拟合导致验证集上小类准了、大类反而倒退。先用采样器评估一个 epoch不够再切换。这是实践中比较稳的顺序。4.3 小数据训练的参数底线batch、学习率和早停怎么配合12 类蘑菇数据集样本总量通常只有几百到几千张这个规模不适合从零随机初始化训练一个大网络。常见做法是加载预训练权重冻结前几层或全部骨干层只训练最后的分类层或最后几个 block学习率要小。这里给一组我用过多次、起步有效的参数基线batch size 32优化器 Adam 或 SGDmomentum初始学习率1e-4如果冻结了大部分层可以放宽到1e-3epoch 数先设 50配合早停。早停是必要配置因为小数据集很快会过拟合训练 loss 还在降的时候验证 loss 可能已经开始回升from torch.optim.lr_scheduler import ReduceLROnPlateau scheduler ReduceLROnPlateau(optimizer, modemin, factor0.5, patience5) best_val_acc 0.0 patience 0 max_patience 10 for epoch in range(50): # 训练一个 epoch... train_loss run_train_epoch(model, train_loader, optimizer, criterion) # 验证... val_acc evaluate(model, val_loader) scheduler.step(val_loss) if val_acc best_val_acc: best_val_acc val_acc torch.save(model.state_dict(), best_model.pt) patience 0 else: patience 1 if patience max_patience: print(早停触发保存最佳模型) breakReduceLROnPlateau的作用是当验证指标连续 5 个 epoch 不再变好时把学习率减半给训练过程一次“跳出小坑”的机会。早停的 patience 设为 10如果验证准确率连续 10 个 epoch 没有创新高就停止训练。这套机制保证了在无人值守的情况下最终保存下来的模型一定是在验证集上表现最优的那一份而不是最后一个 epoch 的权重。很多翻车现场都发生在“训练完直接保存最后一步权重”上这一步习惯一定不要省。5. 避坑文件夹数据集最容易翻车的五个位置和排查顺序5.1 现象类别字典写了 12 类实际目录只有 11 个文件夹两边的 key 数量对不上通常是下载不完整或解压不完整导致的。如果缺失不严重比如只缺一个类别文件夹但样本量本来就少强行用 11 类训练会导致模型永远不认识那一类蘑菇。排查顺序上先重跑一次 2.3 节的比对脚本确认缺失的具体是哪个类别再去原数据源确认压缩包是否完整、是否需要重新下载。千万别直接改字典文件硬凑成 11 类否则后续做模型发布时类别数写死容易和外部使用方的字典对不上。5.2 现象验证集准确率居高不下但换到真实场景测试明显下滑这类问题最典型的表现是val 准确率 95% 以上test 也表现不错但部署后用户拍一张真实照片识别效果明显变差。优先怀疑两件事一是划分时有没有把同一张图或相似图同时放进 train 和 val 里产生了数据泄漏二是划分脚本本身有 bug把 train 的顺序打乱后val 在内存副本上切结果 val 里混进了几个 train 的样本。解决方法是做图片去重检查。常见做法是对全量图片计算感知哈希然后查 train 和 val 之间是否有哈希相同的图片。5.3 现象加载后标签乱序混淆矩阵里预览的类别对不上买了数据集后自己写了评估脚本的人经常会踩这个坑。字典文件里 class_001 到 class_012 排得好好但 PyTorch 的 ImageFolder 是按文件夹名字母序重新分配索引的Keras 也一样。如果评估脚本里用了固定的“class_001 对应索引 0”这样的硬编码混淆矩阵的热力图会彻底乱掉。解决方法是每次加载后打印class_to_idx或class_indices以打印结果为准建立标签映射尤其注意字母序下 class_010 会排在 class_002 前面这个细节最容易出事。5.4 现象训练 loss 一直不降或者降得很慢如果在冻结骨干、只训练分类层的情况下loss 长时间不降先看数据增强是不是太强。随机裁剪加高旋转角度加颜色扰动叠在一起可能把蘑菇主体的纹理细节都破坏了模型看到的是“加了各种噪声的模糊色块”。我自己会先关掉增强跑一次短训练确认模型能在原始图片上过拟合再把增强逐项加回来。另外一个常见原因是学习率过小冻结层训练用1e-3没问题解冻全网络后用1e-4这和预训练权重的幅度有关。5.5 现象训练时验证集可复现但推理时换一张分辨率完全不同的图效果骤降训练时RandomResizedCrop会在 224×224 的尺度下随机裁剪验证时如果直接用Resize((224, 224))两张图的几何变换规律完全不一致。推理时如果再用另一种方式比如先压缩到 512 再居中裁剪效果又会变化。这个事的本质是所有训练阶段涉及几何变换的操作推理阶段必须保持同一套逻辑。常见做法是统一成“先 resize 到短边 256再中心裁剪 224”或者干脆统一成大图直接 resize。选定一种训练、验证、推理三处全部一致。6. 把这个数据集用透用训练/验证分布差异直接验证划分质量最后分享一个我非常依赖的验证脚本。很多数据集声称已经划分好了但划分质量需要自己确认一遍。这里不看准确率只看两个集合的样本分布是否还保持相似。做法是分别统计每个类别在 train 和 val 中的占比然后计算互信息或直接看比值差。当某个类别在 train 的占比是 20%在 val 里却只有 5%说明划分脚本很可能按文件顺序切分的而没有按类别标签做分层采样这种划分会把类别分布打偏。import os import math def class_ratio(root): total 0 per_class {} for cls in os.listdir(root): path os.path.join(root, cls) if not os.path.isdir(path): continue per_class[cls] len(os.listdir(path)) total per_class[cls] return {k: v / total for k, v in per_class.items()} train_ratio class_ratio(mushroom_dataset/train) val_ratio class_ratio(mushroom_dataset/val) diff {} for cls in train_ratio: diff[cls] abs(train_ratio[cls] - val_ratio.get(cls, 0)) for cls, d in sorted(diff.items(), keylambda x: -x[1])[:5]: print(f{cls}: train {train_ratio[cls]:.4f} vs val {val_ratio.get(cls, 0):.4f})一个合格的划分绝大多数类别的 train 与 val 占比差应该维持在几个百分点内。如果某个类差到 10 个百分点以上就要回原数据源检查预处理方式或者自己重新划分。虽然重新划分耽误时间总好过带着一个不均衡的 val 做模型选择。我自己用过太多“标注好、划分好”的数据集实际踩了坑之后才明白任何号称划分好的数据都需要先由自己这一关验收。这份 12 种蘑菇数据集的价值在于它把最繁琐的活干完了同时它也把最后一个责任交到了使用方手上——套用对了它就是一份能快速帮你跑通全流程的标准数据用错了你再回头查配置、查映射、查划分代价远高于重新整理一份数据。承载这类数据集的关键是流程意识而不是某一个具体网络有多强。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑