19类器官细胞医学图像分类实战:YOLOv5与PyTorch迁移学习训练指南

发布时间:2026/10/7 8:52:04
19类器官细胞医学图像分类实战:YOLOv5与PyTorch迁移学习训练指南 简介这份医学图像分类数据集面向医学影像分析与深度学习入门用户聚焦十九种器官和细胞类别如肾上腺、子宫、甲状腺、食道等的图像识别任务。数据已按训练集与测试集清晰划分训练集含两千一百张图片测试集含五百张所有图片按类别存放在独立文件夹并配套类别字典JSON文件可直接接入YOLOv5分类项目或各类CNN分类网络免去繁琐的数据整理工作。资源包共两千个文件其中一千九百九十八张为PNG图片另附一个Python可视化脚本和一份JSON类别字典整体压缩后大小约260.22MB目录结构直观。目前已有176人学习使用适合用于医学图像分类课程设计、模型复现或算法对比实验。用户拿到压缩包后既可通过show脚本预览图像与标签对应关系也能依据划分好的数据快速开始训练与评估极大降低医学图像数据集的获取与预处理门槛。1. 医学图像分类数据集19 类器官细胞识别拿来就能训的分类数据做医学图像分类的人最头疼的不是模型是数据。公开数据集要么类别太杂、标注格式不统一要么划分方式让人抓狂。这份 19 种器官细胞图像识别数据集的好处在于拆好了——训练集 2100 张、测试集 500 张按文件夹归类还带 class_indices.json 类别字典直接能喂给 YOLOv5 分类分支或经典 CNN 网络。覆盖面比较全肾上腺、子宫、甲状腺、食道、皮肤、胰腺、肾脏等 19 类器官细胞切片正好覆盖病理图像分类入门最常碰到的几类组织。适合做医学图像分类实验、毕设、课程设计或者想快速验证一个分类网络效果的从业者。我不喜欢拿到数据还要花半天理路径、写 split 脚本这份数据省的就是这个时间。2. 数据长什么样目录结构、类别字典与图片命名里藏的信息2.1 目录划分逻辑和 YOLOv5 分类任务的对应关系数据集根目录下的 data 文件夹分 train 和 test 两个子目录每个子目录下按类别建子文件夹图片直接躺在对应类别的目录里。这是 YOLOv5 分类任务的标准输入方式不需要额外写数据集配置文件train.py 里指定 --data 路径为 data 根目录就能跑。结构上是这样的data/ ├── train/ │ ├── Adrenal/ # 肾上腺 │ │ ├── img_Adrenal_1_00001.png │ │ └── ... │ ├── Uterus/ # 子宫 │ │ ├── img_Uterus_1_01061.png │ │ └── ... │ └── ...共 19 类 └── test/ ├── Adrenal/ ├── Uterus/ └── ...与训练集类别一一对应train 共 2100 张test 共 500 张。注意 class_indices.json 里的类别索引是按字典序排的还是按目录扫描顺序排的拿到数据后第一件事就是打开看确认训练脚本里读取的类别顺序和 json 一致。提示YOLOv5 的 classify/train.py 会扫描指定根目录下的所有子文件夹作为类别文件夹名就是类别名。label 不需要额外准备图片本身的位置就是监督信号。2.2 class_indices.json 的正确打开方式class_indices.json 是给 PyTorch 的 ImageFolder 或者训练完做推理时用的索引映射文件。常见做法是训练前把 classes 列表 dump 成 json推理时反查索引对应类别名。用下面这段代码读取并打印import json with open(class_indices.json, r) as f: class_indices json.load(f) print(type(class_indices)) # 确认是 dict 还是 list print(len(class_indices)) # 应该等于 19 # 如果是 {0: Adrenal, 1: Uterus, ...} 这种格式 for idx, name in class_indices.items(): print(f索引 {idx} - {name})关键要确认两点第一json 里的类别名称是否和 data/train 下的文件夹名完全一致培养大小写不一致会导致训练时报错找不到类别第二索引顺序是否按字母序这关系到混淆矩阵的横纵轴含义。如果 json 里只有类别名没索引可以用 os.listdir 按排序重新生成一份。2.3 图片命名的规律img_Uterus_1_01061.png 这种命名里其实藏了三个信息器官名Uterus、来源编号1、图片序号01061。判断是否同一来源很简单看第 2 个下划线后面的数字是否变化。如果一个器官的图片编号连号且来源固定说明数据集可能来自少数几个病例的连续切片这时候训练集和测试集的划分就要小心——如果测试集和训练集来自同一个病例模型评估结果会偏乐观。实际项目里病理切片的域偏移问题非常严重不同医院、不同染色仪器的切片图像分布差异很大这份数据适合做算法验证但别指望在一个来源的数据上训完模型就能直接部署到临床环境。3. 跑通 YOLOv5 分类训练命令、超参设置和类别映射3.1 准备 YOLOv5 环境与数据软链YOLOv5 的 classify 分支是独立的依赖 requirement.txt 里的基础包。克隆仓库后先装依赖再把数据软链到 datasets 目录下避免移动大量小文件git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt # 把数据软链到 yolov5 的 datasets 目录 mkdir -p datasets ln -s /你的绝对路径/医学图像分类数据集/data datasets/medical_data # 检查目录结构是否就绪 ls datasets/medical_data # 应输出 train 和 test 两个文件夹软链的好处是训练脚本里的 --data 路径可以统一写 datasets/medical_data而不用每次传全路径。注意 Windows 下 mklink /D 需要管理员权限Linux 下 ln -s 在一台机器上设置完换机器要重新链。3.2 分类训练的核心参数与跑通命令YOLOv5 分类训练入口是 classify/train.py常用参数如下python classify/train.py \ --model yolov5s-cls.pt \ --data datasets/medical_data \ --epochs 50 \ --img 224 \ --batch-size 32 \ --device 0 \ --project runs/classify_medical \ --name exp_19cls \ --pretrained参数说明--model 用 yolov5s-cls.pt这是预训练分类模型在 ImageNet 上训过迁移到医学图像上收敛速度明显更快--img 224 是和预训练权重匹配的输入分辨率改动会破坏权重兼容性--batch-size 32 是 224 分辨率下 8GB 显存的可行值显存不够就降到 16--pretrained 使用 ImageNet 权重初始化。训练过程里关注 top1 acc 和 top5 acc 两个指标医学图像类别间有相似性比如不同类型上皮细胞top5 比 top1 更能反映模型是否学到了有效特征。3.3 训练结果的权重文件和推理验证训练结束后 runs/classify_medical/exp_19cls/weights/ 下会有 best.pt 和 last.pt。验证测试集效果用 classify/val.pypython classify/val.py \ --weights runs/classify_medical/exp_19cls/weights/best.pt \ --data datasets/medical_data \ --img 224 \ --batch-size 32val.py 会输出测试集准确率、每类准确率和混淆矩阵混淆矩阵保存在 runs/classify_medical/exp_19cls/ 下。做单张图片预测用 classify/predict.pypython classify/predict.py \ --weights runs/classify_medical/exp_19cls/weights/best.pt \ --source datasets/medical_data/test/Uterus/img_Uterus_1_01062.png这里建议用 --save-txt --save-conf 参数把类别索引和置信度存下来便于后续批处理分析。注意初次跑通不要贪多50 epochs 足够判断模型是否收敛。医学图像分类在小数据集上大概率在 20-30 轮就过拟合val_acc 不再上升时果断停。4. 自建 CNN 分类网络ImageFolder 加载与训练脚本要点4.1 用 ImageFolder 三步完成数据加载不想依赖 YOLOv5 的话PyTorch 的 torchvision.datasets.ImageFolder 是加载这类按文件夹划分的数据最简单的方式。核心逻辑在下面这段代码里import torch from torchvision import datasets, transforms from torch.utils.data import DataLoader # 训练集数据增强 归一化 transform_train transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.2, contrast0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) # 测试集只做归一化不做数据增强 transform_test 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(rootdata/train, transformtransform_train) test_dataset datasets.ImageFolder(rootdata/test, transformtransform_test) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4) test_loader DataLoader(test_dataset, batch_size32, shuffleFalse, num_workers4) print(f训练集类别数: {len(train_dataset.classes)}) print(f训练集图片数: {len(train_dataset)}) print(f测试集图片数: {len(test_dataset)})这段代码里值得注意的细节ImageFolder 的 classes 属性按目录名字典序排列和 class_indices.json 的对应关系要在训练前打出来确认测试集不开 shuffle 是防止评估时顺序不稳定ColorJitter 对病理图像要慎用——染色图片的色相和饱和度偏移过大会破坏诊断特征建议只调 brightness 和 contrast幅度控制在 0.2 以内。4.2 迁移学习训练模板ResNet18 实战医学图像数据集规模小从头训练 CNN 效果很差。常见做法是加载 ImageNet 预训练的 ResNet18 并替换最后一层import torch.nn as nn import torch.optim as optim from torchvision import models # 加载预训练模型并替换全连接层 model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) num_features model.fc.in_features model.fc nn.Linear(num_features, 19) # 只更新最后一层 指定层或者全量微调 for param in model.parameters(): param.requires_grad False for param in model.fc.parameters(): param.requires_grad True # 训练参数 criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.fc.parameters(), lr0.001)迁移学习的核心思路很清楚前几层学习的是边缘、纹理等通用特征冻结掉可以防止小数据集过拟合只训练最后的分类层。如果想效果好一点可以解冻最后两个 BasicBlocklayer4和 fc 层用更低的学习率比如 1e-4 做 fine-tune。医学图像虽然和 ImageNet 差异大但底层特征仍然可用完全从零训练至少需要 10 万级别的数据量这份数据集不具备这个条件。4.3 训练循环里的两个关键检查点训练循环本身不复杂但有两个环节经常翻车。第一个是类别权重不平衡——19 类里如果某类样本特别少直接影响准确率推荐统计一下每类的图片数量如果差距大就在 CrossEntropyLoss 里传入 weight。第二个是验证集监督频率每 epoch 结束后跑一遍全量测试集记录 best acc 并保存模型权重best_acc 0.0 for epoch in range(epochs): model.train() running_loss 0.0 for inputs, labels in train_loader: inputs, labels inputs.to(device), labels.to(device) optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() * inputs.size(0) # 每个 epoch 结束验证一次 model.eval() correct 0 total 0 with torch.no_grad(): for inputs, labels in test_loader: inputs, labels inputs.to(device), labels.to(device) outputs model(inputs) _, preds torch.max(outputs, 1) total labels.size(0) correct (preds labels).sum().item() acc correct / total print(fEpoch {epoch1}, Loss: {running_loss/len(train_dataset):.4f}, Val Acc: {acc:.4f}) if acc best_acc: best_acc acc torch.save(model.state_dict(), best_model.pth)这段代码里的最佳模型保存判断在医学图像分类里很有必要——训练后期几乎必然出现过拟合只保存验证集上表现最好的那一次权重相当于给模型吃了后悔药。5. 避坑指南19 类医学图像分类最常见的几个翻车现场5.1 类别名大小写不一致导致推理阶段全部预测错现象训练时准确率正常每个 epoch 的 val_acc 都正常但用 predict.py 或自己写推理脚本时预测结果总是乱序甚至全部错位。原因ImageFolder 和 YOLOv5 的类别排序都依赖文件夹名字典序。如果 class_indices.json 里是 Adrenal、Skin 这种首字母大写而部分文件夹被改成小写 adrenal排序结果会完全变掉推理时的类别索引和训练时的索引就对不上了。解决训练前写一行代码核对——打印 train_dataset.classes 和 json 里的 key/name 映射表逐一对比确保完全一致。不要相信眼睛看差不多用脚本比对assert list(train_dataset.classes) [class_indices[str(i)] for i in range(len(class_indices))], 类别名或顺序不一致5.2 训练 Loss 下降但验证准确率不动的数据泄漏现象train_loss 一路降到 0.2 以下但 val_acc 在 50%-60% 横盘怎么调参都上不去。原因病理切片图像很多来自同一个病例的连续切片如果同一个病人的切片被同时分到了 train 和 test模型学到的其实是病例特征而不是器官特征。测试集和训练集图像高度相似但泛化到新数据上效果差这是医学图像分类最典型的数据集泄漏。解决无法确认数据来源时按文件命名中的来源编号做分组划分而不是随机划分。比如命名中第 2 个字段是来源编号就保证同一来源编号的图片只出现在 train 或 test 中的一边。手动重新划分数据时注意保持类别均衡。5.3 Resize 导致器官形态特征丢失现象使用 224x224 输入训练完准确率尚可但部署时遇到高分辨率全切片图直接 Resize 之后预测结果明显变差。原因原始病理图像分辨率可能达到几千乘几千直接压缩到 224 会丢失大量细胞级纹理信息尤其是细胞核形态、染色质分布这些诊断关键特征。YOLOv5 分类分支默认也是把输入 Resize 到 224这属于通用做法但不适合所有医学图像。解决先可视化缩小前后的差异如果纹理信息丢失严重可以先用滑动窗口把大图切成 224x224 的小块训练推理时也按同样方式切块并多数投票或者直接用 448 或 512 分辨率训练代价是显存和训练时间翻倍。5.4 类别权重失衡导致小样本类准确率趋近于零现象整体准确率 85%但查看混淆矩阵发现某两类比如两个器官的上皮细胞几乎不区分一个类别被完全预测成另一个。原因19 类样本数不均衡且类别间形态相似度高。交叉熵损失默认按样本数加权样本多的类主导梯度小样本类学不到判别特征。解决统计每类样本数后在 CrossEntropyLoss 里传 weight 向量值设为该类样本数的倒数如果还不行考虑 Focal Loss对易分类样本降权让模型更关注难样本。5.5 训练中断后重新开始但迭代次数设置过大现象第一次训练 80 epochs 已经收敛到最佳效果重跑训练为了省事设了 100 epochs结果 best.pt 反而比之前的差。原因训练轮数超过过拟合点之后验证集准确率会下降而保存 best.pt 的逻辑要求每个 epoch 都超过历史最佳才更新不会主动停止。过拟合后的权重容易被保存为 best实际上是测试集表现变差了。解决用早停策略patience 设为 10-15 epochs或者直接人工观察 val_acc 连续 10 轮不涨就终止训练。自己写训练脚本的话在 epoch 循环里加一个 stop_counter连续 N 轮 acc 不提升就 break。6. 把 show 脚本玩出花可视化逻辑、混淆矩阵分析和可靠的数据划分验证6.1 show 脚本的三种可视化用法数据集附带 show 脚本用于可视化数据样本。常见做法是读取某个类别的图片并拼接成网格图既能检查图像是否损坏、类别是否混淆也能做数据质量巡检。在此基础上可以做三层可视化验证第一层随机抽 8-10 张图看原始图像质量第二层看预处理的 Resize 结果是否符合预期第三层加 TensorBoard 或给训练中的模型加钩子输出 feature map。重点关注最后一层卷积的激活区域能直观地看到模型到底在看图像哪个位置。6.2 混淆矩阵的排序问题测试集 500 张在 19 类上跑完 val.py 之后自动生成 confusion_matrix.png但我第一眼总被误导。这张图默认按类别名字典序排列而字典序和临床意义的组织分类顺序不一致。比如 Adrenal肾上腺和 Kidney肾脏在字典序里分开很远但形态上可能更接近。建议重新计算并按类别相似度重新排序显示更直观地发现易混淆配对。6.3 训练集和验证集的划分可靠性验证数据划分可靠是复现结果的前提也是医学图像实验里审核人必问的问题。一个简单有效的检验方法是做 k 折交叉验证把这份数据的 train 重新分为 5 折各训一版模型看不同折之间的 top1 acc 方差。如果方差偏大说明某个来源的图片被集中拆到了某个折里训练验证划分不平衡就要考虑按来源编号去划分而不是随机打乱。这个验证能同时发现数据泄漏和划分偏差两大类问题投入很小。最后说一个我的真实教训第一次拿到这份数据集没看命名规律随便随机分了训练集和测试集ResNet18 跑出 96% 的准确率。后来按来源编号重新划分准确率掉到 77%这就说明之前的 96% 里有水分。从那以后我每次拿到医学图像数据集都会先做来源分析再划分检查测试集和训练集之间有没有来自同一个病例的切片这个动作坚持做了两年。希望帮到你。本文还有配套的精品资源点击获取