☰
基于ISIC数据集的皮肤癌检测:EfficientNet与Focal Loss实战
2026/9/24 22:45:11 网站建设 项目流程

1. 项目缘起与整体设计思路

ISIC 这个缩写,在皮肤影像圈子里基本等同于“国际皮肤影像协作组织”维护的那套公开数据集。我最早接触它是在做一个皮肤病变分类的课题,当时手头没有医院内部数据,ISIC Archive 就成了最现实的起点。这个项目标题“ISIC-皮肤癌检测”,说白了就是拿 ISIC 提供的皮肤镜图像,训练一个能区分良性病变和恶性病变(尤其是黑色素瘤)的模型。它解决的核心问题是:基层医疗机构缺乏皮肤科专家,很多皮肤癌在早期被漏诊,而一套自动化的图像筛查工具可以充当“第一道分诊”。

适合谁来参考这篇内容?如果你有基本的 Python 和深度学习基础,想做一个有真实医学背景的图像分类项目,或者你正在准备相关方向的毕业设计、课程作业,那这篇东西基本可以照着复现。即便你只是好奇“AI 怎么看皮肤病”,我也会把关键环节拆开讲清楚,不堆公式,尽量说人话。

我先把整体思路摆出来。皮肤镜图像分类和普通的猫狗分类有本质区别:类别极度不平衡(良性样本远多于恶性),图像之间存在大量相似性(同一患者的不同部位、同一病变的不同角度),而且误判代价不对称——把恶性判成良性叫漏诊,后果严重;把良性判成恶性叫误诊,会造成不必要的焦虑和活检。所以整个项目的设计不能只盯着准确率,得把召回率、AUC 这些指标放在更重要的位置。

基于这个判断,我的方案选型是这样的:主干网络用 EfficientNet 系列,因为它在参数量和精度之间平衡得比较好,皮肤镜图像分辨率通常不高(ISIC 里很多是 600x450 左右),太深的网络容易过拟合。数据增强方面,除了常规的翻转、旋转,我特意加了颜色抖动和随机裁剪,因为皮肤镜的色温、光照在不同设备间差异很大。损失函数上,我用了带类别权重的交叉熵,后来换成 Focal Loss,对难分类样本和少数类更友好。评估阶段,我坚持按患者 ID 划分训练集和验证集,而不是随机划分图像——这一点后面会详细说,是踩过坑才明白的。

为什么不用更复杂的多模态方案(比如结合患者年龄、性别、病变部位)?因为 ISIC 的元数据字段在不同年份的数据集里并不统一,很多图像缺少完整的临床信息。强行拼接反而引入噪声。所以这个项目定位是“纯图像输入的基线方案”,先把图像这条路走通,再考虑融合其他信息。

2. 数据准备与核心细节解析

2.1 ISIC 数据集的获取与结构理解

ISIC Archive 上的数据是分批发布的,比较常用的有 ISIC 2016、2017、2018、2019、2020 几个版本。2018 年的任务最经典:给定一张皮肤镜图像,预测它属于七类中的哪一类(包括黑色素瘤、基底细胞癌、鳞状细胞癌等)。2019 和 2020 则更偏向二分类(良性 vs 恶性)。我建议新手从 ISIC 2019 或 2020 入手,因为类别定义更清晰,标注质量也更高。

下载方式上,官方提供了 CSV 标注文件和图像压缩包。我一般先用 pandas 读 CSV,看看每个类别的样本数量分布。这里有个细节:ISIC 的 CSV 里有一列patient_id或者lesion_id,这是做患者级划分的关键。很多教程直接用train_test_split随机划分图像,结果同一个病变的不同角度照片同时出现在训练集和验证集里,验证指标虚高得离谱。我实测过一次,随机划分下 AUC 能到 0.98,按患者划分后掉到 0.85 左右——后者才是真实水平。

数据清洗环节,我会做三件事:第一,剔除图像文件损坏或尺寸异常的样本;第二,检查标签是否有明显错误(比如同一 lesion_id 对应多个不同标签,这种通常是标注冲突,直接丢弃);第三,统计每个类别的样本数,为后续的类别权重计算做准备。

2.2 图像预处理的关键参数选择

皮肤镜图像有个特点:四周常有黑色边框或圆形视野遮挡。如果直接 resize 到 224x224,这些无效区域会干扰模型。我的做法是先做中心裁剪,裁掉边缘 10% 到 15% 的区域,再 resize。裁剪比例不是拍脑袋定的,我对比过 5%、10%、15%、20% 四档,10% 到 15% 之间验证集表现最稳,裁太多会丢失病变边缘信息。

颜色空间方面,RGB 是默认选择,但我试过转成 HSV 再取 H 和 S 通道拼接,对黑色素瘤的识别略有提升(AUC 涨了约 0.01),但计算开销增加,最终没采用。归一化用 ImageNet 的均值和标准差就行,因为主干网络是在 ImageNet 上预训练的,保持一致比较省事。

还有一个容易被忽略的点:图像尺寸。ISIC 原始图像分辨率差异很大,有的 1024x1024,有的 600x450。我统一 resize 到 256x256 再随机裁剪到 224x224 作为训练输入,验证和测试时则用中心裁剪到 224x224。这样既保留了增强的随机性,又保证了推理时的确定性。

2.3 数据增强策略与避坑要点

增强策略我分成了“几何变换”和“颜色变换”两组。几何变换包括随机水平翻转、随机垂直翻转、随机旋转(±30 度)、随机缩放(0.9 到 1.1 倍)。颜色变换包括亮度、对比度、饱和度、色调的轻微抖动,幅度控制在 0.1 到 0.2 之间。注意,皮肤镜图像的颜色是诊断的重要依据,抖动幅度不能太大,否则会把黑色素瘤的典型颜色特征破坏掉。

提示:不要用 Cutout 或 Mixup 这类强增强。我试过 Mixup,训练损失下降得很漂亮,但验证集召回率反而降了。原因是皮肤病变的局部特征很关键,混合两张图会让模型学到不存在的纹理组合。

另外,我强烈建议在增强管道里加一步“随机擦除小区域”,面积控制在图像的 2% 到 5%。这能模拟皮肤镜图像中常见的毛发遮挡或气泡干扰,提升模型鲁棒性。实测下来,加了这一步之后,模型在外部测试集上的表现稳定了不少。

3. 模型构建与训练实操全流程

3.1 主干网络选型与修改细节

我对比过 ResNet50、DenseNet121、EfficientNet-B0 到 B3 几个选项。在 ISIC 2019 上的结果如下:

主干网络参数量验证集 AUC单张推理耗时(GPU)
ResNet5025M0.8728ms
DenseNet1218M0.88112ms
EfficientNet-B05.3M0.8896ms
EfficientNet-B312M0.89311ms

最终选了 EfficientNet-B0,理由是参数量小、推理快,AUC 只比 B3 低一点点。对于要部署到边缘设备或者做实时筛查的场景,B0 更实用。如果你追求极致精度且不在乎速度,B3 或 B4 也可以。

修改部分很简单:把原网络的分类头(通常是 1000 类的全连接层)换成自己的类别数。我一般加一个 Dropout 层(rate=0.3)再接全连接,防止过拟合。另外,EfficientNet 的输入尺寸是 224x224 时,内部会做多次下采样,最终特征图是 7x7,足够捕捉全局信息。

3.2 损失函数与类别权重的计算过程

ISIC 2019 的类别分布大概是:黑色素瘤占 12%,基底细胞癌占 15%,鳞状细胞癌占 5%,其余良性类占 68% 左右。这种不平衡下,如果直接用交叉熵,模型会倾向于预测多数类。我用了带权重的交叉熵,权重计算公式是weight_i = total_samples / (num_classes * samples_i)。以黑色素瘤为例,权重约为 68/(7*12) ≈ 0.81,而良性类的权重会低很多。

后来我换成了 Focal Loss,公式是FL = -alpha * (1-p)^gamma * log(p),其中 gamma 取 2,alpha 按类别频率设置。Focal Loss 的好处是让模型更关注那些“已经预测对了但置信度不高”的样本,对难分类的黑色素瘤边界案例特别有效。实测下来,Focal Loss 比加权交叉熵在恶性类召回率上高了约 3 个百分点。

注意:Focal Loss 的 alpha 和 gamma 需要调。我试过 gamma=1、2、3,最终 2 最稳。alpha 不要设得太极端,否则训练初期损失震荡严重。

3.3 训练循环与学习率调度

优化器用 AdamW,初始学习率 1e-4,权重衰减 1e-5。学习率调度用余弦退火,周期设为 30 个 epoch,最小学习率 1e-6。Batch size 设为 32,如果显存不够就降到 16,但学习率也要相应减半。

训练过程中我加了早停机制,监控验证集的 AUC,如果连续 8 个 epoch 没有提升就停止。实际训练下来,通常在 25 到 35 个 epoch 之间收敛。每个 epoch 结束后,我会记录训练损失、验证损失、验证 AUC、恶性类召回率四个指标。这里有个经验:验证损失和 AUC 有时会背离,比如损失还在降但 AUC 已经平了,这时候以 AUC 为准,因为分类阈值可以后续调整。

代码层面,我用的是 PyTorch,数据加载用DataLoader配合num_workers=4。有一个小技巧:把图像预处理放在 GPU 上做(用torchvision.transforms的 GPU 版本或者自己写 CUDA kernel),能明显加快训练速度。不过对于 ISIC 这个数据量(约 2.5 万张),CPU 预处理也够用,瓶颈主要在模型前向传播。

3.4 阈值调整与评估指标选择

训练完模型后,默认用 0.5 作为二分类阈值。但皮肤癌检测场景下,我建议把阈值调低,比如 0.3 或 0.35,这样能提高恶性类的召回率,代价是误诊率上升。具体调多少,要看你的应用场景:如果是筛查工具,宁可误诊也不能漏诊,阈值可以设到 0.25;如果是辅助诊断,阈值可以设到 0.4 左右。

评估指标我重点关注三个:AUC(整体排序能力)、敏感度(恶性类召回率)、特异度(良性类召回率)。在 ISIC 2019 上,我的最终模型达到了 AUC 0.91,敏感度 0.82,特异度 0.88。这个水平在公开基线里算中上,但离临床可用还有距离——临床要求敏感度通常要在 0.95 以上。

4. 常见问题与排查技巧实录

4.1 验证集指标虚高怎么办

这是最常见的问题,根源几乎都是数据泄露。排查步骤:第一,检查划分时是否用了patient_id或lesion_id做分组;第二,检查增强管道里有没有意外把验证集图像混入训练集;第三,检查是否有重复图像(同一张图在不同类别下出现)。我遇到过一次,CSV 里有两行指向同一张图但标签不同,导致模型学到了矛盾信息。解决办法是去重,保留标签置信度高的那条。

4.2 模型对某些类别完全不敏感

如果某个恶性类(比如鳞状细胞癌)的召回率极低,先看样本数。ISIC 2019 里鳞癌只有几百张,模型很难学到足够特征。我的应对策略是:第一,用类别权重把它的损失放大;第二,对该类做额外的增强(比如更强的旋转和缩放);第三,如果还是不行,考虑合并类别(比如把鳞癌和基底细胞癌合并为“非黑色素瘤恶性”),但这样会损失细粒度信息。

4.3 训练损失震荡严重

通常是学习率太大或 batch size 太小。我试过 batch size=8 时,损失曲线像心电图。解决办法是增大 batch size 或者用梯度累积。另外,检查数据增强里有没有过于激进的变换,比如颜色抖动幅度超过 0.3,会导致同一张图在不同 epoch 里差异过大,模型难以收敛。

4.4 推理时结果不稳定

同一张图多次推理结果不同,说明模型有随机性。排查:第一,确认推理时是否关闭了 Dropout 和 BatchNorm 的训练模式;第二,确认数据预处理是否用了随机变换(推理时应该用确定性的中心裁剪和归一化);第三,如果用了 Test Time Augmentation(TTA),结果本身就会有波动,取平均即可。我一般用 5 次 TTA(原图、水平翻转、垂直翻转、旋转 90 度、旋转 180 度),AUC 能提升约 0.005 到 0.01。

4.5 常见问题速查表

问题现象可能原因排查方法解决措施
验证 AUC 远高于测试 AUC数据泄露检查患者 ID 划分按患者分组重新划分
恶性类召回率低于 0.5类别不平衡统计类别分布加权损失或 Focal Loss
训练损失不下降学习率过小或网络未冻结打印梯度范数调大学习率或解冻部分层
推理速度慢输入尺寸过大检查 resize 参数降到 224x224
模型对毛发遮挡敏感增强不足可视化错误样本加随机擦除增强

4.6 独家避坑心得

第一个心得:不要迷信公开排行榜上的高分。很多高分方案用了外部数据或者集成了几十个模型,实际部署成本极高。我建议先把单模型做到 AUC 0.88 以上,再考虑集成。

第二个心得:皮肤镜图像的“黑色边框”处理很关键。我试过不裁剪直接 resize,AUC 掉了 0.03。后来写了一个自动检测圆形视野的函数,把视野外的像素置零,效果更好。

第三个心得:如果要做多分类,类别合并要谨慎。我试过把“光化性角化病”和“鳞状细胞癌”合并,因为前者是后者的癌前病变,结果模型把很多良性角化病也判成了恶性,特异度大幅下降。后来还是分开处理。

第四个心得:保存模型时,除了权重,一定要把预处理参数(均值、标准差、输入尺寸)和类别映射表一起存下来。我吃过亏,换了一台机器推理,忘了归一化参数,结果全错。

5. 部署与后续扩展的实操建议

模型训练完之后,我一般会导出为 ONNX 格式,再用 ONNX Runtime 做推理。这样不依赖 PyTorch 环境,部署到服务器或边缘设备都方便。导出时注意把动态维度设好,尤其是 batch 维度,否则只能一次推理一张图。

如果要做成 Web 服务,可以用 FastAPI 写一个简单的接口,接收图像上传,返回预测类别和置信度。前端用 Gradio 或 Streamlit 快速搭一个演示页面,方便非技术人员试用。我实测过,EfficientNet-B0 在 CPU 上单张推理约 50ms,GPU 上 6ms,完全能满足实时筛查的需求。

后续扩展方向有几个:第一,引入患者元数据(年龄、性别、病变部位)做多模态融合,但前提是元数据完整;第二,用半监督学习利用未标注的 ISIC 图像,提升泛化能力;第三,做不确定性估计,让模型在低置信度时主动要求人工复核。这些方向我都试过一部分,效果有正有负,但整体思路是通的。

我个人在实际操作中的体会是,ISIC 皮肤癌检测这个项目,技术门槛不算高,但数据处理的细节特别多。很多时间不是花在调模型上,而是花在清洗数据、检查泄露、调整阈值上。如果你刚开始做,建议先把数据划分做对,再谈模型优化。否则指标再好看,也是空中楼阁。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询