{ "cells": [ { "cell_type": "markdown", "id": "fa7e3e52", "metadata": {}, "source": [ "# ResNet50图像分类\n", "\n", "[![在线运行](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.0.0-alpha/resource/_static/logo_modelarts.png)](https://authoring-modelarts-cnnorth4.huaweicloud.com/console/lab?share-url-b64=aHR0cHM6Ly9vYnMuZHVhbHN0YWNrLmNuLW5vcnRoLTQubXlodWF3ZWljbG91ZC5jb20vbWluZHNwb3JlLXdlYnNpdGUvbm90ZWJvb2svcjIuMC4wLWFscGhhL3R1dG9yaWFscy9hcHBsaWNhdGlvbi96aF9jbi9jdi9taW5kc3BvcmVfcmVzbmV0NTAuaXB5bmI=&imageid=77ef960a-bd26-4de4-9695-5b85a786fb90) [![下载Notebook](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.0.0-alpha/resource/_static/logo_notebook.png)](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/r2.0.0-alpha/tutorials/application/zh_cn/cv/mindspore_resnet50.ipynb) [![下载样例代码](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.0.0-alpha/resource/_static/logo_download_code.png)](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/r2.0.0-alpha/tutorials/application/zh_cn/cv/mindspore_resnet50.py) [![查看源文件](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.0.0-alpha/resource/_static/logo_source.png)](https://gitee.com/mindspore/docs/blob/r2.0.0-alpha/tutorials/application/source_zh_cn/cv/resnet50.ipynb)\n", "\n", "图像分类是最基础的计算机视觉应用,属于有监督学习类别,如给定一张图像(猫、狗、飞机、汽车等等),判断图像所属的类别。本章将介绍使用ResNet50网络对CIFAR-10数据集进行分类。\n", "\n", "## ResNet网络介绍\n", "\n", "ResNet50网络是2015年由微软实验室的何恺明提出,获得ILSVRC2015图像分类竞赛第一名。在ResNet网络提出之前,传统的卷积神经网络都是将一系列的卷积层和池化层堆叠得到的,但当网络堆叠到一定深度时,就会出现退化问题。下图是在CIFAR-10数据集上使用56层网络与20层网络训练误差和测试误差图,由图中数据可以看出,56层网络比20层网络训练误差和测试误差更大,随着网络的加深,其误差并没有如预想的一样减小。\n", "\n", "![resnet-1](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.0.0-alpha/tutorials/application/source_zh_cn/cv/images/resnet_1.png)\n", "\n", "ResNet网络提出了残差网络结构(Residual Network)来减轻退化问题,使用ResNet网络可以实现搭建较深的网络结构(突破1000层)。论文中使用ResNet网络在CIFAR-10数据集上的训练误差与测试误差图如下图所示,图中虚线表示训练误差,实线表示测试误差。由图中数据可以看出,ResNet网络层数越深,其训练误差和测试误差越小。\n", "\n", "![resnet-4](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.0.0-alpha/tutorials/application/source_zh_cn/cv/images/resnet_4.png)\n", "\n", "> 了解ResNet网络更多详细内容,参见[ResNet论文](https://arxiv.org/pdf/1512.03385.pdf)。" ] }, { "cell_type": "markdown", "id": "a987ee48", "metadata": {}, "source": [ "## 数据集准备与加载\n", "\n", "[CIFAR-10数据集](http://www.cs.toronto.edu/~kriz/cifar.html)共有60000张32*32的彩色图像,分为10个类别,每类有6000张图,数据集一共有50000张训练图片和10000张评估图片。首先,如下示例使用`download`接口下载并解压,目前仅支持解析二进制版本的CIFAR-10文件(CIFAR-10 binary version)。" ] }, { "cell_type": "code", "execution_count": 3, "id": "1f9b81fb", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Creating data folder...\n", "Downloading data from https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-binary.tar.gz (162.2 MB)\n", "\n", "file_sizes: 100%|████████████████████████████| 170M/170M [00:08<00:00, 20.6MB/s]\n", "Extracting tar.gz file...\n", "Successfully downloaded / unzipped to ./datasets-cifar10-bin\n" ] }, { "data": { "text/plain": [ "'./datasets-cifar10-bin'" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from download import download\n", "\n", "url = \"https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-binary.tar.gz\"\n", "\n", "download(url, \"./datasets-cifar10-bin\", kind=\"tar.gz\")" ] }, { "cell_type": "markdown", "id": "7e9020ba", "metadata": {}, "source": [ "下载后的数据集目录结构如下:\n", "\n", "```Text\n", "datasets-cifar10-bin/cifar-10-batches-bin\n", "├── batches.meta.text\n", "├── data_batch_1.bin\n", "├── data_batch_2.bin\n", "├── data_batch_3.bin\n", "├── data_batch_4.bin\n", "├── data_batch_5.bin\n", "├── readme.html\n", "└── test_batch.bin\n", "\n", "```\n", "\n", "然后,使用`mindspore.dataset.Cifar10Dataset`接口来加载数据集,并进行相关图像增强操作。" ] }, { "cell_type": "code", "execution_count": 8, "id": "df7fb621", "metadata": {}, "outputs": [], "source": [ "import mindspore.dataset as ds\n", "import mindspore.dataset.vision as vision\n", "import mindspore.dataset.transforms as transforms\n", "import mindspore as ms\n", "import numpy as np\n", "\n", "from mindspore import dtype as mstype\n", "from mindspore import nn\n", "\n", "\n", "data_dir = \"./datasets-cifar10-bin/cifar-10-batches-bin\" # 数据集根目录\n", "batch_size = 256 # 批量大小\n", "image_size = 32 # 训练图像空间大小\n", "workers = 4 # 并行线程个数\n", "num_classes = 10 # 分类数量\n", "\n", "def create_dataset_cifar10(dataset_dir, usage, resize, batch_size, workers):\n", "\n", " data_set = ds.Cifar10Dataset(dataset_dir=dataset_dir,\n", " usage=usage,\n", " num_parallel_workers=workers,\n", " shuffle=True)\n", "\n", " trans = []\n", " if usage == \"train\":\n", " trans += [\n", " vision.RandomCrop((32, 32), (4, 4, 4, 4)),\n", " vision.RandomHorizontalFlip(prob=0.5)\n", " ]\n", "\n", " trans += [\n", " vision.Resize(resize),\n", " vision.Rescale(1.0 / 255.0, 0.0),\n", " vision.Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010]),\n", " vision.HWC2CHW()\n", " ]\n", "\n", " target_trans = transforms.TypeCast(mstype.int32)\n", "\n", " # 数据映射操作\n", " data_set = data_set.map(\n", " operations=trans,\n", " input_columns='image',\n", " num_parallel_workers=workers)\n", "\n", " data_set = data_set.map(\n", " operations=target_trans,\n", " input_columns='label',\n", " num_parallel_workers=workers)\n", "\n", " # 批量操作\n", " data_set = data_set.batch(batch_size)\n", "\n", "\n", " return data_set\n", "\n", "\n", "# 获取处理后的训练与测试数据集\n", "\n", "dataset_train = create_dataset_cifar10(dataset_dir=data_dir,\n", " usage=\"train\",\n", " resize=image_size,\n", " batch_size=batch_size,\n", " workers=workers)\n", "step_size_train = dataset_train.get_dataset_size()\n", "index_label_dict = dataset_train.get_class_indexing()\n", "\n", "dataset_val = create_dataset_cifar10(dataset_dir=data_dir,\n", " usage=\"test\",\n", " resize=image_size,\n", " batch_size=batch_size,\n", " workers=workers)\n", "step_size_val = dataset_val.get_dataset_size()" ] }, { "cell_type": "markdown", "id": "21e86f95", "metadata": {}, "source": [ "对CIFAR-10训练数据集进行可视化。" ] }, { "cell_type": "code", "execution_count": 9, "id": "c3ffabb3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Image shape: (6, 3, 32, 32), Label: [9 8 6 0 8 5]\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "data_iter = next(dataset_train.create_dict_iterator())\n", "\n", "images = data_iter[\"image\"].asnumpy()\n", "labels = data_iter[\"label\"].asnumpy()\n", "print(f\"Image shape: {images.shape}, Label: {labels}\")\n", "\n", "classes = []\n", "\n", "with open(data_dir+\"/batches.meta.txt\", \"r\") as f:\n", " for line in f:\n", " line = line.rstrip()\n", " if line != '':\n", " classes.append(line)\n", "\n", "plt.figure()\n", "for i in range(6):\n", " plt.subplot(2, 3, i+1)\n", " image_trans = np.transpose(images[i], (1, 2, 0))\n", " mean = np.array([0.4914, 0.4822, 0.4465])\n", " std = np.array([0.2023, 0.1994, 0.2010])\n", " image_trans = std * image_trans + mean\n", " image_trans = np.clip(image_trans, 0, 1)\n", " plt.title(f\"{classes[labels[i]]}\")\n", " plt.imshow(image_trans)\n", " plt.axis(\"off\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "76c96f76", "metadata": {}, "source": [ "## 构建网络\n", "\n", "残差网络结构(Residual Network)是ResNet网络的主要亮点,ResNet使用残差网络结构后可有效地减轻退化问题,实现更深的网络结构设计,提高网络的训练精度。本节首先讲述如何构建残差网络结构,然后通过堆叠残差网络来构建ResNet50网络。\n", "\n", "### 构建残差网络结构\n", "\n", "残差网络结构图如下图所示,残差网络由两个分支构成:一个主分支,一个shortcuts(图中弧线表示)。主分支通过堆叠一系列的卷积操作得到,shotcuts从输入直接到输出,主分支输出的特征矩阵$F(x)$加上shortcuts输出的特征矩阵$x$得到$F(x)+x$,通过Relu激活函数后即为残差网络最后的输出。\n", "\n", "![residual](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.0.0-alpha/tutorials/application/source_zh_cn/cv/images/resnet_3.png)\n", "\n", "残差网络结构主要由两种,一种是Building Block,适用于较浅的ResNet网络,如ResNet18和ResNet34;另一种是Bottleneck,适用于层数较深的ResNet网络,如ResNet50、ResNet101和ResNet152。\n", "\n", "#### Building Block\n", "\n", "Building Block结构图如下图所示,主分支有两层卷积网络结构:\n", "\n", "+ 主分支第一层网络以输入channel为64为例,首先通过一个$3\\times3$的卷积层,然后通过Batch Normalization层,最后通过Relu激活函数层,输出channel为64;\n", "+ 主分支第二层网络的输入channel为64,首先通过一个$3\\times3$的卷积层,然后通过Batch Normalization层,输出channel为64。\n", "\n", "最后将主分支输出的特征矩阵与shortcuts输出的特征矩阵相加,通过Relu激活函数即为Building Block最后的输出。\n", "\n", "![building-block-5](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.0.0-alpha/tutorials/application/source_zh_cn/cv/images/resnet_5.png)\n", "\n", "主分支与shortcuts输出的特征矩阵相加时,需要保证主分支与shortcuts输出的特征矩阵shape相同。如果主分支与shortcuts输出的特征矩阵shape不相同,如输出channel是输入channel的一倍时,shortcuts上需要使用数量与输出channel相等,大小为$1\\times1$的卷积核进行卷积操作;若输出的图像较输入图像缩小一倍,则要设置shortcuts中卷积操作中的`stride`为2,主分支第一层卷积操作的`stride`也需设置为2。\n", "\n", "如下代码定义`ResidualBlockBase`类实现Building Block结构。" ] }, { "cell_type": "code", "execution_count": 5, "id": "c7ac0e2d", "metadata": {}, "outputs": [], "source": [ "from typing import Type, Union, List, Optional\n", "from mindspore import nn, train\n", "from mindspore.common.initializer import Normal\n", "\n", "weight_init = Normal(mean=0, sigma=0.02)\n", "gamma_init = Normal(mean=1, sigma=0.02)\n", "\n", "class ResidualBlockBase(nn.Cell):\n", " expansion: int = 1 # 最后一个卷积核数量与第一个卷积核数量相等\n", "\n", " def __init__(self, in_channel: int, out_channel: int,\n", " stride: int = 1, norm: Optional[nn.Cell] = None,\n", " down_sample: Optional[nn.Cell] = None) -> None:\n", " super(ResidualBlockBase, self).__init__()\n", " if not norm:\n", " self.norm = nn.BatchNorm2d(out_channel)\n", " else:\n", " self.norm = norm\n", "\n", " self.conv1 = nn.Conv2d(in_channel, out_channel,\n", " kernel_size=3, stride=stride,\n", " weight_init=weight_init)\n", " self.conv2 = nn.Conv2d(in_channel, out_channel,\n", " kernel_size=3, weight_init=weight_init)\n", " self.relu = nn.ReLU()\n", " self.down_sample = down_sample\n", "\n", " def construct(self, x):\n", " \"\"\"ResidualBlockBase construct.\"\"\"\n", " identity = x # shortcuts分支\n", "\n", " out = self.conv1(x) # 主分支第一层:3*3卷积层\n", " out = self.norm(out)\n", " out = self.relu(out)\n", " out = self.conv2(out) # 主分支第二层:3*3卷积层\n", " out = self.norm(out)\n", "\n", " if self.down_sample is not None:\n", " identity = self.down_sample(x)\n", " out += identity # 输出为主分支与shortcuts之和\n", " out = self.relu(out)\n", "\n", " return out" ] }, { "cell_type": "markdown", "id": "aaa15d3c", "metadata": {}, "source": [ "#### Bottleneck\n", "\n", "Bottleneck结构图如下图所示,在输入相同的情况下Bottleneck结构相对Building Block结构的参数数量更少,更适合层数较深的网络,ResNet50使用的残差结构就是Bottleneck。该结构的主分支有三层卷积结构,分别为$1\\times1$的卷积层、$3\\times3$卷积层和$1\\times1$的卷积层,其中$1\\times1$的卷积层分别起降维和升维的作用。\n", "\n", "+ 主分支第一层网络以输入channel为256为例,首先通过数量为64,大小为$1\\times1$的卷积核进行降维,然后通过Batch Normalization层,最后通过Relu激活函数层,其输出channel为64;\n", "+ 主分支第二层网络通过数量为64,大小为$3\\times3$的卷积核提取特征,然后通过Batch Normalization层,最后通过Relu激活函数层,其输出channel为64;\n", "+ 主分支第三层通过数量为256,大小$1\\times1$的卷积核进行升维,然后通过Batch Normalization层,其输出channel为256。\n", "\n", "最后将主分支输出的特征矩阵与shortcuts输出的特征矩阵相加,通过Relu激活函数即为Bottleneck最后的输出。\n", "\n", "![building-block-6](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.0.0-alpha/tutorials/application/source_zh_cn/cv/images/resnet_6.png)\n", "\n", "主分支与shortcuts输出的特征矩阵相加时,需要保证主分支与shortcuts输出的特征矩阵shape相同。如果主分支与shortcuts输出的特征矩阵shape不相同,如输出channel是输入channel的一倍时,shortcuts上需要使用数量与输出channel相等,大小为$1\\times1$的卷积核进行卷积操作;若输出的图像较输入图像缩小一倍,则要设置shortcuts中卷积操作中的`stride`为2,主分支第二层卷积操作的`stride`也需设置为2。\n", "\n", "如下代码定义`ResidualBlock`类实现Bottleneck结构。" ] }, { "cell_type": "code", "execution_count": 6, "id": "0d46f98e", "metadata": {}, "outputs": [], "source": [ "class ResidualBlock(nn.Cell):\n", " expansion = 4 # 最后一个卷积核的数量是第一个卷积核数量的4倍\n", "\n", " def __init__(self, in_channel: int, out_channel: int,\n", " stride: int = 1, down_sample: Optional[nn.Cell] = None) -> None:\n", " super(ResidualBlock, self).__init__()\n", "\n", " self.conv1 = nn.Conv2d(in_channel, out_channel,\n", " kernel_size=1, weight_init=weight_init)\n", " self.norm1 = nn.BatchNorm2d(out_channel)\n", " self.conv2 = nn.Conv2d(out_channel, out_channel,\n", " kernel_size=3, stride=stride,\n", " weight_init=weight_init)\n", " self.norm2 = nn.BatchNorm2d(out_channel)\n", " self.conv3 = nn.Conv2d(out_channel, out_channel * self.expansion,\n", " kernel_size=1, weight_init=weight_init)\n", " self.norm3 = nn.BatchNorm2d(out_channel * self.expansion)\n", "\n", " self.relu = nn.ReLU()\n", " self.down_sample = down_sample\n", "\n", " def construct(self, x):\n", "\n", " identity = x # shortscuts分支\n", "\n", " out = self.conv1(x) # 主分支第一层:1*1卷积层\n", " out = self.norm1(out)\n", " out = self.relu(out)\n", " out = self.conv2(out) # 主分支第二层:3*3卷积层\n", " out = self.norm2(out)\n", " out = self.relu(out)\n", " out = self.conv3(out) # 主分支第三层:1*1卷积层\n", " out = self.norm3(out)\n", "\n", " if self.down_sample is not None:\n", " identity = self.down_sample(x)\n", "\n", " out += identity # 输出为主分支与shortcuts之和\n", " out = self.relu(out)\n", "\n", " return out" ] }, { "cell_type": "markdown", "id": "d1d8dfc9", "metadata": {}, "source": [ "#### 构建ResNet50网络\n", "\n", "ResNet网络层结构如下图所示,以输入彩色图像$224\\times224$为例,首先通过数量64,卷积核大小为$7\\times7$,stride为2的卷积层conv1,该层输出图片大小为$112\\times112$,输出channel为64;然后通过一个$3\\times3$的最大下采样池化层,该层输出图片大小为$56\\times56$,输出channel为64;再堆叠4个残差网络块(conv2_x、conv3_x、conv4_x和conv5_x),此时输出图片大小为$7\\times7$,输出channel为2048;最后通过一个平均池化层、全连接层和softmax,得到分类概率。\n", "\n", "![resnet-layer](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.0.0-alpha/tutorials/application/source_zh_cn/cv/images/resnet_2.png)\n", "\n", "对于每个残差网络块,以ResNet50网络中的conv2_x为例,其由3个Bottleneck结构堆叠而成,每个Bottleneck输入的channel为64,输出channel为256。\n", "\n", "如下示例定义`make_layer`实现残差块的构建,其参数如下所示:\n", "\n", "+ `last_out_channel`:上一个残差网络输出的通道数。\n", "+ `block`:残差网络的类别,分别为`ResidualBlockBase`和`ResidualBlock`。\n", "+ `channel`:残差网络输入的通道数。\n", "+ `block_nums`:残差网络块堆叠的个数。\n", "+ `stride`:卷积移动的步幅。" ] }, { "cell_type": "code", "execution_count": 7, "id": "3dfa40a1", "metadata": {}, "outputs": [], "source": [ "def make_layer(last_out_channel, block: Type[Union[ResidualBlockBase, ResidualBlock]],\n", " channel: int, block_nums: int, stride: int = 1):\n", " down_sample = None # shortcuts分支\n", "\n", "\n", " if stride != 1 or last_out_channel != channel * block.expansion:\n", "\n", " down_sample = nn.SequentialCell([\n", " nn.Conv2d(last_out_channel, channel * block.expansion,\n", " kernel_size=1, stride=stride, weight_init=weight_init),\n", " nn.BatchNorm2d(channel * block.expansion, gamma_init=gamma_init)\n", " ])\n", "\n", " layers = []\n", " layers.append(block(last_out_channel, channel, stride=stride, down_sample=down_sample))\n", "\n", " in_channel = channel * block.expansion\n", " # 堆叠残差网络\n", " for _ in range(1, block_nums):\n", "\n", " layers.append(block(in_channel, channel))\n", "\n", " return nn.SequentialCell(layers)" ] }, { "cell_type": "markdown", "id": "67dae353", "metadata": {}, "source": [ "ResNet50网络共有5个卷积结构,一个平均池化层,一个全连接层,以CIFAR-10数据集为例:\n", "\n", "+ **conv1**:输入图片大小为$32\\times32$,输入channel为3。首先经过一个卷积核数量为64,卷积核大小为$7\\times7$,stride为2的卷积层;然后通过一个Batch Normalization层;最后通过Reul激活函数。该层输出feature map大小为$16\\times16$,输出channel为64。\n", "+ **conv2_x**:输入feature map大小为$16\\times16$,输入channel为64。首先经过一个卷积核大小为$3\\times3$,stride为2的最大下采样池化操作;然后堆叠3个$[1\\times1,64;3\\times3,64;1\\times1,256]$结构的Bottleneck。该层输出feature map大小为$8\\times8$,输出channel为256。\n", "+ **conv3_x**:输入feature map大小为$8\\times8$,输入channel为256。该层堆叠4个[1×1,128;3×3,128;1×1,512]结构的Bottleneck。该层输出feature map大小为$4\\times4$,输出channel为512。\n", "+ **conv4_x**:输入feature map大小为$4\\times4$,输入channel为512。该层堆叠6个[1×1,256;3×3,256;1×1,1024]结构的Bottleneck。该层输出feature map大小为$2\\times2$,输出channel为1024。\n", "+ **conv5_x**:输入feature map大小为$2\\times2$,输入channel为1024。该层堆叠3个[1×1,512;3×3,512;1×1,2048]结构的Bottleneck。该层输出feature map大小为$1\\times1$,输出channel为2048。\n", "+ **average pool & fc**:输入channel为2048,输出channel为分类的类别数。\n", "\n", "如下示例代码实现ResNet50模型的构建,通过用调函数`resnet50`即可构建ResNet50模型,函数`resnet50`参数如下:\n", "\n", "+ `num_classes`:分类的类别数,默认类别数为1000。\n", "+ `pretrained`:下载对应的训练模型,并加载预训练模型中的参数到网络中。" ] }, { "cell_type": "code", "execution_count": 8, "id": "1ebef3d0", "metadata": {}, "outputs": [], "source": [ "from mindspore import load_checkpoint, load_param_into_net\n", "\n", "\n", "class ResNet(nn.Cell):\n", " def __init__(self, block: Type[Union[ResidualBlockBase, ResidualBlock]],\n", " layer_nums: List[int], num_classes: int, input_channel: int) -> None:\n", " super(ResNet, self).__init__()\n", "\n", " self.relu = nn.ReLU()\n", " # 第一个卷积层,输入channel为3(彩色图像),输出channel为64\n", " self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, weight_init=weight_init)\n", " self.norm = nn.BatchNorm2d(64)\n", " # 最大池化层,缩小图片的尺寸\n", " self.max_pool = nn.MaxPool2d(kernel_size=3, stride=2, pad_mode='same')\n", " # 各个残差网络结构块定义\n", " self.layer1 = make_layer(64, block, 64, layer_nums[0])\n", " self.layer2 = make_layer(64 * block.expansion, block, 128, layer_nums[1], stride=2)\n", " self.layer3 = make_layer(128 * block.expansion, block, 256, layer_nums[2], stride=2)\n", " self.layer4 = make_layer(256 * block.expansion, block, 512, layer_nums[3], stride=2)\n", " # 平均池化层\n", " self.avg_pool = nn.AvgPool2d()\n", " # flattern层\n", " self.flatten = nn.Flatten()\n", " # 全连接层\n", " self.fc = nn.Dense(in_channels=input_channel, out_channels=num_classes)\n", "\n", " def construct(self, x):\n", "\n", " x = self.conv1(x)\n", " x = self.norm(x)\n", " x = self.relu(x)\n", " x = self.max_pool(x)\n", "\n", " x = self.layer1(x)\n", " x = self.layer2(x)\n", " x = self.layer3(x)\n", " x = self.layer4(x)\n", "\n", " x = self.avg_pool(x)\n", " x = self.flatten(x)\n", " x = self.fc(x)\n", "\n", " return x" ] }, { "cell_type": "code", "execution_count": 9, "id": "d16e658e", "metadata": {}, "outputs": [], "source": [ "def _resnet(model_url: str, block: Type[Union[ResidualBlockBase, ResidualBlock]],\n", " layers: List[int], num_classes: int, pretrained: bool, pretrained_ckpt: str,\n", " input_channel: int):\n", " model = ResNet(block, layers, num_classes, input_channel)\n", "\n", " if pretrained:\n", " # 加载预训练模型\n", " download(url=model_url, path=pretrained_ckpt)\n", " param_dict = load_checkpoint(pretrained_ckpt)\n", " load_param_into_net(model, param_dict)\n", "\n", " return model\n", "\n", "\n", "def resnet50(num_classes: int = 1000, pretrained: bool = False):\n", " \"ResNet50模型\"\n", " resnet50_url = \"https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/models/application/resnet50_224_new.ckpt\"\n", " resnet50_ckpt = \"./LoadPretrainedModel/resnet50_224_new.ckpt\"\n", " return _resnet(resnet50_url, ResidualBlock, [3, 4, 6, 3], num_classes,\n", " pretrained, resnet50_ckpt, 2048)" ] }, { "attachments": {}, "cell_type": "markdown", "id": "d40bd05a", "metadata": {}, "source": [ "## 模型训练与评估\n", "\n", "本节使用[ResNet50预训练模型](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/models/application/resnet50_224_new.ckpt)进行微调。调用`resnet50`构造ResNet50模型,并设置`pretrained`参数为True,将会自动下载ResNet50预训练模型,并加载预训练模型中的参数到网络中。然后定义优化器和损失函数,逐个epoch打印训练的损失值和评估精度,并保存评估精度最高的ckpt文件(resnet50-best.ckpt)到当前路径的./BestCheckPoint下。\n", "\n", ">此处我们展示了5个epochs的训练过程,如果想要达到理想的训练效果,建议训练80个epochs。" ] }, { "cell_type": "code", "execution_count": 11, "id": "9cf10c03", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Replace is False and data exists, so doing nothing. Use replace=True to re-download the data.\n" ] } ], "source": [ "import mindspore as ms\n", "# 定义ResNet50网络\n", "network = resnet50(pretrained=True)\n", "\n", "# 全连接层输入层的大小\n", "in_channel = network.fc.in_channels\n", "fc = nn.Dense(in_channels=in_channel, out_channels=10)\n", "# 重置全连接层\n", "network.fc = fc\n", "\n", "for param in network.get_parameters():\n", " param.requires_grad = True" ] }, { "cell_type": "code", "execution_count": 14, "id": "e1c632ff", "metadata": {}, "outputs": [], "source": [ "# 设置学习率\n", "num_epochs = 5\n", "lr = nn.cosine_decay_lr(min_lr=0.00001, max_lr=0.001, total_step=step_size_train * num_epochs,\n", " step_per_epoch=step_size_train, decay_epoch=num_epochs)\n", "# 定义优化器和损失函数\n", "opt = nn.Momentum(params=network.trainable_params(), learning_rate=lr, momentum=0.9)\n", "loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')\n", "\n", "\n", "def forward_fn(inputs, targets):\n", " logits = network(inputs)\n", " loss = loss_fn(logits, targets)\n", "\n", " return loss\n", "\n", "grad_fn = ms.value_and_grad(forward_fn, None, opt.parameters)\n", "\n", "def train_step(inputs, targets):\n", " loss, grads = grad_fn(inputs, targets)\n", " opt(grads)\n", " return loss\n", "\n", "# 实例化模型\n", "model = ms.Model(network, loss_fn, opt, metrics={\"Accuracy\": train.Accuracy()})" ] }, { "cell_type": "code", "execution_count": 15, "id": "b627e30c", "metadata": {}, "outputs": [], "source": [ "# 创建迭代器\n", "data_loader_train = dataset_train.create_tuple_iterator(num_epochs=num_epochs)\n", "data_loader_val = dataset_val.create_tuple_iterator(num_epochs=num_epochs)\n", "\n", "# 最佳模型存储路径\n", "best_acc = 0\n", "best_ckpt_dir = \"./BestCheckpoint\"\n", "best_ckpt_path = \"./BestCheckpoint/resnet50-best.ckpt\"" ] }, { "cell_type": "code", "execution_count": null, "id": "562a04ca", "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "# 开始循环训练\n", "print(\"Start Training Loop ...\")\n", "\n", "for epoch in range(num_epochs):\n", " losses = []\n", " network.set_train()\n", "\n", " # 为每轮训练读入数据\n", "\n", " for i, (images, labels) in enumerate(data_loader_train):\n", " loss = train_step(images, labels)\n", " if i%100 == 0 or i == step_size_train -1:\n", " print('Epoch: [%3d/%3d], Steps: [%3d/%3d], Train Loss: [%5.3f]'%(\n", " epoch+1, num_epochs, i+1, step_size_train, loss))\n", " losses.append(loss)\n", "\n", " # 每个epoch结束后,验证准确率\n", "\n", " acc = model.eval(dataset_val)['Accuracy']\n", "\n", " print(\"-\" * 50)\n", " print(\"Epoch: [%3d/%3d], Average Train Loss: [%5.3f], Accuracy: [%5.3f]\" % (\n", " epoch+1, num_epochs, sum(losses)/len(losses), acc\n", " ))\n", " print(\"-\" * 50)\n", "\n", " if acc > best_acc:\n", " best_acc = acc\n", " if not os.path.exists(best_ckpt_dir):\n", " os.mkdir(best_ckpt_dir)\n", " ms.save_checkpoint(network, best_ckpt_path)\n", "\n", "print(\"=\" * 80)\n", "print(f\"End of validation the best Accuracy is: {best_acc: 5.3f}, \"\n", " f\"save the best ckpt file in {best_ckpt_path}\", flush=True)" ] }, { "cell_type": "markdown", "id": "6ca392ab", "metadata": {}, "source": [ "```Text\n", "Epoch: [ 1/ 5], Steps: [ 1/8334], Train Loss: [2.438]\n", "Epoch: [ 1/ 5], Steps: [101/8334], Train Loss: [2.371]\n", "\n", "......\n", "\n", "Epoch: [ 1/ 5], Steps: [8334/8334], Train Loss: [2.292]\n", "--------------------------------------------------\n", "Epoch: [ 1/ 5], Average Train Loss: [2.007], Accuracy: [0.240]\n", "--------------------------------------------------\n", "\n", "......\n", "\n", "\n", "Epoch: [ 5/ 5], Steps: [8334/8334], Train Loss: [3.519]\n", "--------------------------------------------------\n", "Epoch: [ 5/ 5], Average Train Loss: [1.621], Accuracy: [0.498]\n", "--------------------------------------------------\n", "================================================================================\n", "End of validation the best Accuracy is: 0.498, save the best ckpt file in ./BestCheckpoint/resnet50-best.ckpt\n", "\n", "```" ] }, { "attachments": {}, "cell_type": "markdown", "id": "46e28f6f", "metadata": {}, "source": [ "## 可视化模型预测\n", "\n", "定义`visualize_model`函数,使用上述验证精度最高的模型对CIFAR-10测试数据集进行预测,并将预测结果可视化。若预测字体颜色为蓝色表示为预测正确,预测字体颜色为红色则表示预测错误。\n", "\n", "> 由上面的结果可知,5个epochs下模型在验证数据集的预测准确率不到50%,即仅可以正确预测不到一半数量的图片分类,实际预测的准确率可能会更低。下图我们展示了训练40个epochs后较好的预测结果,但此结果有随机性,一般情况下6张图片中会有1-2张预测失败。如果想要达到理想的训练效果,建议训练80个epochs。" ] }, { "cell_type": "code", "execution_count": 9, "id": "6ba2fa94", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "\n", "def visualize_model(best_ckpt_path, dataset_val):\n", " num_class = 10 # 对狼和狗图像进行二分类\n", " net = resnet50(num_class)\n", " # 加载模型参数\n", " param_dict = ms.load_checkpoint(best_ckpt_path)\n", " ms.load_param_into_net(net, param_dict)\n", " model = ms.Model(net)\n", " # 加载验证集的数据进行验证\n", " data = next(dataset_val.create_dict_iterator())\n", " images = data[\"image\"].asnumpy()\n", " labels = data[\"label\"].asnumpy()\n", " # 预测图像类别\n", " output = model.predict(ms.Tensor(data['image']))\n", " pred = np.argmax(output.asnumpy(), axis=1)\n", "\n", " # 图像分类\n", " classes = []\n", "\n", " with open(data_dir+\"/batches.meta.txt\", \"r\") as f:\n", " for line in f:\n", " line = line.rstrip()\n", " if line != '':\n", " classes.append(line)\n", "\n", " # 显示图像及图像的预测值\n", " plt.figure()\n", " for i in range(6):\n", " plt.subplot(2, 3, i+1)\n", " # 若预测正确,显示为蓝色;若预测错误,显示为红色\n", " color = 'blue' if pred[i] == labels[i] else 'red'\n", " plt.title('predict:{}'.format(classes[pred[i]]), color=color)\n", " picture_show = np.transpose(images[i], (1, 2, 0))\n", " mean = np.array([0.4914, 0.4822, 0.4465])\n", " std = np.array([0.2023, 0.1994, 0.2010])\n", " picture_show = std * picture_show + mean\n", " picture_show = np.clip(picture_show, 0, 1)\n", " plt.imshow(picture_show)\n", " plt.axis('off')\n", "\n", " plt.show()\n", "\n", "# 使用测试数据集进行验证\n", "visualize_model(best_ckpt_path=best_ckpt_path, dataset_val=dataset_val)" ] } ], "metadata": { "kernelspec": { "display_name": "MindSpore", "language": "python", "name": "mindspore" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" }, "vscode": { "interpreter": { "hash": "61b352d89025746abfd3d4fa7053c22c36b9d81e9898372aef9407193f0acc45" } } }, "nbformat": 4, "nbformat_minor": 5 }