一、VGG16简介
VGG16(Visual Geometry Group 16)是一种深度卷积神经网络模型,由牛津大学的研究团队Visual Geometry Group开发。它是2014年的ImageNet图像识别挑战中的参赛模型之一,并在该挑战中取得了非常出色的结果。
VGG16模型的设计思想是通过堆叠多个较小的卷积层和池化层来构建深层网络,以增强模型的表达能力。具体来说,VGG16模型由16个卷积层和3个全连接层组成。其中,卷积层主要用于提取输入图像的特征,而全连接层则用于将提取到的特征映射到类别概率上。
VGG16的卷积部分采用了较小的3x3卷积核和步长为1的卷积操作,这种设计方式使得网络可以更深,从而提升了特征的表达能力。在每两个卷积层之间,VGG16还使用了2x2的最大池化层,以减小特征图的尺寸并保留最显著的特征。在最后的卷积层之后,VGG16采用了三个全连接层,每个全连接层都有4096个隐藏单元,最后一个全连接层输出模型的预测结果。
VGG16的一个重要特点是它的模型结构相对简单且易于理解,没有使用复杂的技巧或模块。这种简单性使得VGG16成为深度学习中的经典模型之一,被广泛应用于计算机视觉领域的各种任务,如图像分类、目标检测和图像生成等。
然而,由于VGG16的结构较为庞大,参数量较多,导致模型的训练和推理过程较为耗时。为了解决这个问题,后续的研究工作提出了一些改进版本的VGG模型,如VGG19和VGGNet等,它们在VGG16的基础上进行了一些调整和优化,以提高模型的性能和效率。
二、VGG16模型结构
输入(224*224)图像 |
conv3-64 |
conv3-64 |
maxpool |
conv3-128 |
conv3-128 |
maxpool |
conv3-256 |
conv3-256 |
conv3-256 |
maxpool |
conv3-512 |
conv3-512 |
conv3-512 |
maxpool |
conv3-512 |
conv3-512 |
conv3-512 |
maxpool |
FC-4096 |
FC-4096 |
FC-1000 |
soft-max |
针对VGG16进行具体分析发现,VGG16共包含:
13个卷积层(Convolutional Layer),分别用conv3-XXX表示 (XXX为输出通道数,3代表kernel_size)
3个全连接层(Fully connected Layer),分别用FC-XXXX表示(XXX为输出神经元个数)
5个池化层(Pool layer),分别用maxpool表示
三、计算过程
结合模型来看,更容易理解。
三、详细解释
1.卷积 (conv2d)
pytorch官网的介绍Conv2d — PyTorch master documentation
参数 | 参数类型 | 说明 |
in_channels | int | 输入图像通道数 |
out_channels | int | 卷积产生的通道数 |
kernel_size | (int or tuple) | 卷积核尺寸,可以设为1个int型数或者一个(int, int)型的元组。例如(2,3)是高2宽3卷积核 |
stride | (int or tuple, optional) | 卷积步长,默认为1。可以设为1个int型数或者一个(int, int)型的元组。 |
padding | (int or tuple, optional) | 填充操作,控制padding_mode 的数目 |
padding_mode | (string, optional) | padding 模式,默认为Zero-padding 。 |
dilation | (int or tuple, optional) | 扩张操作:控制kernel点(卷积核点)的间距,默认值:1。 |
groups | (int, optional) | group参数的作用是控制分组卷积,默认不分组,为1组。 |
bias | (bool, optional) | 为真,则在输出中添加一个可学习的偏差。默认:True。 |
其实就是一个卷积核对图片上的一块区域进行乘积的过程,计算图如下
再看下面这条
输入 | 计算过程 | 输出 |
【224*224*3】 | conv2d(in_channels=3,out_channels=64,kernel_size=3,stride=1,padding=1) | 【224*224*64】 |
输入:224(宽度)*224(高度)*3(图片通道数,因为是RGB,所以是3)
计算过程:conv2d(in_channels=3,out_channels=64,kernel_size=3,stride=1,padding=1),参数设置原因如下
参数 | 说明 |
in_channels=3 | 原始图片RGB通道数为3 |
out_channels=64 | 根据conv3-64,可知第一层输出通道数64 |
kernel_size=3 | 根据conv3-64,可知第一层kernel_size为3 |
stride=1 | 默认值 |
padding=1 | 原论文中padding=same 即为1,也可根据等会的公式推出 |
输出:224(宽度)*224(高度)*64(根据conv3-64,得知输出后的通道数64)
卷积后的计算公式
输出特征图的宽度=(输入特征图的宽度-卷积核尺寸+2*填充)÷步长+1
输出特征图的高度=(输入特征图的高度-卷积核尺寸+2*填充)÷步长+1
验证:
224=(224-3+2*1)÷1+1=224
224=(224-3+2*1)÷1+1=224
所以得出卷积后的特征图为宽为224,高为224,通道数为64
2.最大池化(maxpool)
pytorch官网的介绍MaxPool2d — PyTorch master documentation
参数 | 参数类型 | 说明 |
kernel_size | (Union[int, Tuple[int, int]]) | 表示做最大池化的窗口大小,可以是单个值,也可以是tuple元组 |
stride | (Union[int, Tuple[int, int]]) | 步长 |
其实就是一个卷积核对图片上的一块区域进行取最大值的过程,计算图如下
再看下面这条
输入 | 计算过程 | 输出 |
【224*224*64】 | MaxPool2d(kernel_size=2,stride=2) | 【112*112*64】 |
输入:224(宽度)*224(高度)*64(根据上一层的输出【224*224*64】作为这一层的输入,可知通道数为64)
计算过程:MaxPool2d(kernel_size=2,stride=2),参数设置原因如下
参数 | 说明 |
kernel_size | 作者定义的 |
stride | 默认与kernel_size一致,即为2 |
输出:112(宽度)*112(高度)*64(MaxPooling操作只对特征图的空间维度进行操作,而不会改变特征图的通道数。)
池化后的计算公式
输出特征图的宽度=(输入特征图的宽度-池化核宽度)÷步长+1
输出特征图的高度=(输入特征图的高度-池化核高度)÷步长+1
验证:
112=(224-2)÷2+1
112=(224-2)÷2+1
所以得出池化后的特征图为宽为112,高为112,通道数为64
3.总结
每一层的输出通道数要与下一层的输入通道数要一致,特别是在yolo以及一些轻量级网络的配置中。
四、参数量计算
五、搭建模型
import torch
import torch.nn as nn
# 定义 VGG16 模型
class VGG16(nn.Module):
def __init__(self, num_classes=1000):
super(VGG16, self).__init__()
self.features = nn.Sequential(
# 第一段卷积层
nn.Conv2d(3, 64, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(64, 64, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
# 第二段卷积层
nn.Conv2d(64, 128, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(128, 128, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
# 第三段卷积层
nn.Conv2d(128, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
# 第四段卷积层
nn.Conv2d(256, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
# 第五段卷积层
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes)
)
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
# 创建 VGG16 模型实例
model = VGG16()
# 打印模型结构
print(model)