YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)

YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)_第1张图片

前言:Hello大家好,我是小哥谈。激活函数在神经网络中起到引入非线性因素的作用。如果不使用激活函数,神经网络的每一层都只会做线性变换,多层输入叠加后仍然是线性变换。然而,线性模型的表达能力通常不够,因此激活函数的作用就显现出来了。激活函数通过对线性变换的输出进行非线性映射,引入了非线性因素,从而增强了神经网络的表达能力。在YOLOv5中,使用了不同的激活函数。本节课就简单介绍一下常见的激活函数并重点讲解如何去更换激活函数! 

 YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)_第2张图片前期回顾:

          YOLOv5算法改进(1)— 如何去改进YOLOv5算法

          YOLOv5算法改进(2)— 添加SE注意力机制

          YOLOv5算法改进(3)— 添加CBAM注意力机制

          YOLOv5算法改进(4)— 添加CA注意力机制

          YOLOv5算法改进(5)— 添加ECA注意力机制

          YOLOv5算法改进(6)— 添加SOCA注意力机制

          YOLOv5算法改进(7)— 添加SimAM注意力机制

          YOLOv5算法改进(8)— 替换主干网络之MobileNetV3

          YOLOv5算法改进(9)— 替换主干网络之ShuffleNetV2

          YOLOv5算法改进(10)— 替换主干网络之GhostNet

          YOLOv5算法改进(11)— 替换主干网络之EfficientNetv2

          YOLOv5算法改进(12)— 替换主干网络之Swin Transformer

          YOLOv5算法改进(13)— 替换主干网络之PP-LCNet

          YOLOv5算法改进(14)— 更换Neck之BiFPN

          YOLOv5算法改进(15)— 更换Neck之AFPN

          YOLOv5算法改进(16)— 增加小目标检测层

          YOLOv5算法改进(17)— 更换损失函数(EIoU、AlphaIoU、SIoU和WIoU)

          目录

1.激活函数的概念

2.激活函数的作用

3.常见激活函数的介绍

3.1 ELU

3.2 Hardswish

3.3 Softplus

4.激活函数性质

5.更换激活函数的步骤

步骤1:打开activations.py文件,查看已有函数

步骤2:在common.py文件中更换激活函数

YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)_第3张图片

1.激活函数的概念

激活函数(Activation Function)是一种添加到人工神经网络中的函数,旨在帮助网络学习数据中的复杂模式。类似于人类大脑中基于神经元的模型,激活函数最终决定了要发射给下一个神经元的内容。

YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)_第4张图片

激活函数是神经网络中的一个重要组件,它的作用是引入非线性因素在不使用激活函数的情况下,神经网络的每一层都只进行线性变换,导致多层输入叠加后仍然是线性变换。因为线性模型的表达能力有限,所以激活函数的作用就显现出来了。激活函数通过对输入值的变换,将线性关系转化为非线性关系,从而增强了神经网络的表达能力它可以将神经元的输出限制在特定范围内,使得神经网络能够更好地适应不同类型的数据

YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)_第5张图片


2.激活函数的作用

如果不用激励函数(其实相当于激励函数是f(x) = x),在这种情况下你每一层节点的输入都是上层输出的线性函数,很容易验证,无论你神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果相当,这种情况就是最原始的感知机(Perceptron)了,那么网络的逼近能力就相当有限。正因为上面的原因,我们决定引入非线性函数作为激励函数,这样深层神经网络表达能力就更加强大(不再是输入的线性组合,而是几乎可以逼近任意函数)。更进一步的,激活函数在神经网络中的应用,除了引入非线性表达能力,其在提高模型鲁棒性,缓解梯度消失问题,将特征输入映射到新的特征空间以及加速模型收敛等方面都有不同程度的改善作用

激活函数的作用,一句话总结:为了提高模型的表达能力


3.常见激活函数的介绍

说明:♨️♨️♨️

关于Mish、Sigmoid、Tanh、ReLU、 LeakyReLU、Softmax、SiLU激活函数的介绍,请参考文章:

YOLOv5基础知识入门(6)— 激活函数(Mish、Sigmoid、Tanh、ReLU、Softmax、SiLU等)

3.1 ELU

ELU函数是一种激活函数,它的特点是没有Dead ReLU问题,输出的平均值接近0,并以0为中心。通过减少偏置偏移的影响,ELU函数使正常梯度更接近于单位自然梯度,从而加速学习。在较小的输入下,ELU函数会饱和至负值,减少前向传播的变异和信息。与ReLU相比,ELU具有负值,这使得激活的平均值接近零均值接近零可以加快学习,因为它使得梯度更接近自然梯度。ELU函数的计算强度更高。尽管理论上ELU要优于ReLU,但目前在实践中还没有充分的证据表明ELU总是优于ReLU

公式:

特点:

  • 没有Dead ReLU问题,输出的平均值接近0,以0为中心。
  • ELU 通过减少偏置偏移的影响,使正常梯度更接近于单位自然梯度,从而使均值向零加速学习。
  • ELU函数在较小的输入下会饱和至负值,从而减少前向传播的变异和信息。
  • ELU函数的计算强度更高。与Leaky ReLU类似,尽管理论上比ReLU要好,但目前在实践中没有充分的证据表明ELU总是比ReLU好。

图像:

YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)_第6张图片

3.2 Hardswish

Hardswish是一种激活函数,它是对Swish激活函数的改进。相较于Swish函数,Hardswish具有数值稳定性好、计算速度快等优点因为Swish在嵌入式移动设备上的计算成本更高,而Hardswish可以在准确性上没有明显差别的情况下,具有多重优势。它可以实现为分段功能,减少内存访问次数,从而大大降低了等待时间成本

公式:

YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)_第7张图片

 特点:

  • 计算简单速度快,比较适用于嵌入式设备等。梯度曲线同Swish很像,具有Swish的部分优点,如保留ReLU系列当x>0时梯度较大的优点,使得学习速度比Sigmoid函数快;修改了Sigmoid函数的饱和性,使的梯度消失的可能性大大降低。。
  • 高阶不可导。

图像:

YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)_第8张图片

3.3 Softplus

Softplus函数可以看作是ReLU函数的平滑。根据神经科学家的相关研究,Softplus函数和ReLU函数与脑神经元激活频率函数有神似的地方也就是说,相比于早期的激活函数,Softplus函数和ReLU函数更加接近脑神经元的激活模型,而神经网络正是基于脑神经科学发展而来,这两个激活函数的应用促成了神经网络研究的新浪潮。

公式:

特点:

  • Softplus函数是Logistic-Sigmoid函数原函数,可以看作是ReLU函数的平滑版本,其中加了1是为了保证非负性。
  • 根据神经科学家的相关研究,Softplus函数和ReLU函数与脑神经元激活频率函数有神似的地方。也就是说,相比于早期的激活函数,Softplus函数和ReLU函数更加接近脑神经元的激活模型,而神经网络正是基于脑神经科学发展而来,这两个激活函数的应用促成了神经网络研究的新浪潮。

4.激活函数性质

☘️(1)非线性:当激活函数是非线性的,一个两层的神经网络就可以基本上逼近所有的函数。但如果激活函数是恒等激活函数的时候,即f(x)=x,就不满足这个性质,而且如果MLP使用的是恒等激活函数,那么其实整个网络跟单层神经网络是等价的。

☘️(2)可微性:可微性保证了在优化中梯度的可计算性。传统的激活函数如Sigmoid等满足处处可微。对于分段线性函数比如ReLU,只满足几乎处处可微(即仅在有限个点处不可微)。对于SGD算法来说,由于几乎不可能收敛到梯度接近于0的位置,有限的不可微点对于优化结果不会有很大影响。

☘️(3)计算简单:非线性函数有很多。激活函数在神经网络前向的计算次数与神经元的个数成正比,因此简单的非线性函数自燃更适合用作激活函数。这也是ReLU比其它使用Exp等操作的激活函数更受欢迎的其中一个原因。

☘️(4)非饱和性:饱和指的是在某些区间梯度接近于0(即梯度消失),使得参数无法继续更新的问题。最经典的例子是Sigmoid,它的导数在x为比较大的正值和比较小的负值时都会接近于0。更极端的例子是阶跃函数,由于它在几乎所有位置的梯度都为0,因此处处饱和,无法作为激活函数。ReLU在x>0时导数恒为1,因此对于更大的正值也不会饱和,但同时对于x<0,其梯度恒为0,这时候它也会出现饱和的现象。LeakyReLU的提出正是为了解决这一问题。

☘️(5)单调性:即导数符号不变。当激活函数是单调的时候,单层网络能够保证是凸函数。

☘️(6)输出范围有限:有限的输出范围使得网络对于一些比较大的输入也会比较稳定,这也是为什么早期的激活函数都以此类函数为主的原因,如Sigmoid、Tanh。但这导致了前面提到的梯度消失问题,而且强行让每一层的输出限制在固定范围内会限制其表达能力,因此现在这类函数仅用于某些需要特定输出范围的场合,比如概率输出(此时loss函数中的log操作能够梯度消失的影响)、LSTM里的gate函数。

☘️(7)接近恒等变换:f(x)≈x,即约等于x。这样的好处是使得输出的幅值不会随着深度的增加而发生显著的增加,从而使网络更加稳定,同时梯度也能够更容易地回传。这个与非线性是有点矛盾的,因此激活函数基本上只是部分满足这个条件。

☘️(8)参数少:大部分激活函数都是没有参数的。

☘️(9)归一化:对应的激活函数是SELU,主要思想是使样本分布自动归一化到零均值、单位方差的分布,从而稳定训练。归一化的思想也被用于网络结构的设计,比如Batch Normalization。


5.更换激活函数的步骤

步骤1:打开activations.py文件,查看已有函数

找到utils / activations.py文件,可以看到SiLUHardswishMishMemoryEfficientMishFReLUAconCMetaAconC激活函数已经在文件里定义了,可以直接使用。

YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)_第9张图片

步骤2:在common.py文件中更换激活函数

先导入要更换的函数,这里以MetaAconc为例:

from utils.activations import MetaAconC

然后再在相应的位置进行修改,具体如下图所示:

YOLOv5算法改进(18)— 更换激活函数(SiLU、ReLU、ELU、Hardswish、Mish、Softplus等)_第10张图片

如果想更换其他激活函数,可以参照下面代码,步骤都是一样的。 

class Conv(nn.Module):
    # Standard convolution
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super().__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
        self.bn = nn.BatchNorm2d(c2)
        #self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = nn.Identity() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = nn.Tanh() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = nn.Sigmoid() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = nn.ReLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = nn.LeakyReLU(0.1) if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = nn.Hardswish() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = Mish() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = FReLU(c2) if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = AconC(c2) if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        self.act = MetaAconC(c2) if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = SiLU_beta(c2) if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        #self.act = FReLU_noBN_biasFalse(c2) if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
        # self.act = FReLU_noBN_biasTrue(c2) if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

    def forward_fuse(self, x):
        return self.act(self.conv(x))


你可能感兴趣的:(YOLOv5:从入门到实战,YOLO,python,机器学习,人工智能,目标检测)