VGG
AlexNet比LeNet更深更大,以得到更好的精度
能不能更深和更大?该如何更深更大?
选项:
更多的全连接层(太贵)
更多的卷积层
将卷积层组合成块
VGG块
VGG块:
3×3卷积(填充1)(n层,m通道(即:输入和输出通道是一样的))
2×2最大池化层(步幅2)
为什么用的是3×3而不是5×5?
因为5×5计算量大,得浅一点点。在同样的计算和开销下用3×3比5×5的效果好。即:深但窄效果更好。
VGG架构
多个VGG块后接全连接层。
不同次数的重复块得到不同的架构VGG-16,VGG-19。
进度
总结
①VGG使用可重复使用的卷积块来构建深度卷积神经网络。
②不同的卷积块个数和超参数可以得到不同复杂度的变种。
使用块的网络(VGG)
VGG块
使用了带有 3×3 卷积核、填充为1(保持高度和宽度)的卷积层,和带有 2×2 池化窗口、步幅为2(每个块后的分辨率减半)的最大池化层。在下面的代码中,我们定义了一个名为vgg_block的函数来实现一个VGG块。该函数有三个参数,分别对应于卷积层的数量num_convs、输入通道的数量in_channels 和输出通道的数量out_channels.
import torch
from torch import nn
from d2l import torch as d2l
def vgg_block(num_convs, in_channels, out_channels):
# 创建了一个空列表 layers,用于按顺序存储将要构建的层的实例。
layers = []
# 循环构建卷积层和ReLU层
for _ in range(num_convs):
# 将这个卷积层添加到 layers 列表中。
layers.append(nn.Conv2d(in_channels, out_channels,
kernel_size=3, padding=1))
# 添加一个ReLU激活层到 layers 列表中。
layers.append(nn.ReLU())
# 更新 in_channels 为 out_channels,以便下一个卷积层可以正确接收当前卷积层的输出作为输入。
in_channels = out_channels
# 添加最大池化层
layers.append(nn.MaxPool2d(kernel_size=2,stride=2))
# 使用 nn.Sequential 将 layers 列表中的所有层打包成一个有序的容器。*layers 是解包列表,使其元素作为 nn.Sequential 的参数。
return nn.Sequential(*layers)
VGG网络
与AlexNet、LeNet一样,VGG网络可以分为两部分:第一部分主要由卷积层和池化层组成,第二部分由全连接层组成。
原始VGG网络有5个卷积块,其中前两个块各有一个卷积层,后三个块各包含两个卷积层。 第一个模块有64个输出通道,每个后续模块将输出通道数量翻倍,直到该数字达到512。由于该网络使用8个卷积层和3个全连接层,因此它通常被称为VGG-11。
# 第一个参数为几层卷积,第二个参数为输出通道数
conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
每一块是进行高宽减半,224最后分成5块,为什么是5块呢?
是奇数,已经不能在分了。
实现VGG-11,可以通过在conv_arch上执行for循环来简单实现。
def vgg(conv_arch):
# 初始化一个空列表,用于存储VGG网络中的卷积块
conv_blks = []
# 假设输入图像的通道数为1(例如,灰度图像)。对于彩色图像,这通常是3
in_channels = 1
# 卷积层部分
# 遍历conv_arch列表,其中每个元素是一个包含两个值的元组:(卷积层数, 输出通道数)
for (num_convs, out_channels) in conv_arch:
# 为每个卷积块调用vgg_block函数,并将结果添加到conv_blks列表中
conv_blks.append(vgg_block(num_convs, in_channels, out_channels))
# 更新输入通道数,为构建下一个卷积块做准备
in_channels = out_channels
# 构建并返回整个网络模型
return nn.Sequential(
# 解包conv_blks列表,将其中的所有卷积块按顺序添加到Sequential模型中
# 将卷积层输出的多维特征图展平成一维,以便输入到全连接层
*conv_blks, nn.Flatten(),
# 全连接层部分
# 全连接层的输入特征数量就是特征图的通道数乘以高度再乘以宽度。即:out_channels * 7 * 7
# Dropout层,用于防止过拟合,丢弃率为0.5
nn.Linear(out_channels * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5),
# 第二个全连接层,输入和输出特征数均为4096
nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5),
nn.Linear(4096, 10))
# 调用vgg函数构建网络模型,并将conv_arch作为参数传递
net = vgg(conv_arch)
观察每层输出的形状
构建一个高度和宽度为224的单通道数据样本,以观察每个层输出的形状。
X = torch.randn(size=(1, 1, 224, 224))
for blk in net:
X = blk(X)
print(blk.__class__.__name__,'output shape:\t',X.shape) # VGG使得高宽减半,通道数加倍
结果:
该部分总代码
import torch
from torch import nn
from d2l import torch as d2l
def vgg_block(num_convs,in_channels,out_channels): # 卷积层个数、输入通道数、输出通道数
layers = []
for _ 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) # *layers表示把列表里面的元素按顺序作为参数输入函数
conv_arch = ((1,64),(1,128),(2,256),(2,512),(2,512)) # 第一个参数为几层卷积,第二个参数为输出通道数
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))
net = vgg(conv_arch)
# 观察每个层输出的形状
X = torch.randn(size=(1,1,224,224))
for blk in net:
X = blk(X)
print(blk.__class__.__name__,'output shape:\t', X.shape) # VGG使得高宽减半,通道数加倍
训练模型
由于VGG-11比AlexNet计算量更大,因此我们构建了一个通道数较少的网络,足够用于训练Fashion-MNIST数据集。
ratio = 4
# conv_arch是一个列表,其中包含多个元组(pair)元组pair中包含两个参数,现在对第二个参数整除ratio目的是减小模型的大小
# 所有输出通道除以4
small_conv_arch = [(pair[0], pair[1] // ratio) for pair in conv_arch]
# VGG接受一个卷积层的配置列表
net = vgg(small_conv_arch)
X = torch.randn(size=(1, 1, 224, 224))
for blk in net:
X = blk(X)
print(blk.__class__.__name__,'output shape:\t',X.shape)
lr, num_epochs, batch_size = 0.05, 10, 128
# resize=224参数表明将图像的大小调整为224x224像素
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)
# 尝试获取GPU,如果可用则使用GPU进行训练,以提高训练速度。如果GPU不可用,则默认使用CPU。
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
结果: