精华内容
下载资源
问答
  • YOLOv5 网络组件与激活函数 代码理解笔记
    千次阅读
    2021-12-19 20:57:05

    前言

    最近在看YOLOv5 第6个版本的代码,记录了一下笔记,分享一下。首先看了网络结构、网络组件,对应代码models\common.py。然后看了激活函数,对应代码utils\activations.py。

    目录

    【1】python 把if 写在一行的两种方式

    【2】Python isinstance() 函数

    【3】python - 理解python嵌套一行for循环

    【4】python 单斜杠/和双斜杆//的区别

    【5】in的详解

    【6】group convolution (分组卷积)

    【7】SILU 激活函数(swish)

    【8】DWConv(Depthwise Conv)深度卷积

    【9】layer-normlization

    【10】Transformer各层网络结构详解

    【11】Bottleneck layer结构 瓶颈层

    【12】BottleneckCSP

    【13】C3

    【14】空间金字塔池化(Spatial Pyramid Pooling, SPP)

    【15】Concat

    【16】深度解读轻量网络GhostNet、Ghost bottlenecks 

    【17】SPPF

    【18】Focus

    【19】混合精度

    【20】消融实验(ablation study)

    【21】GFLOPs、FLOPs、FLOPS

    【22】CUDA NMS、Fast NMS、Cluster NMS、Matrix NMS

    【23】smooth_BCE 损失函数

    【24】Hardswish 激活函数

    【25】Mish 激活函数

    【26】FRelU 激活函数

    【27】AconC 激活函数

    【28】MetaAconC 激活函数


    注意:请大家,主要看参考链接的内容!请主要看参考链接的内容!请主要看参考链接的内容!


    【1】python 把if 写在一行的两种方式

    a=1 if a>0 else 0   如果a>0,a赋值1,否则赋值0。 
    参考:https://www.cnblogs.com/Lara1798/p/12989334.html

    【2】Python isinstance() 函数

    isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。考虑继承关系。
    >>>a = 2
    >>> isinstance (a,int)
    True
    >>> isinstance (a,str)
    False
    >>> isinstance (a,(str,int,list))    # 是元组中的一个返回 True
    True

    class A:
        pass
     
    class B(A):
        pass
    isinstance(A(), A)    # returns True
    isinstance(B(), A)    # returns True
    参考:https://www.runoob.com/python/python-func-isinstance.html

    【3】python - 理解python嵌套一行for循环

    s.split() for s in raw_sentences
    相当于:
    for s in raw_sentences:
        s.split()
    参考:https://www.coder.work/article/3128601


    【4】python 单斜杠/和双斜杆//的区别

    不管是单斜杆还是双斜杆,都是属于除法运算符;
    单斜杠是我们最常见的除法计算符号;
    1、它们最大的区别是返回的结果不一样,单斜杠计算的结果是保留若干小数;而双斜杆的结果是保留最小整数(类似于向下取);
    2、A//B的返回类型取决与A和B的数据类型,只有A和B都为int型时结果才是int(此时表示两数正除取商);
    参考:https://blog.csdn.net/qq_39284106/article/details/108208239?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link


    【5】in的详解

    1.用于判断(查找)元素是否在可迭代对象中;
    xxx in XXX :判断xxx是否在XXX中, 如果在,返回真,不在,返回假。
    xxx not in XXX :判断xxx是否不在XXX中, 如果不在,返回真,在,返回假
    if x in X
    if x not in X
    while x in X
    while x not in X
    2.用于逐个取可迭代对象的元素, 一般要配合for使用:
    我们可能常用到的可迭代的对象包括:string, list, dict, tuple, generator, range函数
    list_1 = [n for n in range(10)]
    for i in list:
        print(i)


    【6】group convolution (分组卷积)

    简介:输入feature map分成组,每个卷积核也相应地分成组,在对应的组内做卷积;用了同等的参数量运算量生成了g个feature map;
    作用:group conv常用在轻量型高效网络中,因为它用少量的参数量和运算量就能生成大量的feature map,大量的feature map意味着能够编码更多的信息!

    参数g:输入参数,分组数量
    《深度分离卷积》是分组卷积的一种特殊形式,其分组数,其中是feature map的通道数。
    即把每个feature map分为一组,分别在组内做卷积,每组内的单个卷积核尺寸为,组内一个卷积核生成一个feature map。
    参考:https://www.jianshu.com/p/a936b7bc54e3


    【7】SILU 激活函数(swish)

    简介:按元素应用 Sigmoid 线性单元 (SiLU) 函数。SiLU 函数也称为 swish 函数。
    公式:silu(x)=x∗σ(x),where σ(x) is the logistic sigmoid.
    Swish处处可导,连续光滑。另外还有一个特点就是Swish并非一个单调的函数。但是swish也并非没有任何缺点,最大的缺点就是计算量大,本来sigmoid函数就不容易计算。
    参考:https://zhuanlan.zhihu.com/p/387167769
    参考:https://pytorch.org/docs/stable/generated/torch.nn.SiLU.html
    参考:https://august-us.blog.csdn.net/article/details/106210576?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.fixedcolumn&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.fixedcolumn

    【8】DWConv(Depthwise Conv)深度卷积

    是分组卷积Group Conv的极端,即分组g =Cin = Cout = 通道数
    参考:https://zhuanlan.zhihu.com/p/45209964
    参考:https://zhuanlan.zhihu.com/p/149564248
    参考:https://zhuanlan.zhihu.com/p/80041030


    【9】layer-normlization

    Normalization 有很多种,但是它们都有一个共同的目的,那就是把输入转化成均值为 0 方差为1的数据。
    我们在把数据送入激活函数之前进行normalization(归一化),因为我们不希望输入数据落在激活函数的饱和区,发生梯度消失的问题,使得我们的模型训练变得困难。
    BN的主要思想是: 一个batch里的同一通道 上进行归一化
    LN的主要思想是:一个样本里的不同通道上计算均值和方差,而不是 BN 那种在批方向计算均值和方差!
    参考:https://blog.csdn.net/weixin_45069761/article/details/107834049


    【10】Transformer各层网络结构详解

    Transformer模型中也采用了 encoer-decoder 架构。但其结构相比于Attention更加复杂,论文中encoder层由6个encoder堆叠在一起,decoder层也一样。
    encoder,包含两层,一个self-attention层和一个前馈神经网络,self-attention能帮助当前节点不仅仅只关注当前的词,从而能获取到上下文的语义。
    decoder也包含encoder提到的两层网络,但是在这两层中间还有一层attention层,帮助当前节点获取到当前需要关注的重点内容。

    参考:https://www.cnblogs.com/mantch/p/11591937.html

    【11】Bottleneck layer结构 瓶颈层

    Bottleneck layer又称之为瓶颈层,使用的是1*1的卷积神经网络。之所以称之为瓶颈层,是因为长得比较像一个瓶颈。
    经过 1*1 的网络,中间那个看起来比较细。像一个瓶颈一样。
    使用 1*1 的网络结构很方便改变维度。灵活设计网络,并且减小计算量。
    参考:https://zhuanlan.zhihu.com/p/98692254
    参考:https://blog.csdn.net/duan19920101/article/details/104349188

    shortcut:是否给bottleneck 结构添加shortcut连接,添加后即为ResNet模块;(shortcut(捷径),跳跃连接,输入直接连接到输出的那条支路)
    这里使用的shortcut也成为identity分支,可以理解为恒等映射,另一个分支被称为残差分支(Residual分支)。
    参考:https://www.cnblogs.com/dan-baishucaizi/p/14267602.html#3bottleneck%E7%93%B6%E9%A2%88%E5%B1%82


    【12】BottleneckCSP

    BottlenneckCSP分为两部分,Bottlenneck以及CSP。Bottlenneck其实就是经典的残差结构,先是1x1的卷积层(conv+batch_norm+leaky relu),然后再是3x3的卷积层,最后通过残差结构与初始输入相加。
    也就是说将原输入分成两个分支,分别进行卷积操作使得通道数减半,然后分支一进行Bottlenneck x N操作,随后concat分支一和分支二,从而使得BottlenneckCSP的输入与输出是一样的大小,目的是为了让模型学习到更多的特征。
    很多人都对yaml文件中[[-1, 3, BottleneckCSP, [1024, False]]False的作用不太理解,其实这就是关闭了shortcut的选项。

    CSP瓶颈层结构在Bottleneck部分存在一个可修改的参数n,标识使用的Bottleneck结构个数!
    左侧(Bottleneck * n)这一条也是我们的主分支,是对残差进行学习的主要结构,
    右侧分支nn.Conv2d实际上是shortcut分支实现不同stage的连接(CSP的思想实现)。
    参考:https://www.cnblogs.com/dan-baishucaizi/p/14267602.html#4bottleneckcsp-csp%E7%93%B6%E9%A2%88%E5%B1%82
    参考:https://zhuanlan.zhihu.com/p/164627427


    【13】C3

    C3模块,其结构作用基本相同均为CSP架构,只是在修正单元的选择上有所不同,其包含了3个标准卷积层以及多个Bottleneck模块(数量由配置文件.yaml的n和depth_multiple参数乘积决定)
    C3相对于BottleneckCSP模块不同的是,经历过残差输出后的Conv模块被去掉了,concat后的标准卷积模块中的激活函数也由LeakyRelu变为了SiLU(同上)。
    该模块是对残差特征进行学习的主要模块,其结构分为两支,一支使用了上述指定多个Bottleneck堆叠和3个标准卷积层,另一支仅经过一个基本卷积模块,最后将两支进行concat操作。
    参考:https://blog.csdn.net/zebra_0/article/details/120769404


    【14】空间金字塔池化(Spatial Pyramid Pooling, SPP)

    卷积神经网络(CNN)由卷积层和全连接层组成,其中卷积层对于输入数据的大小并没有要求,
    唯一对数据大小有要求的则是第一个全连接层,因此基本上所有的CNN都要求输入数据固定大小,
    例如著名的VGG模型则要求输入数据大小是 (224*224) 。

    固定输入数据大小有两个问题:
    1.很多场景所得到数据并不是固定大小的,例如街景文字基本上其高宽比是不固定的(图片输入也不固定)
    2.可能你会说可以对图片进行切割,但是切割的话很可能会丢失到重要信息。
    SPP的提出就是为了解决CNN输入图像大小必须固定的问题,从而可以使得输入图像高宽比和大小任意。

    首先是输入层(input image),其大小可以是任意的
    进行卷积运算,到最后一个卷积层(图中是\(conv_5\))输出得到该层的特征映射(feature maps),其大小也是任意的
    下面进入SPP层 
    我们先看最左边有16个蓝色小格子的图,它的意思是将从\(conv_5\)得到的特征映射分成16份,另外16X256中的256表示的是channel,即SPP对每一层都分成16份(不一定是等比分,原因看后面的内容就能理解了)。
    中间的4个绿色小格子和右边1个紫色大格子也同理,即将特征映射分别分成4X256和1X256份
    那么将特征映射分成若干等分是做什么用的呢? 我们看SPP的名字就是到了,是做池化操作,一般选择MAX Pooling,即对每一份进行最大池化
    注意上面划分成多少份是可以自己是情况设置的。
    参考:https://cloud.tencent.com/developer/article/1076488
    参考:https://blog.csdn.net/weixin_43881803/article/details/106350160?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.no_search_link


    【15】Concat

    沿维度连接张量列表

    【16】深度解读轻量网络GhostNet、Ghost bottlenecks 

    轻量级卷积神经网络设计,解决“特征图冗余”,减少模型参数和计算量,精度保持差不多。
    Ghost Module则分为两步操作来获得与普通卷积一样数量的特征图
    第一步:少量卷积(比如正常用32个卷积核,这里就用16个,从而减少一半的计算量);
    第二步:cheap operations,如图中的Φ表示,Φ是诸如3*3的卷积,并且是逐个特征图的进行卷积(Depth-wise convolutional)。
    参考:https://www.bilibili.com/read/cv5655222/


    【17】SPPF

    原理和SPP基本一致,但用到的“池化核”设计不一样;(数量)
    SPP:  “池化核”k=(5, 9, 13),加上一个1*1的;
    SPPF:“池化核”k=(5),加上一个1*1的;
    操作:MaxPool2d


    【18】Focus

    简介:Focus模块在v5中是图片进入backbone前,对图片进行切片操作;
    操作:在一张图片中每隔一个像素拿到一个值,类似于邻近下采样,这样就拿到了四张图片,四张图片互补,长的差不多,但是没有信息丢失
    效果:x(b,c,w,h) -> y(b,4c,w/2,h/2)
    这样一来,将W、H信息就集中到了通道空间,输入通道扩充了4倍,即拼接起来的图片相对于原先的RGB三通道模式变成了12个通道,
    最后将得到的新图片再经过卷积操作,最终得到了没有信息丢失情况下的二倍下采样特征图。
    参考:https://blog.csdn.net/qq_39056987/article/details/112712817


    【19】混合精度

    简介:混合精度是指训练时在模型中同时使用 16 位和 32 位浮点类型,从而加快运行速度,减少内存使用的一种训练方法。
      .通过让模型的某些部分保持使用 32 位类型以保持数值稳定性,可以缩短模型的单步用时,而在评估指标(如准确率)方面仍可以获得同等的训练效果。.

    效果:内存占用更少,计算更快。( float32和半精度float16 )
     通用的模型 fp16 占用的内存只需原来的一半,模型占用的内存更小,训练的时候可以用更大的batchsize。
     目前的不少GPU都有针对 fp16 的计算进行优化。论文指出:在近期的GPU中,半精度的计算吞吐量可以是单精度的 2-8 倍;

    Float16的问题:
    那既然fp16像上面说的那么好,那么是否全部都使用 fp16 即可了呢? 当然不是,如果fp16那么好,那又何来 『混合精度』这么一说呢。
    数据溢出问题:Overflow / Underflow。深度学习而言,最大的问题在于 Underflow(下溢出),在训练后期,例如激活函数的梯度会非常小, 甚至在梯度乘以学习率后,值会更加小。
    舍入误差(Rounding Error)。
    参考:https://zhuanlan.zhihu.com/p/103685761
    参考:https://zhuanlan.zhihu.com/p/408610877


    【20】消融实验(ablation study)

    设立对照组:通过去除某个模块的作用,来证明该模块的必要性,如果消融实验后得到结果不好或者性能大幅下降,说明该模块起到了作用。
    有一点像控制变量。
    参考:https://blog.csdn.net/DragonGirI/article/details/107356658
    参考:https://blog.csdn.net/Cai_deLong/article/details/110903263?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7Eessearch%7Evector-6.essearch_pc_relevant&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7Eessearch%7Evector-6.essearch_pc_relevant


    【21】GFLOPs、FLOPs、FLOPS

    FLOPS:注意全大写,是floating point operations per second的缩写,意指每秒浮点运算次数,理解为计算速度。是一个衡量硬件性能的指标。
    FLOPs:注意s小写,是floating point operations的缩写(s表复数),意指浮点运算数,理解为计算量。可以用来衡量算法/模型的复杂度。
    1GFlops = 1,000MFlops。

    一个 MFLOPS (megaFLOPS) 等于每秒1百万 (=10^6) 次的浮点运算,
    一个 GFLOPS (gigaFLOPS) 等于每秒10亿 (=10^9) 次的浮点运算,
    一个 TFLOPS (teraFLOPS) 等于每秒1万亿 (=10^12) 次的浮点运算,
    一个 PFLOPS (petaFLOPS) 等于每秒1千万亿 (=10^15) 次的浮点运算。

    参考:https://zhuanlan.zhihu.com/p/137719986
    参考:https://baike.baidu.com/item/Gflops/989595


    【22】CUDA NMS、Fast NMS、Cluster NMS、Matrix NMS

    NMS运算效率的瓶颈在哪?答案自然是IoU计算,及顺序迭代,IOU最大的框 抑制 其他IOU小的框。
    参考:https://zhuanlan.zhihu.com/p/157900024
    参考:https://blog.csdn.net/john_bh/article/details/107364782
    参考:https://githubmemory.com/repo/APeiZou/yolov5


    【23】smooth_BCE 损失函数

    这个函数是一个标签平滑的策略(trick),是一种在 分类/检测 问题中,防止过拟合的方法。
    效果:这实际上是一种正则化策略,减少了真实样本标签的类别在计算损失函数时的权重,最终起到抑制过拟合的效果。
    函数:
    def smooth_BCE(eps=0.1):
        """用在ComputeLoss类中  标签平滑操作  [1, 0]  =>  [0.95, 0.05]
        :params eps: 平滑参数
        :return positive, negative label smoothing BCE targets  两个值分别代表正样本和负样本的标签取值
                原先的正样本=1 负样本=0 改为 正样本=1.0 - 0.5 * eps  负样本=0.5 * eps
        """
        return 1.0 - 0.5 * eps, 0.5 * eps
    返回正、负样本的标签取值
    参考:https://github.com/ultralytics/yolov3/issues/238#issuecomment-598028441
    参考:https://blog.csdn.net/qq_38253797/article/details/119444854
    参考:https://blog.csdn.net/qq_35447659/article/details/107818462?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-1.essearch_pc_relevant&spm=1001.2101.3001.4242.2


    【24】Hardswish 激活函数

    hardswish激活函数。在MobileNetV3架构中被提出,相较于swish函数,具有数值稳定性好,计算速度快等优点,
    参考:https://pytorch.org/docs/stable/generated/torch.nn.Hardswish.html
    参考:https://www.bookstack.cn/read/paddlepaddle-2.0-zh/9550b2db596b3c48.md
    具体原理请参考: https://arxiv.org/pdf/1905.02244.pdf
    class Hardswish(nn.Module):  # export-friendly version of nn.Hardswish()
        @staticmethod
        def forward(x):
            # return x * F.hardsigmoid(x)  # for torchscript and CoreML
            return x * F.hardtanh(x + 3, 0.0, 6.0) / 6.0  # for torchscript, CoreML and ONNX
    hardsigmoid 激活函数
    参考:https://pytorch.org/docs/stable/generated/torch.nn.Hardsigmoid.html
    hardtanh 激活函数
    参考:https://pytorch.org/docs/stable/generated/torch.nn.Hardtanh.html

    【25】Mish 激活函数

    Mish =  x * F.softplus(x).tanh() 或 = x * (torch.tanh(F.softplus(x))) 或 x * tanh(ln(1 + exp(x)))
    Mish是一个光滑非单调的激活函数
    参考:https://blog.csdn.net/moxibingdao/article/details/108289489
    参考:https://zhuanlan.zhihu.com/p/84418420

    F.softplus(x) 参考:https://pytorch.org/docs/stable/generated/torch.nn.Softplus.html
    Softplus(x)= (1/β) ∗log(1+exp(β∗x)) 

    【Swish Mish 激活函数 理解】https://blog.csdn.net/bu_fo/article/details/110224213


    【26】FRelU 激活函数

    FReLU的形式为y = max(x,T(x)),其中T(·)是二维空间条件(2D spatial condition)
    此外,空间条件spatial condition以简单的方式实现了像素级建模能力,并通过常规卷积捕获了复杂的视觉layouts。
    ReLU和PReLU分别表示为y = max(x,0)和y = max(x,px)
    y=max(x,T(x)),其中T(x)代表简单高效的空间上下文特征提取器。

    class FReLU(nn.Module):
        def __init__(self, c1, k=3):  # ch_in, kernel
            super().__init__()
            self.conv = nn.Conv2d(c1, c1, k, 1, 1, groups=c1, bias=False)
            self.bn = nn.BatchNorm2d(c1)

        def forward(self, x):
            return torch.max(x, self.bn(self.conv(x)))
    参考:https://blog.csdn.net/sinat_17456165/article/details/107603052


    【27】AconC 激活函数

    作者提出了一系列的ACON函数,其中 ReLU 是 Maxout 的一种特殊形式,Swish 是 ACON 的一种特殊形式
    ACON 函数本质就是一个具有学习能力的Layer;
        r""" ACON activation (activate or not).
        AconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is a learnable parameter
        according to "Activate or Not: Learning Customized Activation" <https://arxiv.org/pdf/2009.04759.pdf>.
        """
    参考:https://zhuanlan.zhihu.com/p/359633625
    参考:https://zhuanlan.zhihu.com/p/363274457

    【28】MetaAconC 激活函数

    r""" ACON activation (activate or not).
        MetaAconC: (p1*x-p2*x) * sigmoid(beta*(p1*x-p2*x)) + p2*x, beta is generated by a small network
        according to "Activate or Not: Learning Customized Activation" <https://arxiv.org/pdf/2009.04759.pdf>.
        """
    参考:https://zhuanlan.zhihu.com/p/359633625
    参考:https://aistudio.baidu.com/aistudio/projectdetail/1871546?channelType=0&channel=0


    本文只供大家参考与学习,谢谢。

    更多相关内容
  • 判断数据类型isinstance()(保姆级图文+例程)


    欢迎关注 『Python』 系列,持续更新中
    欢迎关注 『Python』 系列,持续更新中

    API说明:

    isinstance(type_data,type)

    • 参数1:传入要被判断类型的数据
    • 参数2:传入预期的数据类型
    • 返回值说明:以历程的isinstance(int_data,int)为例子
      • true,表示int_data是int类型
      • false,表示int_data不是int类型

    注意事项

    1. 打印输出是要把isinstance的返回值转为str才能字符串输出,但是你要知道他的返回值原本确实是一个Boolean类型。

    基本例程

    int_data=1234#定义一个int
    print(type(int_data))
    if isinstance(int_data,int):
        print(str(int_data)+"是int:"+str(isinstance(int_data,int)))#isinstance的返回值本应该是bolean类型,这里为了打印输出强制转化成了str!
    

    拓展例程

    # @Time    : 2021/12/9 18:16
    # @Author  : 南黎
    # @FileName: test.py
    
    int_data=1234#定义一个int
    print(type(int_data))
    if isinstance(int_data,int):
        print(str(int_data)+"是int:"+str(isinstance(int_data,int)))#isinstance的返回值本应该是bolean类型,这里为了打印输出强制转化成了str!
    
    str_data= 'abcd'#定义一个字符串
    print(type(str_data))
    if isinstance(str_data,str):
        print(str(str_data)+"是str:"+str(isinstance(str_data,str)))#isinstance的返回值本应该是bolean类型,这里为了打印输出强制转化成了str!
    
    tuple_data=(1,2,3,4)#定义一个列表
    print(type(tuple_data))
    if isinstance(tuple_data,tuple):
        print(str(tuple_data)+"是tuple:"+str(isinstance(tuple_data,tuple)))#isinstance的返回值本应该是bolean类型,这里为了打印输出强制转化成了tuple!
    
    list_data=[1,2,3,4]#定义一个列表
    print(type(list_data))
    if isinstance(list_data,list):
        print(str(list_data)+"是list:"+str(isinstance(list_data,list)))#isinstance的返回值本应该是bolean类型,这里为了打印输出强制转化成了list!
    
    dict_data={'a': 1, 'b': 2, 'b': '3'}#定义一个字典
    print(type(dict_data))
    if isinstance(dict_data,dict):
        print(str(dict_data)+"是dict:"+str(isinstance(dict_data,dict)))#isinstance的返回值本应该是bolean类型,这里为了打印输出强制转化成了dict!
    

    在这里插入图片描述


    总结

    大家喜欢的话,给个👍,点个关注!继续跟大家分享敲代码过程中遇到的问题!

    版权声明:

    发现你走远了@mzh原创作品,转载必须标注原文链接

    Copyright 2021 mzh

    Crated:2021-12-10

    欢迎关注 『Python』 系列,持续更新中
    欢迎关注 『Python』 系列,持续更新中
    【Python安装第三方库一行命令永久提高速度】
    【使用PyInstaller打包Python文件】
    【更多内容敬请期待】


    展开全文
  • 代码分析】Pytorch版YOLO V4代码分析

    万次阅读 多人点赞 2020-05-08 15:54:34
    其中demo.py中,主要调用了函数detect(),其代码如下: def detect(cfgfile, weightfile, imgfile): m = Darknet(cfgfile) #穿件Darknet模型对象m m.print_network() # 打印网络结构 m.load_weights(weightfile) #...

    YOLO V4出来也几天了,论文大致看了下,然后看到大量的优秀者实现了各个版本的YOLOV4了。

    Yolo v4 论文: https://arxiv.org/abs/2004.10934

    AB大神Darknet版本的源码实现: https://github.com/AlexeyAB/darknet

    本文针对Pytorch版本实现的YOLOV4进行分析,感谢Tianxiaomo 分享的工程:Pytorch-YoloV4


    作者分享的权重文件,下载地址:

    该权重文件yolov4.weights 是在coco数据集上训练的,目标类有80种,当前工程支持推理,不包括训练~

    我的测试环境是anaconda配置的环境,pytorch1.0.1, torchvision 0.2.1;


    工程目录如下:

    终端运行指令:

    # 指令需要传入cfg文件路径,权重文件路径,图像路径
    >>python demo.py cfg/yolov4.cfg yolov4.weights data/dog.jpg

    运行结果会生成一张检测后的图:predictions.jpg

    接下来对源码做分析:

    其中demo.py中,主要调用了函数detect(),其代码如下:

    def detect(cfgfile, weightfile, imgfile):
        m = Darknet(cfgfile)  #穿件Darknet模型对象m
    
        m.print_network()    # 打印网络结构
        m.load_weights(weightfile)  #加载权重值
        print('Loading weights from %s... Done!' % (weightfile))
    
        num_classes = 80
        if num_classes == 20:
            namesfile = 'data/voc.names'
        elif num_classes == 80:
            namesfile = 'data/coco.names'
        else:
            namesfile = 'data/names'
    
        use_cuda = 0  # 是否使用cuda,工程使用的是cpu执行
        if use_cuda:
            m.cuda()   # 如果使用cuda则将模型对象拷贝至显存,默认GUP ID为0;
    
        img = Image.open(imgfile).convert('RGB') # PIL打开图像
        sized = img.resize((m.width, m.height))
    
        for i in range(2):
            start = time.time()
            boxes = do_detect(m, sized, 0.5, 0.4, use_cuda)  # 做检测,返回的boxes是昨晚nms后的检测框;
            finish = time.time()
            if i == 1:
                print('%s: Predicted in %f seconds.' % (imgfile, (finish - start)))
    
        class_names = load_class_names(namesfile)   # 加载类别名
        plot_boxes(img, boxes, 'predictions.jpg', class_names)# 画框,并输出检测结果图像文件;

    在创建Darknet()对象过程中,会根据传入的cfg文件做初始化工作,主要是cfg文件的解析,提取cfg中的每个block;网络结构的构建;(如下图)


     现在先说下根据cfg文件是如何解析网络结果吧,主要调用了tool/cfg.py的parse_cfg()函数,它会返回blocks,网络结果是长这个样子的(使用Netron网络查看工具 打开cfg文件,完整版请自行尝试):


    创建网络模型是调用了darknet2pytorch.py中的create_network()函数,它会根据解析cfg得到的blocks构建网络,先创建个ModuleList模型列表,为每个block创建个Sequential(),将每个block中的卷积操作,BN操作,激活操作都放到这个Sequential()中;可以理解为每个block对应一个Sequential();

    构建好的的ModuleList模型列表大致结构如下:

    Darknet(
      (models): ModuleList(
        (0): Sequential(
          (conv1): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish1): Mish()
        )
        (1): Sequential(
          (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish2): Mish()
        )
        (2): Sequential(
          (conv3): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish3): Mish()
        )
        (3): EmptyModule()
        (4): Sequential(
          (conv4): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish4): Mish()
        )
        (5): Sequential(
          (conv5): Conv2d(64, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn5): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish5): Mish()
        )
        (6): Sequential(
          (conv6): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn6): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish6): Mish()
        )
        (7): EmptyModule()
        (8): Sequential(
          (conv7): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn7): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish7): Mish()
        )
        (9): EmptyModule()
        (10): Sequential(
          (conv8): Conv2d(128, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn8): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish8): Mish()
        )
        (11): Sequential(
          (conv9): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn9): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish9): Mish()
        )
        (12): Sequential(
          (conv10): Conv2d(128, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn10): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish10): Mish()
        )
        (13): EmptyModule()
        (14): Sequential(
          (conv11): Conv2d(128, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn11): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish11): Mish()
        )
        (15): Sequential(
          (conv12): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn12): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish12): Mish()
        )
        (16): Sequential(
          (conv13): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn13): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish13): Mish()
        )
        (17): EmptyModule()
        (18): Sequential(
          (conv14): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn14): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish14): Mish()
        )
        (19): Sequential(
          (conv15): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn15): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish15): Mish()
        )
        (20): EmptyModule()
        (21): Sequential(
          (conv16): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn16): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish16): Mish()
        )
        (22): EmptyModule()
        (23): Sequential(
          (conv17): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn17): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish17): Mish()
        )
        (24): Sequential(
          (conv18): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn18): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish18): Mish()
        )
        (25): Sequential(
          (conv19): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn19): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish19): Mish()
        )
        (26): EmptyModule()
        (27): Sequential(
          (conv20): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn20): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish20): Mish()
        )
        (28): Sequential(
          (conv21): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn21): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish21): Mish()
        )
        (29): Sequential(
          (conv22): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn22): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish22): Mish()
        )
        (30): EmptyModule()
        (31): Sequential(
          (conv23): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn23): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish23): Mish()
        )
        (32): Sequential(
          (conv24): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn24): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish24): Mish()
        )
        (33): EmptyModule()
        (34): Sequential(
          (conv25): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn25): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish25): Mish()
        )
        (35): Sequential(
          (conv26): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn26): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish26): Mish()
        )
        (36): EmptyModule()
        (37): Sequential(
          (conv27): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn27): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish27): Mish()
        )
        (38): Sequential(
          (conv28): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn28): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish28): Mish()
        )
        (39): EmptyModule()
        (40): Sequential(
          (conv29): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn29): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish29): Mish()
        )
        (41): Sequential(
          (conv30): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn30): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish30): Mish()
        )
        (42): EmptyModule()
        (43): Sequential(
          (conv31): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn31): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish31): Mish()
        )
        (44): Sequential(
          (conv32): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn32): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish32): Mish()
        )
        (45): EmptyModule()
        (46): Sequential(
          (conv33): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn33): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish33): Mish()
        )
        (47): Sequential(
          (conv34): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn34): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish34): Mish()
        )
        (48): EmptyModule()
        (49): Sequential(
          (conv35): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn35): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish35): Mish()
        )
        (50): Sequential(
          (conv36): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn36): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish36): Mish()
        )
        (51): EmptyModule()
        (52): Sequential(
          (conv37): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn37): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish37): Mish()
        )
        (53): EmptyModule()
        (54): Sequential(
          (conv38): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn38): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish38): Mish()
        )
        (55): Sequential(
          (conv39): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn39): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish39): Mish()
        )
        (56): Sequential(
          (conv40): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn40): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish40): Mish()
        )
        (57): EmptyModule()
        (58): Sequential(
          (conv41): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn41): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish41): Mish()
        )
        (59): Sequential(
          (conv42): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn42): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish42): Mish()
        )
        (60): Sequential(
          (conv43): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn43): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish43): Mish()
        )
        (61): EmptyModule()
        (62): Sequential(
          (conv44): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn44): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish44): Mish()
        )
        (63): Sequential(
          (conv45): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn45): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish45): Mish()
        )
        (64): EmptyModule()
        (65): Sequential(
          (conv46): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn46): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish46): Mish()
        )
        (66): Sequential(
          (conv47): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn47): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish47): Mish()
        )
        (67): EmptyModule()
        (68): Sequential(
          (conv48): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn48): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish48): Mish()
        )
        (69): Sequential(
          (conv49): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn49): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish49): Mish()
        )
        (70): EmptyModule()
        (71): Sequential(
          (conv50): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn50): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish50): Mish()
        )
        (72): Sequential(
          (conv51): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn51): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish51): Mish()
        )
        (73): EmptyModule()
        (74): Sequential(
          (conv52): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn52): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish52): Mish()
        )
        (75): Sequential(
          (conv53): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn53): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish53): Mish()
        )
        (76): EmptyModule()
        (77): Sequential(
          (conv54): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn54): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish54): Mish()
        )
        (78): Sequential(
          (conv55): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn55): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish55): Mish()
        )
        (79): EmptyModule()
        (80): Sequential(
          (conv56): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn56): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish56): Mish()
        )
        (81): Sequential(
          (conv57): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn57): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish57): Mish()
        )
        (82): EmptyModule()
        (83): Sequential(
          (conv58): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn58): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish58): Mish()
        )
        (84): EmptyModule()
        (85): Sequential(
          (conv59): Conv2d(512, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn59): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish59): Mish()
        )
        (86): Sequential(
          (conv60): Conv2d(512, 1024, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn60): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish60): Mish()
        )
        (87): Sequential(
          (conv61): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn61): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish61): Mish()
        )
        (88): EmptyModule()
        (89): Sequential(
          (conv62): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn62): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish62): Mish()
        )
        (90): Sequential(
          (conv63): Conv2d(512, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn63): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish63): Mish()
        )
        (91): Sequential(
          (conv64): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn64): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish64): Mish()
        )
        (92): EmptyModule()
        (93): Sequential(
          (conv65): Conv2d(512, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn65): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish65): Mish()
        )
        (94): Sequential(
          (conv66): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn66): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish66): Mish()
        )
        (95): EmptyModule()
        (96): Sequential(
          (conv67): Conv2d(512, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn67): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish67): Mish()
        )
        (97): Sequential(
          (conv68): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn68): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish68): Mish()
        )
        (98): EmptyModule()
        (99): Sequential(
          (conv69): Conv2d(512, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn69): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish69): Mish()
        )
        (100): Sequential(
          (conv70): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn70): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish70): Mish()
        )
        (101): EmptyModule()
        (102): Sequential(
          (conv71): Conv2d(512, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn71): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish71): Mish()
        )
        (103): EmptyModule()
        (104): Sequential(
          (conv72): Conv2d(1024, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn72): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (mish72): Mish()
        )
        (105): Sequential(
          (conv73): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn73): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky73): LeakyReLU(negative_slope=0.1, inplace)
        )
        (106): Sequential(
          (conv74): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn74): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky74): LeakyReLU(negative_slope=0.1, inplace)
        )
        (107): Sequential(
          (conv75): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn75): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky75): LeakyReLU(negative_slope=0.1, inplace)
        )
        (108): MaxPoolStride1()
        (109): EmptyModule()
        (110): MaxPoolStride1()
        (111): EmptyModule()
        (112): MaxPoolStride1()
        (113): EmptyModule()
        (114): Sequential(
          (conv76): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn76): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky76): LeakyReLU(negative_slope=0.1, inplace)
        )
        (115): Sequential(
          (conv77): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn77): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky77): LeakyReLU(negative_slope=0.1, inplace)
        )
        (116): Sequential(
          (conv78): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn78): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky78): LeakyReLU(negative_slope=0.1, inplace)
        )
        (117): Sequential(
          (conv79): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn79): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky79): LeakyReLU(negative_slope=0.1, inplace)
        )
        (118): Upsample()
        (119): EmptyModule()
        (120): Sequential(
          (conv80): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn80): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky80): LeakyReLU(negative_slope=0.1, inplace)
        )
        (121): EmptyModule()
        (122): Sequential(
          (conv81): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn81): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky81): LeakyReLU(negative_slope=0.1, inplace)
        )
        (123): Sequential(
          (conv82): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn82): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky82): LeakyReLU(negative_slope=0.1, inplace)
        )
        (124): Sequential(
          (conv83): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn83): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky83): LeakyReLU(negative_slope=0.1, inplace)
        )
        (125): Sequential(
          (conv84): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn84): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky84): LeakyReLU(negative_slope=0.1, inplace)
        )
        (126): Sequential(
          (conv85): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn85): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky85): LeakyReLU(negative_slope=0.1, inplace)
        )
        (127): Sequential(
          (conv86): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn86): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky86): LeakyReLU(negative_slope=0.1, inplace)
        )
        (128): Upsample()
        (129): EmptyModule()
        (130): Sequential(
          (conv87): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn87): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky87): LeakyReLU(negative_slope=0.1, inplace)
        )
        (131): EmptyModule()
        (132): Sequential(
          (conv88): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn88): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky88): LeakyReLU(negative_slope=0.1, inplace)
        )
        (133): Sequential(
          (conv89): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn89): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky89): LeakyReLU(negative_slope=0.1, inplace)
        )
        (134): Sequential(
          (conv90): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn90): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky90): LeakyReLU(negative_slope=0.1, inplace)
        )
        (135): Sequential(
          (conv91): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn91): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky91): LeakyReLU(negative_slope=0.1, inplace)
        )
        (136): Sequential(
          (conv92): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn92): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky92): LeakyReLU(negative_slope=0.1, inplace)
        )
        (137): Sequential(
          (conv93): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn93): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky93): LeakyReLU(negative_slope=0.1, inplace)
        )
        (138): Sequential(
          (conv94): Conv2d(256, 255, kernel_size=(1, 1), stride=(1, 1))
        )
        (139): YoloLayer()
        (140): EmptyModule()
        (141): Sequential(
          (conv95): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn95): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky95): LeakyReLU(negative_slope=0.1, inplace)
        )
        (142): EmptyModule()
        (143): Sequential(
          (conv96): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn96): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky96): LeakyReLU(negative_slope=0.1, inplace)
        )
        (144): Sequential(
          (conv97): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn97): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky97): LeakyReLU(negative_slope=0.1, inplace)
        )
        (145): Sequential(
          (conv98): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn98): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky98): LeakyReLU(negative_slope=0.1, inplace)
        )
        (146): Sequential(
          (conv99): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn99): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky99): LeakyReLU(negative_slope=0.1, inplace)
        )
        (147): Sequential(
          (conv100): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn100): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky100): LeakyReLU(negative_slope=0.1, inplace)
        )
        (148): Sequential(
          (conv101): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn101): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky101): LeakyReLU(negative_slope=0.1, inplace)
        )
        (149): Sequential(
          (conv102): Conv2d(512, 255, kernel_size=(1, 1), stride=(1, 1))
        )
        (150): YoloLayer()
        (151): EmptyModule()
        (152): Sequential(
          (conv103): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn103): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky103): LeakyReLU(negative_slope=0.1, inplace)
        )
        (153): EmptyModule()
        (154): Sequential(
          (conv104): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn104): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky104): LeakyReLU(negative_slope=0.1, inplace)
        )
        (155): Sequential(
          (conv105): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn105): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky105): LeakyReLU(negative_slope=0.1, inplace)
        )
        (156): Sequential(
          (conv106): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn106): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky106): LeakyReLU(negative_slope=0.1, inplace)
        )
        (157): Sequential(
          (conv107): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn107): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky107): LeakyReLU(negative_slope=0.1, inplace)
        )
        (158): Sequential(
          (conv108): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn108): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky108): LeakyReLU(negative_slope=0.1, inplace)
        )
        (159): Sequential(
          (conv109): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn109): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (leaky109): LeakyReLU(negative_slope=0.1, inplace)
        )
        (160): Sequential(
          (conv110): Conv2d(1024, 255, kernel_size=(1, 1), stride=(1, 1))
        )
        (161): YoloLayer()
      )
    )

    返回demo.py 的detect()函数,构件好Darknet对象后,打印网络结构图,然后调用darknet2pytorch.py中的load_weights()加载权重文件,这里介绍下这个权重文件中的数值分别是什么以及怎么排序的。

    对于没有bias的模型数据,从yolov4.weights加载的模型数据,其数值排列顺序为先是BNbiasgamma),然后是BNweightalpha)值,然后是BNmean,然后是BNvar, 最后是卷积操作的权重值,如下图,buf是加载后的yolov4.weights数据内容;网络第一个卷积核个数为32个,其对应的BN2操作的bias也有32个,而卷积核参数为3x3x3x32 =864 (含义分别是输入通道是3,因为图像是三通道的,3x3的卷积核大小,然后输出核个数是32)

     

     而如下几个block类型在训练过程中是不会生成权重值的,所以不用从yolov4.weights中取值;

     elif block['type'] == 'maxpool':
                    pass
                elif block['type'] == 'reorg':
                    pass
                elif block['type'] == 'upsample':
                    pass
                elif block['type'] == 'route':
                    pass
                elif block['type'] == 'shortcut':
                    pass
                elif block['type'] == 'region':
                    pass
                elif block['type'] == 'yolo':
                    pass
                elif block['type'] == 'avgpool':
                    pass
                elif block['type'] == 'softmax':
                    pass
                elif block['type'] == 'cost':
                    pass
    

    完成cfg文件的解析,模型的创建与权重文件的加载之后,现在要做的就是执行检测操作了,主要调用了utils/utils.py中的do_detect()函数,在demo.py中就是这行代码:boxes = do_detect(m, sized, 0.5, 0.4, use_cuda)

    def do_detect(model, img, conf_thresh, nms_thresh, use_cuda=1):
        model.eval()  #模型做推理
        t0 = time.time()
    
        if isinstance(img, Image.Image):
            width = img.width
            height = img.height
            img = torch.ByteTensor(torch.ByteStorage.from_buffer(img.tobytes()))
            img = img.view(height, width, 3).transpose(0, 1).transpose(0, 2).contiguous() # CxHxW
            img = img.view(1, 3, height, width)  # 对图像维度做变换,BxCxHxW
            img = img.float().div(255.0)         # [0-255] --> [0-1]
        elif type(img) == np.ndarray and len(img.shape) == 3:  # cv2 image
            img = torch.from_numpy(img.transpose(2, 0, 1)).float().div(255.0).unsqueeze(0)
        elif type(img) == np.ndarray and len(img.shape) == 4:
            img = torch.from_numpy(img.transpose(0, 3, 1, 2)).float().div(255.0)
        else:
            print("unknow image type")
            exit(-1)
    
        if use_cuda:
            img = img.cuda()
        img = torch.autograd.Variable(img)
    
    
        list_boxes = model(img)  # 主要是调用了模型的forward操作,返回三个yolo层的输出
    
        anchors = [12, 16, 19, 36, 40, 28, 36, 75, 76, 55, 72, 146, 142, 110, 192, 243, 459, 401]
        num_anchors = 9  # 3个yolo层共9种锚点
        anchor_masks = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
        strides = [8, 16, 32]   # 每个yolo层相对输入图像尺寸的减少倍数分别为8,16,32
        anchor_step = len(anchors) // num_anchors
        boxes = []
        for i in range(3):
            masked_anchors = []
            for m in anchor_masks[i]:
                masked_anchors += anchors[m * anchor_step:(m + 1) * anchor_step]
            masked_anchors = [anchor / strides[i] for anchor in masked_anchors]
            boxes.append(get_region_boxes1(list_boxes[i].data.numpy(), 0.6, 80, masked_anchors, len(anchor_masks[i])))
            # boxes.append(get_region_boxes(list_boxes[i], 0.6, 80, masked_anchors, len(anchor_masks[i])))
        if img.shape[0] > 1:
            bboxs_for_imgs = [
                boxes[0][index] + boxes[1][index] + boxes[2][index]
                for index in range(img.shape[0])]
            # 分别对每一张图像做nms
            boxes = [nms(bboxs, nms_thresh) for bboxs in bboxs_for_imgs]
        else:
            boxes = boxes[0][0] + boxes[1][0] + boxes[2][0]
            boxes = nms(boxes, nms_thresh)
    
        return boxes   # 返回nms后的boxes
    

    模型forward后输出结果存在list_boxes中,因为有3yolo输出层,所以这个列表list_boxes中又分为3个子列表;

    其中list_boxes[0]中存放的是第一个yolo层输出,其特征图大小对于原图缩放尺寸为8,即strides[0], 对于608x608图像来说,该层的featuremap尺寸为608/8=76;则该层的yolo输出数据维度为[batch, (classnum+4+1)*num_anchors, feature_h, feature_w] , 对于80类的coco来说,测试图像为1,每个yolo层每个特征图像点有3个锚点,该yolo层输出是[1,255,76,76];对应锚点大小为[1.5,2.0,2.375,4.5,5.0,3.5]; (6个数分别是3个锚点的wh,按照w1,h1,w2,h2,w3,h3排列);

    同理第二个yolo层检测结果维度为[1,255,38,38],对应锚点大小为:[2.25,4.6875,4.75,3.4375,4.5,9.125],输出为 [1,255,38,38]

    第三yolo层检测维度为[1,255,19,19],对应锚点大小为:[4.4375,3.4375,6.0,7.59375,14.34375,12.53125]输出为 [1,255,19,19];


    do_detect()函数中主要是调用了get_region_boxes1(output, conf_thresh, num_classes, anchors, num_anchors, only_objectness=1, validation=False) 这个函数对forward后的output做解析并做nms操作;

    每个yolo层输出数据分析,对于第一个yolo层,输出维度为[1,85*3,76,76 ]; 会将其reshape[85, 1*3*76*76],即有1*3*76*76个锚点在预测,每个锚点预测信息有80个类别的概率和4个位置信息和1个是否包含目标的置信度;下图是第一个yolo输出层的数据(实际绘制网格数量不正确,此处只是做说明用

     每个输出的对应代码实现为:

    继续结合上面的图,分析对于某一个yolo层输出的数据是怎么排列的,其示意图如下:

     

     如果置信度满足阈值要求,则将预测的box保存到列表(其中id是所有output的索引,其值在0~batch*anchor_num*h*w范围内)

     

    if conf > conf_thresh:
       bcx = xs[ind]
       bcy = ys[ind]
       bw = ws[ind]
       bh = hs[ind]
       cls_max_conf = cls_max_confs[ind]
       cls_max_id = cls_max_ids[ind]
       box = [bcx / w, bcy / h, bw / w, bh / h, det_conf, cls_max_conf, cls_max_id]
    

    对于3个yolo层先是简单的对每个yolo层输出中是否含有目标做了过滤(含有目标的概率大于阈值);然后就是对三个过滤后的框合并到一个list中作NMS操作了;涉及的代码如下:

    def nms(boxes, nms_thresh):
        if len(boxes) == 0:
            return boxes
    
        det_confs = torch.zeros(len(boxes))
        for i in range(len(boxes)):
            det_confs[i] = 1 - boxes[i][4]
    
        _, sortIds = torch.sort(det_confs)  # sort是按照从小到大排序,那么sortlds中是按照有目标的概率由大到小排序
        out_boxes = []
        for i in range(len(boxes)):
            box_i = boxes[sortIds[i]]
            if box_i[4] > 0:
                out_boxes.append(box_i)   # 取出有目标的概率最大的box放入out_boxes中;
                for j in range(i + 1, len(boxes)):  #然后将剩下的box_j都和这个box_i进行IOU计算,若与box_i重叠率大于阈值,则将box_j的包含目标概率值置为0(即不选它)
                    box_j = boxes[sortIds[j]]
                    if bbox_iou(box_i, box_j, x1y1x2y2=False) > nms_thresh:
                        # print(box_i, box_j, bbox_iou(box_i, box_j, x1y1x2y2=False))
                        box_j[4] = 0
        return out_boxes
    

    补充:

    论文中提到的mish激活函数

    公式是这样的(其中x是输入)

    对应的图是:

     

    ##Pytorch中的代码实现为:
    class Mish(torch.nn.Module):
        def __init__(self):
            super().__init__()
    
        def forward(self, x):
            x = x * (torch.tanh(torch.nn.functional.softplus(x)))
            return x
    
    #--------------------------------------------------------------#
    Tensorflow的代码实现为:
    import tensorflow as tf
    from tensorflow.keras.layers import Activation
    from tensorflow.keras.utils import get_custom_objects
    class Mish(Activation):    
        def __init__(self, activation, **kwargs):        
            super(Mish, self).__init__(activation, **kwargs)        
            self.__name__ = 'Mish'
    def mish(inputs):
        return inputs * tf.math.tanh(tf.math.softplus(inputs))
    get_custom_objects().update({'Mish': Mish(mish)})
    
    #使用方法
    x = Activation('Mish')(x)
    

    文中提到的SPP结构大致是:

     

    Pytorch指定运行的GPUID号的方法,https://www.cnblogs.com/jfdwd/p/11434332.html 

     

     

     

     

    展开全文
  • 解析DeepLabv3+的网络结构及代码【Pytorch版】

    千次阅读 多人点赞 2021-01-16 15:15:30
    主要结合DeepLabv3+的网络结构和开源代码进行了对照解析。通过对网络结构中的每一块的作用及其代码实现中的细节进行了解,就能够对该网络模型有一个宏观和微观上的把握。

    论文地址:https://arxiv.org/pdf/1802.02611.pdf

    论文笔记:https://blog.csdn.net/oYeZhou/article/details/112231858

    参考代码:https://github.com/yassouali/pytorch_segmentation/blob/master/models/deeplabv3_plus.py

    目录

     

    0、写在前面

    1、网络结构

    1.1、Encoder

    1.2、Decoder

     2、代码解析

    2.1、class DeepLab

    2.2、backbone部分

    2.2.1、ResNet作为backbone

    2.2.2、Xception作为backbone

    2.3、class ASPP

    2.4、class Decoder

    3、总结


    0、写在前面

    理解一个网络模型的最好方式,是结合其实现代码对照理解;理解一个网络的开源代码的最好方式,是结合网络结构图来一一对应着看。

    DeepLabV3+的论文已经阅读完毕,阅读笔记可以参考这篇博客,现在开始从开源的代码上来详细了解网络实现的细节,主要参考了这个库中的实现。

    1、网络结构

    DeepLabV3+的网络结构如下图所示,主要为Encoder-Decoder结构。其中,Encoder为改进的DeepLabV3,Decoder为3+版本新提出的。

    图1.  DeepLabV3+网络结构图
    图1.  DeepLabV3+网络结构图

    1.1、Encoder

    在Encoder部分,主要包括了backbone(即:图1中的DCNN)、ASPP两大部分。

    • 其中backbone有两种网络结构:将layer4改为空洞卷积的Resnet系列、改进的Xception。从backbone出来的feature map分两部分:一部分是最后一层卷积输出的feature maps,另一部分是中间的低级特征的feature maps;backbone输出的第一部分送入ASPP模块,第二部分则送入Decoder模块。
    • ASPP模块接受backbone的第一部分输出作为输入,使用了四种不同膨胀率的空洞卷积块(包括卷积、BN、激活层)和一个全局平均池化块(包括池化、卷积、BN、激活层)得到一共五组feature maps,将其concat起来之后,经过一个1*1卷积块(包括卷积、BN、激活、dropout层),最后送入Decoder模块。

    1.2、Decoder

    在Decoder部分,接收来自backbone中间层的低级feature maps和来自ASPP模块的输出作为输入。

    • 首先,对低级feature maps使用1*1卷积进行通道降维,从256降到48(之所以需要降采样到48,是因为太多的通道会掩盖ASPP输出的feature maps的重要性,且实验验证48最佳);
    • 然后,对来自ASPP的feature maps进行插值上采样,得到与低级featuremaps尺寸相同的feature maps;
    • 接着,将通道降维的低级feature maps和线性插值上采样得到的feature maps使用concat拼接起来,并送入一组3*3卷积块进行处理;
    • 最后,再次进行线性插值上采样,得到与原图分辨率大小一样的预测图。

    整个的网络结构即为上述几个部分,下面结合Pytorch实现的代码进行理解。

     2、代码解析

    2.1、class DeepLab

    该class为总的网络结构,我们从forward函数可以看出其整体的流程:输入x经过backbone得到16倍下采样的feature map1和低级feature map2;feature map1送入ASPP模块,得到结果,然后和feature map2一起送入Decoder模块;最后经过插值得到与原图大小相等的预测图。代码如下:

    '''
    -> Deeplab V3 +
    '''
    
    class DeepLab(BaseModel):
        def __init__(self, num_classes, in_channels=3, backbone='xception', pretrained=True, 
                    output_stride=16, freeze_bn=False, **_):
                    
            super(DeepLab, self).__init__()
            assert ('xception' or 'resnet' in backbone)
            if 'resnet' in backbone:
                self.backbone = ResNet(in_channels=in_channels, output_stride=output_stride, pretrained=pretrained)
                low_level_channels = 256
            else:
                self.backbone = Xception(output_stride=output_stride, pretrained=pretrained)
                low_level_channels = 128
    
            self.ASSP = ASSP(in_channels=2048, output_stride=output_stride)
            self.decoder = Decoder(low_level_channels, num_classes)
    
            if freeze_bn: self.freeze_bn()
    
        def forward(self, x):
            H, W = x.size(2), x.size(3)
            x, low_level_features = self.backbone(x)
            x = self.ASSP(x)
            x = self.decoder(x, low_level_features)
            x = F.interpolate(x, size=(H, W), mode='bilinear', align_corners=True)
            return x
    
        # Two functions to yield the parameters of the backbone
        # & Decoder / ASSP to use differentiable learning rates
        # FIXME: in xception, we use the parameters from xception and not aligned xception
        # better to have higher lr for this backbone
    
        def get_backbone_params(self):
            return self.backbone.parameters()
    
        def get_decoder_params(self):
            return chain(self.ASSP.parameters(), self.decoder.parameters())
    
        def freeze_bn(self):
            for module in self.modules():
                if isinstance(module, nn.BatchNorm2d): module.eval()

     需要注意的是:如果使用ResNet系列作为backbone,中间的低级feature map输出维度为256,如果使用Xception作为backbone,中间的低级feature map维度为128。不过,不管是256还是128,最终都要在送入Decoder后降采样到48通道。

    2.2、backbone部分

    上文提到,backbone分ResNet系列和Xception两种。

    2.2.1、ResNet作为backbone

    对于ResNet系列,一共有layer0~4,共五个layer。其中,前三个layers,也即layer0~layer2不变,仅针对layer3、layer4进行了改进,将普通卷积改为了空洞卷积。如果输出步幅(输入尺寸与输出feature map尺寸之比)为8,需要改动layer3和layer4;如果输出步幅为16,则仅改动layer4:

            if output_stride == 16: s3, s4, d3, d4 = (2, 1, 1, 2)
            elif output_stride == 8: s3, s4, d3, d4 = (1, 1, 2, 4)
            
            if output_stride == 8: 
                for n, m in self.layer3.named_modules():
                    if 'conv1' in n and (backbone == 'resnet34' or backbone == 'resnet18'):
                        m.dilation, m.padding, m.stride = (d3,d3), (d3,d3), (s3,s3)
                    elif 'conv2' in n:
                        m.dilation, m.padding, m.stride = (d3,d3), (d3,d3), (s3,s3)
                    elif 'downsample.0' in n:
                        m.stride = (s3, s3)
    
            for n, m in self.layer4.named_modules():
                if 'conv1' in n and (backbone == 'resnet34' or backbone == 'resnet18'):
                    m.dilation, m.padding, m.stride = (d4,d4), (d4,d4), (s4,s4)
                elif 'conv2' in n:
                    m.dilation, m.padding, m.stride = (d4,d4), (d4,d4), (s4,s4)
                elif 'downsample.0' in n:
                    m.stride = (s4, s4)

    此外,中间的低级feature maps在ResNet系列中,是layer1的输出。

    2.2.2、Xception作为backbone

    如果以Xception作为backbone,则需要对Xception的中间流(Middle Flow)和出口流(Exit flow)进行改动:去掉原有的池化层,并将原有的卷积层替换为带有步长的可分离卷积,但是入口流(Entry Flow)不变:

            # Stride for block 3 (entry flow), and the dilation rates for middle flow and exit flow
            if output_stride == 16: b3_s, mf_d, ef_d = 2, 1, (1, 2)
            if output_stride == 8: b3_s, mf_d, ef_d = 1, 2, (2, 4)
            
            # Entry Flow
            self.conv1 = nn.Conv2d(in_channels, 32, 3, 2, padding=1, bias=False)
            self.bn1 = nn.BatchNorm2d(32)
            self.relu = nn.ReLU(inplace=True)
            self.conv2 = nn.Conv2d(32, 64, 3, 1, padding=1, bias=False)
            self.bn2 = nn.BatchNorm2d(64)
    
            self.block1 = Block(64, 128, stride=2, dilation=1, use_1st_relu=False)
            self.block2 = Block(128, 256, stride=2, dilation=1)
            self.block3 = Block(256, 728, stride=b3_s, dilation=1)
    
            # Middle Flow
            for i in range(16):
                exec(f'self.block{i+4} = Block(728, 728, stride=1, dilation=mf_d)')
    
            # Exit flow
            self.block20 = Block(728, 1024, stride=1, dilation=ef_d[0], exit_flow=True)
    
            self.conv3 = SeparableConv2d(1024, 1536, 3, stride=1, dilation=ef_d[1])
            self.bn3 = nn.BatchNorm2d(1536)
            self.conv4 = SeparableConv2d(1536, 1536, 3, stride=1, dilation=ef_d[1])
            self.bn4 = nn.BatchNorm2d(1536)
            self.conv5 = SeparableConv2d(1536, 2048, 3, stride=1, dilation=ef_d[1])
            self.bn5 = nn.BatchNorm2d(2048)

    而中间的低级feature maps在Xception系列中,是Entry Flow中block1的输出。

    2.3、class ASPP

    从backbone出来的输出步幅为16的feature maps被送入了ASPP模块,在该模块中经过不同膨胀率的卷积块和一个全局信息提取块后,concat起来,最后经过一个1*1卷积块之后,即为ASPP模块的输出。

    注意,这里之所以说是“块”,是因为其不单单包含一个操作,也包含了多个其他的操作,如BN、RELU、Dropout等,上文的1.1节等地方均有类似描述。

    如ASPP的不同膨胀率的分支定义如下:

    def assp_branch(in_channels, out_channles, kernel_size, dilation):
        padding = 0 if kernel_size == 1 else dilation
        return nn.Sequential(
                nn.Conv2d(in_channels, out_channles, kernel_size, padding=padding, dilation=dilation, bias=False),
                nn.BatchNorm2d(out_channles),
                nn.ReLU(inplace=True))

    全局信息提取块定义如下:

            self.avg_pool = nn.Sequential(
                nn.AdaptiveAvgPool2d((1, 1)),
                nn.Conv2d(in_channels, 256, 1, bias=False),
                nn.BatchNorm2d(256),
                nn.ReLU(inplace=True))

    ASPP类定义的完整代码如下:

    class ASSP(nn.Module):
        def __init__(self, in_channels, output_stride):
            super(ASSP, self).__init__()
    
            assert output_stride in [8, 16], 'Only output strides of 8 or 16 are suported'
            if output_stride == 16: dilations = [1, 6, 12, 18]
            elif output_stride == 8: dilations = [1, 12, 24, 36]
            
            self.aspp1 = assp_branch(in_channels, 256, 1, dilation=dilations[0])
            self.aspp2 = assp_branch(in_channels, 256, 3, dilation=dilations[1])
            self.aspp3 = assp_branch(in_channels, 256, 3, dilation=dilations[2])
            self.aspp4 = assp_branch(in_channels, 256, 3, dilation=dilations[3])
    
            self.avg_pool = nn.Sequential(
                nn.AdaptiveAvgPool2d((1, 1)),
                nn.Conv2d(in_channels, 256, 1, bias=False),
                nn.BatchNorm2d(256),
                nn.ReLU(inplace=True))
            
            self.conv1 = nn.Conv2d(256*5, 256, 1, bias=False)
            self.bn1 = nn.BatchNorm2d(256)
            self.relu = nn.ReLU(inplace=True)
            self.dropout = nn.Dropout(0.5)
    
            initialize_weights(self)
    
        def forward(self, x):
            x1 = self.aspp1(x)
            x2 = self.aspp2(x)
            x3 = self.aspp3(x)
            x4 = self.aspp4(x)
            x5 = F.interpolate(self.avg_pool(x), size=(x.size(2), x.size(3)), mode='bilinear', align_corners=True)
    
            x = self.conv1(torch.cat((x1, x2, x3, x4, x5), dim=1))
            x = self.bn1(x)
            x = self.dropout(self.relu(x))
    
            return x

    2.4、class Decoder

    Decoder部分属于最后一部分了,其接受backbone的低级feature maps和ASPP输出的feature maps,并对其分别进行了降维、上采样,然后concat,最后经过一组3*3卷积块后输出。其类定义代码如下:

    class Decoder(nn.Module):
        def __init__(self, low_level_channels, num_classes):
            super(Decoder, self).__init__()
            self.conv1 = nn.Conv2d(low_level_channels, 48, 1, bias=False)
            self.bn1 = nn.BatchNorm2d(48)
            self.relu = nn.ReLU(inplace=True)
    
            # Table 2, best performance with two 3x3 convs
            self.output = nn.Sequential(
                nn.Conv2d(48+256, 256, 3, stride=1, padding=1, bias=False),
                nn.BatchNorm2d(256),
                nn.ReLU(inplace=True),
                nn.Conv2d(256, 256, 3, stride=1, padding=1, bias=False),
                nn.BatchNorm2d(256),
                nn.ReLU(inplace=True),
                nn.Dropout(0.1),
                nn.Conv2d(256, num_classes, 1, stride=1),
            )
            initialize_weights(self)
    
        def forward(self, x, low_level_features):
            low_level_features = self.conv1(low_level_features)
            low_level_features = self.relu(self.bn1(low_level_features))
            H, W = low_level_features.size(2), low_level_features.size(3)
    
            x = F.interpolate(x, size=(H, W), mode='bilinear', align_corners=True)
            x = self.output(torch.cat((low_level_features, x), dim=1))
            return x

    需要注意的是,该代码将最后的4倍上采样插值的操作放到Decoder外面了,这一点与论文稍有差别,但只是归属不同,效果是一样的,不影响使用。

    3、总结

    本文主要结合DeepLabv3+的网络结构和开源代码进行了对照解析。通过对网络结构中的每一块的作用及其代码实现中的细节进行了解,就能够对该网络模型有一个宏观和微观上的把握,理解层次也更加深入了。

    对网络结构理解清晰透彻之后,就可以尝试魔改了,比如把线性插值上采样改为反卷积,是不是就可以避免转TensorRT时对上采样操作的不支持呢?这个想法后续有时间会尝试一下。

    展开全文
  • 30段超实用Python代码

    千次阅读 2020-11-30 08:16:16
    本文将简要介绍30个简短的、且能在30秒内掌握的代码片段。1. 唯一性以下方法可以检查给定列表是否有重复的地方,可用set()的属性将其从列表中删除。def all_unique(lst):return len(lst) == len(set(l...
  • 通过传入 -w 参数,2to3 也可以把需要的修改写回到文件中(除非传入了 -n 参数,否则会为原始文件创建一个副本): $ 2to3 -w example.py 在转换完成后,example.py 看起来像是这样: def greet(name): print(...
  • [Python] 纯文本查看 复制代码import osimport time##读取图片def GetFile(filePath):with open(filePath, 'rb') as fp:return fp.read()##从剪贴板保存图像def GetPhoto():from PIL import Image, ImageGrabim = ...
  • BN层代码实现

    千次阅读 2021-12-15 16:17:39
    Batch Normalization开发环境项目代码结构生成虚拟数据程序神经网络构建带初始化模型的神经网络构建带BN的FC网络和不带BN的FC网络对比不同初始化方式带BN的网络模型对比 开发环境 python–3.7 torch–1.8+cu101 ...
  • 目标检测的数据增强 -- 代码实现

    千次阅读 2022-03-26 11:03:27
    import math import random import torch from PIL import Image, ImageDraw 1、resize def resize(img, boxes, size, max_size=1000): # 调整大小缩放 w, h = img.size # 输入img w h (480, 364) if isinstance...
  • python:植物大战僵尸源代码

    千次阅读 2021-12-03 17:46:55
    #1 引入需要的模块 import pygame import random #1 配置图片地址 IMAGE_PATH = 'imgs/' #1 设置页面宽高 scrrr_width=800 scrrr_height =560 #1 创建控制游戏结束的状态 GAMEOVER = False #4 图片加载报错处理 ...
  • YOLOV5检测代码detect.py注释与解析

    万次阅读 多人点赞 2020-08-19 10:11:54
    YOLOV5检测代码detect.py注释与解析检测参数以及main函数解析detect函数解析 本文主要对ultralytics\yolov5-v2.0版本的测试代码detect.py的解析,现在v5已经更新了-v3.0版本, 但该代码部分基本上不会有很大的改动,...
  • def __iter__(self): # 查看原代码可以找到Iterable有__iter__方法 返回True pass def test(self): pass d = Demo(['ellen', 'python']) print(len(d)) print(hasattr(d, 'test')) # 判断是否有这个方法 from ...
  • [深度学习概念]·DenseNet学习笔记(代码实现PyTorch)

    千次阅读 多人点赞 2019-03-12 12:04:51
    其它实验结果见论文。 图8 在CIFAR-100数据集上ResNet vs DenseNet 图9 在ImageNet数据集上ResNet vs DenseNet 综合来看,DenseNet的优势主要体现在以下几个方面: 由于密集连接方式,DenseNet...
  • Python3常用代码块汇总

    千次阅读 2020-03-14 12:45:14
    本文章主要用于平时Python3学习和使用中积累的比较常用的代码块。代码都是经过验证可行的。 基本数据类型 字符串 字符串常识: 可以利用反斜杠(\)对双引号转义:",或者用单引号引起这个字符串。例如:‘I l"o"ve...
  • 如果要判断两个类型是否相同推荐使用 isinstance()。 我是不太明白这个意思,只是学会了它的用法如下: isinstance( 1 , int) isinstance([ 1 , 2 ], list) 执行结果如下: 可以用来判断任何...
  • 周志华西瓜书4.3python代码

    千次阅读 2020-05-02 17:45:40
    '' # 记录此刻是连续值还是离散值,1连续,2离散 flagSeries = 0 # 如果是连续值,记录连续值的划分点 midSeries = 0.0 # 如果是元组的话,说明此时是连续值 if isinstance(bestFeat, tuple): # 重新修改分叉点信息 ...
  • Python为每一个object在创建的时候就指定了一个内部类型,当我们不知道一个变量是什么类型时,就需要通过...下面的程序演示了一些类型的输出:#http://www.169it.com 测试代码class test1:passclass test2(test1)...
  • VGG16源代码详解

    千次阅读 多人点赞 2020-06-07 09:23:17
    VGG16源代码详解 小白第一篇源码阅读笔记,若有错误请多多指教。 本文除了对源码的顺序和注释进行了一点删改外,其他与源码一致 首先看入口,也就是当我要去创建一个VGG模型时,我会调用的那个函数: def vgg11...
  • 2.ResNet代码详解 a.BasicBlock类 class BasicBlock(nn.Module): expansion: int = 1 def __init__( self, inplanes: int, planes: int, stride: int = 1, downsample: Optional[nn.Module] = None, groups: int = ...
  • 代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。数据描述每条数据项储存在列表中,最后一列储存结果多条数据项形成数据集data=[[d1,d2,d3...dn,result],[d1,d2,d3...dn,result],..[d1,d2...
  • 标题:代码整洁之道-编写 Pythonic 代码来自:Python学习开发(微信号:python3-5)很多新手在开始学一门新的语言的时候,往往会忽视一些不应该忽视的细节,比如变量命名和函数命名以及注释等一些内容的规范性,...
  • python代码审计-osroom

    2020-12-03 14:14:55
    if isinstance(tjson, str): tjson = eval(tjson) return tjson 转到一个使用此方法的功能,例如apps\modules\audit\process\rules.py 删除规则处,传入一个ids参数,参数值是一个hash值,但是可以修改为python...
  • CBAM非官方代码及官方代码

    千次阅读 2020-11-05 00:36:29
    非官方代码 两大模块: class ChannelAttention(nn.Module): def __init__(self, in_planes, ratio=16): super(ChannelAttention, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.max_pool = ...
  • 训练代码:train_py3.py 数据集地址 但这个“Bosondata.pkl”是需要我们先到路径“ChineseNER\data\boson”下运行"data_util.py"才生成的 生成“Bosondata.pkl”的位置 当然,原代码也是存在python版本的问题(...
  • BERT代码解读

    千次阅读 2021-08-01 15:33:18
    step 2: intermediate转化,对应论文中的前馈神经网络FFN intermediate_output = self.intermediate(attention_output) # step 3: 做skip-connection layer_output = self.output(intermediate_output, attention_...
  • 1.RTTI 运行时类型信息使你能够在程序运行时发现和使用(比如对象的具体类)类型信息。RTTI主要有两种形式 ...面向对象的基本目的是:让代码只操纵对基类的引用,这样即使(从shape)派生出一个新类来也不会
  • #判断类型语句type()a, b, c, d = 1, 2.5, True, 4+3jprint(type(a))#判断函数类型语句 isinstance(obj,int/str/float..)isinstance(a, int)#格式函数#空行\n#标准空格\t (tab)#字符取字面意思 r'str'#set 为集合,...
  • 【Pranet】论文及代码解读(Res2Net部分)
  • YOLOv5代码阅读笔记及解析

    千次阅读 多人点赞 2021-03-01 14:15:46
    YOLOv5代码阅读笔记 一,backbone 1,Focus ①,原理:把一个nnc的feature map,按步长为2,进行取值下采样。如图一所示,把原始特征图沿x轴、y轴按步长为2进行取值操作,取值后添加到通道里,然后进行一次普通卷积...
  • python查看函数源代码

    千次阅读 2020-12-13 01:41:02
    python查看函数源代码 除了在系统中找到源文件直接打开查看,还可以用inspect.getsource函数查看 示例1 import inspect def print_hh(): print('hh') source = inspect.getsource(print_hh) # 查看自定义函数...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,305
精华内容 4,522
关键字:

isinstance()原代码

友情链接: Modbus TCP.zip