моя контактная информация
Почтамезофия@protonmail.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
ResNet в основном решает проблему «деградации» глубоких сверточных сетей при увеличении глубины. В обычных сверточных нейронных сетях первой проблемой, вызванной увеличением глубины сети, является исчезновение и взрыв градиента. Эта проблема была успешно решена после того, как Сегеди предложил слой BN. Слой BN может нормализовать выходные данные каждого слоя, так что градиент может оставаться стабильным по размеру после прохождения слой за слоем в обратном порядке и не будет слишком маленьким или слишком большим. Однако автор обнаружил, что после добавления BN и увеличения глубины сходиться все равно непросто. Он упомянул вторую проблему — проблему снижения точности: когда уровень в определенной степени достаточно велик, точность будет насыщена. а затем быстро уменьшаться. Это уменьшение не является Исчезновение градиентов вызвано не переобучением, а тем, что сеть слишком сложна, поэтому трудно достичь идеального уровня ошибок только за счет неограниченного обучения в свободном диапазоне.
Проблема снижения точности не является проблемой самой структуры сети, а вызвана существующими методами обучения, которые не идеальны. Широко используемые в настоящее время оптимизаторы, будь то SGD, RMSProp или Adam, не могут достичь теоретически оптимальных результатов сходимости, когда глубина сети становится больше.
Пока существует подходящая структура сети, более глубокая сеть определенно будет работать лучше, чем более мелкая. Процесс доказательства также очень прост: предположим, что к сети A добавляется несколько слоев, чтобы сформировать новую сеть B. Если добавленные слои выполняют только сопоставление идентичности на выходе A, то есть выходные данные A добавляются. через новую сеть A. После того, как уровень становится выходом B, изменений не происходит, поэтому частоты ошибок сетей A и сети B равны, что доказывает, что сеть после углубления не будет хуже, чем сеть до углубления.
Классификация изображений является самым простым приложением компьютерного зрения и относится к категории обучения с учителем. Например, учитывая изображение (кошка, собака, самолет, автомобиль и т. д.), определите категорию, к которой оно принадлежит. В этой главе будет представлено использование сети ResNet50 для классификации набора данных CIFAR-10.
Сеть ResNet50 была предложена Хэ Каймингом из Microsoft Labs в 2015 году и заняла первое место в конкурсе классификации изображений ILSVRC2015. До того, как была предложена сеть ResNet, традиционные сверточные нейронные сети были получены путем сложения ряда сверточных слоев и объединения слоев. Однако, когда сеть сложена до определенной глубины, возникнут проблемы с деградацией. На рисунке ниже представлен график ошибки обучения и ошибки теста с использованием 56-слойной сети и 20-слойной сети в наборе данных CIFAR-10. Из данных на рисунке видно, что ошибка обучения и ошибка теста. В 56-слойной сети больше, чем в 20-слойной сети. Поскольку сеть углубляется, ошибка не уменьшается, как ожидалось.
Сеть ResNet предлагает остаточную структуру сети (остаточную сеть) для смягчения проблемы деградации. Использование сети ResNet позволяет построить более глубокую сетевую структуру (более 1000 уровней). График ошибок обучения и ошибок теста сети ResNet, использованной в статье для набора данных CIFAR-10, показан на рисунке ниже. Пунктирная линия на рисунке представляет ошибку обучения, а сплошная линия представляет ошибку теста. Из данных на рисунке видно, что чем глубже сетевой уровень ResNet, тем меньше его ошибка обучения и ошибка теста.
Набор данных CIFAR-10 содержит в общей сложности 60 000 цветных изображений размером 32*32, разделенных на 10 категорий, каждая категория содержит 6 000 изображений, а набор данных содержит в общей сложности 50 000 обучающих изображений и 10 000 оценочных изображений. Во-первых, в следующем примере для загрузки и распаковки используется интерфейс загрузки. В настоящее время поддерживается только двоичная версия файлов CIFAR-10 (двоичная версия CIFAR-10).
%%capture captured_output
# 实验环境已经预装了mindspore==2.2.14,如需更换mindspore版本,可更改下面mindspore的版本号
!pip uninstall mindspore -y
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14
%%capture captured_output
# 实验环境已经预装了mindspore==2.2.14,如需更换mindspore版本,可更改下面mindspore的版本号
!pip uninstall mindspore -y
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14
# 查看当前 mindspore 版本
!pip show mindspore
# 查看当前 mindspore 版本
!pip show mindspore
from download import download
url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-binary.tar.gz"
download(url, "./datasets-cifar10-bin", kind="tar.gz", replace=True)
from download import download
url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-binary.tar.gz"
download(url, "./datasets-cifar10-bin", kind="tar.gz", replace=True)
'./datasets-cifar10-bin'
Структура каталогов загруженного набора данных следующая:
datasets-cifar10-bin/cifar-10-batches-bin
├── batches.meta.text
├── data_batch_1.bin
├── data_batch_2.bin
├── data_batch_3.bin
├── data_batch_4.bin
├── data_batch_5.bin
├── readme.html
└── test_batch.bin
Затем используйте интерфейс Mindspore.dataset.Cifar10Dataset, чтобы загрузить набор данных и выполнить соответствующие операции по улучшению изображения.
import mindspore as ms
import mindspore.dataset as ds
import mindspore.dataset.vision as vision
import mindspore.dataset.transforms as transforms
from mindspore import dtype as mstype
data_dir = "./datasets-cifar10-bin/cifar-10-batches-bin" # 数据集根目录
batch_size = 256 # 批量大小
image_size = 32 # 训练图像空间大小
workers = 4 # 并行线程个数
num_classes = 10 # 分类数量
def create_dataset_cifar10(dataset_dir, usage, resize, batch_size, workers):
data_set = ds.Cifar10Dataset(dataset_dir=dataset_dir,
usage=usage,
num_parallel_workers=workers,
shuffle=True)
trans = []
if usage == "train":
trans += [
vision.RandomCrop((32, 32), (4, 4, 4, 4)),
vision.RandomHorizontalFlip(prob=0.5)
]
trans += [
vision.Resize(resize),
vision.Rescale(1.0 / 255.0, 0.0),
vision.Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010]),
vision.HWC2CHW()
]
target_trans = transforms.TypeCast(mstype.int32)
# 数据映射操作
data_set = data_set.map(operations=trans,
input_columns='image',
num_parallel_workers=workers)
data_set = data_set.map(operations=target_trans,
input_columns='label',
num_parallel_workers=workers)
# 批量操作
data_set = data_set.batch(batch_size)
return data_set
# 获取处理后的训练与测试数据集
dataset_train = create_dataset_cifar10(dataset_dir=data_dir,
usage="train",
resize=image_size,
batch_size=batch_size,
workers=workers)
step_size_train = dataset_train.get_dataset_size()
dataset_val = create_dataset_cifar10(dataset_dir=data_dir,
usage="test",
resize=image_size,
batch_size=batch_size,
workers=workers)
step_size_val = dataset_val.get_dataset_size()
import mindspore as ms
import mindspore.dataset as ds
import mindspore.dataset.vision as vision
import mindspore.dataset.transforms as transforms
from mindspore import dtype as mstype
data_dir = "./datasets-cifar10-bin/cifar-10-batches-bin" # 数据集根目录
batch_size = 256 # 批量大小
image_size = 32 # 训练图像空间大小
workers = 4 # 并行线程个数
num_classes = 10 # 分类数量
def create_dataset_cifar10(dataset_dir, usage, resize, batch_size, workers):
data_set = ds.Cifar10Dataset(dataset_dir=dataset_dir,
usage=usage,
num_parallel_workers=workers,
shuffle=True)
trans = []
if usage == "train":
trans += [
vision.RandomCrop((32, 32), (4, 4, 4, 4)),
vision.RandomHorizontalFlip(prob=0.5)
]
trans += [
vision.Resize(resize),
vision.Rescale(1.0 / 255.0, 0.0),
vision.Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010]),
vision.HWC2CHW()
]
target_trans = transforms.TypeCast(mstype.int32)
# 数据映射操作
data_set = data_set.map(operations=trans,
input_columns='image',
num_parallel_workers=workers)
data_set = data_set.map(operations=target_trans,
input_columns='label',
num_parallel_workers=workers)
# 批量操作
data_set = data_set.batch(batch_size)
return data_set
# 获取处理后的训练与测试数据集
dataset_train = create_dataset_cifar10(dataset_dir=data_dir,
usage="train",
resize=image_size,
batch_size=batch_size,
workers=workers)
step_size_train = dataset_train.get_dataset_size()
dataset_val = create_dataset_cifar10(dataset_dir=data_dir,
usage="test",
resize=image_size,
batch_size=batch_size,
workers=workers)
step_size_val = dataset_val.get_dataset_size()
Визуализируйте набор обучающих данных CIFAR-10.
import matplotlib.pyplot as plt
import numpy as np
data_iter = next(dataset_train.create_dict_iterator())
images = data_iter["image"].asnumpy()
labels = data_iter["label"].asnumpy()
print(f"Image shape: {images.shape}, Label shape: {labels.shape}")
# 训练数据集中,前六张图片所对应的标签
print(f"Labels: {labels[:6]}")
classes = []
with open(data_dir + "/batches.meta.txt", "r") as f:
for line in f:
line = line.rstrip()
if line:
classes.append(line)
# 训练数据集的前六张图片
plt.figure()
for i in range(6):
plt.subplot(2, 3, i + 1)
image_trans = np.transpose(images[i], (1, 2, 0))
mean = np.array([0.4914, 0.4822, 0.4465])
std = np.array([0.2023, 0.1994, 0.2010])
image_trans = std * image_trans + mean
image_trans = np.clip(image_trans, 0, 1)
plt.title(f"{classes[labels[i]]}")
plt.imshow(image_trans)
plt.axis("off")
plt.show()
import matplotlib.pyplot as plt
import numpy as np
data_iter = next(dataset_train.create_dict_iterator())
images = data_iter["image"].asnumpy()
labels = data_iter["label"].asnumpy()
print(f"Image shape: {images.shape}, Label shape: {labels.shape}")
# 训练数据集中,前六张图片所对应的标签
print(f"Labels: {labels[:6]}")
classes = []
with open(data_dir + "/batches.meta.txt", "r") as f:
for line in f:
line = line.rstrip()
if line:
classes.append(line)
# 训练数据集的前六张图片
plt.figure()
for i in range(6):
plt.subplot(2, 3, i + 1)
image_trans = np.transpose(images[i], (1, 2, 0))
mean = np.array([0.4914, 0.4822, 0.4465])
std = np.array([0.2023, 0.1994, 0.2010])
image_trans = std * image_trans + mean
image_trans = np.clip(image_trans, 0, 1)
plt.title(f"{classes[labels[i]]}")
plt.imshow(image_trans)
plt.axis("off")
plt.show()
Форма изображения: (256, 3, 32, 32), Форма метки: (256,)
Теги: [3 2 7 6 0 4]
Остаточная структура сети (остаточная сеть) является основной особенностью сети ResNet. Использование остаточной структуры сети в ResNet может эффективно смягчить проблему деградации, обеспечить более глубокую разработку структуры сети и повысить точность обучения сети. В этом разделе сначала описывается, как построить остаточную структуру сети, а затем создается сеть ResNet50 путем объединения остаточных сетей.
Постройте остаточную структуру сети
残差网络结构图如下图所示,残差网络由两个分支构成:一个主分支,一个shortcuts(图中弧线表示)。主分支通过堆叠一系列的卷积操作得到,shotcuts从输入直接到输出,主分支输出的特征矩阵 𝐹(𝑥)
加上shortcuts输出的特征矩阵 𝑥 得到 𝐹(𝑥)+𝑥,通过Relu激活函数后即为残差网络最后的输出。
Существует два основных типа остаточных сетевых структур: один — Building Block, который подходит для более мелких сетей ResNet, таких как ResNet18 и ResNet34; другой — Bottleneck, который подходит для более глубоких сетей ResNet, таких как ResNet50, ResNet101 и ResNet152. .
Схема структуры Building Block показана на рисунке ниже. Основная ветвь имеет двухуровневую сверточную сетевую структуру:
Сеть первого уровня основной ветви в качестве примера принимает входной канал 64. Сначала он передает 3×3.
Уровень свертки, затем слой пакетной нормализации и, наконец, слой функции активации Relu, выходной канал равен 64;
Входной канал сети второго уровня основной ветви — 64. Сначала он передает сигнал 3×3.
Затем сверточный слой передается через слой пакетной нормализации, и выходной канал равен 64.
Наконец, добавляются вывод матрицы функций основной веткой и вывод матрицы функций с помощью ярлыков, а окончательный результат Building Block получается с помощью функции активации Relu.
При добавлении матриц признаков, выводимых основной ветвью и ярлыками, необходимо убедиться, что форма матрицы признаков, выводимых основной ветвью и ярлыками, одинакова. Если форма матрицы признаков, выводимая основной ветвью и ярлыками, различается, например, выходной канал в два раза превышает входной канал, ярлыки должны использовать то же количество ядер свертки, что и выходной канал, и размер 1 × 1 для операция свертки; если выходное изображение в два раза меньше входного изображения, шаг в операции свертки в ярлыках должен быть установлен на 2, а также шаг в операции свертки первого слоя основной ветви также должен быть установлен. быть установлен на 2.
Следующий код определяет класс ResidualBlockBase для реализации структуры Building Block.
from typing import Type, Union, List, Optional
import mindspore.nn as nn
from mindspore.common.initializer import Normal
# 初始化卷积层与BatchNorm的参数
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
Схема структуры «Узкого места» показана на рисунке ниже. Когда входные данные одинаковы, структура «Узкого места» имеет меньше параметров, чем структура «Строительного блока», и больше подходит для сетей с более глубокими уровнями. Остаточная структура, используемая ResNet50, является «Узким местом». Основная ветвь этой структуры имеет три слоя структуры свертки, каждый из которых имеет размер 1×1.
Сверточный слой, 3×3
Сверточные слои и 1×1
сверточный слой, где 1×1
Сверточные слои играют роль уменьшения размерности и размерности соответственно.
Сеть первого уровня основной ветви в качестве примера принимает входной канал 256. Номер первого прохода равен 64, а размер равен 1×1.
Ядро свертки выполняет уменьшение размерности, затем проходит через уровень пакетной нормализации и, наконец, проходит через уровень функции активации Relu, и его выходной канал равен 64;
Количество проходов сети второго уровня основной ветки — 64, размер — 3×3.
Ядро свертки извлекает признаки, затем проходит через уровень пакетной нормализации и, наконец, проходит через уровень функции активации Relu, и его выходной канал равен 64;
Количество проходов на третьем уровне основной ветки — 256, размер — 1х1.
Ядру свертки присваиваются размеры, а затем он передается через уровень пакетной нормализации, и его выходной канал равен 256.
Наконец, добавляются вывод матрицы функций основной веткой и вывод матрицы функций с помощью ярлыков, а окончательный результат «Узкого места» получается с помощью функции активации Relu.
При добавлении матриц признаков, выводимых основной ветвью и ярлыками, необходимо убедиться, что форма матрицы признаков, выводимых основной ветвью и ярлыками, одинакова. Если форма матрицы признаков, выводимая основной ветвью и ярлыками, различается, например, если выходной канал в два раза превышает входной канал, количество ярлыков должно быть равно выходному каналу, а размер — 1×1.
Ядро свертки выполняет операцию свертки; если выходное изображение в два раза меньше входного изображения, шаг в операции свертки в ярлыках должен быть установлен равным 2, а также необходим шаг в операции свертки второго уровня основной ветви. установить на 2.
Следующий код определяет класс ResidualBlock для реализации структуры «Узкое место».
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
Структура сетевого уровня ResNet показана на рисунке ниже. На примере входного цветного изображения размером 224×224 сначала передается слой свертки conv1 с количеством 64, размером ядра свертки 7×7 и шагом 2. Размер выходного изображения этого слоя составляет 112×112, выходной канал — 64; затем через слой объединения с максимальным понижением разрешения 3×3 размер выходного изображения этого слоя — 56×56, а затем — 64; сложите 4 оставшихся сетевых блока (conv2_x, conv3_x, conv4_x и conv5_x), в это время размер выходного изображения составляет 7×7, а выходной канал — 2048, наконец, вероятность классификации получается через средний уровень пула, полностью связный уровень; и софтмакс.
Для каждого остаточного блока сети, на примере conv2_x в сети ResNet50, он складывается из 3 структур «Узкое место». Входной канал каждого «Узкого места» равен 64, а выходной канал — 256.
В следующем примере определяется make_layer для реализации построения остаточного блока, и его параметры следующие:
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)
Сеть ResNet50 имеет в общей сложности 5 сверточных структур, средний уровень пула и полностью связный уровень. В качестве примера возьмем набор данных CIFAR-10:
conv1: размер входного изображения — 32×32, а входной канал — 3. Сначала он проходит через слой свертки с номером ядра свертки 64, размером ядра свертки 7×7 и шагом 2, затем он проходит через уровень пакетной нормализации и, наконец, проходит через функцию активации Reul; Размер выходной карты объектов этого слоя составляет 16×16, а выходной канал — 64.
conv2_x: размер входной карты объектов — 16 × 16, а входной канал — 64. Сначала он выполняет операцию объединения с максимальным понижением частоты с размером ядра свертки 3×3 и шагом 2, затем объединяет три узких места с [1×1, 3×3, 64; ] состав. Размер выходной карты объектов этого слоя — 8×8, а выходной канал — 256.
conv3_x: размер входной карты объектов — 8×8, а входной канал — 256. Этот слой объединяет 4 узких места со структурой [1×1, 128; 3×3, 128; Размер выходной карты объектов этого слоя — 4×4, а выходной канал — 512.
conv4_x: размер входной карты объектов — 4 × 4, а входной канал — 512. Этот слой объединяет 6 узких мест со структурой [1×1, 256; 3×3, 256; 1×1, 1024; Размер выходной карты объектов этого слоя — 2×2, а выходной канал — 1024.
conv5_x: размер входной карты объектов — 2 × 2, а входной канал — 1024. Этот слой объединяет три узких места со структурой [1×1, 512; 3×3, 512; 1×1, 2048; Размер выходной карты объектов этого слоя — 1×1, а выходной канал — 2048.
средний пул и fc: входной канал — 2048, выходной канал — количество категорий классификации.
Следующий пример кода реализует построение модели ResNet50. Модель ResNet50 можно создать путем вызова функции resnet50. Параметры функции resnet50 следующие:
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, pretrained_ckpt: str,
input_channel: int):
model = ResNet(block, layers, num_classes, input_channel)
if pretrained:
# 加载预训练模型
download(url=model_url, path=pretrained_ckpt, replace=True)
param_dict = load_checkpoint(pretrained_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 = "./LoadPretrainedModel/resnet50_224_new.ckpt"
return _resnet(resnet50_url, ResidualBlock, [3, 4, 6, 3], num_classes,
pretrained, resnet50_ckpt, 2048)
В этом разделе для точной настройки используется предварительно обученная модель ResNet50. Вызовите resnet50, чтобы создать модель ResNet50, и установите для предварительно обученного параметра значение True. Предварительно обученная модель ResNet50 будет автоматически загружена, а параметры предварительно обученной модели будут загружены в сеть. Затем определите оптимизатор и функцию потерь, распечатайте значение потерь при обучении и точность оценки эпоху за эпохой и сохраните файл ckpt с самой высокой точностью оценки (resnet50-best.ckpt) в ./BestCheckPoint в текущем пути.
Поскольку выходной размер (соответствующий параметр num_classes) полносвязного слоя (fc) предварительно обученной модели равен 1000, для успешной загрузки предварительно обученных весов мы устанавливаем полносвязный выходной размер модели по умолчанию. 1000. Набор данных CIFAR10 содержит в общей сложности 10 категорий. При использовании этого набора данных для обучения выходной размер полностью связного слоя модели, загруженной предварительно обученными весами, необходимо сбросить до 10.
Здесь мы показываем процесс обучения в 5 эпох. Если вы хотите добиться идеального тренировочного эффекта, рекомендуется тренироваться в течение 80 эпох.
# 定义ResNet50网络
network = resnet50(pretrained=True)
# 全连接层输入层的大小
in_channel = network.fc.in_channels
fc = nn.Dense(in_channels=in_channel, out_channels=10)
# 重置全连接层
network.fc = fc
# 设置学习率
num_epochs = 5
lr = nn.cosine_decay_lr(min_lr=0.00001, max_lr=0.001, total_step=step_size_train * num_epochs,
step_per_epoch=step_size_train, decay_epoch=num_epochs)
# 定义优化器和损失函数
opt = nn.Momentum(params=network.trainable_params(), learning_rate=lr, momentum=0.9)
loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
def forward_fn(inputs, targets):
logits = network(inputs)
loss = loss_fn(logits, targets)
return loss
grad_fn = ms.value_and_grad(forward_fn, None, opt.parameters)
def train_step(inputs, targets):
loss, grads = grad_fn(inputs, targets)
opt(grads)
return loss
import os
# 创建迭代器
data_loader_train = dataset_train.create_tuple_iterator(num_epochs=num_epochs)
data_loader_val = dataset_val.create_tuple_iterator(num_epochs=num_epochs)
# 最佳模型存储路径
best_acc = 0
best_ckpt_dir = "./BestCheckpoint"
best_ckpt_path = "./BestCheckpoint/resnet50-best.ckpt"
if not os.path.exists(best_ckpt_dir):
os.mkdir(best_ckpt_dir)
import mindspore.ops as ops
def train(data_loader, epoch):
"""模型训练"""
losses = []
network.set_train(True)
for i, (images, labels) in enumerate(data_loader):
loss = train_step(images, labels)
if i % 100 == 0 or i == step_size_train - 1:
print('Epoch: [%3d/%3d], Steps: [%3d/%3d], Train Loss: [%5.3f]' %
(epoch + 1, num_epochs, i + 1, step_size_train, loss))
losses.append(loss)
return sum(losses) / len(losses)
def evaluate(data_loader):
"""模型验证"""
network.set_train(False)
correct_num = 0.0 # 预测正确个数
total_num = 0.0 # 预测总数
for images, labels in data_loader:
logits = network(images)
pred = logits.argmax(axis=1) # 预测结果
correct = ops.equal(pred, labels).reshape((-1, ))
correct_num += correct.sum().asnumpy()
total_num += correct.shape[0]
acc = correct_num / total_num # 准确率
return acc
# 开始循环训练
print("Start Training Loop ...")
for epoch in range(num_epochs):
curr_loss = train(data_loader_train, epoch)
curr_acc = evaluate(data_loader_val)
print("-" * 50)
print("Epoch: [%3d/%3d], Average Train Loss: [%5.3f], Accuracy: [%5.3f]" % (
epoch+1, num_epochs, curr_loss, curr_acc
))
print("-" * 50)
# 保存当前预测准确率最高的模型
if curr_acc > best_acc:
best_acc = curr_acc
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)
Start Training Loop ...
Epoch: [ 1/ 5], Steps: [ 1/196], Train Loss: [2.389]
Epoch: [ 1/ 5], Steps: [101/196], Train Loss: [1.467]
Epoch: [ 1/ 5], Steps: [196/196], Train Loss: [1.093]
--------------------------------------------------
Epoch: [ 1/ 5], Average Train Loss: [1.641], Accuracy: [0.595]
--------------------------------------------------
Epoch: [ 2/ 5], Steps: [ 1/196], Train Loss: [1.253]
Epoch: [ 2/ 5], Steps: [101/196], Train Loss: [0.974]
Epoch: [ 2/ 5], Steps: [196/196], Train Loss: [0.832]
--------------------------------------------------
Epoch: [ 2/ 5], Average Train Loss: [1.019], Accuracy: [0.685]
--------------------------------------------------
Epoch: [ 3/ 5], Steps: [ 1/196], Train Loss: [0.917]
Epoch: [ 3/ 5], Steps: [101/196], Train Loss: [0.879]
Epoch: [ 3/ 5], Steps: [196/196], Train Loss: [0.743]
--------------------------------------------------
Epoch: [ 3/ 5], Average Train Loss: [0.852], Accuracy: [0.721]
--------------------------------------------------
Epoch: [ 4/ 5], Steps: [ 1/196], Train Loss: [0.911]
Epoch: [ 4/ 5], Steps: [101/196], Train Loss: [0.703]
Epoch: [ 4/ 5], Steps: [196/196], Train Loss: [0.768]
--------------------------------------------------
Epoch: [ 4/ 5], Average Train Loss: [0.777], Accuracy: [0.737]
--------------------------------------------------
Epoch: [ 5/ 5], Steps: [ 1/196], Train Loss: [0.793]
Epoch: [ 5/ 5], Steps: [101/196], Train Loss: [0.809]
Epoch: [ 5/ 5], Steps: [196/196], Train Loss: [0.734]
--------------------------------------------------
Epoch: [ 5/ 5], Average Train Loss: [0.745], Accuracy: [0.742]
--------------------------------------------------
================================================================================
End of validation the best Accuracy is: 0.742, save the best ckpt file in ./BestCheckpoint/resnet50-best.ckpt
Определите функцию Visualize_model, используйте вышеупомянутую модель с высочайшей точностью проверки для прогнозирования набора тестовых данных CIFAR-10 и визуализируйте результаты прогнозирования. Если цвет шрифта прогноза синий, это означает, что прогноз правильный, а если цвет шрифта прогноза красный, это означает, что прогноз ошибочен.
Из приведенных выше результатов видно, что точность прогнозирования модели в наборе проверочных данных для 5 эпох составляет около 70%, то есть при нормальных обстоятельствах 2 из 6 изображений не смогут предсказать. Если вы хотите добиться идеального тренировочного эффекта, рекомендуется тренироваться 80 эпох.
import matplotlib.pyplot as plt
def visualize_model(best_ckpt_path, dataset_val):
num_class = 10 # 对狼和狗图像进行二分类
net = resnet50(num_class)
# 加载模型参数
param_dict = ms.load_checkpoint(best_ckpt_path)
ms.load_param_into_net(net, param_dict)
# 加载验证集的数据进行验证
data = next(dataset_val.create_dict_iterator())
images = data["image"]
labels = data["label"]
# 预测图像类别
output = net(data['image'])
pred = np.argmax(output.asnumpy(), axis=1)
# 图像分类
classes = []
with open(data_dir + "/batches.meta.txt", "r") as f:
for line in f:
line = line.rstrip()
if line:
classes.append(line)
# 显示图像及图像的预测值
plt.figure()
for i in range(6):
plt.subplot(2, 3, i + 1)
# 若预测正确,显示为蓝色;若预测错误,显示为红色
color = 'blue' if pred[i] == labels.asnumpy()[i] else 'red'
plt.title('predict:{}'.format(classes[pred[i]]), color=color)
picture_show = np.transpose(images.asnumpy()[i], (1, 2, 0))
mean = np.array([0.4914, 0.4822, 0.4465])
std = np.array([0.2023, 0.1994, 0.2010])
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(best_ckpt_path=best_ckpt_path, dataset_val=dataset_val)
import matplotlib.pyplot as plt
def visualize_model(best_ckpt_path, dataset_val):
num_class = 10 # 对狼和狗图像进行二分类
net = resnet50(num_class)
# 加载模型参数
param_dict = ms.load_checkpoint(best_ckpt_path)
ms.load_param_into_net(net, param_dict)
# 加载验证集的数据进行验证
data = next(dataset_val.create_dict_iterator())
images = data["image"]
labels = data["label"]
# 预测图像类别
output = net(data['image'])
pred = np.argmax(output.asnumpy(), axis=1)
# 图像分类
classes = []
with open(data_dir + "/batches.meta.txt", "r") as f:
for line in f:
line = line.rstrip()
if line:
classes.append(line)
# 显示图像及图像的预测值
plt.figure()
for i in range(6):
plt.subplot(2, 3, i + 1)
# 若预测正确,显示为蓝色;若预测错误,显示为红色
color = 'blue' if pred[i] == labels.asnumpy()[i] else 'red'
plt.title('predict:{}'.format(classes[pred[i]]), color=color)
picture_show = np.transpose(images.asnumpy()[i], (1, 2, 0))
mean = np.array([0.4914, 0.4822, 0.4465])
std = np.array([0.2023, 0.1994, 0.2010])
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(best_ckpt_path=best_ckpt_path, dataset_val=dataset_val)