• 利用Pytorch实现ResNet34网络


    利用Pytorch实现ResNet34网络

    利用Pytorch实现ResNet网络
    主要是为了学习Pytorch构建神经网络的基本方法,参考自«深度学习框架Pytorch:入门与实践»一书,作者陈云

    1.什么是ResNet网络

    ResNet(Deep Residual Network)深度残差网络,是由Kaiming He等人提出的一种新的卷积神经网络结构,其最重要的特点就是网络大部分是由如图一所示的残差单元组成,许多参数相同的残差单元连接起来组成BasicBlock,而许多BasicBlock组合起来再加上预处理层和最后的全连接分类层就组成了ResNet网络

    图一 残差单元

    1.1 ResNet34网络的具体构造

    具体构造如下图所示:

    图2:ResNet34具体结构

    除了最开始的卷积池化和最后的池化全连接外,网络中有许多结构相似的单元,这些重复单元的共同点就是有个跨层直连的shortcut。

    不同层数的ResNet的配置清单:

    1.2 ResNet34网络的原理与优点

    目前不太理解,待补充。。。。。。

    2.Pytorch代码实现

    2.1 实现代码

    import torch as t
    from torch import  nn
    from torch.nn import functional as F
    
    
    class ResidualBlock(nn.Module):
        def __init__(self,inchanel,outchanel,stride=1,shortcut=None):
            super(ResidualBlock,self).__init__()
            self.left=nn.Sequential(
                nn.Conv2d(inchanel,outchanel,3,stride,1,bias=False),
                nn.BatchNorm2d(outchanel),
                nn.ReLU(inplace=True),
                nn.Conv2d(outchanel,outchanel,3,1,1,bias=False),
                nn.BatchNorm2d(outchanel)
            )
            self.right=shortcut
        def forward(self, x):
            out=self.left(x)
            residual = x if self.right is None else self.right(x)
            out += residual
            return F.relu(out)
    
    class ResNet34(nn.Module):
        """
        实现主module:ResNet34
        ResNet34包含多个layer,每个layer又包含多个residual block
        用子model实现residual block,用__make_layer__函数实现layer
        """
        def __init__(self,num_classes=1000):
            """
            构建ResNet34网络的各层结构
            :param num_classes:
            """
            super(ResNet34,self).__init__()
            self.pre=nn.Sequential(
                nn.Conv2d(3,64,7,2,3,bias=True),
                nn.BatchNorm2d(64),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(3,2,1)
            )
            self.layer1 = self.__make_layer__(64,128,3)
            self.layer2 = self.__make_layer__(128,256,4,stride=2)
            self.layer3 = self.__make_layer__(256,512,6,stride=2)
            self.layer4 = self.__make_layer__(512,512,3,stride=2)
            self.fc = nn.Linear(512,num_classes)
    
        def __make_layer__(self,inchannel,outchannel,block_num,stride=1):
            """
            构建layer,包含多个residual block
            :param inchannel:
            :param outchannel:
            :param block_num:
            :param stride:
            :return:
            """
            shortcut = nn.Sequential(
                nn.Conv2d(inchannel,outchannel,1,stride,bias=False),
                nn.BatchNorm2d(outchannel)
            )
            layers = []
            layers.append(ResidualBlock(inchannel,outchannel,stride,shortcut))
            for i in range(1,block_num):
                layers.append(ResidualBlock(inchannel,outchannel))
            return  nn.Sequential(*layers)
    
        def forward(self, x):
            x = self.pre(x)
    
            x = self.layer1(x)
            x = self.layer2(x)
            x = self.layer3(x)
            x = self.layer4(x)
    
            x = F.avg_pool2d(x,7)
            x = x.view(x.size(0),-1)
            return self.fc(x)
    

    2.2 整体思路

    根据ResNet网络的特点,我们将模型分成两部分,主Model和子Model,子Model实现由多个残差单元组成的一个layer,主Model将最开始的卷积池化和最后的池化全连接以及中间的多个拥有不同参数的layer组合起来,形成ResNet网络。

    2.3 代码分析

    2.3.1 ResidualBlock 部分

    class ResidualBlock(nn.Module):
        def __init__(self,inchanel,outchanel,stride=1,shortcut=None):
            super(ResidualBlock,self).__init__()
            self.left=nn.Sequential(
                nn.Conv2d(inchanel,outchanel,3,stride,1,bias=False),
                nn.BatchNorm2d(outchanel),
                nn.ReLU(inplace=True),
                nn.Conv2d(outchanel,outchanel,3,1,1,bias=False),
                nn.BatchNorm2d(outchanel)
            )
            self.right=shortcut
        def forward(self, x):
            out=self.left(x)
            residual = x if self.right is None else self.right(x)
            out += residual
            return F.relu(out)
    
    1. class ResidualBlock(nn.Module)
      如果想要用Pytorch实现一个模型,必须要先继承nn.Module类,这样我们就可以不手动实现反向传播的过程,只需要实现初始化模型部分以及前向传播部分即可

    2. __init__

      该函数负责实现模型的初始化,模型中的各个参数,各神经层需要在该函数中设置

      可以看到在该层中我们首先使用了一个super(ResidualBlock,self).__init__(),以此来调用nn.Module的构造函数,还有一种调用方式是nn.Module.__init__(self)

      在构造函数中我们可以定义可学习参数或者子Module。如果是定义一个可学习参数,必须封装成Parameter,例如nn.Parameter(torch.randn(out._features))Parameter是一种特殊的Variable ,但是是默认是需要求导的。

      另外,在这里我们还使用了nn.Sequential以及其他的一些在nn包里面实现的Module

      • nn.Sequential
        Sequential类似于一个列表,只不过列表里面的内容是一个一个的Module,假设我们定义了一个Sequential,对于这个Sequential传入一个input,Sequential会按内部模型定义的顺序处理这个input,最终返回一个output给我们

      • torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
        实现了一个简单的二维卷积层,参数含义如下:

        • in_channels 输入的通道数
        • out_channels 输出的通道数
        • kernel_size 卷积核的大小
        • stride 卷积核移动的步数
        • padding 图像矩阵每个维度填充0的个数
      • nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

        实现一个二维的批标准化
        用来保证每一个隐藏层的激活输入都具有相同的分布,BN就是通过一定的规范化手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0方差为1的标准正态分布,解决梯度消失的问题
        具体可以参考

        • num_features 定义了特征数 即(N,C,W,H)中的C
    3. forward(self, x)
      实现该模型前向传播的过程,最终返回结果值

    2.3.2 ResNet部分

    class ResNet34(nn.Module):
        """
        实现主module:ResNet34
        ResNet34包含多个layer,每个layer又包含多个residual block
        用子model实现residual block,用__make_layer__函数实现layer
        """
        def __init__(self,num_classes=1000):
            """
            构建ResNet34网络的各层结构
            :param num_classes:
            """
            super(ResNet34,self).__init__()
            self.pre=nn.Sequential(
                nn.Conv2d(3,64,7,2,3,bias=True),
                nn.BatchNorm2d(64),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(3,2,1)
            )
            self.layer1 = self.__make_layer__(64,128,3)
            self.layer2 = self.__make_layer__(128,256,4,stride=2)
            self.layer3 = self.__make_layer__(256,512,6,stride=2)
            self.layer4 = self.__make_layer__(512,512,3,stride=2)
            self.fc = nn.Linear(512,num_classes)
    
        def __make_layer__(self,inchannel,outchannel,block_num,stride=1):
            """
            构建layer,包含多个residual block
            :param inchannel:
            :param outchannel:
            :param block_num:
            :param stride:
            :return:
            """
            shortcut = nn.Sequential(
                nn.Conv2d(inchannel,outchannel,1,stride,bias=False),
                nn.BatchNorm2d(outchannel)
            )
            layers = []
            layers.append(ResidualBlock(inchannel,outchannel,stride,shortcut))
            for i in range(1,block_num):
                layers.append(ResidualBlock(inchannel,outchannel))
            return  nn.Sequential(*layers)
    
        def forward(self, x):
            x = self.pre(x)
    
            x = self.layer1(x)
            x = self.layer2(x)
            x = self.layer3(x)
            x = self.layer4(x)
    
            x = F.avg_pool2d(x,7)
            x = x.view(x.size(0),-1)
            return self.fc(x)
    

    实现了ResNet模型的主要部分,将许多ResidualBlock整合在一起,大部分地方的实现方法和ResidualBlock Module差不多
    这里实现了一个__make_layer__方法用来拼接任意数量的具有相同参数的残差单元,返回layer

  • 相关阅读:
    [03] html 中引入与使用css
    [04] 前端构建工具区别
    [04]测试框架杂谈
    [03] react 测试
    [02] 前端测试工具集锦
    [01]关于TDD、BDD和DDD的一些看法
    [04]JS获取文件大小方法
    [03]使用阿里RAP搭建前端Mock Server
    [1]区分event对象中的[clientX,offsetX,screenX,pageX]
    [02]a tag只为成button用时候设置href的办法
  • 原文地址:https://www.cnblogs.com/DLKKILL/p/10939904.html
Copyright © 2020-2023  润新知