首页 > 编程笔记 > Python笔记 阅读:25

什么是激活函数,PyTorch常见的激活函数有哪些(非常详细)

在深度学习中,信号从一个神经元传入下一层神经元之前是通过线性叠加来计算的,而进入下一层神经元需要经过非线性的激活函数,继续往下传递,如此循环下去。由于这些非线性函数的反复叠加,才使得神经网络有足够的能力来抓取复杂的特征。

如果不使用非线性的激活函数,这种情况下每一层输出都是上一层输入的线性函数。无论神经网络有多少层,输出都是输入的线性函数,这样就和只有一个隐藏层的效果是一样的。这种情况相当于多层感知机(Multilayer Perceptron,MLP)。多层感知机是一种基础的前馈人工神经网络,用于解决分类和回归问题。

激活函数的发展经历了 Sigmoid->Tanh->ReLU->Leaky ReLU 等多种不同类型的及其改进结构,还有一个特殊的激活函数 Softmax,因为它只会被用在网络中的最后一层,所以主要用来进行最后的分类和归一化。

Sigmoid激活函数

在生物学中,有一个常见的 S 型生长曲线,这就是 Sigmoid 函数。

Sigmoid 函数常被用作神经网络的阈值函数,因为它在信息科学中具备单增、反函数单增等性质,所以它可以将变量映射到 0~1,其公式如下:


Sigmoid 是几十年来应用最多的激活函数之一,它的应用范围比较广泛,值域在 0 和 1 之间,因此可以将其输出作为预测二值型变量取值为 1 的概率,有很好的概率解释性。

Sigmoid 激活函数在其大部分定义域内都饱和,仅仅当输入接近 0 时才会对输入强烈敏感。它能够控制数值的幅度,并在深层网络中保持数据幅度不会出现大的变化。

绘制函数曲线的代码如下:
import numpy as np
import matplotlib.pyplot as plt
# 定义sigmoid函数,用于计算输入x的sigmoid值
def sigmoid(x):
   return 1. / (1. + np.exp(-x))
# 定义plot_sigmoid函数,用于绘制sigmoid函数图像
def plot_sigmoid():
   # 生成x轴数据,范围为-10~10,步长为0.1
   x = np.arange(-10, 10, 0.1)
   # 计算对应的y轴数据,即sigmoid值
   y = sigmoid(x)
   # 绘制图像
   plt.plot(x, y)
   # 显示图像
   plt.show()
# 程序入口
if __name__ == '__main__':
   plot_sigmoid()
绘制的 Sigmoid 激活函数图形如下图所示:


图 2 Sigmoid激活函数图形

Sigmoid 函数具有明显的优势,首先,Sigmoid 函数限定了神经元的输出范围为 0~1,在一些问题中,这种形式的输出可以被看作概率取值。其次,当神经网络的损失函数取交叉熵时,S 函数可用于输入数据的归一化操作,且交叉熵与 Sigmoid 函数的配合能够有效地改善算法迭代速度慢的问题。

Tanh激活函数

Tanh 是双曲函数中的一个成员,它表示的是双曲正切函数。

在数学中,双曲正切是由基本双曲函数双曲正弦和双曲余弦推导而来的,其公式如下:



在分类任务中,Tanh 激活函数正逐渐取代 Sigmoid 激活函数成为神经网络的激活函数部分,根据图像可以看出,它关于原点对称,解决了 Sigmoid 函数中输出值都为正数的问题,而其他属性大多与 Sigmoid 函数相同,具有连续性和可微性。

绘制函数曲线的代码如下:
import numpy as np
import matplotlib.pyplot as plt
# 定义tanh函数,用于计算输入x的双曲正切值
def tanh(x):
   return (np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x))
# 定义plot_tanh函数,用于绘制tanh函数图像
def plot_tanh():
   # 生成x轴数据,范围为-10~10,步长为0.1
   x = np.arange(-10, 10, 0.1)
   # 计算对应的y轴数据,即tanh值
   y = tanh(x)
   # 绘制图像
   plt.plot(x, y)
   # 显示图像
   plt.show()
# 程序入口
if __name__ == '__main__':
   plot_tanh()
绘制的 Tanh 激活函数图形如下图所示:


图 4 Tanh激活函数图形

Tanh 函数关于原点对称,是一个 0 均值的函数,这是它较之 Sigmoid 函数有所改进的地方。Sigmoid 函数的输出具有偏移现象,即输出均为大于 0 的实值。而 Tanh 的输出则均匀地分布在 y 轴两侧。生物神经元的激活具有稀疏性,而 Tanh 函数的输出结果更趋于 0,从而使人工神经网络更接近生物自然状态。

ReLU激活函数

2001 年,线性分段激活函数 ReLU(Rectified Linear Unit)首次被提出,伴随深度神经网络的产生而兴起,其公式如下:
f(x)=max(0,x)
ReLU 函数的一个直观特点就是形式简单,抑制所有小于 0 的输入,仅保留净激活大于 0 的部分。因此,当 x<0 时,函数的导数为 0,即 ReLU 在 x 轴右侧饱和。但与 Sigmoid 函数和 Tanh 函数同时存在左右两个饱和区的情况相比,ReLU 陷入单侧饱和的概率已经大大降低。

另外,ReLU 也是非 0 均值的激活函数,但是其本身具有的稀疏激活性在一定程度上可以抵消非 0 均值输出带来的影响。

绘制函数曲线的代码如下:
import numpy as np
import matplotlib.pyplot as plt
def relu(x):
   return np.maximum(0,x)
def plot_relu():
   x=np.arange(-10,10,0.1)
   y=relu(x)
   plt.plot(x,y)
   plt.show()
if __name__ == '__main__':
   plot_relu()
绘制的 ReLU 激活函数图形如下图所示:


图 5 ReLU激活函数图形

ReLU 函数近年来应用较为广泛,相对于 Sigmoid 和 Tanh 函数,它解决了这两个函数存在的致命缺陷——梯度弥散问题。根据图像不难看出,函数在正无穷处的梯度是一个常量,而不是像前两个函数一样为 0,并且由于函数组成简单,因此运算速度比包含指数函数的 Sigmoid 和 Tanh 要快很多。

Leaky ReLU激活函数

当 ReLU 的输入值为负的时候,输出始终为 0,其一阶导数也始终为 0,这样会导致神经元不能更新参数,也就是神经元不学习了,这种现象叫作“神经元坏死”。

为了解决 ReLU 函数的这个缺点,在ReLU函数的负半区间引入了一个泄露(Leaky)值,所以称为 Leaky ReLU 函数,其公式如下:
f(x)=max(0.01x,x)
带泄露修正线性单元的 Leaky ReLU 函数是经典(以及广泛使用的)的 ReLU 激活函数的变体,该函数输出对负值输入有很小的坡度。由于导数总是不为零,因此能够减少静默神经元的出现,允许基于梯度的学习(虽然会很慢),解决了 ReLU 函数进入负区间后,导致神经元不学习的问题。

绘制激活函数曲线的代码如下:
import numpy as np
import matplotlib.pyplot as plt
# 定义leaky_relu函数,用于计算输入x的Leaky ReLU值
def leaky_relu(x):
   return np.array([i if i > 0 else 0.01*i for i in x ])
# 定义lea_relu_diff函数,用于计算输入x的Leaky ReLU导数值
def lea_relu_diff(x):
   return np.where(x > 0, 1, 0.01)
# 生成x轴数据,范围为-10~10,步长为0.01
x = np.arange(-10, 10, step=0.01)
# 计算对应的y轴数据,即Leaky ReLU值
y_sigma = leaky_relu(x)
# 计算对应的y轴数据,即Leaky ReLU导数值
y_sigma_diff = lea_relu_diff(x)
# 创建一个子图
axes = plt.subplot(111)
# 绘制Leaky ReLU曲线
axes.plot(x, y_sigma, label='leakly_relu')
# 添加图例
axes.legend()
# 显示图像
plt.show()
绘制的 Leaky ReLU 激活函数图形如下图所示:


图 6 Leaky ReLU激活函数图形

其他类型的激活函数

除了前面介绍的 4 类激活函数外,PyTorch 2.2 中还有 24 类激活函数,如下表所示,具体用法和参数含义可以参考 PyTorch 官方文档的介绍。

表:其他激活函数
编号 激活函数 调用方法
1 nn.ELU torch.nn.ELU(alpha=1.0, inplace=False)
2 nn.Hardshrink torch.nn.Hardshrink(lambd=0.5)
3 nn.Hardsigmoid torch.nn.Hardsigmoid(inplace=False)
4 nn.Hardtanh torch.nn.Hardtanh(min_val=-1.0, max_val=1.0, ...)
5 nn.Hardswish torch.nn.Hardswish(inplace=False)
6 nn.LogSigmoid torch.nn.LogSigmoid()
7 nn.MultiheadAttention torch.nn.MultiheadAttention(embed_dim, num_heads, ...)
8 nn.PReLU torch.nn.PReLU(num_parameters=1, init=0.25)
9 nn.ReLU6 torch.nn.ReLU6(inplace=False)
10 nn.RReLU torch.nn.RReLU(lower=0.125, upper=0.33, inplace=False)
11 nn.SELU torch.nn.SELU(inplace=False)
12 nn.CELU torch.nn.CELU(alpha=1.0, inplace=False)
13 nn.GELU torch.nn.GELU()
14 nn.SiLU torch.nn.SiLU(inplace=False)
15 nn.Softplus torch.nn.Softplus(beta=1, threshold=-20)
16 nn.Softshrink torch.nn.Softshrink(lambd=0.5)
17 nn.Softsign torch.nn.Softsign()
18 nn.Tanhshrink torch.nn.Tanhshrink()
19 nn.Threshold torch.nn.Threshold(threshold, value, inplace=False)
20 nn.Softmin torch.nn.Softmin(dim=None)
21 nn.Softmax torch.nn.Softmax(dim=None)
22 nn.Softmax2d torch.nn.Softmax2d()
23 nn.LogSoftmax torch.nn.LogSoftmax(dim=None)
24 nn.AdaptiveLogSoftmaxWithLoss torch.nn.AdaptiveLogSoftmaxWithLoss(...)

相关文章