τα στοιχεία επικοινωνίας μου
Ταχυδρομείο[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Το ResNet λύνει κυρίως το πρόβλημα «υποβάθμισης» των βαθιών συνελικτικών δικτύων όταν το βάθος βαθαίνει. Στα γενικά συνελικτικά νευρωνικά δίκτυα, το πρώτο πρόβλημα που προκαλείται από την αύξηση του βάθους του δικτύου είναι η διαβάθμιση και η έκρηξη. Το επίπεδο BN μπορεί να ομαλοποιήσει την έξοδο κάθε στρώματος, έτσι ώστε η κλίση να μπορεί να παραμείνει σταθερή σε μέγεθος αφού περάσει στρώμα προς στρώμα αντίστροφα και δεν θα είναι πολύ μικρή ή πολύ μεγάλη. Ωστόσο, ο συγγραφέας διαπίστωσε ότι δεν είναι ακόμα εύκολο να συγκλίνει μετά την προσθήκη του BN και την αύξηση του βάθους Ανέφερε το δεύτερο πρόβλημα - το πρόβλημα της πτώσης της ακρίβειας: όταν το επίπεδο είναι αρκετά μεγάλο σε κάποιο βαθμό, η ακρίβεια θα είναι κορεσμένη. και στη συνέχεια μειώνεται γρήγορα Αυτή η μείωση δεν είναι Η εξαφάνιση των κλίσεων δεν προκαλείται από υπερβολική προσαρμογή, αλλά επειδή το δίκτυο είναι πολύ περίπλοκο, έτσι ώστε είναι δύσκολο να επιτευχθεί το ιδανικό ποσοστό σφάλματος μέσω της απεριόριστης εκπαίδευσης ελεύθερης εμβέλειας.
Το πρόβλημα της μειωμένης ακρίβειας δεν είναι πρόβλημα με την ίδια τη δομή του δικτύου, αλλά προκαλείται από τις υπάρχουσες μεθόδους εκπαίδευσης που δεν είναι ιδανικές. Οι ευρέως χρησιμοποιούμενοι βελτιστοποιητές, είτε πρόκειται για SGD, RMSProp ή Adam, δεν μπορούν να επιτύχουν τα θεωρητικά βέλτιστα αποτελέσματα σύγκλισης όταν το βάθος του δικτύου γίνεται μεγαλύτερο.
Εφόσον υπάρχει μια κατάλληλη δομή δικτύου, ένα βαθύτερο δίκτυο θα έχει σίγουρα καλύτερη απόδοση από ένα πιο ρηχό δίκτυο. Η διαδικασία απόδειξης είναι επίσης πολύ απλή: Ας υποθέσουμε ότι προστίθενται μερικά επίπεδα πίσω από ένα δίκτυο Α για να σχηματιστεί ένα νέο δίκτυο Β. Εάν τα προστιθέμενα επίπεδα εκτελούν μόνο μια αντιστοίχιση ταυτότητας στην έξοδο του Α, δηλαδή, προστίθεται η έξοδος του Α μέσω του νέου δικτύου Α. Δεν υπάρχει αλλαγή αφού το επίπεδο γίνει η έξοδος του Β, επομένως τα ποσοστά σφάλματος του δικτύου Α και του δικτύου Β είναι ίσα, γεγονός που αποδεικνύει ότι το δίκτυο μετά την εμβάθυνση δεν θα είναι χειρότερο από το δίκτυο πριν από την εμβάθυνση.
Η ταξινόμηση εικόνας είναι η πιο βασική εφαρμογή υπολογιστικής όρασης και ανήκει στην κατηγορία της εποπτευόμενης μάθησης Για παράδειγμα, με δεδομένη μια εικόνα (γάτα, σκύλος, αεροπλάνο, αυτοκίνητο κ.λπ.), καθορίστε την κατηγορία στην οποία ανήκει η εικόνα. Αυτό το κεφάλαιο θα εισαγάγει τη χρήση του δικτύου ResNet50 για την ταξινόμηση του συνόλου δεδομένων CIFAR-10.
Το δίκτυο ResNet50 προτάθηκε από τον He Kaiming της Microsoft Labs το 2015 και κέρδισε την πρώτη θέση στον διαγωνισμό ταξινόμησης εικόνων ILSVRC2015. Πριν προταθεί το δίκτυο ResNet, τα παραδοσιακά συνελικτικά νευρωνικά δίκτυα αποκτήθηκαν με τη στοίβαξη μιας σειράς συνελικτικών επιπέδων και στρωμάτων συγκέντρωσης, ωστόσο, όταν το δίκτυο στοιβάζεται σε ένα συγκεκριμένο βάθος, θα προκύψουν προβλήματα υποβάθμισης. Το παρακάτω σχήμα είναι ένα γράφημα του σφάλματος εκπαίδευσης και του σφάλματος δοκιμής χρησιμοποιώντας ένα δίκτυο 56 επιπέδων και ένα δίκτυο 20 επιπέδων στο σύνολο δεδομένων CIFAR-10. Μπορεί να φανεί από τα δεδομένα στο σχήμα ότι το σφάλμα εκπαίδευσης και το σφάλμα δοκιμής Το δίκτυο 56 επιπέδων είναι μεγαλύτερο από εκείνο του δικτύου 20 επιπέδων Καθώς το δίκτυο βαθαίνει, το σφάλμα δεν μειώνεται όπως αναμενόταν.
Το δίκτυο ResNet προτείνει μια υπολειπόμενη δομή δικτύου (Residual Network) για την άμβλυνση του προβλήματος υποβάθμισης Η χρήση του δικτύου 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]
Η υπολειπόμενη δομή δικτύου (Residual Network) είναι το κύριο χαρακτηριστικό του δικτύου ResNet Η χρήση της υπολειπόμενης δομής δικτύου από το ResNet μπορεί να μετριάσει αποτελεσματικά το πρόβλημα υποβάθμισης, να επιτύχει βαθύτερο σχεδιασμό της δομής του δικτύου και να βελτιώσει την ακρίβεια εκπαίδευσης του δικτύου. Αυτή η ενότητα περιγράφει πρώτα τον τρόπο δημιουργίας μιας υπολειπόμενης δομής δικτύου και, στη συνέχεια, δημιουργεί ένα δίκτυο ResNet50 με στοίβαξη υπολειπόμενων δικτύων.
Κατασκευάστε μια υπολειπόμενη δομή δικτύου
残差网络结构图如下图所示,残差网络由两个分支构成:一个主分支,一个shortcuts(图中弧线表示)。主分支通过堆叠一系列的卷积操作得到,shotcuts从输入直接到输出,主分支输出的特征矩阵 𝐹(𝑥)
加上shortcuts输出的特征矩阵 𝑥 得到 𝐹(𝑥)+𝑥,通过Relu激活函数后即为残差网络最后的输出。
Υπάρχουν δύο βασικοί τύποι υπολειπόμενων δομών δικτύου, ο ένας είναι το Building Block, το οποίο είναι κατάλληλο για ρηχά δίκτυα ResNet, όπως το ResNet18 και το ResNet34, το άλλο είναι το Bottleneck, το οποίο είναι κατάλληλο για βαθύτερα δίκτυα ResNet, όπως το ResNet50, το ResNet101 και το ResNet152. .
Το διάγραμμα δομής δομικού στοιχείου φαίνεται στο παρακάτω σχήμα Ο κύριος κλάδος έχει μια συνελικτική δομή δικτύου δύο επιπέδων:
Το δίκτυο πρώτου επιπέδου του κύριου κλάδου παίρνει το κανάλι εισόδου ως παράδειγμα 64. Αρχικά, περνάει ένα 3×3
Το επίπεδο συνέλιξης, μετά μέσω του επιπέδου κανονικοποίησης παρτίδας και, τέλος, μέσω του επιπέδου λειτουργίας ενεργοποίησης Relu, το κανάλι εξόδου είναι 64.
Το κανάλι εισόδου του δικτύου δεύτερης στρώσης του κύριου κλάδου είναι 64. Περνά πρώτα ένα 3×3
Στη συνέχεια, το συνελικτικό επίπεδο διέρχεται από το επίπεδο κανονικοποίησης παρτίδας και το κανάλι εξόδου είναι 64.
Τέλος, προστίθεται η έξοδος του πίνακα χαρακτηριστικών από τον κύριο κλάδο και η έξοδος του πίνακα χαρακτηριστικών από συντομεύσεις και η τελική έξοδος του δομικού στοιχείου λαμβάνεται μέσω της συνάρτησης ενεργοποίησης 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
Το διάγραμμα Bottleneck φαίνεται στο παρακάτω σχήμα Όταν η είσοδος είναι η ίδια, η δομή Bottleneck έχει λιγότερες παραμέτρους από τη δομή του Building Block και είναι πιο κατάλληλη για δίκτυα με βαθύτερα επίπεδα. Ο κύριος κλάδος αυτής της δομής έχει τρία στρώματα δομής συνέλιξης, καθένα από τα οποία είναι 1×1
Συνελικτικό στρώμα, 3×3
Συνελικτικά στρώματα και 1×1
συνελικτικό στρώμα, όπου 1×1
Τα συνελικτικά στρώματα παίζουν το ρόλο της μείωσης διαστάσεων και της διάστασης αντίστοιχα.
Το δίκτυο πρώτου επιπέδου του κύριου κλάδου παίρνει ως παράδειγμα το κανάλι εισόδου ως 256. Ο πρώτος αριθμός πρόσβασης είναι 64 και το μέγεθος είναι 1×1
Ο πυρήνας συνέλιξης εκτελεί μείωση διαστάσεων, στη συνέχεια διέρχεται από το επίπεδο κανονικοποίησης παρτίδας και, τέλος, περνά από το επίπεδο λειτουργίας ενεργοποίησης Relu και το κανάλι εξόδου του είναι 64.
Ο αριθμός των διελεύσεων δικτύου δεύτερου επιπέδου κύριας διακλάδωσης είναι 64 και το μέγεθος είναι 3×3
Ο πυρήνας συνέλιξης εξάγει χαρακτηριστικά, μετά περνά από το επίπεδο κανονικοποίησης παρτίδας και τέλος περνά από το επίπεδο λειτουργίας ενεργοποίησης Relu και το κανάλι εξόδου του είναι 64.
Ο αριθμός των περασμάτων στο τρίτο επίπεδο του κύριου κλάδου είναι 256 και το μέγεθος είναι 1×1
Ο πυρήνας συνέλιξης έχει διαστάσεις και, στη συνέχεια, περνά μέσα από το επίπεδο κανονικοποίησης παρτίδας και το κανάλι εξόδου του είναι 256.
Τέλος, προστίθεται η έξοδος του πίνακα χαρακτηριστικών από τον κύριο κλάδο και η έξοδος του πίνακα χαρακτηριστικών από συντομεύσεις και η τελική έξοδος του Bottleneck λαμβάνεται μέσω της συνάρτησης ενεργοποίησης Relu.
Όταν προσθέτετε την έξοδο πίνακες χαρακτηριστικών από τον κύριο κλάδο και τις συντομεύσεις, πρέπει να βεβαιωθείτε ότι το σχήμα των πινάκων χαρακτηριστικών που εξάγονται από τον κύριο κλάδο και τις συντομεύσεις είναι το ίδιο. Εάν η έξοδος του σχήματος μήτρας χαρακτηριστικών από τον κύριο κλάδο και τις συντομεύσεις είναι διαφορετική, για παράδειγμα, εάν το κανάλι εξόδου είναι διπλάσιο από το κανάλι εισόδου, ο αριθμός των συντομεύσεων πρέπει να είναι ίσος με το κανάλι εξόδου και το μέγεθος είναι 1×1
Ο πυρήνας συνέλιξης εκτελεί τη λειτουργία συνέλιξης εάν η εικόνα εξόδου είναι δύο φορές μικρότερη από την εικόνα εισόδου, ο βηματισμός στη λειτουργία συνέλιξης στις συντομεύσεις πρέπει να οριστεί στο 2 και ο βηματισμός στη λειτουργία συνέλιξης του δεύτερου επιπέδου του κύριου κλάδου χρειάζεται επίσης. να οριστεί σε 2.
Ο ακόλουθος κώδικας ορίζει την κλάση ResidualBlock για την υλοποίηση της δομής 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
Η δομή του επιπέδου δικτύου ResNet φαίνεται στο παρακάτω σχήμα Λαμβάνοντας ως παράδειγμα την έγχρωμη εικόνα εισόδου 224×224, περάστε πρώτα το επίπεδο συνέλιξης conv1 με ποσότητα 64, μέγεθος πυρήνα συνέλιξης 7×7 και διασκελισμό 2. Το μέγεθος της εικόνας εξόδου αυτού του επιπέδου είναι 112×112, το κανάλι εξόδου είναι 64, στη συνέχεια, μέσω ενός επιπέδου συγκέντρωσης 3×3, το μέγεθος εικόνας εξόδου αυτού του επιπέδου είναι 56×56 στοίβα 4 υπολειπόμενα μπλοκ δικτύου (conv2_x, conv3_x, conv4_x και conv5_x), αυτή τη στιγμή το μέγεθος της εικόνας εξόδου είναι 7×7 και το κανάλι εξόδου είναι 2048, τέλος, η πιθανότητα ταξινόμησης λαμβάνεται μέσω ενός μέσου επιπέδου συγκέντρωσης, ενός πλήρως συνδεδεμένου επιπέδου και softmax.
Για κάθε υπολειπόμενο μπλοκ δικτύου, λαμβάνοντας ως παράδειγμα το conv2_x στο δίκτυο ResNet50, στοιβάζεται από 3 δομές Bottleneck Το κανάλι εισόδου κάθε Bottleneck είναι 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 και μετά περνά μέσα από ένα επίπεδο κανονικοποίησης παρτίδας και τελικά περνά από τη συνάρτηση ενεργοποίησης Reul. Το μέγεθος χάρτη χαρακτηριστικών εξόδου αυτού του επιπέδου είναι 16×16 και το κανάλι εξόδου είναι 64.
conv2_x: Το μέγεθος του χάρτη χαρακτηριστικών εισόδου είναι 16×16 και το κανάλι εισόδου είναι 64. Πρώτον, περνάει από μια λειτουργία συγκέντρωσης μέγιστης δειγματοληψίας με μέγεθος πυρήνα 3×3 και διασκελισμό 2, στη συνέχεια στοιβάζει τρία σημεία συμφόρησης με δομή [1×1, 1×1 , 256]. Το μέγεθος χάρτη χαρακτηριστικών εξόδου αυτού του επιπέδου είναι 8×8 και το κανάλι εξόδου είναι 256.
conv3_x: Το μέγεθος του χάρτη χαρακτηριστικών εισόδου είναι 8×8 και το κανάλι εισόδου είναι 256. Αυτό το στρώμα στοιβάζει 4 Bottlenecks με δομή [1×1, 128, 1×1, 512]. Το μέγεθος χάρτη χαρακτηριστικών εξόδου αυτού του επιπέδου είναι 4×4 και το κανάλι εξόδου είναι 512.
conv4_x: Το μέγεθος του χάρτη χαρακτηριστικών εισόδου είναι 4×4 και το κανάλι εισόδου είναι 512. Αυτό το επίπεδο στοιβάζει 6 Bottlenecks με δομή [1×1, 256, 1×1, 1024]. Το μέγεθος χάρτη χαρακτηριστικών εξόδου αυτού του επιπέδου είναι 2×2 και το κανάλι εξόδου είναι 1024.
conv5_x: Το μέγεθος χάρτη χαρακτηριστικών εισόδου είναι 2×2 και το κανάλι εισόδου είναι 1024. Αυτό το επίπεδο στοιβάζει τρία σημεία συμφόρησης με δομή [1×1, 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. Υπάρχουν 10 κατηγορίες στο σύνολο δεδομένων CIFAR10 Όταν χρησιμοποιείτε αυτό το σύνολο δεδομένων για εκπαίδευση, το μέγεθος εξόδου του πλήρως συνδεδεμένου στρώματος του μοντέλου που είναι φορτωμένο με προεκπαιδευμένα βάρη πρέπει να επαναφερθεί στο 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)