Alex_McAvoy

想要成为渔夫的猎手

经典卷积神经网络之 VGG

References:

【概述】

VGG 网络是牛津大学的视觉几何小组(Visual Geometry Group)提出的,该网络也由此得名,他们以 $7.32\%$ 的错误率赢得了 2014 年 ILSVRC 分类任务的亚军(冠军由 GoogLeNet 以 6.65% 的错误率夺得),同时以 $25.32\%$ 的错误率夺得定位任务的第一名(GoogLeNet 错误率为 $26.44\%$)

VGG 可以看成是加深版本的 AlexNet,其结构均为卷积层+全连接层,其最大的贡献是证明了网络深度的增加能影响网络性能

【贡献】

相比于 AlexNet,VGG 除了证明了网络深度的增加能影响网络性能外,一个重要改进是采用连续的几个小卷积核代替 AlexNet 中的较大卷积核,进行细粒度特征提取,即使用两个 $3\times 3$ 的卷积核代替 $5\times 5$ 卷积核,用三个 $3\times3$ 的卷积核来代替 $7\times7$ 的卷积核

如下图,使用两个 $3\times 3$ 的卷积核代替 $5\times 5$ 卷积核

$5\times5$ 的卷积核,可以看做是一个小的全连接网络在 $5\times5$ 的区域上滑动,那么可以先用一个 $3\times3$ 的卷积核进行滤波,然后再用一个全连接层连接这个 $3\times 3$ 的卷积输出,而这个全连接层也可以看做是一个 $3\times3$ 的卷积层,这样就可以通过两个 $3\times3$ 的卷积核进行叠加,来代替一个 $5\times 5$ 的卷积

这样做的主要目的是保证在具有相同感受野的条件下,通过堆积小卷积核来增加网络深度,多层非线性层可以保证网络学习到更复杂的模式,而且参数还更少

假设输入图像尺寸为 $H\times W\times C$,滤波器使用 $C$ 个卷积核以得到 $C$ 个特征图,那么,对于一个 $5\times5$ 的卷积核和使用两个 $3\times3$ 的卷积核,所需参数如下:

  • 一个 $5\times5$ 的卷积核:$C\times(5\times 5\times C)=25C^2$
  • 两个 $3\times3$ 的卷积核:$2\times C\times(3\times3\times C)=18C^2$

显然,堆叠小的卷积核所需的参数更少一些,并且卷积过程越多,特征提取也会越细致,加入的非线性变换也随之增多,且不会增大权重参数个数

【网络结构】

VGG 网络结构如下:

VGG 有两种结构,分别是 VGG16 和 VGG19,两者并没有本质上的区别,只是网络深度不同,参数形状如下表:

  • VGG16 有 $16$ 个隐藏层,包括 $13$ 个卷积层和 $3$ 个全连接层,如上表中的 D 列所示
  • VGG19 有 $19$ 个隐藏层,包括 $16$ 个卷积层和 $3$ 个全连接层,如上表中的 E 列所示

【实现】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import torch.nn as nn

# VGG 的 block,各参数分别为:模型层数、输入通道数、输出通道数
def vgg_block(num_convs, in_channels, out_channels):
# 定义第一层卷积,接受的输入通道即图片输入通道数,输出最后的输出通道数
net = [nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), nn.ReLU(True)]

# 定义后续的多层卷积,接受的输入通道数即最后的输出通道数
for i in range(num_convs-1):
net.append(nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1))
net.append(nn.ReLU(True))

# 定义池化层
net.append(nn.MaxPool2d(2, 2))

return nn.Sequential(*net)

# 对 vgg block 进行堆叠
def vgg_stack(num_convs, channels):
net = []
for n, c in zip(num_convs, channels):
in_c = c[0]
out_c = c[1]
net.append(vgg_block(n, in_c, out_c))
return nn.Sequential(*net)

# VGG 网络
class vgg(nn.Module):
def __init__(self):
super(vgg, self).__init__()
self.feature = vgg_net
self.fc = nn.Sequential(
nn.Linear(512, 100),
nn.ReLU(True),
nn.Linear(100, 10)
)
def forward(self, x):
x = self.feature(x)
x = x.view(x.shape[0], -1)
x = self.fc(x)
return x
感谢您对我的支持,让我继续努力分享有用的技术与知识点!