
简介这是一份面向医学图像识别与皮肤病智能诊断方向的图像分类数据集包含23种常见皮肤病类别覆盖湿疹、肿瘤、真菌感染等典型病理表现。数据按train/test目录清晰组织训练集15557张、测试集4002张总量近两万张可直接通过ImageFolder加载也可作为YOLOv5分类模型的训练输入无需额外清洗或格式转换。压缩包共2000个文件其中1998个为jpeg皮肤病灶图像另附1个json类别字典和1个python可视化脚本脚本内置随机取图逻辑运行后自动展示4张图片并保存可视化结果便于开发者快速检查数据分布与标注质量。整体包体大小约933.7MB目录结构规范数据完整度较高。目前已有531人学习下载适合医学影像分析方向的学生、算法工程师及AI医疗产品开发者作为分类模型训练与算法验证的基准数据集。1. 23种皮肤病分类数据集为什么它是「下载当天就能跑 Baseline」的那一份做医学图像分类的人多数都被同一件事卡过数据集拿到了但清理和整理要花掉两三天。这份 23 种皮肤病分类数据集是我拆过的资源里少见的「省心型」——967 MB 总大小train 目录下 15557 张图test 目录下 4002 张图23 个类别按文件夹分好直接用 torchvision 的 ImageFolder 就能读连额外的标注文件都不需要整理。对于正在做皮肤病变识别 Baseline、课程设计或者想把分类模型往 YOLO 系列迁移的从业者来说这套数据的价值恰恰在于它把「数据工程」这一步压缩到了几乎为零。2. 先读透目录结构23 类标签由文件夹隐式定义15557 张图按 split 分布很多人拿到数据集第一件事就是写 Dataset 类这其实把顺序搞反了。ImageFolder 这类读取方式要求数据按「根目录/类别文件夹/图片」三层组织这套数据恰恰是按这个约定存的所以第一步应该是把目录结构读透搞清楚类别名、拆分比例和索引映射规则而不是着急上模型。2.1 目录即标签ImageFolder 的索引机制与 json 字典的配合数据集的目录组织方式可以简单理解为下面这个结构data/ ├── train/ │ ├── eczema/ │ ├── tumor/ │ ├── herpes-zoster/ │ └── ... └── test/ ├── eczema/ ├── tumor/ ├── herpes-zoster/ └── ...每一类皮肤病对应一个文件夹文件夹名就是标签。torchvision 的 ImageFolder 在初始化时会遍历根目录下的子文件夹按字母序生成 class_to_idx 映射。比如 eczema 排在前面的目录索引就是 0tumor 可能就是 1具体顺序取决于字母排序结果。这里有个容易被忽略的细节数据集中附带了一份 23 种分类的字典 json 文件它的作用是把索引和类别名做显式映射。但 ImageFolder 的索引是字母序生成json 里的索引可能按疾病归类顺序排列两者未必一致。所以正确做法是在加载后先打印 dataset.classes以实际输出为准再配合 json 做类别名映射避免标签错位。对比一下三种常见的数据集组织形式就能明白为什么这套数据省事组织形式标签来源需要预处理适用场景文件夹即标签目录名几乎为零分类任务快速验证、YOLO 分类训练CSV 标注文件名与类别列需要写 Dataset 映射标签带附加属性年龄、部位JSON/COCO 标注实例坐标与类别 id需要解析和过滤目标检测、实例分割表格里 CSV 和 JSON 方案虽然灵活但对入门者来说是额外的黑匣子——一旦标注文件和图片对不上排查起来非常痛苦。文件夹方案虽然笨但胜在肉眼可见、出错率低这也是为什么很多人第一次跑皮肤病分类都会选这种数据结构。2.2 训练/测试的 15557/4002 划分先跑一个类分布检查再动手train 总数 15557、test 总数 4002加一起 19559 张图23 个类别。拆开算一下训练集每类平均约 676 张测试集每类平均约 174 张。但「平均」这两个字往往掩盖了真实分布——皮肤病数据集的采集通常来自不同患者、不同病程阶段类别之间的样本数可能差距很大。我拿到这类资源后的第一个固定动作不是直接写训练脚本而是先跑一个类分布统计确认资源描述里的数字和本地文件实际数量一致。import os from collections import Counter data_root data for split in [train, test]: split_path os.path.join(data_root, split) classes [d for d in os.listdir(split_path) if os.path.isdir(os.path.join(split_path, d))] counter Counter() for cls in classes: cls_path os.path.join(split_path, cls) counter[cls] len([ f for f in os.listdir(cls_path) if f.lower().endswith((.jpg, .jpeg, .png)) ]) print(f[{split}] 类别数: {len(counter)}) print(f[{split}] 图片总数: {sum(counter.values())}) print(f[{split}] 最少类别: {counter.most_common()[-1]}) print(f[{split}] 最多类别: {counter.most_common()[0]})这段代码的思路很简单遍历 train 和 test 两个目录分别统计子文件夹数量、图片总数和每个类别的样本数。Counter 的 most_common()[-1] 取的是样本最少的类most_common()[0] 是样本最多的类。跑完后有几个地方要重点看第一train 总数是否是 15557如果不是说明下载过程可能有图片丢失第二最少类和最多类的数量差距是否在一个数量级内第三类别名是否包含空格或特殊字符这会影响后续 YOLO 训练时的标签解析。我一般会把输出结果存成 CSV 留档训练完看分类报告时再对照就能区分是数据问题还是模型问题。3. 图片预览与加载验证把可视化脚本扩展成四步体检流程数据分布检查只是第一步图片内容本身也得看。一般数据打包者会提供一个可视化脚本这套数据里附带的 py 脚本功能是随机传入 4 张图片展示并保存到当前目录。顺着这个思路可以把它扩展成一个更完整的「图片体检」流程先随机抽图看内容再全量扫描坏图确保数据在进模型之前是干净的。3.1 随机抽 4 张图片可视化脚本的可复用改法对应需求「随机传入 4 张图片即可展示并且保存在当前目录」拆解一下核心逻辑从类别列表中随机选一个类别再从该类别目录里随机选一张图片凑够 4 张后拼成一行用 matplotlib 展示。脚本的关键点在于随机策略和保存路径。import os import random import matplotlib.pyplot as plt from PIL import Image data_root data/train classes [d for d in os.listdir(data_root) if os.path.isdir(os.path.join(data_root, d))] # 方式一允许同一类别重复出现更接近真实数据分布 samples [] for _ in range(4): cls random.choice(classes) cls_dir os.path.join(data_root, cls) img_name random.choice(os.listdir(cls_dir)) img_path os.path.join(cls_dir, img_name) samples.append((cls, img_path)) fig, axes plt.subplots(1, 4, figsize(16, 4)) for ax, (cls, path) in zip(axes, samples): img Image.open(path) ax.imshow(img) ax.set_title(cls, fontsize10) ax.axis(off) plt.savefig(skin_preview.png, dpi150, bbox_inchestight) print(已保存 skin_preview.png)这里有一个值得注意的随机策略差异random.choice(classes) 循环 4 次可能出现 4 张图来自同一个类别的情况如果希望一次预览覆盖 4 个不同类别应该改用 random.sample(classes, 4)。前者适合模拟真实采样分布后者适合快速浏览类别差异。保存时用 bbox_inchestight 可以避免标题被裁掉dpi150 足够看清皮肤病变纹理。跑完之后如果发现某类图片明显模糊、过暗或混入非皮肤图像就应该回到上一步用第 2.2 节的统计脚本确认该类图片数量再决定是否要在训练时单独处理。3.2 坏图扫描加载前把 train/test 里的损坏 JPEG 都揪出来图片预览能看到内容质量但有一种问题是肉眼发现不了的文件后缀是 .jpg实际数据不完整。这类「截断图片」在加载时才会抛异常而且往往不是第一张就报错而是在训练中途某个 epoch 突然中断非常恼人。我习惯在正式训练前对 train 和 test 做一次全量识别用 PIL 的 verify() 方法校验文件头和解码信息。import os from PIL import Image for split in [train, test]: split_root os.path.join(data, split) bad_images [] for cls in sorted(os.listdir(split_root)): cls_dir os.path.join(split_root, cls) if not os.path.isdir(cls_dir): continue for f in os.listdir(cls_dir): fpath os.path.join(cls_dir, f) if not f.lower().endswith((.jpg, .jpeg, .png)): continue try: with Image.open(fpath) as img: img.verify() except Exception: bad_images.append(fpath) print(f[{split}] 损坏图片数量: {len(bad_images)}) for fp in bad_images[:10]: print(fp)verify() 是 PIL 提供的校验接口它会解析图片的头结构和编码信息但不会把整张图读入内存所以扫描 19559 张图的速度很快。注意一个细节verify() 执行后文件句柄会被关闭后续如果要真正加载这张图做进一步判断需要重新 Image.open()。这段脚本跑完如果 bad_images 不为空就把这些文件移到一个 backup 目录而不是直接删除防止误删后无法找回。到这一步目录结构、类分布、内容质量都验证过数据才算是真正「可训练」了。4. 实际训练中的四个坑类别不平衡、脏图、测试集泄漏与易混类数据体检做完进入训练阶段。但皮肤病分类和通用物体分类有个显著差异类别之间视觉相似度高、样本分布天然不均衡。以下四个坑是我在类似数据集上实际遇到过的每条都按「现象 → 原因 → 解决」写清楚给后来者一点参考。4.1 类别不平衡别被 0.82 的总准确率骗了现象训练 20 个 epoch 后验证集总准确率到了 0.82看起来不错但打开分类报告发现某些类别 recall 是 0预测结果里几乎看不到这几个类别的身影。原因23 个类别的样本量差异大是皮肤病数据集的常态。某个类只有 100 来张图而常见类有上千张模型在梯度下降过程中会被多数类主导少数类的特征被「淹没」。总准确率是加权平均的结果多数类准确率高就把总体分数抬上去了。解决先看第 2.2 节统计出来的类分布算出 max_count / min_count 的比值。比值超过 5:1 就要引入样本加权常见做法是在 DataLoader 里用 WeightedRandomSampler 按类别样本数的倒数做采样或者在损失函数里传 weight 参数。我倾向于优先用 WeightedRandomSampler因为它不改 loss 的形式只是让每个 epoch 里少数类被抽到的概率更高。实现时注意权重数组要按样本索引对齐而不是按类别索引直接广播。from torch.utils.data import WeightedRandomSampler # class_counts: 长度为 23 的列表记录每类样本数 sample_weights [1.0 / class_counts[label] for label in all_labels] sampler WeightedRandomSampler(sample_weights, num_sampleslen(all_labels), replacementTrue)replacementTrue 表示允许重复采样这是为了保证每个 epoch 的迭代步数不变。如果设成 False少数类可能很快被抽完后续迭代全是多数类加权就失效了。4.2 截断图片引发 NaN lossImageFolder 不负责过滤坏图现象训练到第 7 个 epoch 左右DataLoader 突然抛出 OSError报错信息类似「image file is truncated」loss 在某个 step 直接变成 NaN整个训练中断。原因下载过程中部分图片不完整。ImageFolder 在getitem阶段只做路径索引和图片打开真正的 decode 发生在 collate 之后模型 forward 之前。也就是说坏图问题不是数据加载阶段暴露而是在数据进 GPU 前一刻才炸出来。解决先在训练前跑一遍第 3.2 节的 verify() 脚本这是根治办法。如果项目已经跑了一半不想重来PIL 提供了临时兜底方案from PIL import ImageFile ImageFile.LOAD_TRUNCATED_IMAGES True这个开关会让 PIL 在遇到截断的 JPEG 时尝试用已有数据解码而不是直接抛异常。但它本质上是一种「盲猜」可能会让某些图的内容不完整却不报错从而引入脏数据。我的态度是只能用来保训练任务不中断模型收敛后还是要回去清理坏图重新跑一轮验证。4.3 手动重切测试集最容易忽略的数据泄漏现象拿到数据后嫌 test 目录的 4002 张图太少把 train 和 test 合并重新按 9:1 分层切分结果验证集准确率从 0.76 涨到了 0.88。换到真实临床照片上测试准确率又跌回 0.7 附近。原因合并后重新切分时同一个患者的相似图片可能一部分进了训练集、一部分进了验证集。模型在训练时「记住」了这些图片的特征验证集评估时自然表现好。这属于典型的数据泄漏不是模型真的变强了。解决严格保留原始 train/test 目录test 只做最终评估。需要验证集就从 train 里切比如每类抽 5%~10% 作为 validation。切分时用复制而不是移动避免原始资源被破坏。如果要做 K 折交叉验证也是在 train 内部做折test 始终保持独立。这一点做医学图像分类尤其重要因为同一患者的病灶图往往高度相似一旦混淆论文里的指标就没有临床参考价值。4.4 易混类成对误分疱疹类与湿疹类的「肉眼陷阱」现象从混淆矩阵看herpes-zoster带状疱疹和 herpes-type-1-recurrent1 型单纯疱疹复发互相误分eczema-herpeticum疱疹性湿疹和普通 eczema湿疹也常常混在一起。这两类错误占了总误判的一半以上。原因这些类别本身在皮肤病学上就存在症状重叠。带状疱疹和单纯疱疹早期都是红斑水疱不结合病史甚至医生肉眼都难以区分而她的名字一样图像分类只能学外观特征学不到病史信息。数据集里的图片又是不同患者、不同拍摄设备、不同光照条件下采集的类间方差和类内方差一样大模型找不到稳定的判别边界。解决先别急着加模型复杂度。第一步是打印并抽查被误分的图片看是不是标注本身有问题——有些图片可能确实归类不严谨。如果标注没问题就考虑把强相似类合并成超类先做 10 类左右的粗分类再对超类内部做细分类。这个方案在基层医疗场景里其实更实用先判断「是不是疱疹类」再判断具体是哪种不用一步到位。5. 从 yolo 分类到迁移实验验证集分流与类别合并习惯这套数据除了直接用 torchvision 跑 ResNet 这类常规分类还有一个常见去向是 YOLO 系列的分类模式。YOLOv5 和 YOLOv8 的分类训练同样支持目录结构data 参数指向包含 train 和 val 子目录的父目录和 ImageFolder 的读取方式高度一致。也就是说这套资源的目录组织几乎可以原样迁移到 yolo classify 任务上。迁移前建议先补一个约定YOLO 分类训练要求数据放在 data 目录下的 train 和 val 子目录里而原始资源只有 train 和 test。如果直接把 test 当年 val 用每类 174 张的平均样本量偏少且失去了独立测试集。我一般是从原始 train 目录里每类抽 5% 复制到 val 目录test 保留作为最终评估集。切分脚本很简单import os import random import shutil src_root data/train val_root skin_data/val os.makedirs(val_root, exist_okTrue) val_ratio 0.05 for cls in os.listdir(src_root): cls_src os.path.join(src_root, cls) cls_dst os.path.join(val_root, cls) os.makedirs(cls_dst, exist_okTrue) images os.listdir(cls_src) val_n max(1, int(len(images) * val_ratio)) val_images random.sample(images, val_n) for img in val_images: shutil.copy(os.path.join(cls_src, img), os.path.join(cls_dst, img))跑完后再把 train 目录整体复制成 skin_data/train配合 yolo 命令直接训练yolo classify train modelyolov8n-cls.pt dataskin_data epochs50 imgsz224这里 imgsz 不建议一上来就设 640皮肤病图像很多是近距离拍摄224 已经能满足早期验证需求跑通流程后再用 384 看收益。最后聊一个我的使用习惯。第一次跑这套数据时我把 test 目录也并进了训练流程导致后续换到新拍摄的图片时准确率明显下滑。从那以后我每次拿到新数据集都会强制走一遍「目录体检 → 类别计数 → 损坏扫描 → 抽图预览」四步流程再决定是否改动目录结构。这套 23 类皮肤病数据的价值不在于模型刷分而在于它把常见的数据坑都保留在了可控范围内适合用来建立自己的医学图像分类工作流。希望帮到你。本文还有配套的精品资源点击获取