YOLOv5 网络组件与激活函数 代码理解笔记

Posted 一颗小树x

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了YOLOv5 网络组件与激活函数 代码理解笔记相关的知识,希望对你有一定的参考价值。

前言

最近在看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


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

以上是关于YOLOv5 网络组件与激活函数 代码理解笔记的主要内容,如果未能解决你的问题,请参考以下文章

YOLOv5结构分析与理解—图解

神经网络与深度学习笔记激活函数与参数初始化

机器学习笔记:形象的解释神经网络激活函数的作用是什么?

干货 | 深入理解深度学习中的激活函数

常用激活函数(激励函数)理解与总结

机器学习笔记:激活函数