基于MindSpore的恶性皮肤肿瘤识别
第一节:系统架构结合神经网络的讲解

1 实验介绍
本次实验的目标是基于MindSpore框架,在包含四个类别数据集的环境中,对ResNet50模型进行微调训练,以构建皮肤病识别系统的框架。
基于MindSpore model_zoo提供的ResNet50模型定义,并借助昇思大模型平台对模型进行训练与优化。
1.1 实验目标
-
深入理解MindSpore框架在深度学习模型训练中的核心步骤;
-
透彻分析ResNet50模型结构及其在图像分类任务中的具体应用场景;
-
深入研究如何优化基于特定数据集(皮肤病图像)的模型参数配置;
-
深入理解MindSpore框架在深度学习模型训练中的核心步骤;
-
透彻分析ResNet50模型结构及其在图像分类任务中的具体应用场景;
-
深入研究如何优化基于特定数据集(皮肤病图像)的model parameter configuration;
1.2 使用工具介绍
- MindSpore 框架:一个开放 sourced 的深度学习框架,在端上、边缘以及云端等多种应用场景中提供全面的支持,并旨在为用户提供完整的 AI 解决方案;
- ResNet50 模型:一种基于 deep residual 网络设计的模型,在引入残差学习机制的基础上实现了对深层神经网络训练的有效优化,在图像识别领域具有广泛的适用性和重要性;
- 升星大模型平台:一个基于 MindSpore 深度学习平台构建的专业级解决方案,在提供强大计算资源的同时也配备了丰富的预训练模型库以满足多样化需求;
1.3 实验步骤
- 数据准备:获取并预处理相关皮肤病图像数据集,并保证其质量符合研究要求。
- model选择:采用MindSpore model_zoo中的ResNet50作为研究的基础架构。
- model training:在昇思大平台设置相应的参数配置,并执行相应的训练流程。
- performance evaluation:利用验证集对系统性能进行评估,并通过微调超参数进一步优化系统性能。
- model deployment:将最终的分类器应用于实际临床场景中进行识别测试。
1.4 预备知识
- 掌握Python编程技能,并熟悉Shell及Linux操作系统的基础知识;
- 掌握了一定的深度学习理论知识体系,在包括卷积神经网络(CNN)、损失函数(Loss Function)、优化器(Optimizer)、训练策略(Training Strategy)以及Checkpoint等方面有较为全面的理解;
- 深了解并熟悉MindSpore AI框架的基本功能与应用方法,在其官方网站[昇思 MindSpore | 全场景 AI 框架 | 昇思 MindSpore 社区官网](https://www.mindspore.cn/ "昇思 MindSpore | 全场景 AI 框架 | 昇思 MindSpore 社区官网)上可获得详细的技术支持。
%%capture captured_output
# 实验环境已经预装了mindspore==2.3.0,如需更换mindspore版本,可更改下面 MINDSPORE_VERSION 变量
!pip uninstall mindspore -y
%env MINDSPORE_VERSION=2.3.0
!pip install https://ms-release.obs.cn-north-4.myhuaweicloud.com/${MINDSPORE_VERSION}/MindSpore/unified/aarch64/mindspore-${MINDSPORE_VERSION}-cp39-cp39-linux_aarch64.whl --trusted-host ms-release.obs.cn-north-4.myhuaweicloud.com -i https://pypi.mirrors.ustc.edu.cn/simple
# 查看当前 mindspore 版本
!pip show mindspore
> [https://www.mindspore.cn](https://www.mindspore.cn/ "https://www.mindspore.cn")
2 数据集处理
2.1 数据集介绍
该数据集包含四个不同类型的皮肤图片:基底细胞癌(Basal Cell Carcinoma)、黑色素瘤(Melanoma)、色素性良性角化病(Pigmented Benign Keratosis)以及痣(Nevus),共四种类型
- 基底细胞癌属于较为常见的皮肤癌症类型,在表皮基底层发生发展而成。如果不及时处理可能会局限于局部区域但若延误治疗可能导致侵袭性扩散并损害邻近组织;
- 黑色素瘤属于高度恶性皮肤肿瘤从黑色素细胞出发能够快速蔓延至全身其他部位因此早期诊断与干预对于改善预后至关重要;
- 痣是指表皮中出现的一种色素沉着区域通常由遗传因素决定具有一定的良性特征;
- 角化病则表现为表皮层内出现斑块状的小色素斑块主要为非炎症性病变通常无明显症状但有时会被误诊为黑色素瘤;
2.2 数据集获取和整理
数据集已在此处完成上传至昇思大模型官方平台内,并具体链接如下:昇思大模型平台。
这里可以用git快速获取,下载后存储在pifudata_Maker文件夹内
!git clone https://source-xihe-mindspore.osinfra.cn/knoka/pifudata_Maker.git
%%capture captured_output
# 解压数据集pifudata_Maker/skin-cancer-detection.tar 并存储到pifudata_Maker文件夹内
!tar -xv -f pifudata_Maker/skin-cancer-detection.tar -C pifudata_Maker/
在获取之后,在该数据集呈现如下结构中dataset_dir 根目录名称可以根据需要进行更改
训练图像目录 train 包含用于训练的各个皮肤图像类别的目录
class1class2class3class4
验证图像目录 val 包含用于验证的各个皮瘤图像类别的目录
class1class2class3class4
这部分代码则实现了将文件夹英文名转换为数字0-3的命名格式。至于为何采用这种格式,则源于图像分类任务中通常将数据集组织成文件夹的形式(即每个文件夹代表一个类别)。
因此我们期待每个文件夹名称都是一个可识别的标签。具体而言如果文件夹名称为数字则可以直接将其作为类别标签使用因为这有助于简化分类逻辑;由于在训练过程中模型需要将输入数据(图像)与其对应的输出标签(类别)建立关联
以对应编号的方式重新命名为疾病名称后,在 MindSpore 中提供了一种便捷的方法来加载并处理数据集。具体而言,在文件夹层次结构中自动将图片分配到相应的类别中。从而确保了数据转换过程更加顺利。这样一来,在实际操作中可以直接以文件夹名称(即编号)作为图像的标签使用,并无需额外进行映射步骤。
import os
# 定义疾病名称和编号的映射
diseases = {
0: "basal_cell_carcinoma",
1: "melanoma",
2: "nevus",
3: "pigmented_benign_keratosis",
}
# 定义目标文件夹路径
target_directory = "pifudata_Maker/images/" # 替换为你的文件夹路径
# 遍历每个疾病名称和编号
for folder in ["train", "val"]:
folder_path = os.path.join(target_directory, folder)
print(folder_path)
for number,disease in diseases.items():
# 构造完整的旧文件夹路径
old_folder_path = os.path.join(folder_path, disease)
# 检查旧文件夹是否存在
if not os.path.exists(old_folder_path):
print(f"Folder not found: {old_folder_path}")
continue
# 构造新的文件夹名称
new_folder_name = f"{number}"
# 构造完整的新文件夹路径
new_folder_path = os.path.join(folder_path, new_folder_name)
# 重命名文件夹
os.rename(old_folder_path, new_folder_path)
print(f"Renamed {old_folder_path} to {new_folder_path}")
2.3 数据集比例可视化
通过可视化展示数据集, 从而使得对数据进行直观查看变得更为便捷; 此外, 这一过程有助于完成后续超参数设置等相关工作
涉及基底细胞癌(Basal Cell Carcinoma)、黑色素瘤(Melanoma)、色素性良性角化病(Pigmented Benign Keratosis)以及痣(Nevus)四种皮肤疾病;这些疾病具体包括基底细胞癌、黑色素瘤、色素性良性角化病以及痣等常见于皮肤科的疾病类型;
具体而言,在此实验中我们采用了四个不同的数据集进行对比分析:Nevus数据集包含有357张图片,Melanoma数据集包含有438张图片,Pigmented Benign Keratosis数据集包含有462张图片以及Basal Cell Carcinoma数据集包含有376张图片;以上即为此处的数据分布情况

import os
import matplotlib.pyplot as plt
# 疾病编号到名称的映射
diseases = {
0: "basal_cell_carcinoma",
1: "melanoma",
2: "nevus",
3: "pigmented_benign_keratosis",
}
# 数据集目录路径
data_path_train = "pifudata_Maker/images/train"
data_path_val = "pifudata_Maker/images/val"
# 初始化一个字典来存储每个类别的文件数量
file_counts_train = {}
file_counts_val = {}
# 遍历训练数据文件夹
for folder_name in os.listdir(data_path_train):
folder_path = os.path.join(data_path_train, folder_name)
# 确保是文件夹
if os.path.isdir(folder_path):
# 计算文件夹内的文件数量
count = len(os.listdir(folder_path))
# 使用疾病编号作为字典的键
file_counts_train[diseases.get(folder_name, folder_name)] = count
# 遍历验证数据文件夹
for folder_name in os.listdir(data_path_val):
folder_path = os.path.join(data_path_val, folder_name)
# 确保是文件夹
if os.path.isdir(folder_path):
# 计算文件夹内的文件数量
count = len(os.listdir(folder_path))
# 使用疾病编号作为字典的键
file_counts_val[diseases.get(folder_name, folder_name)] = count
# 获取训练集的疾病名称列表
train_categories = list(file_counts_train.keys())
# 使用matplotlib生成条形图
plt.figure(figsize=(12, 8))
# 绘制训练集的条形图
train_bars = plt.bar(train_categories, file_counts_train.values(), width=0.4, color='blue', label='Train')
# 绘制验证集的条形图,错开位置
val_bars = plt.bar([train_categories.index(cat) + 0.4 for cat in train_categories], [file_counts_val.get(cat, 0) for cat in train_categories], width=0.4, color='green', label='Validation')
# 添加图例
plt.legend()
# 使用疾病名称作为X轴标签
plt.xlabel('labels')
plt.ylabel('Number of Images')
plt.title('Training and Validation Datasets')
print(train_categories)
train_list = [diseases[int(i)] for i in train_categories]
print(train_list)
plt.xticks([train_categories.index(cat) + 0.2 for cat in train_categories], [diseases[int(i)] for i in train_categories], rotation=30,fontsize='small')
# 在柱形条上面显示数量
for i, (train_bar, val_bar) in enumerate(zip(train_bars, val_bars)):
train_height = train_bar.get_height()
val_height = val_bar.get_height() if val_bar else 0
plt.text(train_bar.get_x() + train_bar.get_width() / 2, train_height, '{}'.format(train_height), ha='center', va='bottom')
plt.text(val_bar.get_x() + val_bar.get_width() / 2, val_height, '{}'.format(val_height), ha='center', va='bottom') if val_bar else None
plt.show()

2.4 数据加载
基于 MindSpore 平台的 MindSpore.dataset 模块 实现图像数据集的加载与预处理过程,并以完成模型训练及验证任务为目标。
指定数据集所在的位置,并编写一个函数用于加载并执行数据增强操作(例如:随机裁剪、水平翻转等),将这些操作应用于训练数据以提升模型的一般化能力;同时对验证集执行解码、尺寸调整以及归一化处理。
对图像数据集进行按批次处理,并实现训练阶段的数据批量处理流程。计算并输出训练集与验证集各自的批量处理次数(即数学表达式N = \frac{|\mathcal{D}|}{B}的结果),同时输出验证集中第一批图像的张量形状及其对应的标签信息。
# 超参数定义
batch_size = 64 # 批量大小
image_size = 224 # 训练图像空间大小
num_epochs = 25 # 训练周期数
lr = 0.001 # 学习率
momentum = 0.6 # 动量
workers = 4 # 并行线程个数
import mindspore
mindspore.set_context(device_target="Ascend")
import mindspore as ms
import mindspore.dataset as ds
import mindspore.dataset.vision as vision
# 数据集目录路径
data_path_train = "pifudata_Maker/images/train"
data_path_val = "pifudata_Maker/images/val"
# 创建训练数据集
def create_dataset_canidae(dataset_path, usage):
"""数据加载"""
data_set = ds.ImageFolderDataset(dataset_path,
num_parallel_workers=workers,
shuffle=True,)
# 数据增强操作
mean = [0.485 * 255, 0.456 * 255, 0.406 * 255]
std = [0.229 * 255, 0.224 * 255, 0.225 * 255]
scale = 32
if usage == "train":
# Define map operations for training dataset
trans = [
vision.RandomCropDecodeResize(size=image_size, scale=(0.08, 1.0), ratio=(0.75, 1.333)),
vision.RandomHorizontalFlip(prob=0.5),
vision.Normalize(mean=mean, std=std),
vision.HWC2CHW()
]
else:
# Define map operations for inference dataset
trans = [
vision.Decode(),
vision.Resize(image_size + scale),
vision.CenterCrop(image_size),
vision.Normalize(mean=mean, std=std),
vision.HWC2CHW()
]
# 数据映射操作
data_set = data_set.map(
operations=trans,
input_columns='image',
num_parallel_workers=workers)
# 批量操作
data_set = data_set.batch(batch_size)
return data_set
dataset_train = create_dataset_canidae(data_path_train, "train")
step_size_train = dataset_train.get_dataset_size()
dataset_val = create_dataset_canidae(data_path_val, "val")
step_size_val = dataset_val.get_dataset_size()
print(step_size_train)
print(step_size_val)
data = next(dataset_val.create_dict_iterator())
images = data["image"]
labels = data["label"]
# print(data["image"][0])
print("Tensor of image", images.shape)
print("Labels:", labels)
2.5 数据集可视化
为了更加容易地进行分析和排查代码逻辑问题,我们可以借助matplotlib库和numpy库来呈现最初四个图像的分布情况
import matplotlib.pyplot as plt
import numpy as np
# class_name对应label,按文件夹字符串从小到大的顺序标记label
class_name = {
0: "basal_cell_carcinoma",
1: "melanoma",
2: "nevus",
3: "pigmented_benign_keratosis",
}
# print(images[0])
plt.figure(figsize=(5, 5))
for i in range(4):
# 获取图像及其对应的label
# print(images[i])
data_image = images[i].asnumpy()
data_label = labels[i]
# 处理图像供展示使用
data_image = np.transpose(data_image, (1, 2, 0))
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
data_image = std * data_image + mean
data_image = np.clip(data_image, 0, 1)
# 显示图像
plt.subplot(2, 2, i+1)
plt.imshow(data_image)
plt.title(class_name[int(labels[i].asnumpy())])
plt.axis("off")
plt.show()

3 模型训练
3.1 网络搭建
以MindSpore框架为基础搭建深度残差网络结构,并针对图像识别等应用场景进行设计;通过代码实现基于MindSpore框架的ResNet模型构建,并包含基础级残差模块(如ResidualBlockBase及标准残差模块ResidualBlock)以及完整的ResNet架构设计
提供_resnet和resnet50函数来实例化ResNet模型,加载预训练权重;
from typing import Type, Union, List, Optional
from mindspore import nn, train
from mindspore.common.initializer import Normal
weight_init = Normal(mean=0, sigma=0.02)
gamma_init = Normal(mean=1, sigma=0.02)
class ResidualBlockBase(nn.Cell):
expansion: int = 1 # 最后一个卷积核数量与第一个卷积核数量相等
def __init__(self, in_channel: int, out_channel: int,
stride: int = 1, norm: Optional[nn.Cell] = None,
down_sample: Optional[nn.Cell] = None) -> None:
super(ResidualBlockBase, self).__init__()
if not norm:
self.norm = nn.BatchNorm2d(out_channel)
else:
self.norm = norm
self.conv1 = nn.Conv2d(in_channel, out_channel,
kernel_size=3, stride=stride,
weight_init=weight_init)
self.conv2 = nn.Conv2d(in_channel, out_channel,
kernel_size=3, weight_init=weight_init)
self.relu = nn.ReLU()
self.down_sample = down_sample
def construct(self, x):
"""ResidualBlockBase construct."""
identity = x # shortcuts分支
out = self.conv1(x) # 主分支第一层:3*3卷积层
out = self.norm(out)
out = self.relu(out)
out = self.conv2(out) # 主分支第二层:3*3卷积层
out = self.norm(out)
if self.down_sample is not None:
identity = self.down_sample(x)
out += identity # 输出为主分支与shortcuts之和
out = self.relu(out)
return out
class ResidualBlock(nn.Cell):
expansion = 4 # 最后一个卷积核的数量是第一个卷积核数量的4倍
def __init__(self, in_channel: int, out_channel: int,
stride: int = 1, down_sample: Optional[nn.Cell] = None) -> None:
super(ResidualBlock, self).__init__()
self.conv1 = nn.Conv2d(in_channel, out_channel,
kernel_size=1, weight_init=weight_init)
self.norm1 = nn.BatchNorm2d(out_channel)
self.conv2 = nn.Conv2d(out_channel, out_channel,
kernel_size=3, stride=stride,
weight_init=weight_init)
self.norm2 = nn.BatchNorm2d(out_channel)
self.conv3 = nn.Conv2d(out_channel, out_channel * self.expansion,
kernel_size=1, weight_init=weight_init)
self.norm3 = nn.BatchNorm2d(out_channel * self.expansion)
self.relu = nn.ReLU()
self.down_sample = down_sample
def construct(self, x):
identity = x # shortscuts分支
out = self.conv1(x) # 主分支第一层:1*1卷积层
out = self.norm1(out)
out = self.relu(out)
out = self.conv2(out) # 主分支第二层:3*3卷积层
out = self.norm2(out)
out = self.relu(out)
out = self.conv3(out) # 主分支第三层:1*1卷积层
out = self.norm3(out)
if self.down_sample is not None:
identity = self.down_sample(x)
out += identity # 输出为主分支与shortcuts之和
out = self.relu(out)
return out
def make_layer(last_out_channel, block: Type[Union[ResidualBlockBase, ResidualBlock]],
channel: int, block_nums: int, stride: int = 1):
down_sample = None # shortcuts分支
if stride != 1 or last_out_channel != channel * block.expansion:
down_sample = nn.SequentialCell([
nn.Conv2d(last_out_channel, channel * block.expansion,
kernel_size=1, stride=stride, weight_init=weight_init),
nn.BatchNorm2d(channel * block.expansion, gamma_init=gamma_init)
])
layers = []
layers.append(block(last_out_channel, channel, stride=stride, down_sample=down_sample))
in_channel = channel * block.expansion
# 堆叠残差网络
for _ in range(1, block_nums):
layers.append(block(in_channel, channel))
return nn.SequentialCell(layers)
from mindspore import load_checkpoint, load_param_into_net
class ResNet(nn.Cell):
def __init__(self, block: Type[Union[ResidualBlockBase, ResidualBlock]],
layer_nums: List[int], num_classes: int, input_channel: int) -> None:
super(ResNet, self).__init__()
self.relu = nn.ReLU()
# 第一个卷积层,输入channel为3(彩色图像),输出channel为64
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, weight_init=weight_init)
self.norm = nn.BatchNorm2d(64)
# 最大池化层,缩小图片的尺寸
self.max_pool = nn.MaxPool2d(kernel_size=3, stride=2, pad_mode='same')
# 各个残差网络结构块定义,
self.layer1 = make_layer(64, block, 64, layer_nums[0])
self.layer2 = make_layer(64 * block.expansion, block, 128, layer_nums[1], stride=2)
self.layer3 = make_layer(128 * block.expansion, block, 256, layer_nums[2], stride=2)
self.layer4 = make_layer(256 * block.expansion, block, 512, layer_nums[3], stride=2)
# 平均池化层
self.avg_pool = nn.AvgPool2d()
# flattern层
self.flatten = nn.Flatten()
# 全连接层
self.fc = nn.Dense(in_channels=input_channel, out_channels=num_classes)
def construct(self, x):
x = self.conv1(x)
x = self.norm(x)
x = self.relu(x)
x = self.max_pool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avg_pool(x)
x = self.flatten(x)
x = self.fc(x)
return x
def _resnet(model_url: str, block: Type[Union[ResidualBlockBase, ResidualBlock]],
layers: List[int], num_classes: int, pretrained: bool, pretrianed_ckpt: str,
input_channel: int):
model = ResNet(block, layers, num_classes, input_channel)
if pretrained:
# 加载预训练模型
download(url=model_url, path=pretrianed_ckpt, replace=True)
param_dict = load_checkpoint(pretrianed_ckpt)
load_param_into_net(model, param_dict)
return model
def resnet50(num_classes: int = 1000, pretrained: bool = False):
"ResNet50模型"
resnet50_url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/models/application/resnet50_224_new.ckpt"
resnet50_ckpt = "./pretrainmodel/resnet50_224_new.ckpt"
return _resnet(resnet50_url, ResidualBlock, [3, 4, 6, 3], num_classes,
pretrained, resnet50_ckpt, 2048)
3.2 模型训练
基于MindSpore框架,在NPU环境中进行ResNet50模型的定制开发;经过优化后将该模型应用于四分类任务。
设定优化器、损失函数以及评估指标;创建训练模型实例;选择最佳准确率作为评价标准,并确定最佳模型检查点路径用于保存最优模型。
--------------------
Epoch: [ 23/ 25], Average Train Loss: [0.418], Accuracy: [0.734]
epoch time: 4133.578 ms, per step time: 158.984 ms
--------------------
Epoch: [ 24/ 25], Average Train Loss: [0.448], Accuracy: [0.719]
epoch time: 3491.099 ms, per step time: 134.273 ms
--------------------
Epoch: [ 25/ 25], Average Train Loss: [0.390], Accuracy: [0.719]
epoch time: 3515.689 ms, per step time: 135.219 ms
================================================================================
End of validation the best Accuracy is: 0.734, save the best ckpt file in ./BestCheckpoint/resnet50-best.ckpt
# 代码中首先导入了必要的MindSpore模块和函数,并设置了运行环境。
import mindspore
from mindspore import nn, train
from mindspore.nn import Loss, Accuracy
!pip install download
import mindspore as ms
from download import download
network = resnet50(pretrained=True)
# 通过替换ResNet50的原始全连接层和平均池化层来适配新的任务
# 全连接层输入层的大小
in_channels = network.fc.in_channels
# 输出通道数大小为皮肤肿瘤分类数4
head = nn.Dense(in_channels, 4)
# 重置全连接层
network.fc = head
# 平均池化层kernel size为7
avg_pool = nn.AvgPool2d(kernel_size=7)
# 重置平均池化层
network.avg_pool = avg_pool
import mindspore as ms
import mindspore
# 定义优化器和损失函数
opt = nn.Momentum(params=network.trainable_params(), learning_rate=lr, momentum=momentum)
loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
# 实例化模型
model = train.Model(network, loss_fn, opt, metrics={"Accuracy": Accuracy()})
def forward_fn(inputs, targets):
logits = network(inputs)
loss = loss_fn(logits, targets)
return loss
grad_fn = mindspore.ops.value_and_grad(forward_fn, None, opt.parameters)
def train_step(inputs, targets):
loss, grads = grad_fn(inputs, targets)
opt(grads)
return loss
# 创建迭代器
data_loader_train = dataset_train.create_tuple_iterator(num_epochs=num_epochs)
# 最佳模型保存路径
best_ckpt_dir = "./BestCheckpoint"
best_ckpt_path = "./BestCheckpoint/resnet50-best.ckpt"
import os
import time
# 开始循环训练
print("Start Training Loop ...")
best_acc = 0
# 训练循环中,数据通过迭代器被加载,模型在每个epoch中更新权重,并计算训练损失。
# 在每个epoch结束时,模型在验证集上评估准确率,并保存具有最高准确率的模型检查点。
for epoch in range(num_epochs):
losses = []
network.set_train()
epoch_start = time.time()
# 为每轮训练读入数据
for i, (images, labels) in enumerate(data_loader_train):
labels = labels.astype(ms.int32)
loss = train_step(images, labels)
losses.append(loss)
# 每个epoch结束后,验证准确率
acc = model.eval(dataset_val)['Accuracy']
epoch_end = time.time()
epoch_seconds = (epoch_end - epoch_start)
step_seconds = epoch_seconds/step_size_train
print("-" * 20)
print("Epoch: [%3d/%3d], Average Train Loss: [%5.3f], Accuracy: [%5.3f]" % (
epoch+1, num_epochs, sum(losses)/len(losses), acc
))
print("epoch time: %5.3f ms, per step time: %5.3f ms" % (
epoch_seconds, step_seconds
))
if acc > best_acc:
best_acc = acc
if not os.path.exists(best_ckpt_dir):
os.mkdir(best_ckpt_dir)
ms.save_checkpoint(network, best_ckpt_path)
print("=" * 80)
print(f"End of validation the best Accuracy is: {best_acc: 5.3f}, "
f"save the best ckpt file in {best_ckpt_path}", flush=True)
> [Simple Index](https://repo.huaweicloud.com/repository/pypi/simple/ "Simple Index")<https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/models/application/resnet50_224_new.ckpt>
3.3 模型评估
从验证数据集中抽取了一批次的样本数据,并通过模型对这些样本进行推断。随后,在分析推断结果的基础上,采用蓝色显示图像及其正确的预测标签的方式,并用红色标记出预测错误的情况。
通过matplotlib库生成了一个包含了四个子图的图像;每一个子图都展示了其中一张图像以及其相应的预测结果。
import matplotlib.pyplot as plt
import mindspore as ms
def visualize_model(best_ckpt_path, val_ds):
net = resnet50()
# 全连接层输入层的大小
in_channels = net.fc.in_channels
# 输出通道数大小为分类数4
head = nn.Dense(in_channels, 4)
# 重置全连接层
net.fc = head
# 平均池化层kernel size为7
avg_pool = nn.AvgPool2d(kernel_size=7)
# 重置平均池化层
net.avg_pool = avg_pool
# 加载模型参数
param_dict = ms.load_checkpoint(best_ckpt_path)
ms.load_param_into_net(net, param_dict)
model = train.Model(net)
# 加载验证集的数据进行验证
data = next(val_ds.create_dict_iterator())
images = data["image"].asnumpy()
# print(type(images))
# print(images.shape)
#print(images)
labels = data["label"].asnumpy()
#print(labels)
class_name = {
0: "basal_cell_carcinoma",
1: "melanoma",
2: "nevus",
3: "pigmented_benign_keratosis"
}
# 预测图像类别
data_pre=ms.Tensor(data["image"])
# print(data_pre.shape)
# print(type(data_pre))
output = model.predict(data_pre)
# print(output)
pred = np.argmax(output.asnumpy(), axis=1)
# 显示图像及图像的预测值
plt.figure(figsize=(10, 10))
for i in range(16):
plt.subplot(4, 4, i + 1)
# 若预测正确,显示为蓝色;若预测错误,显示为红色
color = 'blue' if pred[i] == labels[i] else 'red'
plt.title('predict:{}'.format(class_name[pred[i]]), color=color,fontsize=7)
picture_show = np.transpose(images[i], (1, 2, 0))
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
picture_show = std * picture_show + mean
picture_show = np.clip(picture_show, 0, 1)
plt.imshow(picture_show)
plt.axis('off')
plt.show()
visualize_model('BestCheckpoint/resnet50-best.ckpt', dataset_val)

4 推理使用
import numpy as np
import matplotlib.pyplot as plt
import mindspore as ms
from mindspore import nn
from mindspore.train.serialization import load_checkpoint, load_param_into_net
import mindspore.numpy as mnp
def preprocess_image(image_path):
# 使用PIL加载图像
from PIL import Image
image = Image.open(image_path)
# 转换图像为RGB模式
image = image.convert('RGB')
# 调整图像大小以匹配模型输入
image = image.resize((224, 224)) # 假设模型输入大小为224x224
# 将图像转换为numpy数组
image_array = np.array(image)
# 归一化图像数组
image_array = image_array / 255.0
image_array = np.transpose(image_array, (2, 0, 1))
# 扩展维度以匹配模型输入,例如 (1, 3, 224, 224)
image_array = np.expand_dims(image_array, axis=0)
return image_array.astype(np.float32) # 确保数据类型为float32
def visualize_prediction(image_path, best_ckpt_path):
net = resnet50()
# 全连接层输入层的大小
in_channels = net.fc.in_channels
# 输出通道数大小为分类数4
head = nn.Dense(in_channels, 4)
# 重置全连接层
net.fc = head
# 平均池化层kernel size为7
avg_pool = nn.AvgPool2d(kernel_size=7)
# 重置平均池化层
net.avg_pool = avg_pool
# 加载模型参数
param_dict = ms.load_checkpoint(best_ckpt_path)
ms.load_param_into_net(net, param_dict)
model = train.Model(net)
# 预处理图像
image = preprocess_image(image_path)
data_pre = ms.Tensor(image)
print(data_pre.shape)
print(type(data_pre))
class_name = {
0: "basal_cell_carcinoma",
1: "melanoma",
2: "nevus",
3: "pigmented_benign_keratosis"
}
output = model.predict(data_pre)
print(output)
pred = np.argmax(output.asnumpy(), axis=1)
print(pred)
plt.figure(figsize=(5, 5))
plt.subplot(1, 1, 1)
plt.title('predict:{}'.format(class_name[pred[0]]))
picture_show = image[0] # 已经是归一化后的图像
picture_show = np.transpose(picture_show, (1, 2, 0)) # 转换为(224, 224, 3)
plt.imshow(picture_show)
plt.axis('off')
plt.show()
# 调用函数,传入图片路径和模型检查点路径
visualize_prediction('pifudata_Maker/images/val/2/ISIC_0000005.jpg', 'BestCheckpoint/resnet50-best.ckpt')

