
简介面向医学图像分类任务推出的8种微生物识别数据集涵盖阿米巴、眼虫属、水螅、草履虫等8个类别可用作yolov5分类分支或常见CNN分类网络的训练与验证数据。资源已按文件夹划分好训练集与测试集两个目录分别存放同一类别的图片训练集共630张测试集共150张同时附有JSON类别字典方便查看每个类别的名称与映射关系搭配预留的show.py脚本可快速可视化样本检查数据增强或划分效果。该压缩包共含792个文件以jpg图片为主另有png、jpeg格式样本、1个py脚本和1个json配置文件zip包整体约101.99MB下载解压后即可直接开始实验。对于需要现成微生物数据集的算法研究者、学生或工程师资源能省去自行采集、整理和划分数据的步骤更专注于模型调参与效果验证同时也可作为医学图像分类教学实验、课程设计或毕业课题的基础数据用于迁移学习、模型对比或分类精度测试。目前已有177人学习使用在医学图像分类和yolov5分类应用场景中具有较高实用价值。1. 一套8种微生物图像识别数据集省掉最脏的活直接进模型训练医学图像分类里微生物图像识别是病理检验、水质监测、食品卫生筛查里绕不开的一步。很多团队卡在起步阶段不是模型不会写而是数据没整理图片散落、命名混乱、标签对不上光打磨数据就要耗掉两周。这个数据集把最脏的活干完了——8种微生物图像已经按训练/验证/测试分好每个类别一个文件夹还附带类别字典文件。拿到的第一件事不再是“洗数据”而是把字典和文件夹对应起来、跑通数据加载。适合三类人刚动手做医学图像分类的学生、需要快速验证迁移学习方案的算法工程师、以及想把微生物识别从 demo 推向实际检验流程的团队。这套数据给人的第一印象是“小”8个类别的体量不会让你产生分布式训练的冲动但恰恰因为小数据加载、标签对齐和训练流程都能在一台普通 GPU 上完整跑通适合当医学图像分类的起手式。2. 文件夹结构、划分口径与类别字典先理顺数据再谈训练拿到这套数据第一步不是急着写训练脚本而是把“数据长什么样”完整盘查一遍。这个数据集的核心资产有两个按文件夹划分好的 train/valid/test 三个子集以及一份类别字典文件。这两样东西决定了后续所有动作——数据加载、损失函数计算、指标统计、结果可视化全都围绕它们展开。先花半小时把目录结构、样本数量、字典格式和潜在的数据串扰查清楚后面能省出一整天排错时间。2.1 先看文件树train/val/test 的目录约定与样本统计数据集的目录设计沿用了 ImageFolder 约定根目录下按 train、valid、test 三个文件夹划分每个文件夹内部再按类别建子文件夹同类图像归属到同一个目录。这是 PyTorch 和 Keras 都能直接识别的标准布局不需要额外写解析逻辑。先跑一条命令看全貌# 假设数据集解压后叫 microbe8 tree -L 2 --dirsfirst microbe8/预期能看到类似下面的结构根目录三个 split每个 split 下面 8 个类别文件夹。这里第一件要确认的事是三个 split 里的类别文件夹名字是否完全一致。如果 valid 里少了一个类训练时模型没见过这个类的验证样本最后的指标报告就会缺行医学场景里这叫漏诊不能接受。import os from collections import Counter root microbe8 for split in [train, valid, test]: split_path os.path.join(root, split) # 遍历每个类别文件夹统计图片数量 counter Counter() for class_name in os.listdir(split_path): class_dir os.path.join(split_path, class_name) if os.path.isdir(class_dir): n len([f for f in os.listdir(class_dir) if f.lower().endswith((.jpg, .jpeg, .png))]) counter[class_name] n total sum(counter.values()) print(f[{split}] 共 {total} 张) for cls, n in counter.most_common(): print(f {cls}: {n})这段统计脚本的价值有两个第一确认每个 split 的类别数都是 8第二看出每类的样本量分布。微生物数据集极少是均衡的——某几种常见菌图片多稀有菌种可能只有几十张。这个分布直接决定第四章要不要做类别重采样。如果某一类样本数只有其他类的十分之一模型大概率会把它学成“背景类”这是后面避坑章节的重点。2.2 类别字典文件怎么读JSON、TXT、Pickle 三种格式类别字典文件在不同数据集里长得五花八门。常见形态有三种JSON 对象、TXT 两列文本、Pickle 序列化对象。字典内容一般是“类别名”和“数字索引”的映射。模型训练时只认识 0 到 7 的下标可最后要输出“这是念珠菌”没有字典就只能靠猜。提供一个能同时兼容三种格式的读取函数省得每次换数据集重写一遍import json import pickle from pathlib import Path def load_class_dict(path): path Path(path) if path.suffix .json: with open(path, r, encodingutf-8) as f: data json.load(f) elif path.suffix in (.pkl, .pickle): with open(path, rb) as f: data pickle.load(f) elif path.suffix .txt: data {} with open(path, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 2: if parts[0].isdigit(): data[parts[0]] parts[1] else: data[parts[1]] parts[0] else: raise ValueError(f不支持的字典文件格式: {path.suffix}) # 如果字典是 {名称: 索引}统一转成 {索引: 名称} first_key next(iter(data)) if not str(first_key).isdigit(): data {str(v): k for k, v in data.items()} return data class_dict load_class_dict(microbe8/class_dict.json) for idx, name in sorted(class_dict.items(), keylambda x: int(x[0])): print(f索引 {idx} - {name})逻辑说明函数先按扩展名分流解析再判断字典的正反方向。next(iter(data))取第一个键如果第一个键不是数字说明字典是“名称到索引”方向反转成“索引到名称”。这么多此一举是因为有的作者喜欢把类名当键有的喜欢把索引当键而后面 ImageFolder 生成的映射永远是“名字到索引”统一方向才能做对齐比较。注意 TXT 解析里默认一行两个字段用空白分隔遇到分隔符是逗号或 Tab 的情况把split()换成split(,)或split(\t)即可。2.3 划分前的数据体检哈希查重与类别集合比对医学图像数据集最容易犯的错是数据泄漏。典型场景是同一个培养皿被拍了多张照片其中一张进了 train另一张进了 valid模型在验证集上“见过”这个样本的不同视角指标虚高一放到真实检验流程立刻翻车。先做像素级查重import hashlib from pathlib import Path def file_md5(path): h hashlib.md5() with open(path, rb) as f: for chunk in iter(lambda: f.read(8192), b): h.update(chunk) return h.hexdigest() hash_map {} for split in [train, valid, test]: split_path Path(fmicrobe8/{split}) for img_path in split_path.rglob(*): if img_path.suffix.lower() in (.jpg, .jpeg, .png): d file_md5(img_path) hash_map.setdefault(d, []).append(str(img_path)) duplicates {k: v for k, v in hash_map.items() if len(v) 1} print(f完全重复图片组数: {len(duplicates)}) for md5, paths in list(duplicates.items())[:5]: print(md5, paths)这段代码用 MD5 对文件内容做指纹把每个 split 下所有图片的哈希收进字典。哈希相同的路径列表长度大于 1说明同一张图出现在多个 split 里必须手动挪走。需要提醒的是像素级查重只解决“同一张图”的泄漏如果医学影像来源是把一张大图切成若干 patch相邻 patch 之间高度相似但 MD5 不同这种语义级泄漏 MD5 抓不到只能靠人工抽查或按来源样本 ID 重新划分。所以拿到数据后我一般会和作者确认划分粒度是按“图片”还是按“样本/培养皿”这个信息比任何代码都重要。3. 用 PyTorch 按文件夹结构加载 8 类医学图像最小可运行脚本数据体检没问题就可以进加载环节了。PyTorch 的torchvision.datasets.ImageFolder天生就是为“按类别分文件夹”的数据集设计的不需要手写 Dataset。但 ImageFolder 有一个隐藏行为它会按照文件夹名字母序自动生成类别索引。如果类别字典的顺序和字母序不一致索引对不上整个训练都是白做。这一章就把加载脚本和索引对齐分两步讲清楚。3.1 用 ImageFolder 加载 8 类微生物图像最小可运行脚本import torch from torch.utils.data import DataLoader from torchvision import datasets, transforms # 训练集变换resize 到 224做轻量几何增强 train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees15), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_dataset datasets.ImageFolder( rootmicrobe8/train, transformtrain_transform ) train_loader DataLoader( train_dataset, batch_size32, shuffleTrue, num_workers4, pin_memoryTrue ) # 打印 ImageFolder 自动生成的类别映射 print(train_dataset.classes) print(train_dataset.class_to_idx)参数说明Resize((224, 224))是迁就 ImageNet 预训练模型的默认输入尺寸如果你打算用 EfficientNet 或 ViT按对应模型的推荐分辨率改。RandomHorizontalFlip和RandomRotation属于几何增强微生物图像没有“上下颠倒”的语义旋转 15 度以内是安全的。num_workers4一般按 CPU 核数设置开太高在小数据集上反而增加进程调度开销。pin_memoryTrue在 GPU 训练时能减少主机到设备的数据拷贝时间。注意ImageFolder输出的classes是字母序列表打印出来核对一遍这是整个流程里最便宜的一次检查。3.2 与类别字典文件对齐顺序不一致是最隐蔽的翻车点ImageFolder按字母序编号而数据集作者写类别字典时往往是按自己的顺序排的通常与字母序不一致。两者一旦错位模型训练时以为的“索引 3”实际是文件夹里的另一个类训练依然能收敛准确率看着也不差但预测结果和真实类别彻底错乱。这是分类任务里最可怕的错误——指标不会报警只有部署上线后才发现标签全拧了。写一段对齐检查import json with open(microbe8/class_dict.json, r, encodingutf-8) as f: class_dict json.load(f) # 把字典转成按索引升序排列的类名列表 dict_classes [class_dict[str(i)] for i in range(len(class_dict))] # ImageFolder 生成的 classes 是按文件夹名字母序排序的 folder_classes list(train_dataset.classes) print(字典顺序:, dict_classes) print(文件夹顺序:, folder_classes) print(是否一致:, dict_classes folder_classes) if dict_classes ! folder_classes: # 常见做法不改文件夹名直接覆写 dataset 的 classes 与 class_to_idx train_dataset.classes dict_classes train_dataset.class_to_idx { cls: idx for idx, cls in enumerate(dict_classes) }这段代码先按索引排序把字典展开成列表再与ImageFolder的字母序列表硬比较。不一致时覆写classes和class_to_idx是最省事的修正方式不需要重命名文件夹也不影响后续读取。这里有个细节覆写之后要立刻用一个 batch 的dataset.targets和图片文件名做交叉验证确认修正真的生效了。我习惯把这种检查写成一个独立脚本存下来每次换机器、换环境重新跑一遍属于“后悔药”级别的保险。3.3 数据增强怎么设微生物图像的染色特征不能乱动自然图像数据集的增强套路是“怎么花哨怎么来”但微生物图像完全是另一套逻辑——染色颜色本身就是诊断特征。革兰氏阳性菌染出来是紫色阴性菌是红色念珠菌有典型的菌丝形态。如果用ColorJitter把色相、饱和度乱调模型会把“颜色不对”的样本学成另一个类或者在真实检验时对染色差异过度敏感。把增强策略收敛一点效果反而更好增强操作是否使用说明水平翻转建议开显微成像方向无生理学意义随机旋转15~30度建议开培养皿放置角度有随机性随机裁剪缩放小范围可用微生物目标偏小过度裁剪会切掉关键形态色彩抖动 / 亮度对比度谨慎或关闭染色深浅是诊断依据动不得高斯噪声可少量加模拟不同显微镜的噪点差异旋转角度我一般给到 15 度而不是 30 度因为某些细菌的杆状形态在极端旋转后会混淆。亮度对比度如果一定要加幅度控制在 0.05 以内只模拟光照不均不改颜色倾向。验证集和测试集的 transform 只做Resize ToTensor Normalize不做任何增强保证指标可比。数据增强的度用一个词概括就是“克制”。4. 训练医学图像分类模型类别不均衡处理与三个必调参数数据能正确加载训练就是按部就班的事。但微生物图像数据集有个绕不开的麻烦——类别不均衡。常见菌种样本多稀有菌种样本少模型天然倾向于把不确定的样本判给多数类。这一章先讲怎么数清分布再给一套完整的加权采样接法最后把三个必调参数和评价指标说透。4.1 先数数微生物数据集极少是均衡的第二章的统计脚本已经给出了每个类别的样本数这里直接复用结果。假设统计发现“大肠杆菌”有 800 张而某一种弧菌只有 60 张比例超过 10 倍。这时候如果直接拿原始分布训练模型对弧菌的召回率会低到令人发指但总准确率依然有 90% 以上——因为弧菌样本占比小全判错也只影响几个百分点。这就是为什么准确率在医学图像分类里常常是骗人的指标。遇到这种分布第一选择是加权采样而不是删除多数类样本。删数据在医学场景里是大忌本来就稀缺的标注样本丢掉就再也找不回来。4.2 三个必调参数输入尺寸、batch size 和学习率参数建议值调参逻辑输入尺寸224×224 起步微生物目标小可试 448×448尺寸越大保留的形态细节越多但显存和训练时间翻倍batch size16~64显存够就 64不够就减半太小会导致 BN 统计不稳定学习率迁移学习 1e-4~1e-3随机初始化 1e-3~1e-2预训练模型微调用小学习率别动主干前几层Epoch 数20~50配合早停医学小数据 50 epoch 内基本收敛多跑容易过拟合输入尺寸是这组参数里最容易犯错的。很多微生物图像里目标只占整幅图的 20%直接Resize(224)会把形态细节糊成一团。我一般的做法是先用 224 跑通流程再切成 448 对比验证集指标涨点就保留不涨就退回。batch size 和学习率要做联动batch size 加倍时学习率通常也要跟着加倍这是线性缩放法则的粗略版。早停 patience 设在 5~8 个 epoch验证集 loss 连续不降就停能省下一半的训练时间。4.3 类别不均衡的接法WeightedRandomSampler 完整示例加权采样是最直接的解决手段——让稀有类每个 epoch 被抽到的次数增多。它不改变数据集本身只在采样环节做手脚import numpy as np from torch.utils.data import WeightedRandomSampler # 从 dataset 里拿标签转成 numpy 数组 targets np.array(train_dataset.targets) # 统计每个类别的样本数 class_counts np.bincount(targets) print(每个类别的样本数:, class_counts) # 每个样本的权重 该类别样本总数的倒数 class_weights 1.0 / class_counts weights class_weights[targets] sampler WeightedRandomSampler( weightsweights, # 每个样本被采样的概率权重 num_sampleslen(weights), # 每个 epoch 抽样总数 replacementTrue # 允许重复采样稀有类会被反复抽到 ) train_loader DataLoader( train_dataset, batch_size32, samplersampler, # 用了 sampler 就不能再开 shuffle num_workers4, pin_memoryTrue )WeightedRandomSampler的核心逻辑是每个样本的权重等于它所在类别样本数的倒数。多数类样本权重小被抽中的概率低稀有类权重大每个 epoch 都被反复补习。replacementTrue是关键它允许同一张图在一个 epoch 里出现多次这是稀有类“被看到”的保证。注意sampler和shuffleTrue互斥传了 sampler 就得把 shuffle 关掉这个细节写错会直接报参数冲突。如果不想改采样器等价方案是用带weight参数的CrossEntropyLoss两类方法效果接近但加权采样不改损失函数对后续调试更友好。4.4 评价指标别只盯准确率医学图像里敏感度更值钱验证阶段只打印 accuracy 是远远不够的。医学图像分类的实际诉求是“别漏掉阳性样本”也就是召回率敏感度。一个把 70% 阳性判成阴性的模型准确率再高也不能用。用sklearn一键输出完整报告from sklearn.metrics import classification_report # preds 和 labels 分别是验证阶段收集的所有预测与真实标签 # target_names 用第二章对齐好的类别名称列表 report classification_report( labels, preds, target_namesdict_classes, digits4 ) print(report)classification_report会逐类输出 precision、recall、F1 和支撑样本数。看报告时的重点不是平均值而是逐类对比哪一类的 recall 低于 0.8哪个类的 support 小但 precision 飘忽不定这些才是后续要针对性处理的点。医学场景里recall 的价值通常高于 precision——漏诊的代价远大于误报。如果两类真菌的 F1 都很差大概率不是超参问题而是它们形态太像需要回溯检查训练集里这两类是否混入了错误标注。5. 避坑与排查微生物图像数据集常见的 5 个翻车现场这部分是血泪经验。数据和加载没问题之后训练和评估阶段还有五个高频坑每个我都踩过或帮人排过按“现象 → 原因 → 解决”展开。5.1 训练 loss 中途变 NaN先从全黑样本和超参入手现象训练前几个 epoch 正常中途 loss 突然变成 NaN之后一直回不来。原因常见的不是学习率过大而是数据里混进了异常图片——全黑图、纯色图、损坏的 JPEG。这些图片经过 Normalize 后像素值极端前向传播产生超大梯度直接把权重推向数值溢出。微生物数据集里常有显微镜对焦失败拍出来的全黑图像肉眼扫目录根本看不出来。解决先用脚本过滤掉像素标准差接近 0 的图片再降低学习率重跑。过滤脚本的核心逻辑是PIL.Image.open()后转灰度计算np.std(img)标准差小于阈值的直接移到excluded/文件夹。如果过滤后仍出现 NaN把初始学习率从 1e-3 降到 1e-4并在优化器上加clip_grad_norm_梯度裁剪兜底防溢出。5.2 模型全程只输出一个类类别不均衡的典型症状现象训练 20 个 epoch验证集准确率稳定在 70% 左右但打印混淆矩阵发现所有样本都被判成了同一个多数类。原因数据集类别不均衡且没有做任何处理。模型发现只要全判成“大肠杆菌”就能拿到很高的正确率梯度也倾向于把决策边界推向稀有类一侧稀有类的梯度信号被淹没。解决按 4.3 的WeightedRandomSampler重采样或者给CrossEntropyLoss传入class_weights。改完之后观察稀有类的 recall 是否从 0 开始爬升。如果两种方法都试了还没改善检查是不是数据泄漏导致多数类和稀有类的特征高度重合回到第二章的查重和样本来源核查。5.3 验证集很好看、现实一测就崩大概率是数据泄漏现象验证集准确率 95%模型部署到新的检验图像上准确率掉到 60%完全不可用。原因数据划分时把同一来源的相似图像切到了不同 split。MD5 查重查不出“同一培养皿的不同视角”因为每张图的像素都不完全一样但背景纹理、染色批次、拍摄设备的光照条件高度一致模型学到了这些“环境特征”而不是微生物本身的形态。解决先用 MD5 查重排除像素级重复再从 train、valid、test 各抽 20 张图人工看背景是否来自同一批设备。如果是必须按样本来源重新划分。凡是目标检测或分类数据集划分粒度要尽量以“来源样本”为单位而不是以“图片”为单位。5.4 字典说 8 类、文件夹却有 9 个隐式目录清理现象加载时报错Found 9 classes instead of 8但明明确认过数据集是 8 类微生物。原因解压后文件夹里混入了非图片子目录常见的是 macOS 的__MACOSX、Windows 的Thumbs.db、或者作者打包时残留的LICENSE、README目录ImageFolder会把所有子文件夹都当成一个类。解决在加载前做一次目录清理过滤白名单外的文件夹。顺手写一个通用函数按文件名判断目录下是否只有图片def clean_non_image_dirs(root): image_exts {.jpg, .jpeg, .png, .bmp, .tif, .tiff} for dirpath, dirnames, _ in os.walk(root): keep [] for d in dirnames: d_full os.path.join(dirpath, d) # 只保留包含图片文件的子目录 if any(os.path.splitext(f)[1].lower() in image_exts for f in os.listdir(d_full)): keep.append(d) dirnames[:] keep clean_non_image_dirs(microbe8)这段代码就地过滤掉不含任何图片的子目录dirnames[:] keep会同时影响os.walk后续的递归行为确保不会深入那些垃圾目录。注意它修改的是传入目录的dirnames变量不是删除文件所以是无损操作。5.5 灰度图读取报错或 Normalize 后图像异常通道数必须统一现象训练时ToTensor()之后图像张量形状是[1, H, W]而不是[3, H, W]模型第一个卷积层就报通道数不匹配。原因微生物染色图像有的以灰度模式存储ImageFolder读进来是单通道而预训练模型的输入层固定要求三通道。解决在 transform 最前面加一步通道统一用lambda x: x.convert(RGB)强制灰度转三通道。灰度转 RGB 不会丢失信息只是把单通道复制三份模型会学到三份同样的数值等价于原始灰度输入。这比读图后手动np.stack要省事。6. 把准确率变成可信度混淆矩阵、置信度阈值与模型存档细节模型训练完别急着丢给业务方。先回答三个问题哪两类最容易被混淆、置信度阈值设在多少最合理、模型文件里有没有带上类别信息。这三个问题决定了一套数据集方案能不能真正落地。先用混淆矩阵定位“分不清的那对类”——微生物里常见的是两种真菌或两种杆菌形态相似混淆矩阵一眼就能看出来。接着给预测逻辑加一个置信度阈值默认 0.5但在医学场景里我习惯把阈值调低到 0.3 左右牺牲一点 precision 换回更高的 recall凡是置信度低于阈值的样本一律标为“待复核”交给人工二次确认。最后存档时把类别字典和模型权重存在同一个文件里torch.save({ model_state: model.state_dict(), class_dict: dict_classes, # 对齐后的类别名称列表 threshold: 0.3, # 部署时的判定阈值 }, microbe8_classifier.pt)这样部署方加载模型时字典从文件里直接读不用再找 json也就彻底避免了“标签索引错位”的隐患。用这套数据集时我最后悔的一件事就是第一次训练时没做索引对齐检查模型训了 8 个小时准确率 92%结果发现预测标签和真实类别完全是错位的等于白训。从那以后所有带类别字典的数据集加载完第一件事永远是先打印三行字典顺序、文件夹顺序、是否一致。这套数据本身就是为“少踩坑”设计的但我建议你还是保留这个习惯能一直用到自己的业务数据上。希望帮到你。本文还有配套的精品资源点击获取