二、现代卷积神经网络
1. 深度卷积神经网络--AlexNet
(1). AlexNet介绍
AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。AlexNet使用ReLU作为其激活函数,因为ReLU激活函数的计算更简单,它不需要如sigmoid激活函数那般复杂的求幂运算。
左图为LeNet, 右图为AlexNet
用Sequential
块将需要的层连接在一起。
net = nn.Sequential(
# 这里使用一个11*11的更大窗口来捕捉对象。
nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
# 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数
nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
# 使用三个连续的卷积层和较小的卷积窗口。
nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(),
nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Flatten(),
# 使用dropout层来减轻过拟合
nn.Linear(6400, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(4096, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
# 最后是输出层。
nn.Linear(4096, 10))
训练模型。
# 使用Fathion_mnist来训练,将尺寸改为224*224
batch_size = 128
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)
lr, num_epochs = 0.01, 10
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
输出:
2. 使用块网络--VGG
(1).VGG介绍
VGG网络可以分为两部分:第一部分主要由卷积层和池化层组成,第二部分由全连接层组成。
原始VGG网络有5个卷积块,其中前两个块各有一个卷积层,后三个块各包含两个卷积层。该网络使用8个卷积层和3个全连接层,因此它通常被称为VGG-11。
VGG块。
# VGG块
# 卷积层的数量num_convs、输入通道的数量in_channels 和输出通道的数量out_channels
def vgg_block(num_convs, in_channels, out_channels):
layers = []
for i in range(num_convs):
layers.append(nn.Conv2d(in_channels, out_channels,kernel_size=3, padding=1))
layers.append(nn.ReLU())
in_channels = out_channels
# 池化层
layers.append(nn.MaxPool2d(kernel_size=2,stride=2))
return nn.Sequential(*layers)
VGG网络模型:
def vgg(conv_arch):
conv_blks = []
in_channels = 1
# 卷积层部分
for (num_convs, out_channels) in conv_arch:
conv_blks.append(vgg_block(num_convs, in_channels, out_channels))
in_channels = out_channels
return nn.Sequential(
*conv_blks, nn.Flatten(),
# 全连接层部分
nn.Linear(out_channels * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5),
nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5),
nn.Linear(4096, 10))
总结:
1. VGG-11使用可复用的卷积块构造网络。
2. 块的使用导致网络定义的非常简洁。
3. 网络中的网络--NiN
(1).NiN介绍
NiN块以一个普通卷积层开始,后面是两个1×1的卷积层。这两个1×1卷积层充当带有ReLU激活函数的全连接层。第一层的卷积窗口形状通常由用户设置。 随后的卷积窗口形状固定为1×1。
NiN块代码定义:
def nin_block(in_channels, out_channels, kernel_size, strides, padding):
return nn.Sequential(
# 自定义卷积层
nn.Conv2d(in_channels, out_channels, kernel_size, strides, padding),
nn.ReLU(),
# 两个1*1卷积层
nn.Conv2d(out_channels, out_channels, kernel_size=1),nn.ReLU(),
nn.Conv2d(out_channels, out_channels, kernel_size=1),nn.ReLU())
NiN使用全剧平均池化层来代替全连接层,不容易过拟合,更少的参数个数。
NiN模型代码:
net = nn.Sequential(
nin_block(1, 96, kernel_size=11, strides=4, padding=0),
nn.MaxPool2d(3, stride=2),
nin_block(96, 256, kernel_size=5, strides=1, padding=2),
nn.MaxPool2d(3, stride=2),
nin_block(256, 384, kernel_size=3, strides=1, padding=1),
nn.MaxPool2d(3, stride=2),
nn.Dropout(0.5),
# 标签类别数是10
nin_block(384, 10, kernel_size=3, strides=1, padding=1),
nn.AdaptiveAvgPool2d((1, 1)),
# 将四维的输出转成二维的输出,其形状为(批量大小,10)
nn.Flatten())
总结:
1. NiN使用由一个卷积层和多个1×1卷积层组成的块。
2. NiN去除了容易造成过拟合的全连接层,将它们替换为全局平均汇聚层。该汇聚层通道数
量为所需的输出数量。
3. 移除全连接层可减少过拟合,同时显著减少NiN的参数
4. GoogLeNet
(1). GoogLeNet介绍
在GoogLeNet中,基本的卷积块被称为Inception块, 由四条并行路径组成。这四条路径都使用合适的填充来使输入与输出的高和宽一致,最后我们将每条线路的输出在通道维度上连结,并构成Inception块的输出。
Inception块
GoogLeNet模型 一共使用9个Inception块和全局平均池化层。
总结:
1. Inception块相当于一个有4条路径的子网络。它通过不同窗口形状的卷积层和最大汇聚层来并行抽取信息,并使用1×1卷积层减少每像素级别上的通道维数从而降低模型复杂度。
5. 批量规范化
(1). 介绍
批量规范化是在卷积层或全连接层之后、相应的激活函数之前应用的, 可以加速神经网络的收敛。
使用torch.nn.BatchNorm2d()
如下代码是使用了BatchNorm的LeNet
net = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5), nn.BatchNorm2d(6), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, kernel_size=5), nn.BatchNorm2d(16), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
nn.Linear(256, 120), nn.BatchNorm1d(120), nn.Sigmoid(),
nn.Linear(120, 84), nn.BatchNorm1d(84), nn.Sigmoid(),
nn.Linear(84, 10))
6. 残差网络--ResNet
(1). 介绍
残差块里首先有2个有相同输出通道数的3×3卷积层。每个卷积层后接一个批量规范化层和ReLU激活函数。 然后我们将输入直接加在最后的ReLU激活函数前。如果想改变通道数,可以引入1×1卷积层来将输入变换成需要的形状后再做相加运算。
残差块
(2). ResNet-18模型
每个模块有4个卷积层(不包括1×1卷积层)。 加上第一个7×7卷积层和最后一个全局平均池化层,共有18层。
7. 稠密连接网络--DenseNet
(1). 介绍
DenseNet输出是连接(用图中的[,]表示)而不是如ResNet的简单相加。
ResNet(左)与 DenseNet(右)在跨层连接上的主要区别:使用相加和使用连结
DenseNet所有当前层与之前的所有层想拼接。
稠密网络主要由2部分构成:稠密块和过渡层。 前者定义如何连接输入和输出,而后者则控制通道数量。
稠密块由多个卷积块组成,每个卷积块使用相同数量的输出通道。 然而,在前向传播中,我们将每个卷积块的输入和输出在通道维上连结。
class DenseBlock(nn.Module):
def __init__(self, num_convs, input_channels, num_channels):
super(DenseBlock, self).__init__()
layer = []
for i in range(num_convs):
layer.append(conv_block(
num_channels * i + input_channels, num_channels))
self.net = nn.Sequential(*layer)
def forward(self, X):
for blk in self.net:
Y = blk(X)
# 连接通道维度上每个块的输入和输出
X = torch.cat((X, Y), dim=1)
return X
过渡层可以用来控制模型复杂度。 它通过1×1卷积层来减小通道数,并使用步幅为2的平均汇聚层减半高和宽,从而进一步降低模型复杂度。
def transition_block(input_channels, num_channels):
return nn.Sequential(
nn.BatchNorm2d(input_channels), nn.ReLU(),
nn.Conv2d(input_channels, num_channels, kernel_size=1),
nn.AvgPool2d(kernel_size=2, stride=2))