ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

血细胞图像分类数据集实战:从数据预处理到模型训练避坑指南

2026/10/7 10:19:30 拓冰建站 浏览量
血细胞图像分类数据集实战:从数据预处理到模型训练避坑指南 简介血细胞图像分类数据集4分类是一份针对医学图像分类任务的现成数据包适合高校学生、科研人员及算法工程师直接用于深度学习模型训练省去图像搜集与预处理环节。资源包含嗜酸性粒细胞、淋巴细胞、单核细胞和嗜中性粒细胞四类细胞图像每类3000张解压后训练集9957张、测试集2487张并按data-train与data-test目录分开存放子文件夹名称即类别标签可直接配合ImageFolder等工具加载使用。压缩包共2000个文件以1998张jpeg图像为主体另含1个classes.json类别字典和1个Python可视化脚本整体大小99.53MBjson用于生成类别与索引的映射py脚本能随机展示样本图像便于快速检查数据质量与类别分布。目前已有1057人学习下载适合医学图像分类入门、课程设计、论文实验及算法对比等场景是一套开箱即用的医学图像分类基准数据。1. 血细胞图像分类数据集4分类到底在分什么做医学图像分类的人一开始最容易踩的坑就是拿 ImageNet 的思维去处理血细胞图像。ImageNet 分的是猫狗汽车类别之间边界清晰而血细胞图像分类数据集4分类分的是中性粒细胞、嗜酸性粒细胞、单核细胞和淋巴细胞——它们在大类上同属白细胞形态差异极小染色条件一变同一类细胞的颜色和纹理就全变了。这个数据集要解决的核心问题不是“认不认识这种细胞”而是“在染色噪声和样本差异下能不能稳定地分对”。适合谁用做医学影像 AI 落地、做细胞形态学自动筛查、或者练图像分类基本功的人。它的价值在于数据量不大、类别不均衡、标注有噪声恰好能把一个分类模型从“过拟合训练集”逼到“真正学会形态特征”。2. 数据集长什么样目录组织、标注口径与首批检查清单2.1 4分类的类别定义与形态学依据血细胞图像分类数据集的 4 分类指的是四种白细胞中性粒细胞Neutrophil、嗜酸性粒细胞Eosinophil、单核细胞Monocyte、淋巴细胞Lymphocyte。这四类的形态学依据是细胞核的形态和胞浆颗粒的分布。中性粒细胞的核通常是分叶的2 到 5 叶不等胞浆里有细小的颗粒嗜酸性粒细胞的核多为两叶胞浆里有粗大的嗜酸性颗粒染出来偏红单核细胞的核是肾形或不规则形胞浆灰蓝色颗粒细淋巴细胞的核圆形、深染胞浆少呈淡蓝色。为什么不是按“白细胞/红细胞/血小板”来分因为红细胞和血小板在形态上太容易区分分类难度低用来做数据集没有挑战性。4 分类的难点集中在白细胞亚型上这才是临床上镜检分类的真正痛点。你拿到数据集后第一步不是跑模型而是打开图像看一遍每一个类别下的典型样本把形态学特征刻在脑子里否则后面做错误分析时你根本不知道模型错在哪。2.2 目录结构与文件名信息一个标准的血细胞图像分类数据集目录通常是这样组织的blood_cell_dataset/ ├── train/ │ ├── neutrophil/ │ │ ├── NEU_001.bmp │ │ ├── NEU_002.bmp │ │ └── ... │ ├── eosinophil/ │ ├── monocyte/ │ └── lymphocyte/ ├── val/ │ ├── neutrophil/ │ ├── eosinophil/ │ ├── monocyte/ │ └── lymphocyte/ └── test/ ├── neutrophil/ ├── eosinophil/ ├── monocyte/ └── lymphocyte/我一般会建议拿到数据后先做三件事。第一用脚本统计每个子目录的文件数量确认类别分布第二随机抽 20 张图出来肉眼检查图像质量和标注是否正确第三检查图像尺寸、通道数和文件格式是否统一。这三件事花不了十分钟但能避免后面训练时出现“图片解码失败”“类别标签错位”这类低级问题。# 统计每个类别的图像数量 find . -type f \( -name *.bmp -o -name *.jpeg -o -name *.png \) | \ sed s|/[^/]*$|| | sort | uniq -c | sort -k1 -n这段命令的逻辑是先用 find 把所有图像文件的完整路径列出来再用 sed 去掉文件名只保留目录路径sort 排序后用 uniq -c 统计每个目录出现的次数。输出的第一列是图像数量第二列是目录路径。如果你发现某个类别的图像数量比别的类别少一个数量级说明类别不均衡很严重后面训练时要针对性地做处理不能直接拿原始分布去训。2.3 图像格式与尺寸的边界坑血细胞图像数据集的图像格式五花八门常见的有 BMP、JPEG、PNG。BMP 格式虽然体积大但无损适合做训练原始数据JPEG 有压缩失真在细胞边缘会产生伪影对分类任务的干扰需要实测评估。图像尺寸上不同来源的数据集差别很大有的 80×80有的 160×120没有统一的标注。一个常见做法是把所有图像统一缩放resize到固定尺寸比如 224×224再喂给模型。但这里有个坑如果原图是 80×80 的低分辨率直接缩放到 224×224 会引入插值噪声模型学到的不是细胞形态而是插值纹理。我在处理这类数据时一般会先看原始图像的尺寸分布如果大部分图像小于 120×120我会用 128×128 或 96×96 作为训练尺寸而不是盲目跟 ImageNet 用 224。训练尺寸的选择本质上是在保留细胞细节和适应模型输入之间做折中。from PIL import Image import os from collections import Counter img_sizes Counter() for root, dirs, files in os.walk(blood_cell_dataset): for f in files: if f.endswith((.bmp, .jpeg, .png)): path os.path.join(root, f) with Image.open(path) as img: img_sizes[img.size] 1 print(img_sizes.most_common(10))这段代码遍历整个数据目录用 PIL 打开每一张图像记录它的尺寸宽高最后用 Counter 统计前 10 种最常见的尺寸组合。输出结果能直接告诉你图像是统一尺寸还是大小不一是否需要做尺寸归一化。如果输出显示绝大多数图像集中在少数几个尺寸那就可以直接用最常见的尺寸做基线训练如果尺寸极分散就需要在数据加载时做随机裁剪或填充。2.4 标注质量的抽查方法标注质量是医学图像数据集最致命的隐藏问题。血细胞图像数据集的标注通常由标注人员手工完成或者是半自动工具生成后人工校正。由于细胞形态相似标注错误是不可避免的。我见过一个公开数据集里嗜酸性粒细胞类别混入了大量中性粒细胞比例可能达到 5% 到 10%如果你不做检查模型会学到错误的决策边界。抽查标注质量最直接的方法是可视化混淆样本。做法是随机从每个类别中抽取 9 张图像拼成一张大图用 Matplotlib 显示出来人眼确认类别标签是否与图像内容一致。更系统的方法是训练一个基线模型然后用模型的置信度和预测结果去筛“疑似误标注”样本——那些模型低置信度但真实标签是某个类别的样本人工重点检查。3. 从文件到张量加载、拆分与预处理的最小可复现管线3.1 用 torchvision 的 ImageFolder 直接加载对于按目录组织的图像分类数据集最简单可靠的加载方式是 torchvision 的ImageFolder。它自动把目录名映射成类别索引返回的图像张量格式为(C, H, W)值域为[0.0, 1.0]如果设置了ToTensor。这种方式不需要手写标签映射逻辑代码量最少。import torch from torchvision import datasets, transforms # 训练集与验证集使用不同的预处理策略 train_transform transforms.Compose([ transforms.RandomResizedCrop(128, scale(0.7, 1.0)), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.1, hue0.05), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_transform transforms.Compose([ transforms.Resize((128, 128)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_dataset datasets.ImageFolder(blood_cell_dataset/train, transformtrain_transform) val_dataset datasets.ImageFolder(blood_cell_dataset/val, transformval_transform) print(类别映射:, train_dataset.class_to_idx) print(训练集样本数:, len(train_dataset)) print(验证集样本数:, len(val_dataset))这段代码的关键参数有三个。第一RandomResizedCrop(128, scale(0.7, 1.0))表示随机裁剪一个区域裁剪面积占原图的 70% 到 100%然后缩放到 128×128这个操作对血细胞图像很合适因为细胞往往不在图像正中心适度裁剪能模拟细胞偏移。第二ColorJitter的亮度、对比度、饱和度扰动要有度血细胞的染色对颜色敏感扰动太大反而会让模型学不到真实染色特征。第三Normalize用的是 ImageNet 的均值和标准差因为迁移学习时预训练模型是在这个归一化分布下训练出来的如果你从零训练可以用数据集自身的均值和标准差。3.2 类别不均衡的拆分策略血细胞图像数据集的类别分布往往不均衡。在临床上中性粒细胞占白细胞总数的 50% 到 70%淋巴细胞占 20% 到 40%嗜酸性粒细胞和单核细胞占比很低。数据集通常继承了这个先验分布。如果你按随机划分做训练集和验证集验证集中嗜酸性粒细胞的样本可能只有几十张评估指标会很不稳定。我一般会做分层划分stratified split确保每个类别在训练集和验证集中的比例和全量数据一致。在 PyTorch 里可以用torch.utils.data.random_split配合每个类别的索引来实现import numpy as np from torch.utils.data import Subset, random_split # 按类别分组索引 indices_by_class {} for idx, (_, label) in enumerate(train_dataset.samples): indices_by_class.setdefault(label, []).append(idx) train_indices, val_indices [], [] val_ratio 0.15 for label, indices in indices_by_class.items(): n_val max(1, int(len(indices) * val_ratio)) rng np.random.RandomState(42) shuffled rng.permutation(indices).tolist() val_indices.extend(shuffled[:n_val]) train_indices.extend(shuffled[n_val:]) train_subset Subset(train_dataset, train_indices) val_subset Subset(train_dataset, val_indices) print(f训练集样本数: {len(train_subset)}, 验证集样本数: {len(val_subset)})这个做法的核心是按类别分别划分而不是对全体索引做一次随机切分。val_ratio0.15表示每个类别都拿出 15% 的样本做验证这样即使嗜酸性粒细胞只有 100 张验证集也有 15 张保证评估时所有类别都出现。RandomState(42)固定随机种子确保实验结果可复现。这里要特别注意如果你的数据目录已经有独立的 train/val/test 划分就不需要再做分层拆分直接用现成的划分即可。3.3 数据增强不要盲目套用 ImageNet 方案血细胞图像分类的数据增强是一个需要克制的地方。ImageNet 上常见的RandomRotation(30)、RandomAffine这类强几何增强在血细胞图像上不一定适用。原因有两点第一细胞的朝向和位置在涂片制备时是随机的几何变换不会引入新的形态变体增益有限第二过度旋转和缩放会破坏细胞核的分叶结构——核分叶是中性粒细胞和嗜酸性粒细胞分类的关键依据你把它转得糊了模型就学不到这个特征了。我常用的血细胞增强组合是小幅度的随机平移、随机缩放、颜色扰动和水平翻转。平移范围控制在 10% 以内缩放控制在 0.9 到 1.1 之间颜色扰动要小——因为染色色调在不同显微镜和染色方案下有差异但同一数据集内是相对一致的。如果发现模型过拟合优先考虑的是加 Dropout 或增大权重衰减而不是增加增强强度。train_transform_v2 transforms.Compose([ transforms.RandomAffine(degrees0, translate(0.05, 0.05), scale(0.9, 1.1)), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.1, contrast0.1, saturation0.05), transforms.Resize((128, 128)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])注意这里RandomAffine的degrees0表示不做旋转。translate(0.05, 0.05)表示水平和垂直方向最多平移图像宽高的 5%这个幅度在血细胞图像上已经够用。scale(0.9, 1.1)是缩放范围代表最多放大 10% 或缩小 10%再大就可能丢掉细胞边缘信息了。颜色扰动参数比初版小了一半这是我根据实际训练结果调出来的参数——血细胞分类任务颜色扰动过大的表现是验证集准确率上不去因为模型在学习“染色变化”而不是“细胞形态”。3.4 数据加载器与训练循环的最小配置数据加载器配置里有两个隐藏参数需要说明。num_workers控制数据加载的并行进程数一般建议设置为 CPU 核心数的一半太大容易在数据加载阶段把内存打满。pin_memoryTrue在 GPU 训练时能减少数据从 CPU 到 GPU 的拷贝时间显存小的场景下收益明显。from torch.utils.data import DataLoader train_loader DataLoader( train_subset, batch_size32, shuffleTrue, num_workers4, pin_memoryTrue, drop_lastTrue ) val_loader DataLoader( val_subset, batch_size32, shuffleFalse, num_workers4, pin_memoryTrue ) for x, y in train_loader: print(输入张量:, x.shape, 标签张量:, y.shape) breakbatch_size32是这类小数据集几千到几万张的常见选择。如果图像尺寸是 128×12832 张一批的显存占用大约在 1 到 2 GB 之间大多数消费级显卡都能跑。drop_lastTrue表示如果最后一个批次不足 32 张则丢弃这能避免 batch 大小不一致导致的批归一化BatchNorm计算错误。验证集加载器shuffleFalse保持固定顺序确保每次评估时样本顺序一致这能让验证结果的波动更可控。4. 血细胞分类的6个常见坑脏标签、过拟合与类别不均衡4.1 类别不均衡导致嗜酸性粒细胞被碾压现象模型整体准确率达到 90% 以上但查看每个类别的召回率时发现嗜酸性粒细胞的召回率只有 40%大量嗜酸性粒细胞被分成了中性粒细胞。原因嗜酸性粒细胞在数据集中占比太小比如总量可能只有几百张模型在训练时几乎没有见过足够的正样本决策边界偏向于样本量大的类别。这是分类任务在医学数据上的经典问题准确率指标会掩盖这个小类别的失败。解决在损失函数中按类别频率设置权重。PyTorch 里可以直接把weight传给CrossEntropyLoss权重取总样本数除以每个类别的样本数的倒数再归一化。另一种做法是用 Focal Loss它能降低已分类正确样本的损失迫使模型把注意力集中在难分样本上。我的经验是先用类别权重训练一个基线如果嗜酸性粒细胞还是拉胯再用 Focal Loss。from torch.nn import CrossEntropyLoss from collections import Counter # 统计训练集每个类别的样本数 label_counts Counter(label for _, label in train_subset.dataset.samples if label in []) # 上面这行实际会误伤推荐直接用下面的写法 label_iter [label for _, label in train_subset.dataset.samples] label_counts Counter(label_iter) total sum(label_counts.values()) weights [total / label_counts[i] for i in range(4)] weights torch.tensor(weights, dtypetorch.float) print(类别权重:, weights) criterion CrossEntropyLoss(weightweights)这里的关键是weights的设计样本数少的类别获得更大的权重使得每个样本对损失的贡献大致均等。比如淋巴细胞有 3000 张嗜酸性粒细胞只有 500 张那嗜酸性粒细胞的权重就是淋巴细胞的 6 倍。需要注意权重不能设得过大否则模型会过度拟合少数类样本导致验证集上的“假高分”。4.2 脏标签训练集里混了误标注样本现象模型在训练集上的准确率持续上升但验证集准确率在某个点开始回撤且错误集中在某两个形态相似的类别之间比如中性粒细胞和嗜酸性粒细胞。原因标注人员在区分分叶核和双叶核时主观性太强部分样本被贴了错误的标签。模型学到的是标注者的错误模式而不是真正的形态特征。这类错误在医学图像数据集中非常普遍尤其是公开数据集中没有经过专家二审的。解决训练一个基线模型后用模型对训练集做一遍“自我预测”把预测结果和给定标签不一致的样本挑出来逐一人工检查。这不是一个绝对可靠的方案因为模型自己也可能是错的但它能把几千张图像筛选到几十张可疑样本人工复查的成本大幅降低。我处理这类问题时会把可疑样本按置信度降序排列优先检查置信度高的错分样本——那种样本说明标签可能是错的。4.3 染色差异让验证集准确率虚高现象训练集中的图像来自某个染色批次验证集来自另一个批次模型在验证集上表现很好但在真实新场景中准确率下降 20 个百分点以上。原因不同批次的染色剂浓度、染色时间和显微镜白平衡设置不同导致同一类细胞在不同批次下的颜色分布不同。模型学到了第一批次的颜色特征而不是形态特征。这是医学图像分类和自然图像分类最本质的区别之一。解决使用染色归一化Stain Normalization做数据预处理。常见做法是用 Macenko 方法或 Reinhard 方法将颜色分布映射到一个标准参考图像的颜色空间。另一个务实的做法是在你的数据增强里加大ColorJitter的亮度、对比度扰动模拟不同染色条件——虽然不能完全替代染色归一化但能使模型对颜色变化不那么敏感。如果数据集标签里有来源信息比如来自不同医院按来源划分验证集会得到更真实的评估结果。4.4 数据泄漏重复图像同时出现在训练集和验证集现象模型在训练集上表现正常在验证集上的准确率远超预期比如 98% 以上但一到实际应用就崩盘。原因血细胞图像数据集经常包含同一患者同一视野的多张连拍图像或者同一细胞的不同缩放版本。如果按文件目录直接随机拆分同一来源的图像可能同时出现在训练集和验证集中模型相当于“见过”验证集的数据。解决检查图像之间的重复性。可以按文件名前缀分组也可以计算图像的感知哈希pHash找出内容近似的图像。最稳妥的做法是按患者或病例 ID 划分数据集而不是按图像文件划分。如果数据集没有患者 ID退而求其次的做法是用 pHash 检测重复图像把重复组内的全部图像分到同一个集合。import imagehash from PIL import Image hash_map {} for root, dirs, files in os.walk(blood_cell_dataset): for f in files: if f.endswith((.bmp, .jpeg, .png)): path os.path.join(root, f) h imagehash.phash(Image.open(path)) # 64-bit 感知哈希 hash_map.setdefault(h, []).append(path) for h, paths in hash_map.items(): if len(paths) 1: print(f感知哈希 {h} 重复次数: {len(paths)}) for p in paths[:3]: print( , p)imagehash.phash生成的 64 位哈希值代表了图像的感知指纹内容相似的两张图哈希值也相近相同哈希值视为相同图像。重复次数大于 1 的分组会被打印出来你就能看到哪些图像可能是同一来源。这段代码的局限在于它只能发现完全相同的感知指纹如果图像是同一细胞的不同视角需要配合文件名前缀和人工检查才能发现。4.5 小数据集过拟合验证集 loss 上升准确率卡住现象模型在训练集上的准确率接近 100%但验证集准确率在 85% 左右徘徊上不去。原因数据量不够模型记住了训练集中的形态细节和噪声而不是泛化特征。血细胞图像数据集的规模通常在几千到几万张相比于 ImageNet 的百万级数据量任何一个参数量稍大的模型都有过拟合风险。解决优先换小模型。常见做法是用 ResNet-18 替代 ResNet-50或者用 MobileNetV3-Small参数量能降一个数量级过拟合风险显著降低。其次增加正则化把 Dropout 加在分类头前面权重衰减从默认的 1e-4 提升到 1e-2 或更高。最后才是考虑数据增强和迁移学习。迁移学习是这类数据集的最优选加载在 ImageNet 上预训练的模型冻结前若干层特征提取器只微调后面几层和分类头这种方案在小数据集上往往能稳定提升 5 到 10 个百分点的验证集准确率。4.6 图像归一化参数用错导致颜色失真现象训练后把图像可视化发现细胞整体偏蓝或偏绿模型输入与原始图像在肉眼上已经不一样了。原因在预处理里直接套用了错误的均值和标准差。比如直接用数据集自身的 RGB 均值做归一化但数据集本身有染色偏色归一化不等于校准。ImageNet 的均值和标准差是一个通用基准对血细胞数据集来说不是最优的但它是迁移学习时模型遇到的目标分布。解决如果是迁移学习保持 ImageNet 的归一化参数不变如果是从零训练计算数据集自身的均值和标准差重新归一化后再训练。import numpy as np from PIL import Image means np.zeros(3) stds np.zeros(3) n_pixels 0 for root, dirs, files in os.walk(blood_cell_dataset): for f in files: if f.endswith((.bmp, .jpeg, .png)): img np.array(Image.open(os.path.join(root, f)).convert(RGB)) means img.reshape(-1, 3).sum(axis0) n_pixels img.shape[0] * img.shape[1] means / n_pixels for root, dirs, files in os.walk(blood_cell_dataset): for f in files: img np.array(Image.open(os.path.join(root, f)).convert(RGB)) img img.reshape(-1, 3) - means stds (img ** 2).sum(axis0) n_pixels 1 stds np.sqrt(stds / (n_pixels * 128 * 128)) print(均值:, means) print(标准差:, stds)这段代码分两步先算每通道均值再算标准差。注意它把每个像素看成一个样本所以最后标准差计算时除以了总像素数近似。如果你看了输出发现均值和 ImageNet 的标准值差异很大说明这个数据集的色彩基调和自然图像差异明显那就要考虑是不是染色归一化需要提前做而不是先归一化再训练。5. 把模型训到可信训练配置、迁移学习与逐类验证5.1 迁移学习的最小配置从 ImageNet 预训练模型开始血细胞图像数据集规模通常不大从零训练一个深度模型很容易过拟合。我一般会直接加载 ImageNet 预训练的 ResNet-18替换最后一层分类头然后冻结前面大部分层的参数只训练最后几个残差块和全连接层。import torchvision.models as models model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) num_features model.fc.in_features model.fc torch.nn.Linear(num_features, 4) # 冻结前 6 个残差块只微调最后 2 个块和分类头 for name, param in model.named_parameters(): if name.startswith(layer4) or name.startswith(fc): param.requires_grad True else: param.requires_grad False optimizer torch.optim.AdamW( filter(lambda p: p.requires_grad, model.parameters()), lr1e-4, weight_decay1e-3 )关键参数说明lr1e-4比常规从头训练的 1e-3 小一个量级因为冻结层输出的特征已经很强微调层只需要小幅调整。weight_decay1e-3用于控制过拟合血细胞数据集样本少正则化强度要比自然图像任务大。冻结策略是按名字匹配的layer4是 ResNet-18 最后一个残差块fc是新替换的全连接层只有这两部分参与训练。如果你的数据集类别数量不是 4把model.fc的输出维度改成你的类别数即可。用这种配置训 30 到 50 个 epoch验证集准确率通常能到 90% 以上训练时间在单张消费级 GPU 上不超过半小时。5.2 别只看准确率混淆矩阵与逐类指标医学图像分类任务的评估必须看逐类指标。准确率在小类别上会骗人比如整体 95% 的准确率嗜酸性粒细胞可能只有 50% 的召回率。我每次训练完都会打印混淆矩阵和每个类别的精确率、召回率、F1 分数。from sklearn.metrics import confusion_matrix, classification_report import numpy as np all_preds, all_labels [], [] with torch.no_grad(): for x, y in val_loader: x x.to(device) logits model(x) preds logits.argmax(dim1).cpu().numpy() all_preds.extend(preds) all_labels.extend(y.numpy()) all_labels np.array(all_labels) all_preds np.array(all_preds) cm confusion_matrix(all_labels, all_preds) print(混淆矩阵:) print(cm) print(\n逐类指标:) print(classification_report(all_labels, all_preds, target_names[neutrophil, eosinophil, monocyte, lymphocyte], digits3))classification_report输出四行指标精确率precision、召回率recall、F1 分数和支持数每个类别的样本数。你看报告时应该按类别一对一看如果 eosinophil 的召回率明显低于其他类别说明模型把嗜酸性粒细胞误判成其他细胞通常是中性粒细胞这时回到混淆矩阵看具体的错分去向再决定是调类别权重、加数据还是换特征提取器。混淆矩阵对角线越“亮”越好非对角线上的大数字就是你改进模型的方向。5.3 把模型输出变成可解释的可视化注意力定位血细胞分类模型的可解释性比自然图像分类更重要因为医生需要知道模型是根据细胞核的哪个区域做的判断。常用的可视化工具是 Grad-CAM它能生成热力图标出模型分类时关注的图像区域。from torchcam.methods import GradCAM from torchcam.utils import overlay_mask from PIL import Image cam_extractor GradCAM(model, target_layerlayer4) x val_dataset[0][0].unsqueeze(0).to(device) out model(x) activation cam_extractor(0, out) # 将热力图叠回原图 original_img val_dataset[0][0].permute(1, 2, 0).numpy() heatmap activation.squeeze().cpu().numpy() overlay overlay_mask(original_img, heatmap, alpha0.6) plt.imshow(overlay) plt.axis(off) plt.show()GradCAM的target_layerlayer4指定使用 ResNet-18 最后一个残差块的特征图计算热力图。运行后你会看到热力图集中在细胞核区域还是胞浆区域这能帮你判断模型是否学到了合理的形态特征。如果热力图散在图外或背景上说明模型可能在学背景纹理或染色伪影这时要回到数据检查看是否混入了与类别无关却不小心相关的信息。这个验证手段比调多少个参数都更能说明模型是否真的可信。血细胞图像分类数据集4分类不是一个“难做”的数据集但做好它需要花大力气处理类别不均衡、染色差异和标注噪声。我自己的教训是第一次做的时候只顾着调模型结构忽略了数据层面的问题结果整体准确率刷到 92%逐类一看单核细胞和嗜酸性粒细胞直接崩盘复盘后才发现是拆分时混入了重复图像。后来每次拿到数据先花半天做检查和分层拆分再做训练反而省了后面几天的调参时间。希望帮到你。本文还有配套的精品资源点击获取