前言
继续学习霹雳大神的神经网络讲解视频
更新不易,希望大家可以去看原视频支持up主
VGG网络详解及感受野的计算
使用pytorch搭建VGG网络
VGG网络结构详解与模型的搭建
简单介绍VGG
模型结构
- 经常用的是16层结构👆13层个卷积层以及3个全连接层
亮点
- 网络亮点:通过堆叠多个3x3 的卷积核来以替代大尺度卷积核(减少所需参数)
感受野
概念
例子
感受野计算公式
也就是说我们特征层3中的一个单元就相当于我们原图中的5*5的感受视野
为什么论文中说堆叠两个3x3的卷积核代替5x5的卷积核,堆叠三个3x3的卷积核替代7x7的卷积核?
在VGG网络中卷积核的步距(stride)是默认为 1 的
可以经过计算得到
目的:减少网络中训练参数的个数
同样可以通过计算证明
一个C是卷积核的深度就是有多层卷积,还有一个C是卷积核的个数,(因为这里假设的输入输出channel相同,所以输出的特征矩阵也是C
VGG16
参数使用
卷积层
通过这个参数设定的卷积层输出的高度和宽度不变:
由计算可以算得
我们设定的卷积核的大小就是 3*3
out =( in - 3 + 2 )/ 1 + 1 = in
下采样层
通过这个参数设定的下采样层输出的高度和宽度直接缩小为原来的一半:
out =( in - 2 + 0 )/ 2 + 1 = in / 2
基本结构
16 weight layers
Input (224x224 RGB images)
两层3x3的卷积核+ReLU
Maxpool最大下采样层
两层3x3的卷积核+ReLU
Maxpool最大下采样层
三层3x3的卷积核+ReLU
Maxpool最大下采样层
三层3x3的卷积核+ReLU
Maxpool最大下采样层
连接两个全连接层+ReLU
一层全连接层
加上一个soft-max处理进行激活
(2层)由于采用的卷积核conv3-64 的深度是64 所以输出的特征矩阵 宽和高不变 深度变成64
22422464
由于采用的下采样层maxpool 将特征矩阵 宽和高缩减为原来的一半 深度不变还是64 11211264
(2层)后面又是卷积核conv3-128的深度是128 所以输出的特征矩阵 宽和高不变 深度变成128
112112128
下采样层maxpool 将特征矩阵 宽和高缩减为原来的一半 深度不变还是128
5656128
(三层)卷积核conv3-256的深度是256 所以输出的特征矩阵 宽和高不变 深度变成256
5656256
下采样层maxpool 将特征矩阵 宽和高缩减为原来的一半 深度不变还是256
2828256
(三层)卷积核conv3-512的深度是512 所以输出的特征矩阵 宽和高不变 深度变成512
2828512
下采样层maxpool 将特征矩阵 宽和高缩减为原来的一半 深度不变还是512
1414512
(三层)卷积核conv3-512的深度是512 所以输出的特征矩阵 宽和高不变 深度变成512
1414512
下采样层maxpool 将特征矩阵 宽和高缩减为原来的一半 深度不变还是512
77512
(三层)全连接层
FC-4096(ReLU)
FC-4096(ReLU)
FC-1000
model.py中代码解读
提取特征网络结构
卷积层+下采样层
cfgs = {
'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
#所有的卷积核大小都是3*3 64个卷积核 最大池化下采样层 128个卷积核 最大池化层 256个卷积核 256个卷积核 下采样层 512个卷积核 512个卷积核 下采样层 512个卷积核 512个卷积核 下采样层
'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 make_features(cfg: list):#传入一个配置变量 是一个list的类型 传入定义配置的列表
layers = [] #定义一个空列表来存放我们创建的每一层结构
in_channels = 3 #输入通道是3
for v in cfg: #由for循环遍历我们的配置列表就可以得到 可以得到一个有卷积操作和池化操作做组成一个列表
if v == "M": #如果是一个最大池化层的话
layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
#创建一个最大池化下采样2d 已经规定了池化核的大小都是2, 步长也是2
else: #否则就是一个卷积核
conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
#创建一个卷积层 输入的彩色图像的深度是3 输出的特征矩阵的深度也就是对应着我们卷积核的个数,卷积核大小是3*3 padding=1 stride默认是等于1
layers += [conv2d, nn.ReLU(True)]
#每一个卷积层都是采用ReLU激活函数 定义在一起 并添加到layers的空列表中
in_channels = v
#讲in channels改变 使得上一层的输出变成下一层的输入
return nn.Sequential(*layers)
#Sequential函数将我们的列表通过非关键字参数的形式传入
#Sequential
分类网络结构
全连接层
class VGG(nn.Module):#继承自nn.Module的父类
def __init__(self, features, num_classes=1000, init_weights=False):
#初始化函数中 features是我们通过makefeatures函数生成的特征网络结构 所需要分类的类别个数
#是否对我们网络进行权重初始化= False
super(VGG, self).__init__()
self.features = features #是我们刚刚生成的网络features
self.classifier = nn.Sequential( #同样是利用sequential函数来生成分类网络结构
nn.Dropout(p=0.5), #目的是为了减少过拟合 0.5概率随机失活神经元
nn.Linear(512*7*7, 2048), #输入节点个数就是我们展平之后所得到的一维向量的元素个数
#全连接层的输出节点个数原论文中是4096 为了减少训练参数就是设置为2048
nn.ReLU(True),#激活函数
nn.Dropout(p=0.5),
nn.Linear(2048, 2048),#输入节点个数是上一层的输出节点个数
#全连接层的输出节点个数原论文中是4096 为了减少训练参数就是设置为2048
nn.ReLU(True),
nn.Linear(2048, num_classes)
#输入节点个数是上一层的输出节点个数 输出节点个数是对应我们分类的类别个数
)
if init_weights: #如果有初始化的参数的话
self._initialize_weights() #进入到提前定义好的初始化权重函数当中
def forward(self, x): #forward正向传播的过程 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) #通过一个展平处理 第0个维度是我们的batch维度 从channel开始展平
# 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)
#用我们的xavier的初始化方法去初始化我们的卷积层的权重
if m.bias is not None: #如果采用了偏置的话
nn.init.constant_(m.bias, 0) #将我们的偏置初始化为0
elif isinstance(m, nn.Linear):#如果遍历的当前层是一个全连接层的话
nn.init.xavier_uniform_(m.weight)
#用我们的xavier的初始化方法去初始化我们的全连接层权重
# nn.init.normal_(m.weight, 0, 0.01)
nn.init.constant_(m.bias, 0)
#将我们的偏置初始化为0
实例化我们的VGG网络
def vgg(model_name="vgg16", **kwargs):
#model_name可以实例化的我们所给定的网络模型
try:
cfg = cfgs[model_name] #得到我们的配置列表
except:
print("Warning: model number {} not in cfgs dict!".format(model_name))
exit(-1)
model = VGG(make_features(cfg), **kwargs) #通过VGG的类来实例化我们VGG网络
#第一个参数 make_features是一个函数 feature的参数是所对应的配置文件, **kwargs可变长度的字典变量,包括了class_num=1000.init_weights=False.
return model
train.py代码解读
和训练AlexNet基本上一样
predict.py
和训练AlexNet基本上一样