当前位置:   article > 正文

昇思MindSpore 25天学习打卡营|day10

昇思MindSpore 25天学习打卡营|day10

前段时间忙着小学期的实验还有答辩,今天刚刚忙完,就赶着来学习MindSpore,继续完成打卡。

ResNet50迁移学习

加载数据集

狼狗数据集提取自ImageNet分类数据集,使用mindspore.dataset.ImageFolderDataset接口来加载数据集,并进行相关图像增强操作。

首先执行过程定义一些输入:

  1. batch_size = 18 # 批量大小
  2. image_size = 224 # 训练图像空间大小
  3. num_epochs = 5 # 训练周期数
  4. lr = 0.001 # 学习率
  5. momentum = 0.9 # 动量
  6. workers = 4 # 并行线程个数

对数据集的加载并进行相关图像增强操作:

  1. import mindspore as ms
  2. import mindspore.dataset as ds
  3. import mindspore.dataset.vision as vision
  4. # 数据集目录路径
  5. data_path_train = "./datasets-Canidae/data/Canidae/train/"
  6. data_path_val = "./datasets-Canidae/data/Canidae/val/"
  7. # 创建训练数据集
  8. def create_dataset_canidae(dataset_path, usage):
  9. """数据加载"""
  10. data_set = ds.ImageFolderDataset(dataset_path,
  11. num_parallel_workers=workers,
  12. shuffle=True,)
  13. # 数据增强操作
  14. mean = [0.485 * 255, 0.456 * 255, 0.406 * 255]
  15. std = [0.229 * 255, 0.224 * 255, 0.225 * 255]
  16. scale = 32
  17. if usage == "train":
  18. # Define map operations for training dataset
  19. trans = [
  20. vision.RandomCropDecodeResize(size=image_size, scale=(0.08, 1.0), ratio=(0.75, 1.333)),
  21. vision.RandomHorizontalFlip(prob=0.5),
  22. vision.Normalize(mean=mean, std=std),
  23. vision.HWC2CHW()
  24. ]
  25. else:
  26. # Define map operations for inference dataset
  27. trans = [
  28. vision.Decode(),
  29. vision.Resize(image_size + scale),
  30. vision.CenterCrop(image_size),
  31. vision.Normalize(mean=mean, std=std),
  32. vision.HWC2CHW()
  33. ]
  34. # 数据映射操作
  35. data_set = data_set.map(
  36. operations=trans,
  37. input_columns='image',
  38. num_parallel_workers=workers)
  39. # 批量操作
  40. data_set = data_set.batch(batch_size)
  41. return data_set
  42. dataset_train = create_dataset_canidae(data_path_train, "train")
  43. step_size_train = dataset_train.get_dataset_size()
  44. dataset_val = create_dataset_canidae(data_path_val, "val")
  45. step_size_val = dataset_val.get_dataset_size()

数据可视化

从mindspore.dataset.ImageFolderDataset接口加载的训练数据返回值为字典,用户可通过create_dict_iterator 接口创建数据迭代器,使用next迭代访问数据集。上文中我设置的batch_size是18,所以用next一次可获取18个图像及标签数据。

  1. data = next(dataset_train.create_dict_iterator())
  2. images = data["image"]
  3. labels = data["label"]
  4. print("Tensor of image", images.shape)
  5. print("Labels:", labels)
Tensor of image (18, 3, 224, 224)
Labels: [0 1 0 0 0 0 0 0 0 1 1 1 1 1 1 0 1 0]

对获取到的图像及标签数据进行可视化,标题为图像对应的label名称

  1. import matplotlib.pyplot as plt
  2. import numpy as np
  3. # class_name对应label,按文件夹字符串从小到大的顺序标记label
  4. class_name = {0: "dogs", 1: "wolves"}
  5. plt.figure(figsize=(5, 5))
  6. for i in range(4):
  7. # 获取图像及其对应的label
  8. data_image = images[i].asnumpy()
  9. data_label = labels[i]
  10. # 处理图像供展示使用
  11. data_image = np.transpose(data_image, (1, 2, 0))
  12. mean = np.array([0.485, 0.456, 0.406])
  13. std = np.array([0.229, 0.224, 0.225])
  14. data_image = std * data_image + mean
  15. data_image = np.clip(data_image, 0, 1)
  16. # 显示图像
  17. plt.subplot(2, 2, i+1)
  18. plt.imshow(data_image)
  19. plt.title(class_name[int(labels[i].asnumpy())])
  20. plt.axis("off")
  21. plt.show()

训练模型

使用ResNet50模型进行训练。搭建好模型框架后,通过将pretrained参数设置为True来下载ResNet50的预训练模型并将权重参数加载到网络中。

构建Resnet50网络

  1. from typing import Type, Union, List, Optional
  2. from mindspore import nn, train
  3. from mindspore.common.initializer import Normal
  4. weight_init = Normal(mean=0, sigma=0.02)
  5. gamma_init = Normal(mean=1, sigma=0.02)
  1. class ResidualBlockBase(nn.Cell):
  2. expansion: int = 1 # 最后一个卷积核数量与第一个卷积核数量相等
  3. def __init__(self, in_channel: int, out_channel: int,
  4. stride: int = 1, norm: Optional[nn.Cell] = None,
  5. down_sample: Optional[nn.Cell] = None) -> None:
  6. super(ResidualBlockBase, self).__init__()
  7. if not norm:
  8. self.norm = nn.BatchNorm2d(out_channel)
  9. else:
  10. self.norm = norm
  11. self.conv1 = nn.Conv2d(in_channel, out_channel,
  12. kernel_size=3, stride=stride,
  13. weight_init=weight_init)
  14. self.conv2 = nn.Conv2d(in_channel, out_channel,
  15. kernel_size=3, weight_init=weight_init)
  16. self.relu = nn.ReLU()
  17. self.down_sample = down_sample
  18. def construct(self, x):
  19. """ResidualBlockBase construct."""
  20. identity = x # shortcuts分支
  21. out = self.conv1(x) # 主分支第一层:3*3卷积层
  22. out = self.norm(out)
  23. out = self.relu(out)
  24. out = self.conv2(out) # 主分支第二层:3*3卷积层
  25. out = self.norm(out)
  26. if self.down_sample is not None:
  27. identity = self.down_sample(x)
  28. out += identity # 输出为主分支与shortcuts之和
  29. out = self.relu(out)
  30. return out
  31. class ResidualBlock(nn.Cell):
  32. expansion = 4 # 最后一个卷积核的数量是第一个卷积核数量的4倍
  33. def __init__(self, in_channel: int, out_channel: int,
  34. stride: int = 1, down_sample: Optional[nn.Cell] = None) -> None:
  35. super(ResidualBlock, self).__init__()
  36. self.conv1 = nn.Conv2d(in_channel, out_channel,
  37. kernel_size=1, weight_init=weight_init)
  38. self.norm1 = nn.BatchNorm2d(out_channel)
  39. self.conv2 = nn.Conv2d(out_channel, out_channel,
  40. kernel_size=3, stride=stride,
  41. weight_init=weight_init)
  42. self.norm2 = nn.BatchNorm2d(out_channel)
  43. self.conv3 = nn.Conv2d(out_channel, out_channel * self.expansion,
  44. kernel_size=1, weight_init=weight_init)
  45. self.norm3 = nn.BatchNorm2d(out_channel * self.expansion)
  46. self.relu = nn.ReLU()
  47. self.down_sample = down_sample
  48. def construct(self, x):
  49. identity = x # shortscuts分支
  50. out = self.conv1(x) # 主分支第一层:1*1卷积层
  51. out = self.norm1(out)
  52. out = self.relu(out)
  53. out = self.conv2(out) # 主分支第二层:3*3卷积层
  54. out = self.norm2(out)
  55. out = self.relu(out)
  56. out = self.conv3(out) # 主分支第三层:1*1卷积层
  57. out = self.norm3(out)
  58. if self.down_sample is not None:
  59. identity = self.down_sample(x)
  60. out += identity # 输出为主分支与shortcuts之和
  61. out = self.relu(out)
  62. return out
  63. def make_layer(last_out_channel, block: Type[Union[ResidualBlockBase, ResidualBlock]],
  64. channel: int, block_nums: int, stride: int = 1):
  65. down_sample = None # shortcuts分支
  66. if stride != 1 or last_out_channel != channel * block.expansion:
  67. down_sample = nn.SequentialCell([
  68. nn.Conv2d(last_out_channel, channel * block.expansion,
  69. kernel_size=1, stride=stride, weight_init=weight_init),
  70. nn.BatchNorm2d(channel * block.expansion, gamma_init=gamma_init)
  71. ])
  72. layers = []
  73. layers.append(block(last_out_channel, channel, stride=stride, down_sample=down_sample))
  74. in_channel = channel * block.expansion
  75. # 堆叠残差网络
  76. for _ in range(1, block_nums):
  77. layers.append(block(in_channel, channel))
  78. return nn.SequentialCell(layers)
  79. from mindspore import load_checkpoint, load_param_into_net
  80. class ResNet(nn.Cell):
  81. def __init__(self, block: Type[Union[ResidualBlockBase, ResidualBlock]],
  82. layer_nums: List[int], num_classes: int, input_channel: int) -> None:
  83. super(ResNet, self).__init__()
  84. self.relu = nn.ReLU()
  85. # 第一个卷积层,输入channel为3(彩色图像),输出channel为64
  86. self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, weight_init=weight_init)
  87. self.norm = nn.BatchNorm2d(64)
  88. # 最大池化层,缩小图片的尺寸
  89. self.max_pool = nn.MaxPool2d(kernel_size=3, stride=2, pad_mode='same')
  90. # 各个残差网络结构块定义,
  91. self.layer1 = make_layer(64, block, 64, layer_nums[0])
  92. self.layer2 = make_layer(64 * block.expansion, block, 128, layer_nums[1], stride=2)
  93. self.layer3 = make_layer(128 * block.expansion, block, 256, layer_nums[2], stride=2)
  94. self.layer4 = make_layer(256 * block.expansion, block, 512, layer_nums[3], stride=2)
  95. # 平均池化层
  96. self.avg_pool = nn.AvgPool2d()
  97. # flattern层
  98. self.flatten = nn.Flatten()
  99. # 全连接层
  100. self.fc = nn.Dense(in_channels=input_channel, out_channels=num_classes)
  101. def construct(self, x):
  102. x = self.conv1(x)
  103. x = self.norm(x)
  104. x = self.relu(x)
  105. x = self.max_pool(x)
  106. x = self.layer1(x)
  107. x = self.layer2(x)
  108. x = self.layer3(x)
  109. x = self.layer4(x)
  110. x = self.avg_pool(x)
  111. x = self.flatten(x)
  112. x = self.fc(x)
  113. return x
  114. def _resnet(model_url: str, block: Type[Union[ResidualBlockBase, ResidualBlock]],
  115. layers: List[int], num_classes: int, pretrained: bool, pretrianed_ckpt: str,
  116. input_channel: int):
  117. model = ResNet(block, layers, num_classes, input_channel)
  118. if pretrained:
  119. # 加载预训练模型
  120. download(url=model_url, path=pretrianed_ckpt, replace=True)
  121. param_dict = load_checkpoint(pretrianed_ckpt)
  122. load_param_into_net(model, param_dict)
  123. return model
  124. def resnet50(num_classes: int = 1000, pretrained: bool = False):
  125. "ResNet50模型"
  126. resnet50_url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/models/application/resnet50_224_new.ckpt"
  127. resnet50_ckpt = "./LoadPretrainedModel/resnet50_224_new.ckpt"
  128. return _resnet(resnet50_url, ResidualBlock, [3, 4, 6, 3], num_classes,
  129. pretrained, resnet50_ckpt, 2048)

固定特征进行训练

使用固定特征进行训练的时候,需要冻结除最后一层之外的所有网络层。通过设置 requires_grad 冻结参数,以便不在反向传播中计算梯度。

  1. import mindspore as ms
  2. import matplotlib.pyplot as plt
  3. import os
  4. import time
  5. net_work = resnet50(pretrained=True)
  6. # 全连接层输入层的大小
  7. in_channels = net_work.fc.in_channels
  8. # 输出通道数大小为狼狗分类数2
  9. head = nn.Dense(in_channels, 2)
  10. # 重置全连接层
  11. net_work.fc = head
  12. # 平均池化层kernel size为7
  13. avg_pool = nn.AvgPool2d(kernel_size=7)
  14. # 重置平均池化层
  15. net_work.avg_pool = avg_pool
  16. # 冻结除最后一层外的所有参数
  17. for param in net_work.get_parameters():
  18. if param.name not in ["fc.weight", "fc.bias"]:
  19. param.requires_grad = False
  20. # 定义优化器和损失函数
  21. opt = nn.Momentum(params=net_work.trainable_params(), learning_rate=lr, momentum=0.5)
  22. loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
  23. def forward_fn(inputs, targets):
  24. logits = net_work(inputs)
  25. loss = loss_fn(logits, targets)
  26. return loss
  27. grad_fn = ms.value_and_grad(forward_fn, None, opt.parameters)
  28. def train_step(inputs, targets):
  29. loss, grads = grad_fn(inputs, targets)
  30. opt(grads)
  31. return loss
  32. # 实例化模型
  33. model1 = train.Model(net_work, loss_fn, opt, metrics={"Accuracy": train.Accuracy()})

训练和评估

开始训练模型,与没有预训练模型相比,将节约一大半时间,因为此时可以不用计算部分梯度。保存评估精度最高的ckpt文件于当前路径的./BestCheckpoint/resnet50-best-freezing-param.ckpt。

  1. import mindspore as ms
  2. import matplotlib.pyplot as plt
  3. import os
  4. import time
  5. dataset_train = create_dataset_canidae(data_path_train, "train")
  6. step_size_train = dataset_train.get_dataset_size()
  7. dataset_val = create_dataset_canidae(data_path_val, "val")
  8. step_size_val = dataset_val.get_dataset_size()
  9. num_epochs = 5
  10. # 创建迭代器
  11. data_loader_train = dataset_train.create_tuple_iterator(num_epochs=num_epochs)
  12. data_loader_val = dataset_val.create_tuple_iterator(num_epochs=num_epochs)
  13. best_ckpt_dir = "./BestCheckpoint"
  14. best_ckpt_path = "./BestCheckpoint/resnet50-best-freezing-param.ckpt"
  1. import mindspore as ms
  2. import matplotlib.pyplot as plt
  3. import os
  4. import time
  5. # 开始循环训练
  6. print("Start Training Loop ...")
  7. best_acc = 0
  8. for epoch in range(num_epochs):
  9. losses = []
  10. net_work.set_train()
  11. epoch_start = time.time()
  12. # 为每轮训练读入数据
  13. for i, (images, labels) in enumerate(data_loader_train):
  14. labels = labels.astype(ms.int32)
  15. loss = train_step(images, labels)
  16. losses.append(loss)
  17. # 每个epoch结束后,验证准确率
  18. acc = model1.eval(dataset_val)['Accuracy']
  19. epoch_end = time.time()
  20. epoch_seconds = (epoch_end - epoch_start) * 1000
  21. step_seconds = epoch_seconds/step_size_train
  22. print("-" * 20)
  23. print("Epoch: [%3d/%3d], Average Train Loss: [%5.3f], Accuracy: [%5.3f]" % (
  24. epoch+1, num_epochs, sum(losses)/len(losses), acc
  25. ))
  26. print("epoch time: %5.3f ms, per step time: %5.3f ms" % (
  27. epoch_seconds, step_seconds
  28. ))
  29. if acc > best_acc:
  30. best_acc = acc
  31. if not os.path.exists(best_ckpt_dir):
  32. os.mkdir(best_ckpt_dir)
  33. ms.save_checkpoint(net_work, best_ckpt_path)
  34. print("=" * 80)
  35. print(f"End of validation the best Accuracy is: {best_acc: 5.3f}, "
  36. f"save the best ckpt file in {best_ckpt_path}", flush=True)
Start Training Loop ...
--------------------
Epoch: [  1/  5], Average Train Loss: [0.702], Accuracy: [0.717]
epoch time: 123599.202 ms, per step time: 8828.514 ms
--------------------
Epoch: [  2/  5], Average Train Loss: [0.585], Accuracy: [0.817]
epoch time: 738.841 ms, per step time: 52.774 ms
--------------------
Epoch: [  3/  5], Average Train Loss: [0.532], Accuracy: [0.933]
epoch time: 738.426 ms, per step time: 52.745 ms
--------------------
Epoch: [  4/  5], Average Train Loss: [0.454], Accuracy: [0.967]
epoch time: 709.648 ms, per step time: 50.689 ms
--------------------
Epoch: [  5/  5], Average Train Loss: [0.412], Accuracy: [0.933]
epoch time: 743.312 ms, per step time: 53.094 ms
================================================================================
End of validation the best Accuracy is:  0.967, save the best ckpt file in ./BestCheckpoint/resnet50-best-freezing-param.ckpt

可视化模型预测

使用固定特征得到的best.ckpt文件对验证集的狼和狗图像数据进行预测。若预测字体为蓝色即为预测正确,若预测文字为红色则预测错误。

  1. import matplotlib.pyplot as plt
  2. import mindspore as ms
  3. def visualize_model(best_ckpt_path, val_ds):
  4. net = resnet50()
  5. # 全连接层输入层的大小
  6. in_channels = net.fc.in_channels
  7. # 输出通道数大小为狼狗分类数2
  8. head = nn.Dense(in_channels, 2)
  9. # 重置全连接层
  10. net.fc = head
  11. # 平均池化层kernel size为7
  12. avg_pool = nn.AvgPool2d(kernel_size=7)
  13. # 重置平均池化层
  14. net.avg_pool = avg_pool
  15. # 加载模型参数
  16. param_dict = ms.load_checkpoint(best_ckpt_path)
  17. ms.load_param_into_net(net, param_dict)
  18. model = train.Model(net)
  19. # 加载验证集的数据进行验证
  20. data = next(val_ds.create_dict_iterator())
  21. images = data["image"].asnumpy()
  22. labels = data["label"].asnumpy()
  23. class_name = {0: "dogs", 1: "wolves"}
  24. # 预测图像类别
  25. output = model.predict(ms.Tensor(data['image']))
  26. pred = np.argmax(output.asnumpy(), axis=1)
  27. # 显示图像及图像的预测值
  28. plt.figure(figsize=(5, 5))
  29. for i in range(4):
  30. plt.subplot(2, 2, i + 1)
  31. # 若预测正确,显示为蓝色;若预测错误,显示为红色
  32. color = 'blue' if pred[i] == labels[i] else 'red'
  33. plt.title('predict:{}'.format(class_name[pred[i]]), color=color)
  34. picture_show = np.transpose(images[i], (1, 2, 0))
  35. mean = np.array([0.485, 0.456, 0.406])
  36. std = np.array([0.229, 0.224, 0.225])
  37. picture_show = std * picture_show + mean
  38. picture_show = np.clip(picture_show, 0, 1)
  39. plt.imshow(picture_show)
  40. plt.axis('off')
  41. plt.show()
visualize_model(best_ckpt_path, dataset_val)

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/844470
推荐阅读
相关标签
  

闽ICP备14008679号