VGG16源代码详解
⼩⽩第⼀篇源码阅读笔记,理解⼀个⼗分简单的VGG⽹络源码,有助于新⼿⼊门pytorch,若有错误请多多指教。
先上
本⽂除了对源码的顺序和注释进⾏了⼀点删改外,其他与源码⼀致
⾸先看⼊⼝,也就是当我要去创建⼀个VGG模型时,我会调⽤的那个函数:
def vgg11(pretrained=False, progress=True,**kwargs):
return _vgg('vgg11','A',False, pretrained, progress,**kwargs)
def vgg11_bn(pretrained=False, progress=True,**kwargs):
return _vgg('vgg11_bn','A',True, pretrained, progress,**kwargs)
def vgg13(pretrained=False, progress=True,**kwargs):
return _vgg('vgg13','B',False, pretrained, progress,**kwargs)
def vgg13_bn(pretrained=False, progress=True,**kwargs):
return _vgg('vgg13_bn','B',True, pretrained, progress,**kwargs)
def vgg16(pretrained=False, progress=True,**kwargs):
return _vgg('vgg16','D',False, pretrained, progress,**kwargs)
def vgg16_bn(pretrained=False, progress=True,**kwargs):
return _vgg('vgg16_bn','D',True, pretrained, progress,**kwargs)
def vgg19(pretrained=False, progress=True,**kwargs):
return _vgg('vgg19','E',False, pretrained, progress,**kwargs)
def vgg19_bn(pretrained=False, progress=True,**kwargs):
return _vgg('vgg19_bn','E',True, pretrained, progress,**kwargs)
可以看到,就是以上的⼏个函数,从函数名应该可以看出模型的结构,⽐如需要的是vgg11,那就调⽤名称为vgg11的函数即可。
vgg11_bn代表vgg11⽹络中会加⼊batch normalization,没错,是否使⽤bn居然不是⽤bool变量作为⼀个参数实现的,⽽是⽤不同的函数调⽤的,有点匪夷所思,若有朋友知道是为什么请告知⼀下。
可以看到,上边所有的调⽤都使⽤了_vgg这个函数,那我们就来看看_vgg:
def_vgg(arch, cfg, batch_norm, pretrained, progress,**kwargs):
if pretrained:
kwargs['init_weights']=False
model = VGG(make_layers(cfgs[cfg], batch_norm=batch_norm),**kwargs)
if pretrained:
state_dict = load_state_dict_from_url(model_urls[arch],
progress=progress)
model.load_state_dict(state_dict)
false是什么函数return model
函数_vgg使⽤了⼀堆参数:
batch_norm控制着⽤不⽤bn(也就是上上个代码块中函数名带_bn的,batch_norm就为True,不带就为False)
pretrained控制着⽤不⽤预训练模型,可以看到在函数体中有两处使⽤了pretrained变量:
(1)第⼀处使⽤让kwargs中的⼀个值“init_weights”变False,从名字可以猜出,这个语句的⼤概意思是“如果要预训练模型的话,那就别初始化了吧,我有上好的权重给你⽤”
(2)第⼆处使⽤时可以看到执⾏了“load_state_dict_from_url”,url就是⼀个⽹址,联系到“pretrained”,意思就是如果要⽤预训练数据,那就根据⽹址下载预训练参数,然后再给到模型"model"⾥边。那么参数“arch”的作⽤就是从字典model_urls中取出⼀个⽹址,⽽bool变量“progress”是⽤来决定要不要展⽰下载进度条的,不是很重要。
实际上,model_urls字典是在源码中给出的:
model_urls ={
'vgg11':'/models/vgg11-bbd30ac9.pth',
'vgg13':'/models/vgg13-c768596a.pth',
'vgg16':'/models/vgg16-397923af.pth',
'vgg19':'/models/vgg19-dcbb9e9d.pth',
'vgg11_bn':'/models/vgg11_bn-6002323d.pth',
'vgg13_bn':'/models/vgg13_bn-abd245e5.pth',
'vgg16_bn':'/models/vgg16_bn-6c64b313.pth',
'vgg19_bn':'/models/vgg19_bn-c79401a0.pth',
}
然后剩下_vgg中间最关键的⼀句话,建模型:
model = VGG(make_layers(cfgs[cfg], batch_norm=batch_norm), **kwargs)
可以看到这个建模的过程实际上是⼀个创建VGG类的实例的过程,那么我们就需要看⼀下VGG这个类是怎么写的了:
class VGG(nn.Module):
def__init__(self, features, num_classes=1000, init_weights=True):
super(VGG, self).__init__()
self.features = features
self.avgpool = nn.AdaptiveAvgPool2d((7,7))
self.classifier = nn.Sequential(
nn.Linear(512*7*7,4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096,4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, num_classes),
)
if init_weights:
self._initialize_weights()
def forward(self, x):
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x,1)
x = self.classifier(x)
return x
def_initialize_weights(self):
for m dules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
if m.bias is not None:
stant_(m.bias,0)
elif isinstance(m, nn.BatchNorm2d):
stant_(m.weight,1)
stant_(m.bias,0)
elif isinstance(m, nn.Linear):
al_(m.weight,0,0.01)
stant_(m.bias,0)
__init__和forward函数可以⼀起看,⽹络主要结构很清晰,features–>avgpool–>classifier,forward中classifier前边⽤了⼀个flatten,说明classifier就是⼀个最后⽤来分类的全连接层,classifier在__init__中写得也很清晰,就不说了。avgpool是⼀个⾃适应均值池化层,也在__init__中写明,不谈。
_initialize_weights函数在_init_函数中提到过,就是当超参数“init_weights”设置为True时就执⾏该函数,意思当然就是给建好的模型(features–>avgpool–>classifier)初始化参数的,怎么添加的参数读者可以⾃⼰去看
那么关键是这个features,注意这个features不是指样本中的特征,⽽是由外部参数给定,是模型的⼀部分结构,要我的话我就盲猜它肯定也和classifier⼀样,是⼀个nn.Sequential,那么这个features到底长什么样?可以回到_vgg创建实例的那句话来看:
model = VGG(make_layers(cfgs[cfg], batch_norm=batch_norm), **kwargs)
在这⾥,传⼊的features是函数make_layers的返回值,这个make_layers函数源码也有定义:
def make_layers(cfg, batch_norm=False):
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)
if batch_norm:
layers +=[conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
else:
layers +=[conv2d, nn.ReLU(inplace=True)]
in_channels = v
return nn.Sequential(*layers)
⼀看make_layers的返回值,哈!果然不出我所料,是⼀个nn.Sequential(其实我瞎扯的,这谁能猜出来),那么Sequential中必然是⼀个各种层组成的列表,那这个layers必然是个列表,于是抬头⼀看,发现果然在开头定义了layer=[],那么可以断定,这个make_layers函数就是循环往layer列表中加⼊各种层的操作,那么整个VGG⽹络最核⼼的部分应该就是这⾥没错了。
那么先看make_layers参数cfg:
make_layers(cfgs[cfg], batch_norm=batch_norm)
从传参的⽅式来看,参数cfg来⾃于⼀个字典cfgs,源码中有:
cfgs ={
'A':[64,'M',128,'M',256,256,'M',512,512,'M',512,512,'M'],
'B':[64,64,'M',128,128,'M',256,256,'M',512,512,'M',512,512,'M'],
'D':[64,64,'M',128,128,'M',256,256,256,'M',512,512,512,'M',512,512,512,'M'],
'E':[64,64,'M',128,128,'M',256,256,256,256,'M',512,512,512,512,'M',512,512,512,512,'M'],
}
那么可以看出cfg这个参数实际上是⼀个列表,这个列表是数字和字母’M‘组成,那么回到make_layers函数体中,for v in cfg 语句循环cfg列表,根据列表中不同的值加⼊不同的层,那就应该很容易猜到,cfg中的值控制了整个features的结构,具体在什么样的取值下加⼊什么层就不详说了,读者可以⾃⼰去看。
最后补上导包代码和⼀个限制导⼊范围的字典__all__,就讲完了VGG所有源码:
import torch
as nn
from.utils import load_state_dict_from_url
__all__ =[
'VGG','vgg11','vgg11_bn','vgg13','vgg13_bn','vgg16','vgg16_bn',
'vgg19_bn','vgg19',
]
以上实际上就讲完了,可能⾄此读者还有点迷糊,因为我是从出⼝开始倒叙的,但如果我将整个⽣成过程讲述⼀遍⼀切将豁然开朗:
1、函数make_layers根据cfgs中的列表⽣成⼀个名叫features的nn.Sequential
3、函数_vgg拿着features去类VGG实例化了⼀个model,组成了⼀个拥有features–>avgpool–>classifier结构的模型
这就是VGG代码的主要结构,其他涉及什么参数初始化,是否使⽤预训练模型,是否使⽤bn都是⼀些其他的细节,读者可以⾃⼰品
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论