如何用Python从头开始构建VGG Net?
磐创AI介绍
VGG-Network 是 K. Simonyan 和 A. Zisserman 在论文“Very Deep Convolutional Networks for Large-Scale Image Recognition”[1] 中提出的卷积神经网络模型。该架构在 ImageNet 中实现了 92.7% 的 top-5 测试准确率,该网络拥有超过 1400 万张属于 1000 个类别的图像。它是深度学习领域著名的架构之一。将第1层和第2层的大内核大小的过滤器分别替换为11和5,显示了对 AlexNet 架构的改进,多个3×3内核大小的过滤器相继出现。它接受了数周的训练,并使用了 NVIDIA Titan Black GPU。
VGG16 架构卷积神经网络的输入是一个固定大小的 224 × 224 RGB 图像。它所做的唯一预处理是从每个像素中减去在训练数据集上计算的平均 RGB 值。然后图像通过一堆卷积 (Conv.) 层运行,其中的过滤器的感受野非常小,为 3 × 3,这是捕捉左/右、上/下概念的最小尺寸,和中心部分。在其中一种配置中,它还利用了1 × 1卷积滤波器,可以观察到输入通道的线性变换之后是非线性的。卷积步幅固定为1像素; 卷积层输入的空间填充使卷积后的空间分辨率保持不变,即3 × 3个卷积层的填充为1像素。然后空间池化由五个最大池化层执行,其中 16 个最大池层跟在一些 Conv 层之后,但不是所有的 Conv 层。这个最大池化是在一个 2 × 2 像素的窗口上执行的,步长为 2。
资料来源:neurohive.io
该架构包含一堆卷积层,这些层在不同的架构中具有不同的深度,然后是三个全连接 (FC) 层:前两个 FC 各有 4096 个通道,第三个 FC 执行 1000 路分类,因此包含 1000 个通道,每个类一个通道。最后一层是 soft-max 层。全连接层的配置在所有网络中都是相似的。所有隐藏层都配备了整流 (ReLU) 非线性。此外,这里的一个网络包含局部响应归一化 (LRN),这种归一化不会提高训练数据集的性能,但使用它会导致内存消耗和计算时间增加。架构总结:模型的输入是固定大小的 224×224224×224 RGB 图像预处理是从每个像素中减去训练集 RGB 值的平均值卷积层 17– 步幅固定为 1 像素– 3×33×3 的填充为 1 像素空间池化层– 按照惯例,这一层不计入网络的深度– 空间池化是使用最大池化层完成的– 窗口大小为 2×22×2– 步幅固定为 2– Convnets 使用了 5 个最大池化层全连接层:第一:4096 (ReLU)第二:4096(ReLU)第三:1000(Softmax)架构配置下图包含 VGG 网络的卷积神经网络配置以下:VGG-11VGG-11 (LRN)VGG-13VGG-16 (Conv1)VGG-16VGG-19
来源:“用于大规模图像识别的深度卷积网络”
上面提到的卷积神经网络配置每列一个。在下文中,网络以其名称 (A-E) 表示。所有配置都遵循传统设计,仅在深度上有所不同:从网络A中的11个权重层(8个转换和3个FC层)到网络E中的19个权重层(16个转换和3个FC层)。每个卷积层的宽度很小,通道数很小,从第一层的64开始,然后在每个最大池化层之后继续增加2倍,直到达到512。每个配置的参数数量如下所述。虽然它有很大的深度,但网络中的权值的数目并不大于较浅的网络中的权值的数目,而该网络具有较大的卷积层宽度和感受野
来源:“用于大规模图像识别的非常深的卷积网络”
训练损失函数是多项逻辑回归学习算法是基于动量反向传播的小批量随机梯度下降 (SGD)批量大小为 256动量是 0.9正则化L2 权重衰减(惩罚乘数为 0.0005)前两个全连接层的 Dropout 设置为 0.5学习率初始:0.01当验证集精度停止提高时,它会降低到 10。尽管与 Alexnet 相比,它具有更多的参数和深度,但由于 CNN 的损失函数收敛所需的时期更少小卷积核和大深度的更多正则化。某些层的预初始化。训练图像大小S 是同位素重缩放图像的最小边设置S的两种方法Fix S,称为单尺度训练这里 S = 256 和 S = 384Vary S,称为多尺度训练S 来自 [Smin, Smax] 其中 Smin = 256, Smax = 512– 然后 224×224224×224图像是从每次 SGD 迭代重新缩放的图像中随机裁剪的。
主要特征VGG16 共有 16 层,具有一定的权重。仅使用卷积和池化层。始终使用 3 x 3 内核进行卷积。202×2 大小的最大池。1.38 亿个参数。在ImageNet 数据上训练。它的准确率为 92.7%。另一个版本是 VGG 19,共有 19 个带权重的层。这是一个非常好的深度学习架构,用于对任何特定任务进行基准测试。VGG 的预训练网络是开源的,因此它可以开箱即用地用于各种类型的应用程序。让我们实现 VGG 网络首先让我们为 VGG 网络的每个版本创建过滤器映射。参考上面的配置图片了解过滤器的数量。即为键名为 VGG11、VGG13、VGG16、VGG19 的版本创建一个字典,并分别根据每个版本中的过滤器数量创建一个列表。这里列表中的“M”称为 Maxpool 操作。import torch
import torch.nn as nn
VGG_types = {
"VGG11": [64, "M", 128, "M", 256, 256, "M", 512, 512, "M", 512, 512, "M"],
"VGG13": [64, 64, "M", 128, 128, "M", 256, 256, "M", 512, 512, "M", 512, 512, "M"],
"VGG16": [64,64,"M",128,128,"M",256,256,256,"M",512,512,512,"M",512,512,512,"M",],
"VGG19": [64,64,"M",128,128,"M",256,256,256,256,"M",512,512,512,512,
"M",512,512,512,512,"M",],}
创建一个全局变量来说明架构的版本。然后创建一个名为 VGG_net 的类,输入为 in_channels 和 num_classes,它接受输入,如图像通道数和输出类数。初始化 Sequential 层,即在序列中,Linear layer->ReLU->Dropout。然后创建一个名为 create_conv_layers 的函数,它将 VGGnet 架构配置作为输入,即我们上面为不同版本创建的列表。当它遇到上面列表中的字母“M”时,它执行 MaxPool2d 操作。VGGType = "VGG16"
class VGGnet(nn.Module):
def __init__(self, in_channels=3, num_classes=1000):
super(VGGnet, self).__init__()
self.in_channels = in_channels
self.conv_layers = self.create_conv_layers(VGG_types[VGGType])
self.fcs = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(4096, 4096),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(4096, num_classes),
)
def forward(self, x):
x = self.conv_layers(x)
x = x.reshape(x.shape[0], -1)
x = self.fcs(x)
return x
def create_conv_layers(self, architecture):
layers = []
in_channels = self.in_channels
for x in architecture:
if type(x) == int:
out_channels = x
layers += [
nn.Conv2d(
in_channels=in_channels,
out_channels=out_channels,
kernel_size=(3, 3),
stride=(1, 1),
padding=(1, 1),
),
nn.BatchNorm2d(x),
nn.ReLU(),
]
in_channels = x
elif x == "M":
layers += [nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))]
return nn.Sequential(*layers)
完成后,编写一个小的测试代码来检查我们的实现是否运行良好。在下面的测试代码中,给出的类数是 500。if __name__ == "__main__":
device = "cuda" if torch.cuda.is_available() else "cpu"
model = VGGnet(in_channels=3, num_classes=500).to(device)
# print(model)
x = torch.randn(1, 3, 224, 224).to(device)
print(model(x).shape)
输出应该是这样的:
如果你想查看网络架构,你可以取消对上述代码中的*print(model)*语句的注释。也可以通过更改变量 VGGType 中的 VGG 版本来尝试使用不同的版本。