♂️ 个人主页艾派森的个人主页✍作者简介Python学习者 希望大家多多支持我们一起进步如果文章对你有帮助的话欢迎评论 点赞 收藏 加关注目录1.项目背景2.数据集介绍3.技术工具4.实验过程4.1导入数据4.2数据可视化4.3特征工程4.4构建模型4.5训练模型4.6模型评估4.7模型预测5.总结源代码1.项目背景在皮肤病理学领域黑色素瘤因其极高的致死率和隐蔽的早期特征被公认为“皮肤癌之王”。这种恶性程度极高的肿瘤其早期形态常与普通的良性痣高度相似肉眼诊断往往依赖于经验丰富的皮肤科医生且容易受到光影、肤色及病灶微观纹理的干扰。统计数据显示黑色素瘤的早期发现与晚期治疗在预后生存率上存在巨大差异因此开发一种高精度、非侵入式的自动化辅助诊断工具不仅是医学影像技术的突破方向更是挽救患者生命的现实需求。本项目立足于计算机视觉的前沿架构探索利用EfficientNetV2S模型对大规模黑色素瘤数据集进行深度学习与特征分类。该数据集包含 13,900 张经过严格筛选的皮肤病灶图像涵盖了从良性色素痣到恶性黑色素瘤的多种复杂表现形式。相比于传统的人工特征提取EfficientNetV2S 凭借其改进的融合卷积Fused-MBConv和渐进式学习机制能够以极高的计算效率捕捉到病灶边缘的不规则性及内部色素分布的异质性。我们通过对 224 x 224 像素维度的图像进行像素级归一化与全量微调旨在构建一个能够精准识别微观病变信号的智能系统。本项目的核心目标不仅是追求高达 97% 的恶性样本召回率更希望通过数据驱动的方法为临床医生提供一个可靠的“数字放大镜”从而在像素经纬之间重新定义癌症的早期检测标准。2.数据集介绍本实验数据集来源于Kaggle本数据集包含 13,900 张精心挑选的图像是推动皮肤病学和计算机辅助诊断领域发展的宝贵资源。深入探索黑色素瘤的复杂世界每一个像素都可能重新定义早期检测。黑色素瘤是一种致命的皮肤癌需要及时准确的诊断。该数据集利用最先进的技术助力研究人员和临床医生开发强大的机器学习模型从而区分良性和恶性病变。这些图像尺寸统一为 224 x 224 像素全面展现了黑色素瘤的各种表现形式。该数据集的灵感源于皮肤病学领域对先进诊断工具的迫切需求。图像来源于多种渠道展现了传统诊断方法难以应对的复杂特征。3.技术工具Python版本:3.9代码编辑器jupyter notebook4.实验过程4.1导入数据在医学影像分类任务中环境的稳定性与数据流水线的效率直接决定了模型最终的收敛质量。我们首先搭建了基于 TensorFlow 和 Keras 的实验环境并显式检测 GPU 算力支持以应对 EfficientNetV2S 庞大的梯度计算需求。为了保证实验的可复现性我们统一配置了全局随机种子确保每一轮数据打乱与参数初始化在逻辑上是一致的。在数据输入端我们选用了224x224作为标准分辨率这不仅是 EfficientNet 系列的最佳实践尺寸也能在保留病灶边缘细节的同时兼顾计算效率。# --- 导入基础数据处理与评估库 --- import numpy as np # 矩阵运算核心库 import pandas as pd # 用于标签管理与路径处理 from sklearn.metrics import classification_report, confusion_matrix # 核心性能评估指标 # --- 导入可视化与图形展示库 --- import matplotlib.pyplot as plt # 绘制训练曲线与样本图 import seaborn as sns # 用于混淆矩阵热力图的专业展示 # --- 导入深度学习核心框架 (Keras/TensorFlow) --- import tensorflow as tf import keras as krs from keras.utils import image_dataset_from_directory # 工业级高效数据流读取工具 from keras.models import Model from keras.layers import Dense, GlobalAveragePooling2D, Dropout # 核心网络层配置 from keras.applications import EfficientNetV2S # 引入 V2 系列轻量化高性能主干 # --- 环境配置与随机性控制 --- # 获取物理显卡列表确保模型运行在 GPU 加速环境 gpus tf.config.list_physical_devices(GPU) # 设置全局随机种子确保实验在不同运行环境下具备一致性 seed 42 tf.random.set_seed(seed) np.random.seed(seed) # --- 定义数据集路径与超参数 --- DATA_DIR /kaggle/input/melanoma-cancer-dataset IMG_SIZE (224, 224) # 设置统一的输入尺寸 BATCH_SIZE 32 # 训练批次大小 # --- 构建高效的数据加载流水线 --- # 1. 构建训练集从指定目录递归读取图像自动完成标签映射与批处理转换 trainset image_dataset_from_directory( DATA_DIR /train, image_size IMG_SIZE, batch_size BATCH_SIZE, label_mode binary, # 针对良恶性二分类任务配置二值化标签 shuffle True # 开启训练随机打乱 ) # 2. 构建测试集主要用于最终性能验证 testset image_dataset_from_directory( DATA_DIR /test, image_size IMG_SIZE, batch_size BATCH_SIZE * 2, # 验证阶段不计算梯度可适当增大 Batch 以提升速度 label_mode binary, shuffle False # 关闭打乱确保预测结果与原始文件一一对应 )通过image_dataset_from_directory接口我们将存储在物理磁盘上的数千张皮肤图像转化为 Prefetch 格式的数据流这种“边读取边处理”的模式极大地释放了显存压力。值得注意的是我们将测试集的shuffle参数设为False这一严谨的操作是为了在后续生成混淆矩阵时能够让模型的预测结果与真实的病理标签实现绝对对齐。至此一个高效、规范的医学影像输入矩阵已准备就绪即将进入黑色素瘤的特征判别阶段。4.2数据可视化为了深入理解皮肤病灶的视觉异质性我们利用trainset.take(1)从首个批次中提取了 9 张具有代表性的样本图像。通过 3 x 3 的矩阵布局我们将“良性Benign”与“恶性Malignant”的皮肤图像进行对比展示。这种直观的视觉复核不仅能帮助我们确认图像预处理如尺寸缩放是否导致了关键病理特征的丢失还能让我们预判模型在面对低对比度、光照不均或毛发干扰时的鲁棒性需求。# --- 随机样本可视化与病理特征初探 --- plt.figure(figsize(10, 10)) # 从训练集中提取一个 Batch (32张) 进行展示 for images, labels in trainset.take(1): for i in range(9): # 构建 3x3 的图像展示矩阵 ax plt.subplot(3, 3, i 1) # 将 Tensor 格式转换为可显示的 uint8 图像格式 plt.imshow(images[i].numpy().astype(uint8)) # 获取当前图像对应的类别名称良性 vs 恶性 # 注意labels 此时为浮点张量需转换为整数索引 class_name trainset.class_names[int(labels[i])] plt.title(class_name) # 标注病灶类别 plt.axis(off) # 隐藏坐标轴聚焦病灶细节 plt.tight_layout() # 自动调整排版防止标题重叠 plt.show()4.3特征工程我们构建了一个专门的归一化层Rescaling(1./255)其核心任务是将原始图像中 0到 255之间的整数像素值线性缩放至 [0, 1]的浮点数区间。这一步对于黑色素瘤识别至关重要医学影像中的病灶对比度往往较低归一化不仅能加速神经网络的收敛过程还能有效避免在深度卷积运算中出现梯度爆炸或消失的问题。通过对整个trainset进行映射处理我们确保了输入流水线在输送每一批次Batch图像时都已完成了高精度的数值标准化。# --- 构建像素归一化流水线 --- # 定义缩放层将 8 位色彩深度转换为 [0, 1] 之间的浮点数 normalization_layer tf.keras.layers.Rescaling(1./255) # 利用 map 函数将归一化逻辑注入数据流 # 这样在模型训练时CPU 会异步完成图像缩放确保 GPU 不必等待数据 normalized_ds trainset.map(lambda x, y: (normalization_layer(x), y)) # --- 验证预处理结果 --- # 提取一个批次的数据进行结构检查 image_batch, labels_batch next(iter(normalized_ds))4.4构建模型为了充分释放EfficientNetV2S的特征提取潜力我们将其trainable属性设为True允许模型在训练过程中对 ImageNet 预训练权重进行精细化调整以适应医疗皮肤影像的特殊色调。在基座模型之上我们添加了全局平均池化层GlobalAveragePooling2D来压缩空间特征并构建了一个包含 64 个神经元的稠密层配合 10% 的随机失活Dropout来增强模型的泛化能力。由于是良恶性二分类任务输出层采用 Sigmoid 激活函数直接输出病灶为恶性的概率值。# --- 构建 EfficientNetV2S 特征提取基座 --- base_model EfficientNetV2S( include_topFalse, # 去掉原有的 ImageNet 分类头 weightsimagenet, # 加载预训练权重 input_shapeIMG_SIZE (3, ) ) # 开启全量微调模式让模型深度适配皮肤病灶特征 base_model.trainable True # --- 定义函数式模型拓扑结构 --- inputs krs.Input(shapeIMG_SIZE (3, )) # 将输入喂入基座并设置 trainingTrue 以激活 BatchNormalization 层的实时更新 x base_model(inputs, trainingTrue) x GlobalAveragePooling2D()(x) # 空间特征降维 x Dense(64, activationrelu)(x) # 增加非线性表达能力 x Dropout(0.1)(x) # 轻量级防过拟合 outputs Dense(1, activationsigmoid)(x) # 输出 0-1 之间的预测概率 model Model(inputs, outputs)在皮肤癌诊断中恶性样本往往少于良性样本。为了解决这一问题我们在编译阶段放弃了传统的交叉熵转而采用BinaryFocalCrossentropy二元焦距损失。该函数能自动调低简单样本的权重迫使模型在训练过程中更加关注那些难以辨认的“硬骨头”病灶。同时我们引入了AUC曲线下面积作为核心评价指标这比单纯的准确率更能客观反映模型在不同阈值下区分良恶性黑色素瘤的真实能力。# --- 模型编译配置 --- model.compile( optimizerkrs.optimizers.Adam( learning_rate0.0005, # 采用适中的微调学习率 ), # 使用 Focal Loss 针对性处理医学图像中的难分类样本 losskrs.losses.BinaryFocalCrossentropy(from_logitsFalse), # 监控二分类准确率与 AUC 指标 metrics[ krs.metrics.BinaryAccuracy(nameb_Accuracy), krs.metrics.AUC(nameAUC) ] ) # 查看模型分层详情与参数规模 model.summary()4.5训练模型我们通过model.fit启动了正式的训练流程。由于在之前的步骤中已经开启了基座模型的trainable属性此时的每一个 Epoch 都在对数千万个神经元参数进行协同优化。通过将testset作为validation_data传入我们可以在每一个训练周期结束后立即获取模型在未见过样本上的 AUC 指标与分类准确率。这种实时的闭环反馈对于监测黑色素瘤的误报率至关重要能让我们清晰地观察到BinaryFocalCrossentropy损失函数是如何在迭代中逐步攻克那些细微、模糊的疑似癌变区域。# --- 启动黑色素瘤判别模型训练 --- # 设置迭代周期为 10 轮 # 在每一轮结束时自动执行验证集评估 history model.fit( trainset, epochs10, validation_datatestset # 实时监控模型在独立测试集上的表现 )4.6模型评估我们首先编写了plot_history函数将训练过程中的损失值Loss、准确率Accuracy以及核心指标AUC进行全量可视化。通过对比训练集实线与验证集虚线的演进轨迹我们可以直观地判断模型是否在第 10 轮达到了稳态。对于黑色素瘤识别AUC 曲线的平稳爬升比准确率更有说服力因为它反映了模型在不同阈值下区分良恶性病灶的鲁棒性确保了预测结果并非源于对样本数量分布的偶然拟合。# --- 定义训练历史可视化函数 --- def plot_history(history): # 提取非验证集的监控指标名称 metrics [key for key in history.history.keys() if not key.startswith(val_)] num_metrics len(metrics) plt.figure(figsize(6 * num_metrics, 5)) for i, metric in enumerate(metrics): plt.subplot(1, num_metrics, i 1) # 绘制训练集指标 plt.plot(history.history[metric], labelf训练集 {metric}) val_key val_ metric # 绘制对应的验证集指标 if val_key in history.history: plt.plot(history.history[val_key], labelf验证集 {metric}) plt.title(f{metric.capitalize()} 性能演变图) plt.xlabel(迭代轮数 (Epochs)) plt.ylabel(metric) plt.legend() plt.grid(True) plt.tight_layout() plt.show() # 执行可视化观察 Loss 下降与 AUC/Accuracy 增长情况 plot_history(history)评估的下半场聚焦于“误判逻辑”。通过混淆矩阵Confusion Matrix的热力图展示我们可以一眼看清模型在良性Benign与恶性Malignant样本之间的交错分布。随后我们利用classification_report生成了详尽的分类报告重点解读召回率Recall指标。在黑色素瘤识别中高召回率意味着模型能更有效地识别出潜在的癌症患者这比单纯追求精确度在医学伦理上更具意义。# --- 构建混淆矩阵与分类报告 --- # 生成预测结果并计算混淆矩阵 cm confusion_matrix(y_true, predictions) print(混淆矩阵详情:\n, cm) # 利用 Seaborn 绘制专业热力图 sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelstrainset.class_names, yticklabelstrainset.class_names) plt.xlabel(模型预测类别 (Predicted)) plt.ylabel(真实病理类别 (True)) plt.title(黑色素瘤分类混淆矩阵) plt.show() # 打印详细的精确率、召回率及 F1 分数报告 print(\n全量分类报告:\n, classification_report(y_true, predictions, target_namestrainset.class_names))4.7模型预测为了直观呈现模型的诊断能力我们首先通过model.predict获取了测试集全量的概率输出。由于输出层采用的是 Sigmoid 激活函数我们设定了 $0.5$ 作为良恶性判定的临界阈值。随后我们从测试集中随机抽取 9 张皮肤病灶图像将模型的预测结果与专家标注的真实标签同框对比。这种视觉化的校验能让我们清晰地观察到模型是否能精准识别出那些具有典型“恶性特征”如不对称、边缘锯齿状的样本。# --- 执行测试集大规模推理 --- # 获取模型对测试集的概率预测值 predictions model.predict(testset) # 将多维预测结果展平为一维概率列表 predictions predictions.flatten() # --- 提取测试集真实标签用于对比 --- # 遍历测试集数据流合并所有 Batch 的真实 Label y_true np.concatenate([y for x, y in testset], axis0) # --- 设定分类阈值并执行硬分类 --- threshold 0.5 # 概率 0.5 判定为恶性(1)否则为良性(0) predictions_binary (predictions threshold).astype(int32) # --- 随机样本预测可视化 --- plt.figure(figsize(10, 10)) # 从测试集中抽取首个 Batch 进行视觉验证 for images, labels in testset.take(1): for i in range(9): ax plt.subplot(3, 3, i 1) # 展示皮肤病灶原始图像 plt.imshow(images[i].numpy().astype(uint8)) # 实时比对预测值与真实值 # 0 代表 Benign (良性), 1 代表 Malignant (恶性) plt.title(f预测: {predictions_binary[i]}, 真实: {int(labels[i].numpy())}) plt.axis(off) plt.tight_layout() plt.show()5.总结本实验基于 Kaggle 提供的包含 13,900 张精心挑选图像的黑色素瘤皮肤癌数据集深入探索了计算机视觉在皮肤病学早期检测中的实战应用。针对黑色素瘤这一极具致命性且视觉特征复杂的皮肤癌我们利用EfficientNetV2S架构构建了高性能的深度学习模型旨在从多维度的皮肤病灶图像中精准区分良性与恶性病变。实验结果证明了该方案的高效性模型在训练集上展现出极强的特征捕获能力AUC 达 0.9976准确率 0.9793而在验证集上也保持了出色的泛化水平val_AUC 为 0.9790验证准确率 0.9225。通过对混淆矩阵与分类报告的深度复盘模型在恶性病变Malignant的识别上表现尤为卓越召回率Recall达到了 0.97这意味着在 1000 例恶性样本中仅有 31 例漏诊极大地降低了临床筛查中的漏诊风险。总体而言本项目不仅验证了利用最先进的卷积神经网络解决复杂皮肤病理诊断的可行性也为开发非侵入式、高可靠性的辅助医疗诊断工具提供了重要的技术范式为黑色素瘤的早期精准干预贡献了数字化力量。源代码import numpy as np import pandas as pd from sklearn.metrics import classification_report, confusion_matrix import matplotlib.pyplot as plt import seaborn as sns import tensorflow as tf import keras as krs from keras.utils import image_dataset_from_directory from keras.models import Model from keras.layers import Dense, GlobalAveragePooling2D, Dropout, Dense from keras.applications import EfficientNetV2S gpus tf.config.list_physical_devices(GPU) tf.random.set_seed(seed) np.random.seed(seed) DATA_DIR/kaggle/input/melanoma-cancer-dataset IMG_SIZE (224 , 224) BATCH_SIZE 32 trainset image_dataset_from_directory( DATA_DIR/train, image_size IMG_SIZE, batch_size BATCH_SIZE, ) testset image_dataset_from_directory( DATA_DIR/test, image_size IMG_SIZE, batch_size BATCH_SIZE*2, shuffleFalse ) plt.figure(figsize(10, 10)) for images, labels in trainset.take(1): for i in range(9): ax plt.subplot(3, 3, i 1) plt.imshow(images[i].numpy().astype(uint8)) plt.title(trainset.class_names[labels[i]]) plt.axis(off) plt.show() normalization_layer tf.keras.layers.Rescaling(1./255) normalized_ds trainset.map(lambda x, y: (normalization_layer(x), y)) image_batch, labels_batch next(iter(normalized_ds)) first_image image_batch[0] base_model EfficientNetV2S( include_topFalse, weightsimagenet, input_shapeIMG_SIZE (3, ) ) base_model.trainable True inputs krs.Input(shapeIMG_SIZE (3, )) x base_model(inputs, trainingTrue) x GlobalAveragePooling2D()(x) x Dense(64, activationrelu)(x) x Dropout(0.1)(x) outputs Dense(1, activationsigmoid)(x) model Model(inputs, outputs) model.compile( optimizerkrs.optimizers.Adam( learning_rate0.0005, ), losskrs.losses.BinaryFocalCrossentropy(from_logitsFalse), metrics[krs.metrics.BinaryAccuracy(nameb_Accuracy), krs.metrics.AUC(nameAUC)] ) model.summary() history model.fit(trainset, epochs10, validation_datatestset ) def plot_history(history): metrics [key for key in history.history.keys() if not key.startswith(val_)] num_metrics len(metrics) plt.figure(figsize(6 * num_metrics, 5)) for i, metric in enumerate(metrics): plt.subplot(1, num_metrics, i 1) plt.plot(history.history[metric], labelfTrain {metric}) val_key val_ metric if val_key in history.history: plt.plot(history.history[val_key], labelfVal {metric}) plt.title(f{metric.capitalize()} Grafiği) plt.xlabel(Epochs) plt.ylabel(metric) plt.legend() plt.grid(True) plt.tight_layout() plt.show() plot_history(history) cmconfusion_matrix(y_true, predictions) print(Confusion Matrix:\n, cm) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelstrainset.class_names, yticklabelstrainset.class_names) plt.xlabel(Predicted Label) plt.ylabel(True Label) plt.title(Confusion Matrix) plt.show() print(\nClassification Report:\n, classification_report(y_true, predictions, target_namestrainset.class_names)) predictions model.predict(testset) predictions predictions.flatten() y_true np.concatenate([y for x, y in testset], axis0) threshold 0.5 predictions (predictions threshold).astype(int32) plt.figure(figsize(10, 10)) for images, labels in testset.take(1): for i in range(9): ax plt.subplot(3, 3, i 1) plt.imshow(images[i].numpy().astype(uint8)) plt.title(fPred: {predictions[i]}, True: {labels[i].numpy()}) plt.axis(off)资料获取更多粉丝福利关注下方公众号获取
深度学习实战-基于EfficientNet的黑色素瘤癌症图像分类识别模型
♂️ 个人主页艾派森的个人主页✍作者简介Python学习者 希望大家多多支持我们一起进步如果文章对你有帮助的话欢迎评论 点赞 收藏 加关注目录1.项目背景2.数据集介绍3.技术工具4.实验过程4.1导入数据4.2数据可视化4.3特征工程4.4构建模型4.5训练模型4.6模型评估4.7模型预测5.总结源代码1.项目背景在皮肤病理学领域黑色素瘤因其极高的致死率和隐蔽的早期特征被公认为“皮肤癌之王”。这种恶性程度极高的肿瘤其早期形态常与普通的良性痣高度相似肉眼诊断往往依赖于经验丰富的皮肤科医生且容易受到光影、肤色及病灶微观纹理的干扰。统计数据显示黑色素瘤的早期发现与晚期治疗在预后生存率上存在巨大差异因此开发一种高精度、非侵入式的自动化辅助诊断工具不仅是医学影像技术的突破方向更是挽救患者生命的现实需求。本项目立足于计算机视觉的前沿架构探索利用EfficientNetV2S模型对大规模黑色素瘤数据集进行深度学习与特征分类。该数据集包含 13,900 张经过严格筛选的皮肤病灶图像涵盖了从良性色素痣到恶性黑色素瘤的多种复杂表现形式。相比于传统的人工特征提取EfficientNetV2S 凭借其改进的融合卷积Fused-MBConv和渐进式学习机制能够以极高的计算效率捕捉到病灶边缘的不规则性及内部色素分布的异质性。我们通过对 224 x 224 像素维度的图像进行像素级归一化与全量微调旨在构建一个能够精准识别微观病变信号的智能系统。本项目的核心目标不仅是追求高达 97% 的恶性样本召回率更希望通过数据驱动的方法为临床医生提供一个可靠的“数字放大镜”从而在像素经纬之间重新定义癌症的早期检测标准。2.数据集介绍本实验数据集来源于Kaggle本数据集包含 13,900 张精心挑选的图像是推动皮肤病学和计算机辅助诊断领域发展的宝贵资源。深入探索黑色素瘤的复杂世界每一个像素都可能重新定义早期检测。黑色素瘤是一种致命的皮肤癌需要及时准确的诊断。该数据集利用最先进的技术助力研究人员和临床医生开发强大的机器学习模型从而区分良性和恶性病变。这些图像尺寸统一为 224 x 224 像素全面展现了黑色素瘤的各种表现形式。该数据集的灵感源于皮肤病学领域对先进诊断工具的迫切需求。图像来源于多种渠道展现了传统诊断方法难以应对的复杂特征。3.技术工具Python版本:3.9代码编辑器jupyter notebook4.实验过程4.1导入数据在医学影像分类任务中环境的稳定性与数据流水线的效率直接决定了模型最终的收敛质量。我们首先搭建了基于 TensorFlow 和 Keras 的实验环境并显式检测 GPU 算力支持以应对 EfficientNetV2S 庞大的梯度计算需求。为了保证实验的可复现性我们统一配置了全局随机种子确保每一轮数据打乱与参数初始化在逻辑上是一致的。在数据输入端我们选用了224x224作为标准分辨率这不仅是 EfficientNet 系列的最佳实践尺寸也能在保留病灶边缘细节的同时兼顾计算效率。# --- 导入基础数据处理与评估库 --- import numpy as np # 矩阵运算核心库 import pandas as pd # 用于标签管理与路径处理 from sklearn.metrics import classification_report, confusion_matrix # 核心性能评估指标 # --- 导入可视化与图形展示库 --- import matplotlib.pyplot as plt # 绘制训练曲线与样本图 import seaborn as sns # 用于混淆矩阵热力图的专业展示 # --- 导入深度学习核心框架 (Keras/TensorFlow) --- import tensorflow as tf import keras as krs from keras.utils import image_dataset_from_directory # 工业级高效数据流读取工具 from keras.models import Model from keras.layers import Dense, GlobalAveragePooling2D, Dropout # 核心网络层配置 from keras.applications import EfficientNetV2S # 引入 V2 系列轻量化高性能主干 # --- 环境配置与随机性控制 --- # 获取物理显卡列表确保模型运行在 GPU 加速环境 gpus tf.config.list_physical_devices(GPU) # 设置全局随机种子确保实验在不同运行环境下具备一致性 seed 42 tf.random.set_seed(seed) np.random.seed(seed) # --- 定义数据集路径与超参数 --- DATA_DIR /kaggle/input/melanoma-cancer-dataset IMG_SIZE (224, 224) # 设置统一的输入尺寸 BATCH_SIZE 32 # 训练批次大小 # --- 构建高效的数据加载流水线 --- # 1. 构建训练集从指定目录递归读取图像自动完成标签映射与批处理转换 trainset image_dataset_from_directory( DATA_DIR /train, image_size IMG_SIZE, batch_size BATCH_SIZE, label_mode binary, # 针对良恶性二分类任务配置二值化标签 shuffle True # 开启训练随机打乱 ) # 2. 构建测试集主要用于最终性能验证 testset image_dataset_from_directory( DATA_DIR /test, image_size IMG_SIZE, batch_size BATCH_SIZE * 2, # 验证阶段不计算梯度可适当增大 Batch 以提升速度 label_mode binary, shuffle False # 关闭打乱确保预测结果与原始文件一一对应 )通过image_dataset_from_directory接口我们将存储在物理磁盘上的数千张皮肤图像转化为 Prefetch 格式的数据流这种“边读取边处理”的模式极大地释放了显存压力。值得注意的是我们将测试集的shuffle参数设为False这一严谨的操作是为了在后续生成混淆矩阵时能够让模型的预测结果与真实的病理标签实现绝对对齐。至此一个高效、规范的医学影像输入矩阵已准备就绪即将进入黑色素瘤的特征判别阶段。4.2数据可视化为了深入理解皮肤病灶的视觉异质性我们利用trainset.take(1)从首个批次中提取了 9 张具有代表性的样本图像。通过 3 x 3 的矩阵布局我们将“良性Benign”与“恶性Malignant”的皮肤图像进行对比展示。这种直观的视觉复核不仅能帮助我们确认图像预处理如尺寸缩放是否导致了关键病理特征的丢失还能让我们预判模型在面对低对比度、光照不均或毛发干扰时的鲁棒性需求。# --- 随机样本可视化与病理特征初探 --- plt.figure(figsize(10, 10)) # 从训练集中提取一个 Batch (32张) 进行展示 for images, labels in trainset.take(1): for i in range(9): # 构建 3x3 的图像展示矩阵 ax plt.subplot(3, 3, i 1) # 将 Tensor 格式转换为可显示的 uint8 图像格式 plt.imshow(images[i].numpy().astype(uint8)) # 获取当前图像对应的类别名称良性 vs 恶性 # 注意labels 此时为浮点张量需转换为整数索引 class_name trainset.class_names[int(labels[i])] plt.title(class_name) # 标注病灶类别 plt.axis(off) # 隐藏坐标轴聚焦病灶细节 plt.tight_layout() # 自动调整排版防止标题重叠 plt.show()4.3特征工程我们构建了一个专门的归一化层Rescaling(1./255)其核心任务是将原始图像中 0到 255之间的整数像素值线性缩放至 [0, 1]的浮点数区间。这一步对于黑色素瘤识别至关重要医学影像中的病灶对比度往往较低归一化不仅能加速神经网络的收敛过程还能有效避免在深度卷积运算中出现梯度爆炸或消失的问题。通过对整个trainset进行映射处理我们确保了输入流水线在输送每一批次Batch图像时都已完成了高精度的数值标准化。# --- 构建像素归一化流水线 --- # 定义缩放层将 8 位色彩深度转换为 [0, 1] 之间的浮点数 normalization_layer tf.keras.layers.Rescaling(1./255) # 利用 map 函数将归一化逻辑注入数据流 # 这样在模型训练时CPU 会异步完成图像缩放确保 GPU 不必等待数据 normalized_ds trainset.map(lambda x, y: (normalization_layer(x), y)) # --- 验证预处理结果 --- # 提取一个批次的数据进行结构检查 image_batch, labels_batch next(iter(normalized_ds))4.4构建模型为了充分释放EfficientNetV2S的特征提取潜力我们将其trainable属性设为True允许模型在训练过程中对 ImageNet 预训练权重进行精细化调整以适应医疗皮肤影像的特殊色调。在基座模型之上我们添加了全局平均池化层GlobalAveragePooling2D来压缩空间特征并构建了一个包含 64 个神经元的稠密层配合 10% 的随机失活Dropout来增强模型的泛化能力。由于是良恶性二分类任务输出层采用 Sigmoid 激活函数直接输出病灶为恶性的概率值。# --- 构建 EfficientNetV2S 特征提取基座 --- base_model EfficientNetV2S( include_topFalse, # 去掉原有的 ImageNet 分类头 weightsimagenet, # 加载预训练权重 input_shapeIMG_SIZE (3, ) ) # 开启全量微调模式让模型深度适配皮肤病灶特征 base_model.trainable True # --- 定义函数式模型拓扑结构 --- inputs krs.Input(shapeIMG_SIZE (3, )) # 将输入喂入基座并设置 trainingTrue 以激活 BatchNormalization 层的实时更新 x base_model(inputs, trainingTrue) x GlobalAveragePooling2D()(x) # 空间特征降维 x Dense(64, activationrelu)(x) # 增加非线性表达能力 x Dropout(0.1)(x) # 轻量级防过拟合 outputs Dense(1, activationsigmoid)(x) # 输出 0-1 之间的预测概率 model Model(inputs, outputs)在皮肤癌诊断中恶性样本往往少于良性样本。为了解决这一问题我们在编译阶段放弃了传统的交叉熵转而采用BinaryFocalCrossentropy二元焦距损失。该函数能自动调低简单样本的权重迫使模型在训练过程中更加关注那些难以辨认的“硬骨头”病灶。同时我们引入了AUC曲线下面积作为核心评价指标这比单纯的准确率更能客观反映模型在不同阈值下区分良恶性黑色素瘤的真实能力。# --- 模型编译配置 --- model.compile( optimizerkrs.optimizers.Adam( learning_rate0.0005, # 采用适中的微调学习率 ), # 使用 Focal Loss 针对性处理医学图像中的难分类样本 losskrs.losses.BinaryFocalCrossentropy(from_logitsFalse), # 监控二分类准确率与 AUC 指标 metrics[ krs.metrics.BinaryAccuracy(nameb_Accuracy), krs.metrics.AUC(nameAUC) ] ) # 查看模型分层详情与参数规模 model.summary()4.5训练模型我们通过model.fit启动了正式的训练流程。由于在之前的步骤中已经开启了基座模型的trainable属性此时的每一个 Epoch 都在对数千万个神经元参数进行协同优化。通过将testset作为validation_data传入我们可以在每一个训练周期结束后立即获取模型在未见过样本上的 AUC 指标与分类准确率。这种实时的闭环反馈对于监测黑色素瘤的误报率至关重要能让我们清晰地观察到BinaryFocalCrossentropy损失函数是如何在迭代中逐步攻克那些细微、模糊的疑似癌变区域。# --- 启动黑色素瘤判别模型训练 --- # 设置迭代周期为 10 轮 # 在每一轮结束时自动执行验证集评估 history model.fit( trainset, epochs10, validation_datatestset # 实时监控模型在独立测试集上的表现 )4.6模型评估我们首先编写了plot_history函数将训练过程中的损失值Loss、准确率Accuracy以及核心指标AUC进行全量可视化。通过对比训练集实线与验证集虚线的演进轨迹我们可以直观地判断模型是否在第 10 轮达到了稳态。对于黑色素瘤识别AUC 曲线的平稳爬升比准确率更有说服力因为它反映了模型在不同阈值下区分良恶性病灶的鲁棒性确保了预测结果并非源于对样本数量分布的偶然拟合。# --- 定义训练历史可视化函数 --- def plot_history(history): # 提取非验证集的监控指标名称 metrics [key for key in history.history.keys() if not key.startswith(val_)] num_metrics len(metrics) plt.figure(figsize(6 * num_metrics, 5)) for i, metric in enumerate(metrics): plt.subplot(1, num_metrics, i 1) # 绘制训练集指标 plt.plot(history.history[metric], labelf训练集 {metric}) val_key val_ metric # 绘制对应的验证集指标 if val_key in history.history: plt.plot(history.history[val_key], labelf验证集 {metric}) plt.title(f{metric.capitalize()} 性能演变图) plt.xlabel(迭代轮数 (Epochs)) plt.ylabel(metric) plt.legend() plt.grid(True) plt.tight_layout() plt.show() # 执行可视化观察 Loss 下降与 AUC/Accuracy 增长情况 plot_history(history)评估的下半场聚焦于“误判逻辑”。通过混淆矩阵Confusion Matrix的热力图展示我们可以一眼看清模型在良性Benign与恶性Malignant样本之间的交错分布。随后我们利用classification_report生成了详尽的分类报告重点解读召回率Recall指标。在黑色素瘤识别中高召回率意味着模型能更有效地识别出潜在的癌症患者这比单纯追求精确度在医学伦理上更具意义。# --- 构建混淆矩阵与分类报告 --- # 生成预测结果并计算混淆矩阵 cm confusion_matrix(y_true, predictions) print(混淆矩阵详情:\n, cm) # 利用 Seaborn 绘制专业热力图 sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelstrainset.class_names, yticklabelstrainset.class_names) plt.xlabel(模型预测类别 (Predicted)) plt.ylabel(真实病理类别 (True)) plt.title(黑色素瘤分类混淆矩阵) plt.show() # 打印详细的精确率、召回率及 F1 分数报告 print(\n全量分类报告:\n, classification_report(y_true, predictions, target_namestrainset.class_names))4.7模型预测为了直观呈现模型的诊断能力我们首先通过model.predict获取了测试集全量的概率输出。由于输出层采用的是 Sigmoid 激活函数我们设定了 $0.5$ 作为良恶性判定的临界阈值。随后我们从测试集中随机抽取 9 张皮肤病灶图像将模型的预测结果与专家标注的真实标签同框对比。这种视觉化的校验能让我们清晰地观察到模型是否能精准识别出那些具有典型“恶性特征”如不对称、边缘锯齿状的样本。# --- 执行测试集大规模推理 --- # 获取模型对测试集的概率预测值 predictions model.predict(testset) # 将多维预测结果展平为一维概率列表 predictions predictions.flatten() # --- 提取测试集真实标签用于对比 --- # 遍历测试集数据流合并所有 Batch 的真实 Label y_true np.concatenate([y for x, y in testset], axis0) # --- 设定分类阈值并执行硬分类 --- threshold 0.5 # 概率 0.5 判定为恶性(1)否则为良性(0) predictions_binary (predictions threshold).astype(int32) # --- 随机样本预测可视化 --- plt.figure(figsize(10, 10)) # 从测试集中抽取首个 Batch 进行视觉验证 for images, labels in testset.take(1): for i in range(9): ax plt.subplot(3, 3, i 1) # 展示皮肤病灶原始图像 plt.imshow(images[i].numpy().astype(uint8)) # 实时比对预测值与真实值 # 0 代表 Benign (良性), 1 代表 Malignant (恶性) plt.title(f预测: {predictions_binary[i]}, 真实: {int(labels[i].numpy())}) plt.axis(off) plt.tight_layout() plt.show()5.总结本实验基于 Kaggle 提供的包含 13,900 张精心挑选图像的黑色素瘤皮肤癌数据集深入探索了计算机视觉在皮肤病学早期检测中的实战应用。针对黑色素瘤这一极具致命性且视觉特征复杂的皮肤癌我们利用EfficientNetV2S架构构建了高性能的深度学习模型旨在从多维度的皮肤病灶图像中精准区分良性与恶性病变。实验结果证明了该方案的高效性模型在训练集上展现出极强的特征捕获能力AUC 达 0.9976准确率 0.9793而在验证集上也保持了出色的泛化水平val_AUC 为 0.9790验证准确率 0.9225。通过对混淆矩阵与分类报告的深度复盘模型在恶性病变Malignant的识别上表现尤为卓越召回率Recall达到了 0.97这意味着在 1000 例恶性样本中仅有 31 例漏诊极大地降低了临床筛查中的漏诊风险。总体而言本项目不仅验证了利用最先进的卷积神经网络解决复杂皮肤病理诊断的可行性也为开发非侵入式、高可靠性的辅助医疗诊断工具提供了重要的技术范式为黑色素瘤的早期精准干预贡献了数字化力量。源代码import numpy as np import pandas as pd from sklearn.metrics import classification_report, confusion_matrix import matplotlib.pyplot as plt import seaborn as sns import tensorflow as tf import keras as krs from keras.utils import image_dataset_from_directory from keras.models import Model from keras.layers import Dense, GlobalAveragePooling2D, Dropout, Dense from keras.applications import EfficientNetV2S gpus tf.config.list_physical_devices(GPU) tf.random.set_seed(seed) np.random.seed(seed) DATA_DIR/kaggle/input/melanoma-cancer-dataset IMG_SIZE (224 , 224) BATCH_SIZE 32 trainset image_dataset_from_directory( DATA_DIR/train, image_size IMG_SIZE, batch_size BATCH_SIZE, ) testset image_dataset_from_directory( DATA_DIR/test, image_size IMG_SIZE, batch_size BATCH_SIZE*2, shuffleFalse ) plt.figure(figsize(10, 10)) for images, labels in trainset.take(1): for i in range(9): ax plt.subplot(3, 3, i 1) plt.imshow(images[i].numpy().astype(uint8)) plt.title(trainset.class_names[labels[i]]) plt.axis(off) plt.show() normalization_layer tf.keras.layers.Rescaling(1./255) normalized_ds trainset.map(lambda x, y: (normalization_layer(x), y)) image_batch, labels_batch next(iter(normalized_ds)) first_image image_batch[0] base_model EfficientNetV2S( include_topFalse, weightsimagenet, input_shapeIMG_SIZE (3, ) ) base_model.trainable True inputs krs.Input(shapeIMG_SIZE (3, )) x base_model(inputs, trainingTrue) x GlobalAveragePooling2D()(x) x Dense(64, activationrelu)(x) x Dropout(0.1)(x) outputs Dense(1, activationsigmoid)(x) model Model(inputs, outputs) model.compile( optimizerkrs.optimizers.Adam( learning_rate0.0005, ), losskrs.losses.BinaryFocalCrossentropy(from_logitsFalse), metrics[krs.metrics.BinaryAccuracy(nameb_Accuracy), krs.metrics.AUC(nameAUC)] ) model.summary() history model.fit(trainset, epochs10, validation_datatestset ) def plot_history(history): metrics [key for key in history.history.keys() if not key.startswith(val_)] num_metrics len(metrics) plt.figure(figsize(6 * num_metrics, 5)) for i, metric in enumerate(metrics): plt.subplot(1, num_metrics, i 1) plt.plot(history.history[metric], labelfTrain {metric}) val_key val_ metric if val_key in history.history: plt.plot(history.history[val_key], labelfVal {metric}) plt.title(f{metric.capitalize()} Grafiği) plt.xlabel(Epochs) plt.ylabel(metric) plt.legend() plt.grid(True) plt.tight_layout() plt.show() plot_history(history) cmconfusion_matrix(y_true, predictions) print(Confusion Matrix:\n, cm) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelstrainset.class_names, yticklabelstrainset.class_names) plt.xlabel(Predicted Label) plt.ylabel(True Label) plt.title(Confusion Matrix) plt.show() print(\nClassification Report:\n, classification_report(y_true, predictions, target_namestrainset.class_names)) predictions model.predict(testset) predictions predictions.flatten() y_true np.concatenate([y for x, y in testset], axis0) threshold 0.5 predictions (predictions threshold).astype(int32) plt.figure(figsize(10, 10)) for images, labels in testset.take(1): for i in range(9): ax plt.subplot(3, 3, i 1) plt.imshow(images[i].numpy().astype(uint8)) plt.title(fPred: {predictions[i]}, True: {labels[i].numpy()}) plt.axis(off)资料获取更多粉丝福利关注下方公众号获取