机器学习 / A LEAF IN THE GARDEN

VGG

机器学习 · 动手深度学习。来自 Daily Learning 的学习笔记。

简介

由牛津大学视觉几何组(Visual Geometry Group)提出。主要特点是使用小卷积核(3x3)堆叠成块,使用块构建深度网络。

VGG

VGG 块

一个 VGG 块有若干个卷积层和一个池化层收尾。

  • n 个 3x3 卷积核(步幅 1,填充 1)
  • 2x2 最大池化(步幅 2)

多个 3x3 卷积核堆叠可以扩大感受野,并且比大卷积核的非线性拟合能力更强。

VGG 块

VGG 用块堆叠的方式改进 AlexNet 一些略显随意的设计

AlexNet 和 VGG

实现

代码 3 · In [1]
import torch
from torch import nn
import torchinfo
import util
已保存输出
/root/autodl-tmp/envs/daily_learning/lib/python3.11/site-packages/requests/__init__.py:92: RequestsDependencyWarning: Unable to find acceptable character detection dependency (chardet or charset_normalizer).
  warnings.warn(

VGG 块

代码 5 · In [2]
def vgg_block(num_convs: int, in_channels: int, out_channels: int):
    """VGG 块
    :param num_convs: 卷积层数
    :param in_channels: 输入通道数
    :param out_channels: 输出通道数
    """
    layers = []
    # 逐层堆叠卷积层和 ReLU
    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)
代码 6 · In [3]
def vgg(conv_arch: list[tuple[int, int]], in_channels: int=1) -> nn.Sequential:
    """VGG 网络
    :param conv_arch: 卷积层架构,列表中每个元素是一个元组 (num_convs, out_channels)
    :param in_channels: 输入通道数
    :return: VGG 网络
    """
    # 固定 5 块,正好把 224x224 的图像缩小到 7x7
    assert len(conv_arch) == 5, "VGG 网络必须有 5 个卷积块"
    conv_blks = [] # 存储 VGG 块
    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, # 堆叠所有 VGG 块
        nn.Flatten(), # 7x7 的特征图展平为向量
        # 三个全连接层
        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)
    )

VGG-11 的架构,每一块高宽减半,通道数翻倍

代码 8 · In [4]
conv_arch = [(1, 64),
             (1, 128),
             (2, 256),
             (2, 512),
             (2, 512)]
net = vgg(conv_arch, in_channels=1)
torchinfo.summary(net, input_size=(1, 1, 224, 224), col_names=["input_size", "output_size", "num_params"])
已保存输出
===================================================================================================================
Layer (type:depth-idx)                   Input Shape               Output Shape              Param #
===================================================================================================================
Sequential                               [1, 1, 224, 224]          [1, 10]                   --
├─Sequential: 1-1                        [1, 1, 224, 224]          [1, 64, 112, 112]         --
│    └─Conv2d: 2-1                       [1, 1, 224, 224]          [1, 64, 224, 224]         640
│    └─ReLU: 2-2                         [1, 64, 224, 224]         [1, 64, 224, 224]         --
│    └─MaxPool2d: 2-3                    [1, 64, 224, 224]         [1, 64, 112, 112]         --
├─Sequential: 1-2                        [1, 64, 112, 112]         [1, 128, 56, 56]          --
│    └─Conv2d: 2-4                       [1, 64, 112, 112]         [1, 128, 112, 112]        73,856
│    └─ReLU: 2-5                         [1, 128, 112, 112]        [1, 128, 112, 112]        --
│    └─MaxPool2d: 2-6                    [1, 128, 112, 112]        [1, 128, 56, 56]          --
├─Sequential: 1-3                        [1, 128, 56, 56]          [1, 256, 28, 28]          --
│    └─Conv2d: 2-7                       [1, 128, 56, 56]          [1, 256, 56, 56]          295,168
│    └─ReLU: 2-8                         [1, 256, 56, 56]          [1, 256, 56, 56]          --
│    └─Conv2d: 2-9                       [1, 256, 56, 56]          [1, 256, 56, 56]          590,080
│    └─ReLU: 2-10                        [1, 256, 56, 56]          [1, 256, 56, 56]          --
│    └─MaxPool2d: 2-11                   [1, 256, 56, 56]          [1, 256, 28, 28]          --
├─Sequential: 1-4                        [1, 256, 28, 28]          [1, 512, 14, 14]          --
│    └─Conv2d: 2-12                      [1, 256, 28, 28]          [1, 512, 28, 28]          1,180,160
│    └─ReLU: 2-13                        [1, 512, 28, 28]          [1, 512, 28, 28]          --
│    └─Conv2d: 2-14                      [1, 512, 28, 28]          [1, 512, 28, 28]          2,359,808
│    └─ReLU: 2-15                        [1, 512, 28, 28]          [1, 512, 28, 28]          --
│    └─MaxPool2d: 2-16                   [1, 512, 28, 28]          [1, 512, 14, 14]          --
├─Sequential: 1-5                        [1, 512, 14, 14]          [1, 512, 7, 7]            --
│    └─Conv2d: 2-17                      [1, 512, 14, 14]          [1, 512, 14, 14]          2,359,808
│    └─ReLU: 2-18                        [1, 512, 14, 14]          [1, 512, 14, 14]          --
│    └─Conv2d: 2-19                      [1, 512, 14, 14]          [1, 512, 14, 14]          2,359,808
│    └─ReLU: 2-20                        [1, 512, 14, 14]          [1, 512, 14, 14]          --
│    └─MaxPool2d: 2-21                   [1, 512, 14, 14]          [1, 512, 7, 7]            --
├─Flatten: 1-6                           [1, 512, 7, 7]            [1, 25088]                --
├─Linear: 1-7                            [1, 25088]                [1, 4096]                 102,764,544
├─ReLU: 1-8                              [1, 4096]                 [1, 4096]                 --
├─Dropout: 1-9                           [1, 4096]                 [1, 4096]                 --
├─Linear: 1-10                           [1, 4096]                 [1, 4096]                 16,781,312
├─ReLU: 1-11                             [1, 4096]                 [1, 4096]                 --
├─Dropout: 1-12                          [1, 4096]                 [1, 4096]                 --
├─Linear: 1-13                           [1, 4096]                 [1, 10]                   40,970
===================================================================================================================
Total params: 128,806,154
Trainable params: 128,806,154
Non-trainable params: 0
Total mult-adds (Units.GIGABYTES): 7.55
===================================================================================================================
Input size (MB): 0.20
Forward/backward pass size (MB): 59.47
Params size (MB): 515.22
Estimated Total Size (MB): 574.90
===================================================================================================================

太大了,减少一些通道数来训练

代码 10 · In [9]
ratio = 4
small_arch = [(convs, channels // ratio) for (convs, channels) in conv_arch]
net = vgg(small_arch, in_channels=1)
代码 11 · In [11]
lr = 0.09
num_epochs = 10
batch_size = 128
train_iter, test_iter = util.data.load_data_fashion_mnist(batch_size=batch_size, resize=224)
util.train.train_ch6(net, train_iter, test_iter, num_epochs, lr, device=util.try_gpu())
已保存输出
loss 0.167, train acc 0.938, test acc 0.920
6058.3 examples/sec on cuda:0

代码 11 的已保存图表

代码 12 · In [13]
torch.save(net.state_dict(), "./models/VGG.pth")