ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

乳腺癌图像分类实战:从数据集选型到迁移学习的深度学习指南

乳腺癌图像分类实战:从数据集选型到迁移学习的深度学习指南 简介面向深度学习和医学图像分类初学者这份乳腺癌症图像分类数据集可作为图像二分类任务的练手与教学素材。数据按目录整理同类别样本归于同一文件夹整体划分为训练集约480张、验证集约140张、测试集约70张并附有JSON类别文件和Python脚本便于读取标签与构建数据管道。压缩包共692个文件以JPG图像为主体辅以PNG示意图、py脚本和JSON配置大小约17.85MB轻量易下载适合在普通算力环境下快速跑通CNN、ResNet等分类模型。目前已有286人学习下载适合入门者做实验、课堂演示或毕业设计预研尤其便于从数据预处理、模型训练到结果评估形成完整流程。1. 乳腺癌症图像分类数据集先搞清楚你在跟什么样的小众视觉任务打交道深度学习数据集里的乳腺癌症图像分类任务这几年被越来越多算法团队当作医学影像落地的第一站。标题里写的“乳腺癌症”就是乳腺癌而这项任务的核心矛盾很反直觉真正决定模型好坏的往往不是网络结构而是数据集怎么切、标签怎么定义、评价指标怎么选。钼靶、超声、核磁或者病理切片上真正有病灶的区域可能只占一张图的百分之几甚至千分之几标签也常常不是“整图有癌”而是“这个可疑区域是良性还是恶性”。这篇文章写给两类人一是想拿真实小数据任务练手的深度学习工程师二是医疗影像产品落地前要把数据、模型和评估体系理顺的算法同学。后面按“选数据集 → 预处理 → 训练 → 排查 → 验证”展开每一步都给出具体命令和可调参数。2. 公开数据集选型CBIS-DDSM、INbreast、BUSI 怎么挑以及类不平衡问题2.1 乳腺影像的四种模态同一“乳腺癌”标签视觉差异巨大做乳腺图像分类第一步不是找模型而是确定你的原始图像是什么模态。钼靶是 X 光把乳房压扁后拍出来的视野大、分辨率高病灶通常是白色小团块或细条状钙化背景里还有大量腺体纹理超声是动态扫查里截出的单帧噪声明显病灶边界相对清晰但设备、探头频率对图像影响很大磁共振是序列扫描一张病例可能带几十上百个层面标签往往按病例而不是按层给病理切片是染色后的细胞图像目标不再是“找肿块”而是“看细胞核密度和结构”。这个模态差异直接决定你该不该做颜色标准化、该不该用灰度图、该不该把训练限制在某个 ROI 内。很多公开数据集来自不同国家地区的医院扫描仪品牌和曝光条件各不相同亮度、对比度分布差很多。哪怕都叫“乳腺图像分类”你在一个数据集上学到的亮度规律换到另一台设备上可能完全失效。我一般会在选定数据集后单独做一个“按设备/按拍摄中心分组”的探索性分析而不是直接全量随机划分训练集和验证集。2.2 五个常见公开数据集对比做乳腺图像分类绕不开的公开数据集主要是以下几类数据集模态常见规模标签粒度主要用途CBIS-DDSM钼靶1000 病例ROI 级别病灶级 ROI、良性/恶性、钙化/肿块二分类、检测、ROI 分类INbreast钼靶115 例 / 约 400 张病例级 病灶标注检测、分类、分割MIAS钼靶胶片扫描322 张图像级 病灶位置快速原型、入门BACH病理切片400 张图像级四分类病理图像分类BUSI超声约 780 张图像级、正常/良性/恶性超声二分类/三分类选型时我一般按这个顺序问自己要解决的是“整图有没有病”还是“这个 ROI 是良性还是恶性”。前者可以选 INbreast、MIAS 这类带图像级标签的数据后者建议直接看 CBIS-DDSM因为它把 DDSM 里的病灶裁剪出来提供 ROI 掩码和良恶性标注能省掉大量在全图上找病灶的预处理。BACH 是病理模态如果不是做病理不要因为它“图多”就选染色差异和临床场景都不一样。CBIS-DDSM 是很多钼靶分类论文的基准但有个容易踩的细节它本身是 DDSM 的重整理版本原始扫描是 16bit 灰度 DICOM包含“钙化簇”和“肿块”两类病灶统计口径和病例数经常被论文引用得不一样。复现时一定要确认别人用的是哪个任务定义是 ROI 分类还是全片分类有没有排除“正常”类别。我见过不少同学拿 CBIS-DDSM 的 ROI 分类结果去跟全片分类结果比精度然后怀疑自己代码有 bug其实只是任务定义没对齐。BUSI 在超声赛道很常用但它存在一个不太被提及的问题病例数少、类别比例不均衡恶性样本占比较少且不同论文对同一张图的预处理方式差异很大。如果你拿 BUSI 做“正常/良性/恶性”三分类要留意正常样本数量最多直接看 accuracy 会得到一个虚高的值。后面讲损失函数时会专门说怎么用加权方式处理这个不平衡。2.3 ROI 与全图标签选错标签粒度模型学到的就是“亮度”乳腺图像分类最隐蔽的坑是标签粒度选错。拿全图标签直接训 CNN模型很容易学到全局背景特征比如“这台扫描仪的图画偏亮所以是恶性”。这在实际部署里就是灾难换一台机器立刻崩。更稳妥的做法是优先使用带 ROI 或病灶框的数据集先切到病灶区域再分类如果没有 ROI就用分割模型或医生画的框先自动裁一遍宁可多裁再用分类模型过滤。另外公开数据集的标签里经常出现“良性但可疑”这类过渡类别。很多论文会把它直接归入良性或者直接丢弃两种做法都合法但必须写进实验记录。否则复现时你会发现别人源码里设了两类自己怎么跑都是三类问题。还要确认数据集有没有区分“钙化”和“肿块”这两种病灶形态和病理机制完全不同混在一起训练会让模型学成“大而白的是肿块小而白的是钙化”这不是你真正要的分类能力。类不平衡是乳腺数据绕不开的问题恶性样本通常比良性少正常样本又比前两者多。我处理不平衡的第一原则不是急着上采样或下采样而是先把评估指标定下来。这个任务里灵敏度召回、特异性、ROC-AUC 比 accuracy 有意义得多。如果测试集里的恶性样本只有几十张别只看 top-1 准确率一定要打开混淆矩阵看每类到底分成了什么样。原始数据格式也差别很大。CBIS-DDSM 和 INbreast 大多是 DICOMMIAS 是老胶片扫描的 PGM 类格式BACH 发布的是 TIFF 病理大图BUSI 常见 PNG。预处理第一步就该把 DICOM 的 16bit 值域搞清楚在转成普通 8bit 图之前先存一份中间产物比如 HDF5 或 NumPy 数组。所有后续实验都从同一份预处理结果出发能省掉很多“我预处理和你不一样”的扯皮。3. 数据预处理与增强乳腺图像分类里的前处理链路与参数3.1 加载 DICOM 与归一化先解决“图是 16 bit”的问题钼靶 DICOM 通常不是 8bit 图像像素值范围可能在 0-4096 甚至更宽。直接用图像库打开再存成 8bit等于把大量低对比度信息直接压没。常见做法是用 pydicom 读出像素数组再做百分位裁剪。我一般用 2% 到 98% 分位作为上下界把两端离群像素截掉再线性映射到 [0,1]。如果图里有定位贴片、过曝区域或者扫描仪自带的字符这几个高亮点会把 min-max 归一化带偏整张图对比度会变得很奇怪。import numpy as np import pydicom def dicom_to_float(path: str, lo: float 2.0, hi: float 98.0) - np.ndarray: dcm pydicom.dcmread(path) img dcm.pixel_array.astype(np.float32) if dcm.RescaleIntercept is not None and dcm.RescaleSlope is not None: img img * float(dcm.RescaleSlope) float(dcm.RescaleIntercept) low, high np.percentile(img, [lo, hi]) img np.clip(img, low, high) img (img - low) / (high - low 1e-8) return img这里先把 RescaleSlope 和 RescaleIntercept 应用上再做百分位截断。pydicom 读出来的 pixel_array 有时已经应用过 rescale但不同设备未必一致所以最好先检查元数据里有没有这两个字段。1e-8 是为了避免低和高相等时除零。输出保持为 float32 的 [0,1] 数组先不要乘 255 存成整数因为后面要做数据增强和归一化精度保留得越多越好。3.2 ROI 裁剪与背景去除把分类问题先变成“只看病灶区”全片图直接缩放到 224x224会把病灶细节压得非常小而且背景占绝对主导。如果数据集自带了掩码就按掩码裁剪如果没有掩码常见做法是先做一个简单阈值粗分割乳腺影像里乳房区域的灰度通常高于纯黑背景把最大连通域之外的部分清掉。技巧是不要只裁最小外接矩形要留一点余量让分类器能看到病灶周围的组织纹理因为乳腺影像诊断本身就依赖“病灶与周围腺体的对比”。def crop_from_mask(img: np.ndarray, mask: np.ndarray, pad: int 20, min_side: int 64): ys, xs np.where(mask 0) if len(ys) min_side: return img y0, y1 max(ys.min() - pad, 0), min(ys.max() pad, img.shape[0]) x0, x1 max(xs.min() - pad, 0), min(xs.max() pad, img.shape[1]) return img[y0:y1, x0:x1]边界裁剪时要注意坐标不能变负数所以用 max、min 夹住。pad 我一般设置在 16 到 32 像素之间太小会让增强旋转后病灶出画太大又会重新引入背景。min_side 用来过滤标注噪声如果某个掩码只有几十个像素这种 ROI 大概率是标错或极小伪影强行裁给模型只会让模型学噪声。如果做的是全片分类我建议把左右翻转增强先关掉。乳腺结构有解剖侧向性左乳和右乳的组织纹理并不是完全镜像对称的。公开数据集实验里可以开但到真实产品里左右翻转会给模型解释引入非常别扭的结果。3.3 数据增强小数据集最值得先调的旋钮乳腺数据集通常只有几百到几千张直接上深层 CNN 几乎必过拟合所以增强不是“锦上添花”而是训练管线里的主功能模块。我常用 Albumentations因为它把裁剪、旋转、亮度抖动、归一化拼在一起还方便后续对同一个样本做多次采样。要注意乳腺影像的增强策略跟 ImageNet 不一样不建议开水平翻转不建议用会把腺体纹理抹掉的强模糊也不太建议直接用 AutoAugment 那种带随机擦除的增强策略。更稳妥的组合是随机小角度旋转、随机缩放裁剪、轻微亮度对比度扰动。import albumentations as A train_transform A.Compose([ A.RandomResizedCrop(size(224, 224), scale(0.75, 1.0), ratio(0.9, 1.1)), A.Rotate(limit15, border_mode0, value0), A.RandomBrightnessContrast(brightness_limit0.15, contrast_limit0.15, p0.3), A.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) valid_transform A.Compose([ A.Resize(224, 224), A.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ])RandomResizedCrop 的 scale 下限不要设太低0.75 以上我比较放心因为病灶占比本来就小裁得太狠等于把病灶裁没了。Rotate 限制在 15 度左右钼靶影像中病灶形态与拍摄方向有关过度旋转会制造出临床上不存在的形态。border_mode0 并设置 value0 做黑色填充而不是反射填充原因是医学影像里反射填充会伪造组织纹理模型可能学到以画幅边缘判断类别这是容易翻车的地方。Normalize 先用 ImageNet 的 mean 和 std因为后续要用 ImageNet 预训练权重输入分布保持一致。做完这套预处理建议把中间产物统一保存成 HDF5 或 npy 文件同时记录每个样本的原始来源、裁剪坐标和是否翻转。这个“后悔药”很重要后面任何人改完模型都不需要重新做几十 GB 的影像 IO而且还能追溯到底哪个样本被裁坏了。4. 用 ResNet/EfficientNet 训练乳腺图像分类模型最小训练流程与关键参数4.1 迁移学习为什么选 ImageNet 预训练而非随机初始化乳腺图像分类数据集通常只有几百到几千张随机初始化训练深层 CNN大多数情况下只会得到“loss 缓慢下降但泛化很差”的模型。常见做法是用 ImageNet 预训练权重做迁移学习网络底层提取的是边缘、纹理这类通用特征跟医学图像并不冲突。真正的争议在于该全量微调还是冻结前几层只训后半段。我一般分两步先整网用小学习率微调如果发现前几层梯度更新过大而后层更新过慢再把前几层冻结掉避免破坏底层特征。在模型结构上ResNet50 是保守选择计算成本低、复现点多EfficientNet-B0 或 B1 在同样精度下参数更少适合快速原型。这几年也有一些适合小数据的混合模型但从工程角度看先跑通 ResNet 再换模型是最高效的路径。不要太早依赖论文里“最新图像分类算法”的复现结果那个前提往往是几万张图的训练集而你现在可能只有几百张。4.2 损失函数和类别权重不平衡数据上的三选一对于正常、良性、恶性三分类直接交叉熵会在正常样本占比高时让模型偏向多数类。常用替换方案有三个给交叉熵加类别权重、用 Focal Loss、用加权采样。我对小数据集的第一选择是“类别权重 加权采样”搭配使用。权重按 1 除以类别样本数计算再归一化采样时给少数类多复制几次但不要无脑复制到完全均匀因为恶性样本总数少过分重复会让验证集和训练集出现隐式重叠。实际训练时我会先拿 5 折验证比较加权交叉熵和 Focal Loss。Focal Loss 的 gamma 从 2 开始调alpha 按类别比例设置。但在几百张训练集上gamma 太大比如 5 会让难样本主导训练反而把 loss 拖得很高。很多论文推荐 gamma2但在小数据上我见过 gamma1 结果更稳定。这条属于比较反直觉的经验需要用验证集 AUC 来判断不要凭训练 loss 决定。4.3 训练参数的落地lr、batch size、checkpoint迁移学习里学习率是最难一次调对的参数。我的起步值是 1e-4AdamW或 1e-3SGD with momentum配合余弦衰减。用 ImageNet 预训练权重做全量微调时一开始就用 1e-3 的 AdamW很容易让底层 BN 统计量出问题loss 变成一个黑匣子。所以我会先用 batch size 16 或 32 跑 10 个 epoch 的观察阶段确认 train loss 在合理下降再决定放大学习率还是增加总轮数。import torch import torch.nn as nn from torchvision import models model models.resnet50(weightsmodels.ResNet50_Weights.DEFAULT) model.fc nn.Linear(model.fc.in_features, 3) criterion nn.CrossEntropyLoss( weighttorch.tensor([0.3, 0.4, 1.2]) # 正常/良性/恶性的反频率权重 ) optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-2) for epoch in range(30): model.train() for images, labels in train_loader: images, labels images.to(device), labels.to(device) preds model(images) loss criterion(preds, labels) optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm5.0) optimizer.step()CrossEntropyLoss 的 weight 决定了对少数类的补偿力度。注意不要直接把“恶性比正常等于 1 比 4”按原值填进去那样权重过大模型会把一切判成恶性。先按样本数反比算好再整体归一化。clip_grad_norm 在医学小数据上很有用预训练模型微调的第一个 batch 梯度常会出现异常大值卡一下能避免 loss 直接变成 NaN。这里还有个常见单位陷阱常有人问“深度学习里的 parameter 应该不是 MB 吧”。ResNet50 的参数量约 2560 万PyTorch 保存 fp32 的 checkpoint 约 100MB。如果你训练时显存爆了先调 batch size而不是急着换小模型。训练日志我会记录四个数train loss、验证 accuracy、验证 AUC、混淆矩阵。不要只盯 accuracy这个任务里恶性样本的召回比整体准确率更有价值。每 5 个 epoch 存一次 checkpoint同时保存优化器状态避免训练中断后要从头再来。5. 常见问题与排查乳腺图像分类的五个坑现象、原因和解决5.1 训练 Loss 不降准确率卡在水平线附近现象第一个 epoch 之后 train loss 几乎不动准确率一直停留在随机水平换 ResNet、EfficientNet 都一样。原因最常见的是非 ROI 样本太多模型收到一堆全黑或纯背景图另一个高频原因是标签和文件名错位比如数据加载时按文件名排序但 CSV 里的标签没有对齐模型在学一张“文件名到随机标签”的映射。解决先做数据加载自检取一个 batch把 images 和 labels 一起打印出来确认图片内容与标签真实对应。然后统计每张样本的有效像素比例把全黑、全白样本单独过滤或放进困难样本集合。我自己的习惯是先训练 3 个 epoch 再看混淆矩阵如果某一类完全没被预测出来九成是标签错位而不是模型结构有问题。5.2 准确率高但 ROC-AUC 低指标打架现象验证集 accuracy 有 90%但 ROC-AUC 只有 0.75 左右临床同事反馈漏检了不少恶性。原因类别不平衡下 accuracy 被多数类抬起来了模型只是学会了把大多数样本判成正常恶性样本的召回率很低。解决立即把优化目标从 accuracy 换到加权损失并把恶性样本召回率作为主要报告指标。查看验证集 PR 曲线在乳腺数据上 PR-AUC 比 ROC-AUC 更贴近真实场景因为医生更关心“我检出的人里到底有多少是真的恶性”。如果测试集中正常样本特别多ROC 容易显得乐观这种情况用 PR 曲线能看到更真实的下限。5.3 验证集正常测试时被全黑或全白样本打穿现象公开数据集上指标很好看换到另一家医院的测试集准确率从 90% 掉到 60%。原因模型学到了设备风格包括亮度、边框、分辨率纹理而不是病灶结构。这种特征偏移在乳腺影像里特别常见尤其是直接从 DICOM 读出原始像素不做归一化的团队。解决两个方向。数据层面做颜色归一化和对比度标准化上面 3.1 的百分位截断就是为这一步。评估层面做“跨数据域验证”比如用 CBIS-DDSM 训练再用 INbreast 做外部验证哪怕样本量不大也能暴露亮度相关泄漏。不要只迷信公开测试集分数那只是同分布成绩。5.4 数据量小、模型严重过拟合现象train loss 一路降到 0.1验证 loss 在第 10 个 epoch 后开始回升验证 AUC 停滞在 0.8 附近上不去。原因几百张训练图配深层 CNN参数空间太大ROI 裁剪后同一病灶的增样互相太像模型等于记住了训练样本。解决先减模型复杂度把 ResNet50 换成 ResNet18 或高效模型观察验证 AUC 是否反而上升然后调强增强特别是旋转和随机缩放裁剪的概率最后配合第 6 章的 K 折验证。这里记住一个心理预期乳腺 ROI 分类在小数据集上验证 AUC 到 0.8 已经是打磨得不错的状态别拿 ImageNet 上那种 95% 的预期来带这个任务。5.5 调参后结果忽高忽低复现不稳定现象同样的代码、同样的数据集昨天跑 AUC 0.84今天重跑变成 0.79看起来像玄学。原因训练时 shuffle、随机增强和 Dropout 的随机性在小数据集上影响很大。另一个隐藏点是验证集划分没固定每次脚本重启都重新随机划分。解决固定全局随机种子把训练集和验证集的划分文件写成 CSV 提交进版本库数据加载器也使用固定顺序。我把划分配置也当代码一样管理因为对乳腺影像这种小数据来说划分方式对分数的影响可能比换模型还大。下一步再做重复多次实验取均值和标准差而不是拿单次结果写结论。6. 让模型从“能跑”变成“可信”分层交叉验证与部署前检查清单6.1 五次分层 K 折小数据集上最值得先做的一步单次随机划分在乳腺影像上很难给出可发布的结论。我通常用分层 K 折做五次重复统计均值与标准差并且保证同一个患者的多个视图被分到同一折。这样才能避免“同一个病人的两张图分别出现在训练集和验证集”导致的乐观偏差。数据集如果带 patient_id这一步是必做的没有 patient_id 时我会先做去重检查把高度相似的样本挑出来。6.2 部署前检查清单把该验证的指标逐项跑一遍每次实验结果记录混乱是乳腺影像项目里最容易被低估的返工原因。我收尾时通常会逐项核对下面这些内容检查项具体做法通过标准每折稳定性记录每折的 AUC、恶性召回、混淆矩阵五折标准差小于 0.03跨设备稳定性用另一家医院的数据做外部验证外部 AUC 不低于内部 0.8 倍输入尺寸敏感度测试 192、224、256 三种尺寸分数波动小于 0.02概率阈值校准找让恶性召回优先的阈值召回率显著高于默认 0.5低置信度分析单独挑出概率在 0.4-0.6 的样本确认这些样本可人工复核乳腺影像不是“模型准确率高就能上线”的任务。临床上你更需要知道模型什么时候会犹豫而不是强行给出二分类。我最后悔的一次实验就是只看平均 AUC 上线测试结果漏检的两个样本刚好都是早期钙化。现在我会在项目收尾前专门跑一次低置信度样本分析把概率在 0.4 到 0.6 之间的图挑出来交给医生复核。这个工作占不了多少时间但能大大降低漏检风险。希望对你有所帮助。本文还有配套的精品资源点击获取
返回列表