提示:本文是参考李沐老师和另一个B站up主代码以及讲解对自己所学东西的整理,具体资料连接会在文章中给出。且全部实验代码是在kaggle平台上验证过滴。
前言
建议先看我推荐的资料
李沐老师参考资料地址:link.
B站up主霹雳吧啦Wz:link.
注意:本文主要是对VGG网络的梳理,且主要是对代码的梳理,是Pytorch版本。视以后情况,可能会增加tensorflow版本代码。看懂改代码需要一定MLP、CNN和Pytorch基础知识,B站有相关up主讲解比较详细,在此我推荐几个up主吧,大家自行决定决定要不要看吧。
李沐老师主页:link.
B站up主刘二大人:link.
B站up主二次元的Datawhale:link.
其中二次元的Datawhale是一个开源组织,这个开源组织还有其他资料也比较好,pandas教程,西瓜书教程(偏理论教学),其中南瓜书就是由这个开源组织编写的。我觉得可能对刚入门的小伙伴比较友好一些。
还有请大家知晓一下啦,本博客基本是对自己所学知识整理,方便以后自己复习(主要是代码整理)。而且自己也还是学生,初学深度学习(但是不是人工智能方向相关专业学生哦,只是需要用到深度学习作为一个工具使用),有很多表述可能有不当和错误,希望大家可以指出来哦!谢谢大家。
一、VGG网络详解
第一章参考博客地址。
VGG网络原论文参考地址。
VGG网络的创新点:通过堆叠多个小卷积核来替代大尺度卷积核,可以减少训练参数,同时能保证相同的感受野。
论文中提到,可以通过堆叠两个3×3的卷积核替代5x5的卷积核,堆叠三个3×3的卷积核替代7x7的卷积核。
1.1 CNN感受野的定义
感受野的定义:在卷积神经网络中,决定某一层输出结果中一个元素所对应的输入层的区域大小,被称作感受野。
以下图为例,输出层 layer3 中一个单元对应 输入层 layer2 上区域大小为2×2(池化操作),对应输入层 layer1 上大小为5×5。
感受野的计算公式:
F
(
i
)
=
(
F
(
i
+
1
)
−
1
)
×
S
t
r
i
d
e
+
K
s
i
z
e
F(i)=(F(i+1)-1)×Stride+Ksize
F(i)=(F(i+1)−1)×Stride+Ksize
其中
F
(
i
)
为
第
i
层
感
受
野
S
t
r
i
d
e
是
第
i
层
的
步
距
K
s
i
z
e
为
第
i
层
核
(
如
卷
积
核
、
池
化
核
)
尺
寸
F(i)为第i层感受野\\Stride是第i层的步距\\Ksize为第i层核(如卷积核、池化核)尺寸
F(i)为第i层感受野Stride是第i层的步距Ksize为第i层核(如卷积核、池化核)尺寸
以上图为例:
F
e
a
t
u
r
e
m
a
p
:
F
(
3
)
=
1
P
o
o
l
1
:
F
(
2
)
=
(
F
(
3
)
−
1
)
×
2
+
2
=
2
C
o
n
v
1
:
F
(
1
)
=
(
F
(
2
)
−
1
)
×
2
+
3
=
5
Feature\quad map:F(3) = 1\\Pool1:F(2)=(F(3)-1)×2+2=2\\Conv1:F(1)=(F(2)-1)×2+3=5
Featuremap:F(3)=1Pool1:F(2)=(F(3)−1)×2+2=2Conv1:F(1)=(F(2)−1)×2+3=5
1.2 参数对比
为了减少模型参数,VGG网络堆叠使用小卷积核来代替大卷积核。下面计算两者参数:
CNN参数个数计算公式:
C
N
N
参
数
个
数
=
卷
积
尺
寸
核
×
输
入
特
征
矩
阵
深
度
×
输
出
特
征
矩
阵
深
度
CNN参数个数=卷积尺寸核×输入特征矩阵深度×输出特征矩阵深度
CNN参数个数=卷积尺寸核×输入特征矩阵深度×输出特征矩阵深度现假设 输入特征矩阵深度 = 输出特征矩阵深度 = C
使用7×7的卷积核模型所需参数为:
7
×
7
×
C
×
C
=
49
C
2
7×7×C×C = 49C^2
7×7×C×C=49C2堆叠使用三个3×3的卷积核所需要的的参数个数为:
3
×
3
×
C
×
C
×
3
=
27
C
2
3×3×C×C×3=27C^2
3×3×C×C×3=27C2
1.3 VGG16
VGG网络有多个版本,一般常用的是VGG-16模型,其网络结构如下所示:
在填充padding = 1,步幅Stride = 1的情况下,经过3×3卷积的特征矩阵的尺寸是不改变的,计算公式如下:
o
u
t
s
i
z
e
=
(
i
n
s
i
z
e
−
F
s
i
z
e
+
2
P
)
/
S
+
1
=
(
i
n
s
i
z
e
−
3
+
2
)
/
1
+
1
=
i
n
s
i
z
e
{out}_{size} = ({in}_{size} - F_{size} + 2P)/S + 1 = ({in}_{size} - 3 + 2)/1 + 1 = {in}_{size}
outsize=(insize−Fsize+2P)/S+1=(insize−3+2)/1+1=insize
二、Pytorch搭建VGG网络
代码参考地址。在github上可能需要合理翻墙。
我们实现上述A、B、D、E的模型,代码如下。
2.1 模型建立
import torch.nn as nn
import torch
# official pretrain weights,官方预训练好的模型参数(在学迁移学习的时候使用)
model_urls = {
'vgg11': 'https://download.pytorch.org/models/vgg11-bbd30ac9.pth',
'vgg13': 'https://download.pytorch.org/models/vgg13-c768596a.pth',
'vgg16': 'https://download.pytorch.org/models/vgg16-397923af.pth',
'vgg19': 'https://download.pytorch.org/models/vgg19-dcbb9e9d.pth'
}
class VGG(nn.Module):
def __init__(self, features, num_classes=1000, init_weights=False):
super(VGG, self).__init__()
# 提取特征
self.features = features
# 线形层用于分类
self.classifier = nn.Sequential(
'''也可以加Flatten层'''
# nn.Flatten(),
nn.Linear(512*7*7, 4096),
nn.ReLU(True),
nn.Dropout(p=0.5),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Dropout(p=0.5),
nn.Linear(4096, num_classes)
)
if init_weights:
self._initialize_weights()
def forward(self, x):
# N x 3 x 224 x 224
x = self.features(x)
# N x 512 x 7 x 7
x = torch.flatten(x, start_dim=1)
# N x 512*7*7
x = self.classifier(x)
return x
def _initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
# nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
nn.init.xavier_uniform_(m.weight)
if m.bias is not None:
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
nn.init.xavier_uniform_(m.weight)
# nn.init.normal_(m.weight, 0, 0.01)
nn.init.constant_(m.bias, 0)
def make_features(cfg: list):
layers = []
in_channels = 3
for v in cfg:
if v == "M":
layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
else:
conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
layers += [conv2d, nn.ReLU(True)]
in_channels = v
return nn.Sequential(*layers)
cfgs = {
'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'],
}
def vgg(model_name="vgg16", **kwargs):
assert model_name in cfgs, "Warning: model number {} not in cfgs dict!".format(model_name)
cfg = cfgs[model_name]
model = VGG(make_features(cfg), **kwargs)
return model
2.2 模型定义
# 定义vgg16模型
model_name = "vgg16"
net = vgg(model_name=model_name, num_classes=5, init_weights=True)
其余包括数据处理,模型训练、预测部分代码可看AlexNet版本二处代码,都是一样的。
链接地址。