informasi kontak saya
Surat[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
ResNet terutama memecahkan masalah "degradasi" jaringan konvolusional dalam ketika kedalamannya semakin dalam. Dalam jaringan saraf konvolusional umum, masalah pertama yang disebabkan oleh peningkatan kedalaman jaringan adalah hilangnya gradien dan ledakan. Masalah ini berhasil diselesaikan setelah Szegedy mengusulkan lapisan BN. Lapisan BN dapat menormalkan keluaran setiap lapisan, sehingga gradien tetap stabil ukurannya setelah dilewati lapisan demi lapisan secara terbalik, dan tidak akan terlalu kecil atau terlalu besar. Namun, penulis menemukan bahwa masih tidak mudah untuk melakukan konvergensi setelah menambahkan BN dan menambah kedalaman. Ia menyebutkan masalah kedua – masalah penurunan akurasi: ketika levelnya cukup besar hingga batas tertentu, akurasinya akan jenuh, dan kemudian menurun dengan cepat. Hilangnya gradien ini bukan disebabkan oleh overfitting, tetapi karena jaringan terlalu kompleks, sehingga sulit untuk mencapai tingkat kesalahan ideal hanya melalui pelatihan jarak bebas yang tidak dibatasi.
Permasalahan menurunnya akurasi ini bukan merupakan permasalahan pada struktur jaringan itu sendiri, namun disebabkan oleh metode pelatihan yang ada yang kurang ideal. Pengoptimal yang banyak digunakan saat ini, baik SGD, RMSProp, atau Adam, tidak dapat mencapai hasil konvergensi optimal secara teoritis ketika kedalaman jaringan menjadi lebih besar.
Selama ada struktur jaringan yang sesuai, jaringan yang lebih dalam pasti akan berperforma lebih baik daripada jaringan yang lebih dangkal. Proses pembuktiannya juga sangat sederhana: Misalkan beberapa lapisan ditambahkan di belakang jaringan A untuk membentuk jaringan baru B. Jika lapisan yang ditambahkan hanya melakukan pemetaan identitas pada keluaran A, maka keluaran A ditambahkan. melalui jaringan baru A. Tidak ada perubahan setelah level menjadi keluaran B, sehingga tingkat kesalahan jaringan A dan jaringan B sama, yang membuktikan bahwa jaringan setelah pendalaman tidak akan lebih buruk dari jaringan sebelum pendalaman.
Klasifikasi gambar merupakan aplikasi computer vision yang paling dasar dan termasuk dalam kategori pembelajaran terbimbing. Misalnya, jika diberi gambar (kucing, anjing, pesawat terbang, mobil, dll), tentukan kategori gambar tersebut. Bab ini akan memperkenalkan penggunaan jaringan ResNet50 untuk mengklasifikasikan kumpulan data CIFAR-10.
Jaringan ResNet50 diusulkan oleh He Kaiming dari Microsoft Labs pada tahun 2015 dan memenangkan juara pertama dalam kompetisi klasifikasi gambar ILSVRC2015. Sebelum jaringan ResNet diusulkan, jaringan saraf konvolusional tradisional diperoleh dengan menumpuk serangkaian lapisan konvolusional dan mengumpulkan lapisan, namun, ketika jaringan ditumpuk hingga kedalaman tertentu, masalah degradasi akan terjadi. Gambar dibawah merupakan grafik dari training error dan test error menggunakan jaringan 56 layer dan jaringan 20 layer pada data set CIFAR-10. Terlihat dari data pada gambar tersebut yaitu training error dan test error dari jaringan 56 lapisan lebih besar dibandingkan dengan jaringan 20 lapisan. Ketika jaringan semakin dalam, kesalahan tidak berkurang seperti yang diharapkan.
Jaringan ResNet mengusulkan struktur jaringan sisa (Residual Network) untuk mengatasi masalah degradasi. Menggunakan jaringan ResNet dapat membangun struktur jaringan yang lebih dalam (lebih dari 1000 lapisan). Grafik kesalahan pelatihan dan kesalahan pengujian jaringan ResNet yang digunakan dalam makalah kumpulan data CIFAR-10 ditunjukkan pada gambar di bawah ini. Garis putus-putus pada gambar menunjukkan kesalahan pelatihan, dan garis padat menunjukkan kesalahan pengujian. Terlihat dari data pada gambar bahwa semakin dalam lapisan jaringan ResNet maka semakin kecil kesalahan pelatihan dan kesalahan pengujiannya.
Kumpulan data CIFAR-10 memiliki total 60.000 gambar berwarna 32*32, dibagi menjadi 10 kategori, setiap kategori memiliki 6.000 gambar, dan kumpulan data memiliki total 50.000 gambar pelatihan dan 10.000 gambar evaluasi. Pertama, contoh berikut menggunakan antarmuka pengunduhan untuk mengunduh dan mendekompresi. Saat ini, hanya versi biner file CIFAR-10 (versi biner CIFAR-10) yang didukung.
%%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)
'./dataset-cifar10-bin'
Struktur direktori kumpulan data yang diunduh adalah sebagai berikut:
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
Kemudian, gunakan antarmuka mindpore.dataset.Cifar10Dataset untuk memuat kumpulan data dan melakukan operasi penyempurnaan gambar terkait.
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()
Visualisasikan kumpulan data pelatihan 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()
Bentuk gambar: (256, 3, 32, 32), Bentuk label: (256,)
Label: [3 2 7 6 0 4]
Struktur jaringan sisa (Jaringan Residu) adalah sorotan utama jaringan ResNet. Penggunaan struktur jaringan sisa oleh ResNet dapat secara efektif mengatasi masalah degradasi, mencapai desain struktur jaringan yang lebih dalam, dan meningkatkan akurasi pelatihan jaringan. Bagian ini pertama-tama menjelaskan cara membangun struktur jaringan sisa, lalu membangun jaringan ResNet50 dengan menumpuk jaringan sisa.
Bangun struktur jaringan sisa
残差网络结构图如下图所示,残差网络由两个分支构成:一个主分支,一个shortcuts(图中弧线表示)。主分支通过堆叠一系列的卷积操作得到,shotcuts从输入直接到输出,主分支输出的特征矩阵 𝐹(𝑥)
加上shortcuts输出的特征矩阵 𝑥 得到 𝐹(𝑥)+𝑥,通过Relu激活函数后即为残差网络最后的输出。
Ada dua tipe utama struktur jaringan sisa. Yang pertama adalah Building Block, yang cocok untuk jaringan ResNet yang lebih dangkal, seperti ResNet18 dan ResNet34; yang lainnya adalah Bottleneck, yang cocok untuk jaringan ResNet yang lebih dalam, seperti ResNet50, ResNet101, dan ResNet152. .
Diagram struktur Building Block ditunjukkan pada gambar di bawah ini. Cabang utama memiliki struktur jaringan konvolusional dua lapis:
Jaringan lapisan pertama dari cabang utama mengambil saluran input sebagai contoh 64. Pertama, melewati 3×3
Lapisan konvolusi, kemudian melalui lapisan Normalisasi Batch, dan terakhir melalui lapisan fungsi aktivasi Relu, saluran keluarannya adalah 64;
Saluran masukan dari jaringan lapisan kedua dari cabang utama adalah 64. Pertama melewati 3×3
Lapisan konvolusional kemudian dilewatkan melalui lapisan Normalisasi Batch, dan saluran keluarannya adalah 64.
Terakhir, keluaran matriks fitur oleh cabang utama dan keluaran matriks fitur melalui pintasan ditambahkan, dan keluaran akhir dari Blok Penyusun diperoleh melalui fungsi aktivasi Relu.
Saat menambahkan keluaran matriks fitur menurut cabang utama dan pintasan, Anda perlu memastikan bahwa bentuk keluaran matriks fitur menurut cabang utama dan pintasan adalah sama. Jika bentuk matriks fitur keluaran oleh cabang utama dan pintasan berbeda, misalnya saluran keluaran dua kali saluran masukan, pintasan perlu menggunakan jumlah kernel konvolusi yang sama dengan saluran keluaran dan ukuran 1×1 untuk operasi konvolusi; jika keluaran gambar dua kali lebih kecil dari gambar masukan, langkah dalam operasi konvolusi di pintasan harus disetel ke 2, dan langkah dalam operasi konvolusi lapisan pertama cabang utama juga harus diatur. disetel ke 2.
Kode berikut mendefinisikan kelas ResidualBlockBase untuk mengimplementasikan struktur 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
Diagram struktur Bottleneck ditunjukkan pada gambar di bawah ini. Jika inputnya sama, struktur Bottleneck memiliki parameter yang lebih sedikit dibandingkan struktur Building Block dan lebih cocok untuk jaringan dengan lapisan yang lebih dalam. Cabang utama struktur ini memiliki tiga lapisan struktur konvolusi yang masing-masing berukuran 1×1
Lapisan konvolusional, 3×3
Lapisan konvolusional dan 1×1
lapisan konvolusional, di mana 1×1
Lapisan konvolusional masing-masing memainkan peran reduksi dimensi dan dimensi.
Jaringan lapisan pertama dari cabang utama mengambil saluran input sebagai contoh 256. Nomor lintasan pertama adalah 64 dan ukurannya 1×1
Kernel konvolusi melakukan reduksi dimensi, kemudian melewati lapisan Normalisasi Batch, dan terakhir melewati lapisan fungsi aktivasi Relu, dan saluran keluarannya adalah 64;
Jumlah lintasan jaringan lapisan kedua cabang utama adalah 64 dan ukurannya 3×3
Kernel konvolusi mengekstraksi fitur, kemudian melewati lapisan Normalisasi Batch, dan akhirnya melewati lapisan fungsi aktivasi Relu, dan saluran keluarannya adalah 64;
Jumlah lintasan pada cabang utama tingkat ketiga adalah 256, dan ukurannya 1×1
Kernel konvolusi diberi dimensi, dan kemudian melewati lapisan Normalisasi Batch, dan saluran keluarannya adalah 256.
Terakhir, keluaran matriks fitur oleh cabang utama dan keluaran matriks fitur melalui pintasan ditambahkan, dan keluaran akhir Bottleneck diperoleh melalui fungsi aktivasi Relu.
Saat menambahkan keluaran matriks fitur menurut cabang utama dan pintasan, Anda perlu memastikan bahwa bentuk keluaran matriks fitur menurut cabang utama dan pintasan adalah sama. Jika bentuk matriks fitur keluaran oleh cabang utama dan pintasan berbeda, misalnya, jika saluran keluaran dua kali lipat saluran masukan, jumlah pintasan harus sama dengan saluran keluaran, dan ukurannya adalah 1×1
Kernel konvolusi melakukan operasi konvolusi; jika gambar keluaran dua kali lebih kecil dari gambar masukan, langkah dalam operasi konvolusi di pintasan perlu disetel ke 2, dan langkah dalam operasi konvolusi lapisan kedua dari cabang utama juga perlu diatur. untuk disetel ke 2.
Kode berikut mendefinisikan kelas ResidualBlock untuk mengimplementasikan struktur Bottleneck.
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
Struktur lapisan jaringan ResNet ditunjukkan pada gambar di bawah ini. Mengambil gambar berwarna masukan 224×224 sebagai contoh, pertama-tama lewati lapisan konvolusi conv1 dengan kuantitas 64, ukuran kernel konvolusi 7×7, dan langkah 2 Ukuran gambar keluaran dari lapisan ini adalah 112×112, saluran keluaran adalah 64; kemudian melalui lapisan pengumpulan downsampling maksimum 3×3, ukuran gambar keluaran dari lapisan ini adalah 56×56, saluran keluaran adalah 64; tumpukan 4 blok jaringan sisa (conv2_x, conv3_x, conv4_x dan conv5_x), saat ini ukuran gambar keluaran adalah 7×7 dan saluran keluaran adalah 2048; dan softmax.
Untuk setiap blok jaringan sisa, dengan mengambil conv2_x di jaringan ResNet50 sebagai contoh, blok tersebut ditumpuk oleh 3 struktur Bottleneck. Saluran input dari setiap Bottleneck adalah 64 dan saluran output adalah 256.
Contoh berikut mendefinisikan make_layer untuk mengimplementasikan konstruksi blok sisa, dan parameternya adalah sebagai berikut:
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)
Jaringan ResNet50 memiliki total 5 struktur konvolusional, lapisan pengumpulan rata-rata, dan lapisan yang terhubung sepenuhnya. Ambil kumpulan data CIFAR-10 sebagai contoh:
konv1: Ukuran gambar masukan adalah 32×32, dan saluran masukan adalah 3. Pertama, ia melewati lapisan konvolusi dengan nomor kernel konvolusi 64, ukuran kernel konvolusi 7×7, dan langkahnya 2; kemudian melewati lapisan Normalisasi Batch; Ukuran peta fitur keluaran lapisan ini adalah 16×16, dan saluran keluaran adalah 64.
conv2_x: Ukuran peta fitur masukan adalah 16×16, dan saluran masukan adalah 64. Pertama, ia melewati operasi pengumpulan downsampling maksimum dengan ukuran kernel konvolusi 3×3 dan langkah 2; kemudian ia menumpuk tiga Bottleneck dengan [1×1, 64; ] struktur. Ukuran peta fitur keluaran lapisan ini adalah 8×8, dan saluran keluaran adalah 256.
conv3_x: Ukuran peta fitur masukan adalah 8×8 dan saluran masukan adalah 256. Lapisan ini menumpuk 4 Bottleneck dengan struktur [1×1, 128; Ukuran peta fitur keluaran lapisan ini adalah 4×4, dan saluran keluarannya adalah 512.
conv4_x: Ukuran peta fitur masukan adalah 4×4 dan saluran masukan adalah 512. Lapisan ini menumpuk 6 Bottleneck dengan struktur [1×1, 256; Ukuran peta fitur keluaran lapisan ini adalah 2×2, dan saluran keluarannya adalah 1024.
conv5_x: Ukuran peta fitur masukan adalah 2×2, dan saluran masukan adalah 1024. Lapisan ini menumpuk tiga Bottleneck dengan struktur [1×1, 512; Ukuran peta fitur keluaran lapisan ini adalah 1×1, dan saluran keluarannya adalah 2048.
rata-rata pool & fc: Saluran masukan adalah 2048, dan saluran keluaran adalah jumlah kategori klasifikasi.
Contoh kode berikut mengimplementasikan konstruksi model ResNet50. Model ResNet50 dapat dibuat dengan memanggil fungsi resnet50. Parameter fungsi resnet50 adalah sebagai berikut:
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)
Bagian ini menggunakan model terlatih ResNet50 untuk penyesuaian. Panggil resnet50 untuk membuat model ResNet50 dan mengatur parameter terlatih ke True. Model terlatih ResNet50 akan diunduh secara otomatis dan parameter dalam model terlatih akan dimuat ke dalam jaringan. Kemudian tentukan fungsi pengoptimal dan kerugian, cetak nilai kerugian pelatihan dan akurasi evaluasi periode demi periode, dan simpan file ckpt dengan akurasi evaluasi tertinggi (resnet50-best.ckpt) ke ./BestCheckPoint di jalur saat ini.
Karena ukuran keluaran (parameter num_classes yang sesuai) dari lapisan yang terhubung sepenuhnya (fc) dari model yang dilatih sebelumnya adalah 1000, agar berhasil memuat bobot yang telah dilatih sebelumnya, kami menetapkan ukuran keluaran model yang terhubung sepenuhnya ke default 1000. Kumpulan data CIFAR10 memiliki total 10 kategori. Saat menggunakan kumpulan data ini untuk pelatihan, ukuran output dari lapisan model yang terhubung sepenuhnya yang dimuat dengan bobot yang telah dilatih sebelumnya perlu diatur ulang menjadi 10.
Di sini kami menampilkan proses pelatihan selama 5 epoch. Jika Anda ingin mencapai efek pelatihan yang ideal, disarankan untuk berlatih selama 80 epoch.
# 定义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
Tentukan fungsi visualize_model, gunakan model yang disebutkan di atas dengan akurasi verifikasi tertinggi untuk memprediksi kumpulan data pengujian CIFAR-10, dan visualisasikan hasil prediksi. Jika warna font prediksi berwarna biru berarti prediksi tersebut benar, dan jika warna font prediksi berwarna merah berarti prediksi tersebut salah.
Terlihat dari hasil di atas bahwa keakuratan prediksi model pada kumpulan data verifikasi dalam 5 epoch adalah sekitar 70%, artinya dalam keadaan normal 2 dari 6 gambar akan gagal diprediksi. Jika Anda ingin mencapai efek pelatihan yang ideal, disarankan untuk berlatih selama 80 epoch.
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)