Contents
- 1 Intorduction
- 2 残差块
- 3 ResNet模型
- 4 获取数据和训练模型
1 Intorduction
ResNet在2015年的ImageNet图像识别挑战赛夺魁。
由于存在梯度消失和梯度爆炸问题,深度很深的神经网络是很难训练的。解决方法之一是人为地让神经网络某些层跳过下一层神经元的连接,隔层相连,弱化每层之间的强联系,即跳跃连接(skip connection)。用它可以构建能够训练深度网络的ResNets,这种神经网络被称为Residual Networks(ResNets)。Residual Networks由许多隔层相连的Residual block组成。
2 残差块
ResNet沿用了VGG全3×3卷积层的设计。残差块里首先有2个有相同输出通道数的3×3卷积层。每个卷积层后接一个批量归一化层和ReLU激活函数。然后我们将输入跳过这两个卷积运算后直接加在最后的ReLU激活函数前。这样的设计要求两个卷积层的输出与输入形状一样,从而可以相加。如果想改变通道数,就需要引入一个额外的1×1卷积层来将输入变换成需要的形状后再做相加运算。
# 残差块的实现如下,它可以设定输出通道数、是否使用额外的1×1卷积层来修改通道数以及卷积层的步幅
import time
import torch
from torch import nn, optim
import torch.nn.functional as F
import sys
sys.path.append("..")
import d2lzh_pytorch as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
class Residual(nn.Module):
def __init__(self, in_channels, out_channels, use_1x1conv = False, stride = 1):
super(Residual, self).__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels,kernel_size = 3, padding = 1, stride=stride)
self.conv2 = nn.Conv2d(out_channels, out_channels,kernel_size = 3, padding = 1)
if use_1x1conv:
self.conv3 = nn.Conv2d(in_channels, out_channels,kernel_size = 1, stride=stride)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm2d(out_channels)
self.bn2 = nn.BatchNorm2d(out_channels)
def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(Y)
return F.relu(Y+X)
其中卷积操作、批量归一化BN、激活函数ReLU操作顺序可参考下图:
3 ResNet模型
net = nn.Sequential(
nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
# ResNet使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。
def resnet_block( in_channels, out_channels, num_resduals, first__block = False):
assert in_channels==out_channels
blk=[]
for i in range(num_residuals):
if i == 0 and not first_block:
blk.append(Residual(in_channels, out_channels, use_1x1conv=True, stride=2))
else:
blk.append(Residual(out_channels, out_channels))
return nn.Sequential(*blk)
net.add_module("resnet_block1", resnet_block(64, 64, 2, first_block=True))
net.add_module("resnet_block2", resnet_block(64 128, 2))
net.add_module("resnet_block3", resnet_block(128, 256, 2))
net.add_module("resnet_block4", resnet_block(256, 512, 2))
# 加上全局平均池化层后接上全连接输出层
net.add_module("global_avg_pool", d2l.GlobalAvgPool2d()) # GlobalAvgPool2d的输出: (Batch, 512, 1, 1)
net.add_module("fc", nn.Sequential(d2l.FlattenLayer(), nn.Linear(512, 10)))
每个模块里有4个卷积层(不计算1×1卷积层),加上最开始的卷积层和最后的全连接层,共计18层。这个模型通常也被称为ResNet-18。
以上的模型参数是ResNet-18中的模型参数,也可按照下图修改其参数:
4 获取数据和训练模型
# 硬件限制,不跑结果了……
batch_size = 256
# 如出现“out of memory”的报错信息,可减小batch_size或resize
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
lr, num_epochs = 0.001, 5
optimizer = torch.optim.Adam(net.parameters(), lr=lr)
d2l.train_ch5(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs)