• 【Pytorch深度学习开发实践学习】【VGG】经典算法复现-Pytorch实现VGG主干网络(1)model.py


    模型结构

    VGG网络是计算机视觉领域一种主流的特征提取的主干网络
    它最早来自牛津大学视觉组的论文《VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION》
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    他们利用3*3卷积核、最大池化和全连接层构建了5种类型的VGG神经网络,
    下面就具体实现一下这些类型的VGG神经网络

    模型部分代码实现

    VGG类

    我们首先定义一个VGG类,这个类就是定义和实现网络结构

    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(
    			nn.Linear(7*7*512,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):
    		x = self.features(x)
    		x = torch.flatten(x,start_dim=1)
    		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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    逐行解释上面的代码
    我们首先定义一个叫做VGG的类,它继承了nn.Module类
    我们定义了VGG类的初始化函数 init ,它接受了三个参数:
    ①是feature,这个我们后面会提到,这是VGG的特征提取部分的网络
    ②是num_classes,这是VGG最后一个全连接层的输出的维数,它表示要进行分类的图像一共有多少个类
    ③是init_weights,它表示我们要不要初始化权重,因为我们要使用预训练权重,所有这个我们在类初始化函数里面

    super(VGG,self).init()
    super()是一个内置函数,用于临时地替换当前的类,从而可以调用父类的方法。VGG是当前的子类,self是子类的一个实例,super(VGG,self)返回了一个临时对象,该对象绑定了VGG的父类,并允许调用其方法。
    init()这是父类的初始化方法,当我们调用super(VGG,self).init()时,我们是调用父类的__init__()方法。

    self.features = features
    这是将传入的features参数赋值给类的features属性。
    self.classifier = nn.Sequential()
    这个代码定义了一个顺序模型,它是pytorch定义的一个包含多个层的容器,这些层会按照它们被添加到容器中的顺序被应用,具体来说,这个分类器包括了以下层:
    nn.Linear(77512,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)
    首先是一个全连接层,它的输入是77512,这是最后一个卷积层输出的维度,然后输出是4096,
    然后是一个ReLU激活函数
    后面是一个Dropout,
    然后是一个全连接层,它的输入是4096,输出是4096,
    然后是一个ReLU激活函数
    后面是一个Dropout,
    最后一层还是全连接层,它的输入是4096,输出就是num_classes,这个参数是我们在VGG类的初始化函数中指定的,根据实际任务来定。

    def forward(self, x):
            x = self.features(x)
            x = torch.flatten(x, start_dim=1)
            x = self.classifier(x)
            return x
    
    • 1
    • 2
    • 3
    • 4
    • 5

    forward函数定义了整个网络的前向计算过程
    首先是我们定义的features函数,它的输入是预处理的图像
    维度是(batch_size,3,224,224)
    经过特征提取网络输出的维度 (batch_size,512,7,7)
    因为要输入到全连接层,我们要将网络展开
    x = torch.flatten(x, start_dim=1) 这里是把(batch_size,512,7,7)从第1维进行展开,注意是我们是从第0维开始计算的,第1维是第2个,就是512
    所以展开以后就是(batch_size,51277)
    展平操作将多维张量转换为一维向量,以便可以传递给全连接层
    最后将展平后的x传递给classifier,最后输出的是类别

    make_features方法和cfg字典

    
    
    def make_features(cfgs:list):
    	layers = []
    	in_channels =3
    	for v in cfgs:
    		if v == 'M':
    			layers += [nn.Maxpool2d(kersize=2,stride=2)]
    		else:
    			layers +=[nn.conv2d(in_channels,v,kernel_size =3,padding=1),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']
    }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这里我们定义了一个叫cfgs的字典,这个字典的键是模型的名字,11、13、16、19分别表现VGG的权重层数,这里的权重层数指的是有可学习权重的层,具体来说就是卷积层和池化层。
    64、128、256、512这都是通道数,VGG都是使用的33的卷积核
    我们以VGG11为例,大家可以结合最上面的图来看,首先是一个3
    364的卷积层,然后跟一个最大池化层,再接着是1个33128的卷积层,再是一个最大池化层,接着是2个33256的卷积层,接着一个最大池化层,再是2个33256的卷积层,接着一个最大池化层,再是2个33*256的卷积层,最后接着一个最大池化层。

    这个make_features函数接受一个cfgs的参数,我们指定这个参数的类型是一个列表。代码中的冒号:在类型提示中用来指定参数的类型。类型提示是python3.5以及更高版本的一个特性。它允许你为函数参数和返回值提供预期类型的信息。这有助于代码的可读性和维护性,但是并不强制执行类型检查。

    我们首先初始化了一个空列表layers,
    我们指定初始的输入通道数是3,in_channels
    然后遍历列表,如果读取的是‘M’,那我们向layers列表中加入最大池化层
    如果不是,我们加入卷积层和Relu激活层。
    最后我们通过nn.Sequential通过非关键字参数的形式将layers输入到Sequential中,形成我们的特征提取网络
    为什么要使用非关键字参数传递的形式呢?
    我们可以看看Sequential是如何定义的

    在这里插入图片描述
    在这里插入图片描述
    给Sequential传递参数有两种形式,一种就是我们顺序地填入非关键字参数,另外一种就是我们顺序地定义一个字典

    为了帮助大家更好地理解这个
    我们实例化一个vgg19的实例,断点调试一下
    在这里插入图片描述
    在这里插入图片描述
    可以看到layer实际上就是一个列表

    这个返回的nn.Sequential实际上就是我们前面定义的VGG类中初始化函数的接受的属性features,那么后面的实例化VGG类的时候,我们就需要调用make_features类去得到一个feature并把这个传给VGG类

    vgg函数

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    那么问题来了,根据权重层数的不同,VGG有四种类型的网络,我们在实例化VGG类的时候到底是使用那一类呢?聪明的同学肯定已经想到了,我们必须还要写一个函数去指定我们使用那一类的网络模型

    所以这里我们定义一个vgg函数,它接受一个model_name的参数,这个参数就是我们指定的网络的名称

    接下来是一个断言,确定模型名称是不是在我们建立的cfgs的字典中,
    如果在,我们去获取这个名称对应的值
    这个值就是作为一个列表提供给make_feature函数
    然后我们实例化一个VGG类,调用make_feature函数生成features提供给实例化的VGG

    model.py的完整代码

    那么model.py全部的代码如下:

    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(
                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
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
  • 相关阅读:
    仿英雄联盟网页HTML代码 学生网页设计与制作期末作业下载 大学生网页设计与制作成品下载 DW游戏介绍网页作业代码下载
    leetcode top100 (9)找到字符串中所有字母异位词
    DC-4靶机
    AutoCAD 2022安装及激活
    NAACL最佳方法论文:课本上的A*搜索算法可以提升文本生成效果!
    【云原生之Docker实战】使用Docker部署Taskcafe项目管理工具
    怎样在CSDN赚点零花钱
    『亚马逊云科技产品测评』活动征文|搭建带有“弱”图像处理功能的流媒体服务器
    深分页问题,mysql查询 limit 1000,10 和limit 10 一样快吗?
    了解异常的特性
  • 原文地址:https://blog.csdn.net/weixin_44184852/article/details/136629333