简介:医学图像分类数据集,面向医学影像分析与深度学习分类任务,涵盖肾上腺、子宫、甲状腺、食道等19种器官细胞图像,训练集2100张、测试集500张,已按类别分文件夹保存,并附带类别字典JSON文件,可直接用于CNN分类网络或YOLOv5分类模型训练与评估。资源包共2000个文件,以1998张PNG图片为主,另有1个类别字典文件和1个Python可视化脚本,压缩包约260.22MB。目前已有176人学习使用。数据目录结构清晰,训练集与测试集独立存放,便于完成加载、训练和精度对比;show脚本可快速预览样本图像,class_indices.json提供类别索引映射,免去手工整理标签的麻烦。分类类别丰富、样本划分完整,适合医学图像分类入门、课程设计以及分类算法验证,配套相关CSDN专栏还可帮助进一步理解分类网络和YOLOv5分类实现。
1. 医学图像分类数据集:19类器官细胞,拿到手就能训练
做医学图像分类,最烦的不是搭模型,而是找一份能直接用的数据。这份数据集给到了19类器官/细胞图片,训练集2100张、测试集500张,目录按data/train和data/test分好,每个类别一个文件夹,还附带class_indices.json类别字典和可视化脚本。解压之后不需要再写划分代码,直接能喂给yolov5分类项目或者自己写CNN分类网络。我拆过不少所谓"医学数据集",很多都是只有原始图、标签要自己从XML里抠,这份起码把数据源头理顺了。适合做细胞病理识别、器官影像分类的从业者,或者拿真实医学图做baseline的算法工程师,尤其是需要快速验证分类思路、又不想花一星期整理标注的人。
2. 数据集目录结构与类别字典:先看懂 json 再动手
拿到数据集的第一步不是训练,而是把目录和映射文件搞清楚。这套数据本身组织得很规整,train和test各自内部按类别建文件夹,同一类图片放在同一个目录里。这种组织方式在PyTorch的ImageFolder、Keras的flow_from_directory、yolov5的datasets里都是天然支持的,不需要额外写映射表。
2.1 train/test 划分与文件命名规律
解压后你会看到data目录下有两个子目录:训练集、测试集。每个子目录内部再按类别建文件夹,图片命名形如img_Uterus_1_01061.png、img_Pancreatic_1_02328.png。命名结构可以拆成三段:img是固定前缀,中间是类别英文名,后面是序号和编号。这种命名的好处是,后期如果发现某张图有问题,你能直接从文件名反查类别,不用打开图片看。
我建议先跑一段脚本统计每个文件夹的图片数量,确认和描述一致:训练集总量2100,测试集总量500。如果数量对不上,先检查是不是下载过程丢包,或者解压时有文件被系统拦截。
import os data_root = "data" for split in ["train", "test"]: split_path = os.path.join(data_root, split) print(f"=== {split} ===") for cls in sorted(os.listdir(split_path)): cls_path = os.path.join(split_path, cls) if os.path.isdir(cls_path): count = len([f for f in os.listdir(cls_path) if f.lower().endswith(".png")]) print(f"{cls}: {count}")这段代码遍历train/test两个目录,统计每个类别下png图片的数量。注意我加了endswith(".png")过滤,防止目录里混入DS_Store之类的隐藏文件导致计数虚高。如果你后续要把数据转成其他格式,记得同步改后缀。跑完这个脚本,你就能得到一个19类的数量分布表,立刻看出有没有类别特别少、样本特别不均衡的情况。
类别名都是英文标识,比如Uterus是子宫、Skin是皮肤、Pancreatic是胰腺、Kidney是肾脏。数据集说明里提到的肾上腺、甲状腺、食道等,具体文件夹叫什么、有多少个类别,请以json文件为准。千万不要凭文件名猜类别总数,19个类别的实际英文标识很可能和你预想的不一样。如果后续需要重新划分数据集,建议在复制文件时保留原始文件名,因为文件名里已经携带了类别信息,重命名会在数据清洗时增加不必要的麻烦。
2.2 class_indices.json 的读取与映射
class_indices.json是整个数据集里最值得先读的文件。它本质上是类别名到索引编号的映射,训练时模型输出的softmax维度顺序就是按这个json的键值顺序来的。如果这个映射搞错,你训练出来的模型预测结果和真实的类别名会对不上,部署时会出大问题。
用下面的代码直接加载并打印,同时确认json的键值结构:
import json with open("class_indices.json", "r", encoding="utf-8") as f: class_indices = json.load(f) print("类别总数:", len(class_indices)) print("映射内容:") for k, v in class_indices.items(): print(f"{v}: {k}")这里强制指定了utf-8编码,因为json里如果包含中文类别名,在Windows默认的gbk环境下不指定编码会直接抛异常。输出结果是索引到类别名的对应关系。注意,有的json是{类名: 索引},有的是{索引: 类名},打印出来先确认结构,不要想当然地拿v当类别名用。
这个json文件在两类场景下必须引用。第一类是训练时,如果使用PyTorch的ImageFolder,它会按照文件夹名的字母顺序自动生成标签索引,这个索引顺序可能和class_indices.json不一致。所以你需要手动调整数据集类的类别顺序,或者直接用json里的映射重新组织训练数据。第二类是推理时,模型输出的是索引,你必须用这个json把索引翻译回类别名,才能得到"Uterus"而不是"3"这样的结果。
我一般会写一个包装函数,把类别映射加载成一个list,按索引顺序存放类别名,这样推理时直接按索引取值:
import json def load_class_names(json_path): with open(json_path, "r", encoding="utf-8") as f: mapping = json.load(f) # 兼容 {类名: 索引} 和 {索引: 类名} first_key = list(mapping.keys())[0] if isinstance(first_key, int): class_names = [mapping[i] for i in sorted(mapping.keys())] else: class_names = [None] * len(mapping) for name, idx in mapping.items(): class_names[idx] = name return class_names names = load_class_names("class_indices.json") print(names)这个函数的逻辑是先看看key是整数还是字符串:如果key是整数,直接按key排序取值;否则按value作为索引填到对应位置。这样不管json作者用什么风格,你都能得到一份索引有序的类别名list。注意,如果json里类别数和实际文件夹数不一致,函数返回的list长度也会不对,训练前人工核对一遍最稳妥。
3. 用内置 show 脚本可视化:验证图片与标签是否对齐
数据集的干净程度直接决定训练效率。光看目录结构和json还不够,图片和文件夹名对不对得上,必须用肉眼确认。好在资源里带了一个show脚本,专门干这个事。
3.1 show 脚本用法与依赖
show脚本就是一段Python程序,依赖matplotlib和Pillow。先把环境装好:
pip install matplotlib pillow然后运行脚本。多数这类脚本会读取指定目录下的图片,以网格形式展示,常见用法:
python show.py --data data/train --num 8参数--data指定要可视化的目录,--num表示每类显示几张图。不同脚本参数名可能不一样,比如--split或--classdir,先跑一下python show.py --help看看支持什么参数,不要硬猜。如果脚本没有命令行参数,那就直接改脚本里的路径变量。
可视化主要解决三类问题:图片是否损坏(加载时抛异常或显示空白);图片内容和文件夹类别是否匹配(比如Uterus文件夹里混入一张皮肤图,肉眼一眼能看出来);样本是否过于相似或重复(同一个文件夹下很多张图几乎一样,说明原始采集存在重复采样,会影响模型泛化)。
3.2 结果怎么看:先对类别,后对细节
跑完show脚本,屏幕上会显示一个大图,包含多个子图,每张子图左上角通常标着类别名和文件名。先快速扫一遍每个类别是否都是对应器官或细胞的图片,确认大类没问题,再放大看细节。医学图像的标志性结构比较明显,比如皮肤组织的角质层、肾脏的肾小球结构,如果你熟悉这些特征,能很快判断分类是否合理。
如果发现某个类别的图片明显不对,比如文件夹叫Uterus但图片都是肝脏组织,大概率是原始数据打包时放错了。解决方法是打开class_indices.json,确认类别名和文件夹名字一一对应,再把对应的子目录整体检查一遍。图片少就一张张看,图片多就随机抽样看。这里有个经验:不要只检查一个类,要每类都抽几张,尤其是样本数量少的类,往往出错率更高。
如果你觉得matplotlib默认显示窗口太小,看细节费劲,可以把figure的size调大:
plt.figure(figsize=(12, 12))在脚本里找到plt.figure这一行,把figsize参数改大。如果脚本用subplot方式,可以调整子图的排列,让每张图更大一点。用PIL单独看单张图也行,但效率低,不推荐作为主流程。
4. 直接喂给 yolov5 分类模型:数据组织与训练命令
这套数据集最常见的用途就是做yolov5分类训练。yolov5从某版本开始集成了分类训练脚本classify/train.py,它要求的数据目录结构是"根目录/类别文件夹/图片"。这份数据集本身结构就满足要求,只需要把data目录指定给--data参数即可。
4.1 yolov5 分类数据集目录要求
yolov5分类训练时,--data参数可以是一个目录路径,该目录下每个子文件夹对应一个类别。比如:
python classify/train.py --model yolov5s-cls.pt --data data/ --epochs 50 --batch-size 32 --img 224注意,这里的--data是你自己的data目录,它下面必须有train和test两个子目录。yolov5会自动读取train和test,并按照文件夹名称自动生成类别映射。如果你想要单独的验证集,就把测试集当作验证集用,或者自己从train里分出部分图片建一个val目录。
这里有个关键坑:yolov5自动生成的类别顺序是按照文件夹名的字母顺序排列的,可能和class_indices.json里的顺序不一致。如果你训练完成后直接输出类别名做部署,最好在训练前把class_indices.json的映射替换成yolov5生成的映射。怎么得到yolov5的映射?训练时yolov5会把类别名写进训练日志,或者在训练结束后打开runs目录下的结果文件查看。
我更推荐的做法:开始训练前,手动检查数据目录下的类别文件夹列表,按字母排序后和json对照。用下面的命令输出文件夹顺序:
ls -1 data/train/把输出的顺序记录下来,和class_indices.json比对。如果顺序不一致,优先以文件夹顺序为准,因为yolov5内部就是按文件夹名排序的。然后写一个小脚本,按排序后的文件夹名重新生成json,覆盖掉原来的class_indices.json,这样训练和推理用的是同一套映射。
4.2 训练参数设置与结果验证
yolov5分类训练有自己的一套参数,直接套用目标检测的训练参数经常翻车。关键参数有这么几个:
--img决定输入图片尺寸。医学图像很多是显微镜下的细胞图,分辨率高但内容相对简单,用224或256就够了,过大反而增加显存压力。--epochs建议50起步,医学分类数据集小,过拟合风险高,50个epoch足够观察趋势。--batch-size根据显存调整,常见8或16,显存紧张就降到4。
训练命令示例:
python classify/train.py \ --model yolov5s-cls.pt \ --data ./data \ --epochs 50 \ --batch-size 32 \ --img 224 \ --project runs/classify \ --name med19--project和--name用来指定输出目录,跑完会在runs/classify/med19下生成训练曲线和最佳权重。训练完成后,用验证脚本看准确率:
python classify/val.py --data ./data --weights runs/classify/med19/weights/best.pt --img 224这个命令会输出每类的准确率、混淆矩阵等信息。医学图像分类最怕所有类准确率都高但某一类医学上关键的类别偶尔漏检,所以建议多看confusion matrix,而不是只看总的acc。如果发现某一类的召回率明显低于其他类,回第2章统计样本分布,优先处理类别不平衡。
5. 避坑:医学图像分类数据集的五个常见问题
整理一下我实际拆分和训练这套数据时踩过的坑,每条按现象、原因、解决三步写。
5.1 训练损失不降,准确率一直低位徘徊
现象:loss前几个epoch有下降,但后面一直震荡,acc卡在10%左右,甚至接近随机猜测的概率(1/19≈5.26%)。数据集看起来没问题,网络却学不动。
原因:最常见的有两个。第一是类别不平衡,比如某个类别有800张,另一个只有50张,模型偏向学大样本类。第二是学习率设置过高,导致loss在最优解附近反复横跳,小数据集尤其敏感。
解决:先跑统计脚本看每类数量,如果差异超过3倍,就需要重采样。常见做法是使用PyTorch的WeightedRandomSampler,给样本少的类别更高的采样权重。学习率方面,yolov5的默认学习率是针对ImageNet级别的数据量,医学小数据集建议降低一个数量级,比如把lr从0.01降到0.001,或者用余弦退火调度。
5.2 训练过程中报错,提示图片尺寸不一致
现象:某个epoch报错"Expected 3D or 4D tensor"或者"size mismatch",检查代码发现是模型输入尺寸和图片实际尺寸不匹配。
原因:数据集内部图片分辨率不一定统一。有的图片是1024x1024,有的是512x768,如果数据加载器没有统一resize,而模型默认输入是224x224,就会在维度拼接时报错。
解决:在数据加载时强制统一resize。用PyTorch的transforms.Resize((224, 224))最省事,但要注意医学图像的长宽比被改变可能影响诊断特征,更推荐做长边缩放到224后再pad到正方形。在yolov5里直接设置--img 224,它会自动做resize和letterbox处理。
5.3 预测时类别标签对不上
现象:训练完测试,模型输出索引3,按class_indices.json查是"甲状腺",但实际图片看起来像"食道"。或者混淆矩阵里某一类互相错位严重。
原因:训练时yolov5按文件夹字母序生成索引,而class_indices.json的索引顺序不是字母序。推理时用了json里的映射,两者对不上。
解决:训练前先确认yolov5的类别顺序。以文件夹排序为准重新生成json。写一个脚本,遍历train目录下的文件夹名,按排序输出映射,覆盖掉class_indices.json。这样训练和推理用的是同一个映射,就不会错位。我一般会在训练完成后,把模型输出的类别索引和实际文件夹名再核对一遍,用几张图片做盲测。
5.4 测试集和训练集存在重复图片,准确率虚高
现象:测试准确率接近100%,但拿到新图预测准确率骤降。怀疑过拟合,但重训后仍然如此。
原因:数据划分时没有做去重。原始图像可能有相同细胞的不同切片,或者同一张图被重复保存了多次,只是文件名不同。这种重复样本既出现在train又出现在test,会让模型"记住"图片而不是学泛化特征。
解决:对全部图片计算感知哈希(pHash),找出重复项。常用库是imagehash,代码如下:
from PIL import Image import imagehash import os def deduplicate(root_dir): hashes = {} dupes = [] for split in ["train", "test"]: split_path = os.path.join(root_dir, split) for cls in os.listdir(split_path): cls_path = os.path.join(split_path, cls) for fname in os.listdir(cls_path): if not fname.lower().endswith(".png"): continue path = os.path.join(cls_path, fname) try: h = imagehash.phash(Image.open(path)) except Exception: continue if h in hashes: dupes.append((path, hashes[h])) else: hashes[h] = path return dupes dupes = deduplicate("data") print("重复对数量:", len(dupes)) for d in dupes[:10]: print(d)这里用哈希值为键,如果两张图哈希相同说明视觉内容高度相似。跑完把重复的测试图像从test中剔除,或者把重复样本全部只保留一份。对于医学图像,完全相同的图很少,但非常相似的切片会很多,所以阈值可能需要调高。imagehash的phash默认是64位,先用它扫一遍,如果发现没有重复,再换用dhash等其他方法。
5.5 读取class_indices.json乱码或者键值顺序错乱
现象:在Windows下用Python读json,打印出来中文类别名变成乱码,或者键值顺序和预期不一致。
原因:json文件可能是UTF-8编码,但Windows默认控制台编码是GBK,直接print中文就会乱码。键值顺序错乱则是因为旧版本Python中dict不保证插入顺序,或者手动排序时搞混了。
解决:读取时强制指定encoding="utf-8",打印时先设置环境变量PYTHONIOENCODING=utf-8。如果不想改环境变量,就在代码里不要依赖字典顺序,而是按照索引遍历。第2章给出的load_class_names函数已经兼容了两种映射风格,建议直接复制使用。
这些坑看起来零碎,但任何一个都能让你在训练后多花两三天排错。数据集本身是干净的,问题往往出在数据加载和标签映射环节,所以每次新拿一个数据集,我都会先跑一遍去重和映射检查,再上模型。
6. 最后一个技巧:写一个轻量数据加载器,把数据集接进自定义CNN
如果你不用yolov5,而是想自己搭一个CNN分类网络,PyTorch的数据集类怎么写也很关键。这里给出一个可以直接套用的torch Dataset实现,它同时处理了标签映射、图片读取、数据增强三个问题。
import torch from torch.utils.data import Dataset from PIL import Image import os class OrganCellDataset(Dataset): def __init__(self, root_dir, class_indices, transform=None): self.samples = [] self.class_indices = class_indices # {类名: 索引} self.transform = transform for cls_name, cls_idx in class_indices.items(): cls_path = os.path.join(root_dir, cls_name) if not os.path.isdir(cls_path): continue for fname in os.listdir(cls_path): if not fname.lower().endswith(".png"): continue self.samples.append((os.path.join(cls_path, fname), cls_idx)) def __len__(self): return len(self.samples) def __getitem__(self, idx): path, label = self.samples[idx] image = Image.open(path).convert("RGB") if self.transform: image = self.transform(image) return image, label这个加载器的逻辑是从json里拿类别名到索引的映射,然后去root_dir下找对应的文件夹。好处是标签顺序完全由json决定,不会出现yolov5那种按字母序排序的问题。transform参数留出来,你在外面定义自己的预处理和增强链,比如:
from torchvision import transforms transform_train = transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(15), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) dataset = OrganCellDataset("data/train", class_indices, transform=transform_train)这里用了ImageNet的mean和std做归一化,对医学图像来说虽然不是最精确的统计值,但因为迁移学习用的预训练权重大多在ImageNet上训练,沿用这组参数能保持特征分布一致性。如果从零训练,建议先统计自己数据的mean和std再归一化,能略微提升收敛速度。
数据增强里我只用了水平翻转和随机旋转,没有用随机裁剪,因为医学图像里细胞的位置和形态本身有诊断意义,过度随机的crop可能把关键区域截掉。如果要做更激进的数据增强,建议用albumentations的弹性形变,对细胞图像来说更符合真实变形情况,但不要同时叠加旋转加裁剪,容易破坏结构。
验证模型时,我养成了一个习惯:每次训练完,挑5张训练图、5张测试图,用模型预测并打印对应类别名,人工看一眼预测结果。这个动作看着糙,但能一次性发现标签映射错位、数据增强过头、过拟合三个问题。从那以后我每次新拿到分类数据集,都强制走一遍"查数量→看json→跑show脚本→去重→小步训练盲测"这个流程,省掉了后面反复返工的几小时。希望帮到你。
本文还有配套的精品资源,点击获取