现代卷积神经网络2-使用块的网络(VGG)

92 阅读1分钟

1. 引言

深度学习的模型设计从最初关注单个神经元,逐步发展到以“层”为单位,现在进一步抽象为“块”的概念。VGG网络由牛津大学视觉几何组提出,它通过模块化的方式设计神经网络,使得网络结构清晰、简单且具有复用性。本文将以浅显易懂的方式带你理解VGG网络的设计与实现。

2. VGG块:网络的基本构件

2.1 VGG块的定义

一个典型的VGG块包含以下组成部分:

  1. 多个卷积层:使用固定大小的卷积核(通常为 3×33 \times 3),并通过填充保持输出尺寸不变。
  2. ReLU激活函数:为网络增加非线性。
  3. 最大池化层:用于空间下采样,将特征图的宽和高减半。

在代码中,VGG块可以通过以下函数实现:

import torch
from torch import nn


def vgg_block(num_convs, in_channels, out_channels):
    """
    定义一个VGG块,由若干个卷积层和一个最大池化层组成。

    参数:
    - num_convs (int): 当前块中卷积层的数量。
    - in_channels (int): 输入特征图的通道数。
    - out_channels (int): 输出特征图的通道数。

    返回:
    - nn.Sequential: 包含所有卷积层和池化层的顺序容器。
    """
    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)

2.2 数学公式

  1. 卷积层的输出大小计算公式
Hout=Hin+2PKS+1,Wout=Win+2PKS+1H_{\text{out}} = \frac{H_{\text{in}} + 2P - K}{S} + 1, \quad W_{\text{out}} = \frac{W_{\text{in}} + 2P - K}{S} + 1
  • 𝐻in,𝑊in\mathbf{𝐻_{in}}, \mathbf{𝑊_{in}}:输入特征图的高度和宽度;
  • 𝐾\mathbf{𝐾}:卷积核大小;
  • 𝑃\mathbf{𝑃}:填充大小;
  • 𝑆\mathbf{𝑆}:步幅。
  1. 最大池化层的输出大小公式
Hpool=HinSpool,Wpool=WinSpoolH_{\text{pool}} = \frac{H_{\text{in}}}{S_{\text{pool}}}, \quad W_{\text{pool}} = \frac{W_{\text{in}}}{S_{\text{pool}}}

3. VGG网络结构设计

3.1 整体架构

VGG网络由两部分组成:

  1. 卷积部分:包含若干个VGG块,用于特征提取。
  2. 全连接部分:将提取的特征展平后输入到全连接层进行分类。

vgg.svg

图7.2.1 从AlexNet到VGG,它们本质上都是块设计

代码实现:

def vgg(conv_arch):
    """
    定义VGG网络,由多个卷积块和全连接层组成。

    参数:
    - conv_arch (list): VGG块的配置列表,其中每个元素是一个元组,格式为
      (num_convs, out_channels),表示卷积块中卷积层的数量和输出通道数。

    返回:
    - nn.Sequential: 包含卷积块和全连接层的顺序容器。
    """
    conv_blks = []
    in_channels = 1  # 假设输入为单通道灰度图(例如Fashion-MNIST)

    # 构造卷积块部分
    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(in_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的架构
conv_arch = [(1, 64), (1, 128), (2, 256), (2, 512), (2, 512)]
net = vgg(conv_arch)

3.2 输出形状分析

我们以输入为 224×224224 \times 224 的单通道图像为例,查看每一层的输出形状:

# 测试VGG网络的输出形状
X = torch.randn(size=(1, 1, 224, 224))  # 假设输入为 224x224 的单通道图像
for blk in net:
    X = blk(X)
    print(f"{blk.__class__.__name__} output shape: {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])

4. 模型训练与测试

4.1 数据集准备

我们使用 Fashion-MNIST 数据集,并将图片尺寸调整为 224×224224 \times 224

batch_size = 128
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)

4.2 模型训练

为了减少计算量,缩小通道数比例:

ratio = 4
small_conv_arch = [(pair[0], pair[1] // ratio) for pair in conv_arch]
net = vgg(small_conv_arch)

lr, num_epochs = 0.05, 10
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

屏幕截图 2025-01-17 173038.png 屏幕截图 2025-01-17 173256.png

屏幕截图 2025-01-17 173111.png

5. 总结

  1. VGG网络通过模块化设计实现了简单高效的网络构建。
  2. 重复使用卷积块的设计思想在深度学习领域具有重要的启发性。
  3. VGG的尝试证明了深层、窄卷积网络的优越性,为后续网络的设计提供了重要参考。

VGG的理念体现了现代神经网络设计中的“简单即美”的原则,为深度学习的发展打下了坚实基础。