李沐动手学深度学习V2-VGG11模型和代码实现

1. VGG11

虽然AlexNet证明深层神经网络卓有成效,但它没有提供一个通用的模板来指导后续的研究人员设计新的网络。研究人员开始从单个神经元的角度思考问题,发展到整个层,现在又转向块(不断使用重复层的模式)。

1.1 VGG块

经典卷积神经网络的基本组成部分是下面的这个序列:

  1. 带填充以保持分辨率(保持输入输出形状尺寸大小相同)的卷积层;
  2. 非线性激活函数,如ReLU;
  3. 汇聚层,如最大汇聚层。

一个VGG块与之类似,由一系列卷积层组成,后面再加上用于空间下采样的最大汇聚层。在最初的VGG论文中,作者使用了带有 3×3 卷积核、填充为1(保持高度和宽度)的卷积层,和带有 2×2 汇聚窗口、步幅为2(每个块后的分辨率减半)的最大汇聚层。
下面代码vgg_block()实现了一个VGG块,该函数有三个参数,分别对应于卷积层的数量num_convs、输入通道的数量in_channels 和输出通道的数量out_channels.:

def vgg_block(nums_conv2d,in_channels,out_channels):
    blocks = []
    for _ in range(nums_conv2d):
        blocks.append(nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=3,padding=1))
        blocks.append(nn.ReLU())
        in_channels = out_channels
    blocks.append(nn.MaxPool2d(kernel_size=2,stride=2))
    return nn.Sequential(*blocks)

1.2 VGG网络

与AlexNet、LeNet一样,VGG网络可以分为两部分:第一部分主要由卷积层和汇聚层组成,第二部分由全连接层组成,如下图所示。卷积块的作用主要是替换AlexNet网络前面五层卷积层,因为AlexNet卷积层的卷积核大小,通道数,步幅数设置的没有规律,而使用VGG块更有利于网络更加简洁,从而使网络架构不会那么复杂
原始VGG网络有5个卷积块,其中前两个块各有一个卷积层,后三个块各包含两个卷积层。 第一个模块有64个输出通道,每个后续模块将输出通道数量翻倍,直到该数字达到512。由于该网络使用8个卷积层和3个全连接层,因此它通常被称为VGG-11。
李沐动手学深度学习V2-VGG11模型和代码实现_第1张图片
VGG模型代码:

def vgg(conv2d_arch):
    VGG_blocks = []
    in_channels = 1
    #for循环来添加每个卷积块,卷积层模块
    for nums_conv2d,out_channels in conv2d_arch:
        VGG_blocks.append(vgg_block(nums_conv2d=nums_conv2d,in_channels=in_channels,out_channels=out_channels))
        in_channels = out_channels #下一层卷积层输入channels数为上一层卷积层输出channels
    return nn.Sequential(*VGG_blocks,
                         nn.Flatten(),
                         # 全连接层模块则与AlexNet中的相同。in_channels数为最后一层卷积层的输出channels(然后赋值给in_channels,在for循环里面赋值进行的,作为下一层Flatten层的输入,铺平展开后,输入到全连接层,因此全连接层输入in_features=in_channels*7*7)
                         nn.Linear(in_features=in_channels*7*7,out_features=4096),#7*7是因为输入尺寸是224*224,经过每个卷积块后尺寸都会除以2,经过五个卷积块后,则输出尺寸大小为224/2/2/2/2/2=7
                         nn.ReLU(),
                         nn.Dropout(0.5),
                         nn.Linear(in_features=4096,out_features=4096),
                         nn.ReLU(),
                         nn.Dropout(),
                         nn.Linear(in_features=4096,out_features=10))
X = torch.randn(size=(1,1,224,224))
#conv2d_arch含有五个VGG块,指定了每个VGG块里卷积层个数和输出通道数,为一个超参数
conv2d_arch = ((1,64),(1,128),(2,256),(2,512),(2,512))
#ratio = 4
#small_conv2d_arch = ((pair[0],pair[1] // ratio) for pair in conv2d_arch)
VGGNet = vgg(conv2d_arch)
#构建一个高度和宽度为224的单通道数据样本X,来观察每个层输出的形状
for layer in VGGNet:
    X = layer(X)
    print(layer.__class__.__name__,'output shape : \t',X.shape)
每层尺寸输出结果如下:
Sequential output shape:	 torch.Size([1, 64, 112, 112])
Sequential output shape:	 torch.Size([1, 128, 56, 56])
Sequential output shape:	 torch.Size([1, 256, 28, 28])
Sequential output shape:	 torch.Size([1, 512, 14, 14])
Sequential output shape:	 torch.Size([1, 512, 7, 7])
Flatten output shape:	 torch.Size([1, 25088])
Linear output shape:	 torch.Size([1, 4096])
ReLU output shape:	 torch.Size([1, 4096])
Dropout output shape:	 torch.Size([1, 4096])
Linear output shape:	 torch.Size([1, 4096])
ReLU output shape:	 torch.Size([1, 4096])
Dropout output shape:	 torch.Size([1, 4096])
Linear output shape:	 torch.Size([1, 10])

从上面的输出结果看出VGG在经过每个卷积块(每个卷积块内部最后面有一个最大池化层,步幅为2,kernel size为2*2)后输出形状尺寸的高度和宽度减半,经过五个卷积块后,输出尺寸为输入尺寸除以五次2,也即是要除以32等于7,因此最终输出尺寸形状高度和宽度都为7。最后再展平表示,送入全连接层处理。

1.3 小结

  1. VGG-11使用可复用的卷积块构造网络。不同的VGG模型可通过每个块中卷积层数量和输出通道数量的差异来定义。
  2. 块的使用导致网络定义的非常简洁。使用块可以有效地设计复杂的网络。
  3. 在VGG论文中,Simonyan和Ziserman尝试了各种架构。特别是他们发现 深层且窄的卷积(即 3×3 )比较浅层且宽的卷积更有效。

2. VGG11模型训练全部代码(学习率lr为0.05,训练轮数epochs为20轮,batch_size为100):

import d2l.torch
import torch
from torch import nn

def vgg_block(nums_conv2d,in_channels,out_channels):
    blocks = []
    for _ in range(nums_conv2d):
        blocks.append(nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=3,padding=1))
        blocks.append(nn.ReLU())
        in_channels = out_channels
    blocks.append(nn.MaxPool2d(kernel_size=2,stride=2))
    return nn.Sequential(*blocks)

def vgg(conv2d_arch):
    VGG_blocks = []
    in_channels = 1
    #for循环来添加每个卷积块,卷积层模块
    for nums_conv2d,out_channels in conv2d_arch:
        VGG_blocks.append(vgg_block(nums_conv2d=nums_conv2d,in_channels=in_channels,out_channels=out_channels))
        in_channels = out_channels #下一层卷积层输入channels数为上一层卷积层输出channels
    return nn.Sequential(*VGG_blocks,
                         nn.Flatten(),
                         # 全连接层模块则与AlexNet中的相同。in_channels数为最后一层卷积层的输出channels(然后赋值给in_channels,在for循环里面赋值进行的,作为下一层Flatten层的输入,铺平展开后,输入到全连接层,因此全连接层输入in_features=in_channels*7*7)
                         nn.Linear(in_features=in_channels*7*7,out_features=4096),#7*7是因为输入尺寸是224*224,经过每个卷积块后尺寸都会除以2,经过五个卷积块后,则输出尺寸大小为224*2*2*2*2*2=7
                         nn.ReLU(),
                         nn.Dropout(0.5),
                         nn.Linear(in_features=4096,out_features=4096),
                         nn.ReLU(),
                         nn.Dropout(),
                         nn.Linear(in_features=4096,out_features=10))
X = torch.randn(size=(1,1,224,224))
#conv2d_arch含有五个VGG块,指定了每个VGG块里卷积层个数和输出通道数,为一个超参数
conv2d_arch = ((1,64),(1,128),(2,256),(2,512),(2,512))
ratio = 4
small_conv2d_arch = ((pair[0],pair[1] // ratio) for pair in conv2d_arch)#由于VGG-11比AlexNet计算量更大,因此我们构建了一个通道数较少的网络,足够用于训练Fashion-MNIST数据集。
VGGNet = vgg(small_conv2d_arch)
#构建一个高度和宽度为224的单通道数据样本X,来观察每个层输出的形状
for layer in VGGNet:
    X = layer(X)
    print(layer.__class__.__name__,'output shape : \t',X.shape)
lr,num_epochs,batch_size = 0.05,20,100 #除了使用比AlexNet略高的学习率外,模型训练过程与 AlexNet类似
train_iter,test_iter = d2l.torch.load_data_fashion_mnist(batch_size=batch_size,resize=224)
#调用d2l里面封装好的训练模型函数,这个训练函数跟前面LeNet里面训练模型函数一模一样
d2l.torch.train_ch6(VGGNet,train_iter=train_iter,test_iter=test_iter,num_epochs=num_epochs,lr=lr,device=d2l.torch.try_gpu())

VGG模型训练和测试输出结果如下图所示:
李沐动手学深度学习V2-VGG11模型和代码实现_第2张图片

你可能感兴趣的:(李沐动手学深度学习笔记,深度学习,神经网络,pytorch,机器学习,人工智能)