【神经网络】常见激活函数详解及python代码实现

前言

    1943年,心理学家Warren McCulloch和数学家Walter Pitts发表论文《A Logical Calculus Of The Ideas Immanent In Nervous Activity》(神经活动中固有思想的逻辑演算),提出了简化脑细胞的概念,也就是所谓的MP神经元模型
    McCulloch和Pitts把神经细胞描述为带有二元输出的简单逻辑门。多个信号到达树突,然后整合到细胞体,当累计信号量超过一定阈值时,输出信号就通过轴突。
人工神经元示例

    将MP神经元模型放在数学模型中来看,我们用 x x x来表示输入,假设一个神经元接收 m m m个输入,令向量 x = [ x 1 x 2 ⋮ x m ] , ω = [ ω 1 ω 2 ⋮ ω m ] x = \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_m \\ \end{bmatrix},\omega = \begin{bmatrix} \omega_1 \\ \omega_2 \\ \vdots \\ \omega_m \\ \end{bmatrix} x= x1x2xm ,ω= ω1ω2ωm 来表示输入层,接收来自其他神经元的信号。
这些信号的来源不同,对神经元的影响也不同,因此需要给他们分配不同的权重, ω \omega ω则表示权重。
对接收到的输入信号加权求和之后,与产生神经兴奋的阈值 θ θ θ相比较,得到中间结果 z z z z z z也叫做净输入(net input), z = ω 1 x 1 + ω 2 x 2 + . . . + ω m x m z=\omega_1x_1+\omega_2x_2+...+\omega_mx_m z=ω1x1+ω2x2+...+ωmxm
    最后通过阶跃函数模型神经兴奋。当 z z z的值小于0时,神经元处于抑制状态,输出为-1.当 z z z的值大于0时,神经元被激活,处于兴奋状态,输出为1.
ϕ ( z ) = { 1 , z ≥ 0 − 1 , e l s e \phi(z)=\begin{cases} 1 , z≥0 \\ -1 , else \\ \end{cases} ϕ(z)={1,z01,else
这个阶跃函数,就被称为激活函数。
数学模型表示如下图:
人工神经元数学模型

激活函数的性质

    激活函数在神经元中是非常重要的,为了增强网络的表示能力和学习能力,激活函数需要具备以下几点性质:

  • 连续并且可导(允许少数点上不可导)的非线性函数。可导的激活函数可以直接利用数值优化的方法来学习网络参数;
  • 激活函数及其导函数要尽可能地简单,有利于提高网络计算效率;
  • 激活函数的导函数的值域要在一个合适的区间内,不能太大也不能太小,否则会影响训练的效率和稳定性。

    假设 f ( x ) f(x) f(x)是一个激活函数,当 x → − ∞ x→-∞ x时,其导函数 f ′ ( x ) → 0 f'(x)→0 f(x)0则称其为左饱和激活函数,当 x → + ∞ x→+∞ x+时,其导函数 f ′ ( x ) → 0 f'(x)→0 f(x)0则称其为右饱和激活函数。当一个函数既满足左饱和、又满足右饱和,那么就被称为饱和激活函数,否则就被称为非饱和激活函数。
#常见的激活函数

1.Sigmoid函数

    Sigmoid型函数是传统神经网络中最常用的激活函数,指一类S型曲线函数,为两端饱和函数,常用的Sigmoid型函数有Logistic函数Tanh函数

1.1 Logistic函数

    Logistic函数定义为: σ ( x ) = 1 1 + e − x \sigma(x)=\displaystyle\frac{1}{1+e^{-x}} σ(x)=1+ex1
其值域为 ( 0 , 1 ) (0,1) (0,1) ,可以看成是一个“挤压”函数,把一个实数域的输入挤压到 ( 0 , 1 ) (0,1) (0,1) ,当输入值在0附近时,Sigmoid函数近似为线性函数;当输入值靠近两端时,对函数进行抑制,输入越小,越接近于0;输入越大,越接近于1。这样的特点与生物神经元类似,对一些输入会产生兴奋(输出为1)对另一些输入会产生抑制(输出为0)。
    使用Python绘制Logistic函数的实现代码及绘图结果如下:

#Logistic
import numpy as np
import matplotlib.pyplot as plt

# 定义Logistic激活函数
def logistic(x):
    return 1.0 / (1.0 + np.exp(-x))

# 生成输入值范围
x = np.arange(-10, 10, 0.1)

# 计算Logistic函数的输出
y = logistic(x)

# 绘制Logistic函数的图像
plt.plot(x, y, label='Logistic', color='b')
plt.xlabel('Input')
plt.ylabel('Output')
plt.title('Logistic Activation Function')
plt.axhline(0, color='black', linewidth=0.5, linestyle='--')
plt.axvline(0, color='black', linewidth=0.5, linestyle='--')
plt.legend()
plt.grid(True)
plt.show()

在这里插入图片描述

Logistic函数的优点为:

  • 输出映射在(0,1)之间,输出可以直接看作概率分布,单调连续,优化稳定;
  • 易于求导。

Logistic函数的缺点为:

  • 容易出现梯度弥散,反向传播时容易出现梯度消失和梯度爆炸;
  • 输出不是0均值(zero-centered),导致收敛速度下降;
  • 解析式中含有幂运算,求解较为耗时。

1.2 Tanh函数

Tanh函数定义为: t a n h ( x ) = e x − e − x e x + e − x tanh(x)=\displaystyle\frac{e^x-e^{-x}}{e^x+e^{-x}} tanh(x)=ex+exexex
Tanh函数可以看作是放大且平移的Logistic函数,其值域是(-1,1)。
t a n h ( x ) = 2 σ ( 2 x ) − 1 tanh(x)=2\sigma(2x)-1 tanh(x)=2σ(2x)1
使用Python绘制Tanh函数的实现代码及绘图结果如下:

#Tanh
import numpy as np
import matplotlib.pyplot as plt

# 定义Tanh函数
def tanh(x):
    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))

# 生成输入值范围
x = np.arange(-10, 10, 0.1)

# 计算Tanh函数的输出
y = tanh(x)

# 绘制Tanh函数的图像
plt.plot(x, y, label='Tanh', color='b')
plt.xlabel('Input')
plt.ylabel('Output')
plt.title('Tanh Activation Function')
plt.axhline(0, color='black', linewidth=0.5, linestyle='--')
plt.axvline(0, color='black', linewidth=0.5, linestyle='--')
plt.legend()
plt.grid(True)
plt.show()

在这里插入图片描述

Tanh函数的优点为:

  • 输出是零中心化的(zero-centered),提高收敛速度

Tanh函数的缺点为:

  • 没有解决反向传播时容易出现梯度消失和梯度爆炸的问题

2.ReLU函数

    ReLU(Rectified Linear Unit,修正线性单元),也叫Rectifier函数,是目前深度神经网络中经常使用的激活函数。ReLU实际上是一个斜坡(ramp)函数,定义为: R e L U ( x ) = { x , x ≥ 0 0 , x < 0 ReLU(x)=\begin{cases} x, x\geq0\\ 0, x<0\\ \end{cases} ReLU(x)={x,x00,x<0
使用Python绘制ReLU函数的实现代码及绘图结果如下:

#ReLU
import numpy as np
import matplotlib.pyplot as plt

# 定义ReLU函数
def relu(x):
    return np.maximum(0, x)

# 生成一系列输入值
x = np.linspace(-5, 5, 100)

# 计算ReLU函数的输出
y = relu(x)

# 绘制ReLU函数图像
plt.plot(x, y, label='ReLU', color='b')
plt.xlabel('Input')
plt.ylabel('Output')
plt.title('ReLU Function')
plt.axhline(0, color='black', linewidth=0.5, linestyle='--')
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend()
plt.show()

在这里插入图片描述

ReLU函数的优点为:

  • 只需要进行加、乘和比较的操作,计算上更加高效;
  • 具有生物合理性,比如单侧抑制、宽兴奋边界等,有很好的稀疏性,有约一半的神经元同时处于激活状态;
  • 在正区间解决了梯度消失的问题;梯度不饱和,收敛速度远远快于Sigmoid函数

ReLU函数的缺点为:

  • 输出不是0均值(zero-centered),影响梯度下降的效率;
  • 在训练时,如果参数在一次不恰当的更新后,第一个隐藏层中的某个ReLU神经元在所有的训练数据上都不能被激活,那么这个神经元自身参数的梯度将永远都是0,在以后的训练过程中永远不能被激活,也就是所谓的死亡问题
  • 对参数初始化和学习率非常敏感

    为了解决以上问题,提出了ReLU的变体。

2.1 Leaky ReLU函数

    带泄露的ReLU(Leaky ReLU)在输入 x < 0 x<0 x<0时,保持一个很小的梯度 γ \gamma γ.这样当神经元非激活时也能有一个非零的梯度可以更新参数,避免永远不能被激活的情况。Leaky ReLU函数定义为: L e a k y R e L U ( x ) = { x , x > 0 γ x , x ≤ 0 LeakyReLU(x)=\begin{cases} x, x>0\\ \gamma x, x\leq0\\ \end{cases} LeakyReLU(x)={x,x>0γx,x0
其中 γ \gamma γ 是一个很小的常数,比如0.01.当 γ < 1 \gamma<1 γ<1时,LeakyReLU也可以写为:
L e a k y R e L U ( x ) = m a x ( x , γ x ) LeakyReLU(x)=max(x,\gamma x) LeakyReLU(x)=max(x,γx)
使用Python绘制LeakyReLU函数的实现代码及绘图结果如下:

#LeakyReLU
import numpy as np
import matplotlib.pyplot as plt

# 定义Leaky ReLU函数
def leaky_relu(x, negative_slope=0.01):
    return np.where(x >= 0, x, negative_slope * x)

# 生成输入值范围
x = np.linspace(-5, 5, 100)

# 计算Leaky ReLU的输出
y = leaky_relu(x, negative_slope=0.01)

# 绘制Leaky ReLU函数的图像
plt.plot(x, y, label='Leaky ReLU (0.01)', color='b')
plt.xlabel('Input')
plt.ylabel('Output')
plt.title('Leaky ReLU Activation Function')
plt.axhline(0, color='black', linewidth=0.5, linestyle='--')
plt.axvline(0, color='black', linewidth=0.5, linestyle='--')
plt.legend()
plt.grid(True)
plt.show()

在这里插入图片描述

LeakyReLU函数的优点为:

  • 解决了ReLU函数的死亡神经元问题(Dead Neurons Problem),使得神经元在负数输入情况下也能产生非零输出,从而增强了模型的稀疏表示能力;
  • 可以避免梯度消失问题(Vanishing Gradient Problem),从而加快收敛速度和提高模型的训练性能;
  • 相比于其他激活函数(如sigmoid和tanh等),LeakyReLU计算速度更快。

LeakReLU函数的缺点为:

  • 在输入小于零的情况下,LeakyReLU引入了一些线性项,可能导致模型过于依赖于线性关系,影响模型的表达能力;
  • 需要额外的超参数来调整负数输入情况下的斜率,增加了模型设计的复杂性;
  • 由于不具备严格的非线性特性,可能不适用于某些需要更强非线性表达能力的任务。

2.2 PReLU函数

    带参数的ReLU(PReLU)引入一个可学习的参数,不同神经元可以有不同的参数。对于第i个神经元,PReLU函数定义为: P R e L U ( x ) = { x , x > 0 γ i x , x ≤ 0 PReLU(x)=\begin{cases} x, x>0\\ \gamma_i x, x\leq0\\ \end{cases} PReLU(x)={x,x>0γix,x0
其中 γ i \gamma_i γi x ≤ 0 x\leq0 x0时函数的斜率。因此PReLU是非饱和函数。如果 γ i = 0 \gamma_i=0 γi=0,那么PReLU就退化为ReLU.如果 γ i \gamma_i γi是一个很小的常数,那么PReLU就可以看作是Leaky ReLU。
使用Python绘制PReLU函数的实现代码及绘图结果如下:

#PReLU
import numpy as np
import matplotlib.pyplot as plt

#定义prelu函数
def prelu(x):
    return np.where(x<0, x * 0.5, x)

# 生成输入值范围
x = np.arange(-10, 10, 0.01)

# 计算PReLU的输出
y = prelu(x)

# 绘制PReLU函数的图像
plt.plot(x, y, label='PReLU (0.01)', color='b')
plt.xlabel('Input')
plt.ylabel('Output')
plt.title('PReLU Activation Function')
plt.axhline(0, color='black', linewidth=0.5, linestyle='--')
plt.axvline(0, color='black', linewidth=0.5, linestyle='--')
plt.legend()
plt.grid(True)
plt.show()

在这里插入图片描述

PReLU函数的优点为:

  • 解决了ReLU函数的死亡神经元问题和梯度消失问题,因为PReLU可以在负数输入情况下学习到不同的斜率,使得神经元能够更好地适应不同的任务和数据;
  • 具有更强的表达能力和灵活性,因为PReLU的参数可以根据数据的特征进行学习和调整,从而提高模型的适应性;
  • 在一些复杂的任务中,PReLU可以比LeakyReLU和ReLU更好地模拟数据的非线性关系,从而提高模型的性能。

PReLU函数的缺点为:

  • PReLU引入了额外的参数,增加了模型的复杂性和计算成本;
  • 在某些情况下,PReLU可能会在训练过程中导致模型过拟合,需要进行适当的正则化和参数调整;
  • 学习到的参数可能与数据的分布相关,如果数据在不同区域有不同的分布特点,可能需要额外的技术手段来适应这种情况。

2.3 ELU函数

    ELU(Exponential Linear Unit,指数线性单元)是一个近似的零中心化的非线性函数,其定义为: E L U ( x ) = { x , x > 0 γ ( e x − 1 ) , x ≤ 0 = m a x ( 0 , x ) + m i n ( 0 , γ ( e x − 1 ) ) ELU(x)=\begin{cases} x, x>0\\ \gamma(e^x-1), x\leq0\\ \end{cases} =max(0,x)+min(0,\gamma(e^x-1)) ELU(x)={x,x>0γ(ex1),x0=max(0,x)+min(0,γ(ex1))
其中 γ ≥ 0 \gamma\geq0 γ0 是一个超参数,决定 x ≤ 0 x\leq0 x0时的饱和曲线,并调整输出均值在0附近。
使用Python绘制ELU函数的实现代码及绘图结果如下:

#ELU
import numpy as np
import matplotlib.pyplot as plt

# 定义ELU函数
def elu(x, alpha=1.0):
    return np.where(x >= 0, x, alpha * (np.exp(x) - 1))

# 生成输入值范围
x = np.arange(-10, 10, 0.1)

# 计算ELU函数的输出
y = elu(x)

# 绘制ELU函数的图像
plt.plot(x, y, label='ELU', color='b')
plt.xlabel('Input')
plt.ylabel('Output')
plt.title('ELU Activation Function')
plt.axhline(0, color='black', linewidth=0.5, linestyle='--')
plt.axvline(0, color='black', linewidth=0.5, linestyle='--')
plt.legend()
plt.grid(True)
plt.show()

在这里插入图片描述

ELU函数的优点为:

  • 解决了梯度消失问题:当输入值为负时,ELU函数不会产生饱和现象,可以保持较大的导数。这有助于减轻梯度消失问题,使得网络更容易训练;
  • 提供了更平滑的导数曲线:ELU函数在正区域和负区域的斜率都比较平滑,这有助于提高训练的稳定性和收敛速度;
  • 具有近似指数线性性质:当输入值为正时,ELU函数呈现指数线性增长,有助于模型学习非线性特征。

ELU函数的缺点为:

  • 计算复杂度高:ELU函数的计算包含指数运算,相比于其他激活函数,如ReLU,计算复杂度要高一些,导致模型的训练速度稍慢;
  • 不具备整流性质:与ReLU相比,ELU函数在负区域的输出值不是恒定的0,这可能使得模型在某些应用场景中表现得不太好;
  • 参数依赖性:ELU函数引入了一个额外的超参数(alpha),这需要进行调优以获得最佳性能。

2.4 Softplus函数

    Softplus函数可以看作是ReLU函数的平滑版本,其定义为: S o f t p l u s ( x ) = l o g ( 1 + e x ) Softplus(x)=log(1+e^x) Softplus(x)=log(1+ex)
Softplus函数的导数刚好是Logistic函数,Softplus函数也具有单侧抑制、宽兴奋边界的特性,但没有稀疏激活性。
使用Python绘制Softplus函数的实现代码及绘图结果如下:

#Softplus
import numpy as np
import matplotlib.pyplot as plt

# 定义softplus函数
def softplus(x):
    return np.log(1 + np.exp(x))

# 生成输入值范围
x = np.arange(-10, 10, 0.1)

# 计算softplus函数的输出值
y = softplus(x)

# 绘制softplus函数的图像
plt.plot(x, y, label='Softplus', color='b')
plt.xlabel('Input')
plt.ylabel('Output')
plt.title('Softplus Function')
plt.axhline(0, color='black', linewidth=0.5, linestyle='--')
plt.axvline(0, color='black', linewidth=0.5, linestyle='--')
plt.legend()
plt.grid(True)
plt.show()

在这里插入图片描述

Softplus函数的优点为:

  • 与sigmoid函数相比,softplus函数的梯度更加稳定,不存在梯度饱和现象,因此在深度学习模型中更常被使用;
  • softplus函数是一个连续、平滑、非常接近于ReLU函数的函数,因此能够保留输入信号的特征,同时最小化信息丢失。

Softplus函数的缺点为:

  • softplus函数在输入值很小的时候,输出值接近于0,这使得梯度的数值变得非常小,这会影响模型的训练速度和效率;
  • softplus函数在输入值很大的时候,可能会发生数值上溢,这会导致计算机无法计算出对应的输出值,因此在实现该函数时需要进行处理。

3.Swish函数(SiLU函数)

    Swish函数是一种自门控激活函数,其定义为: S w i s h ( x ) = x σ ( β x ) Swish(x)=x\sigma(\beta x) Swish(x)=xσ(βx)
其中 σ ( ⋅ ) \sigma(·) σ()为Logistic函数, β \beta β为可学习的参数或一个固定参数。 σ ( ⋅ ) ∈ ( 0 , 1 ) \sigma(·)\in(0,1) σ()(0,1) 可以看作是一种软性的门控机制。当 σ ( β x ) \sigma(\beta x) σ(βx)接近于1时,门处于“开”的状态,激活函数的输出近似于x本身;当 σ ( β x ) \sigma(\beta x) σ(βx)接近于0时,门处于“关”的状态,激活函数的输出近似于0.
使用Python绘制Swish函数的实现代码及绘图结果如下:

#Swish
import numpy as np
import matplotlib.pyplot as plt
# 定义Swish函数
def swish(x, beta):
    return x / (1 + np.exp(-beta*x))
# 生成输入值范围
x = np.arange(-10, 10, 0.1)
# 定义不同的β值
beta_values = [0, 0.5, 1, 100]
# 绘制Swish函数在不同的β值下的图像
for beta in beta_values:
    y = swish(x, beta)
    plt.plot(x, y, label=f'Beta = {beta}')
plt.xlabel('Input')
plt.ylabel('Output')
plt.title('Swish Function with Different Beta Values')
plt.axhline(0, color='black', linewidth=0.5, linestyle='--')
plt.axvline(0, color='black', linewidth=0.5, linestyle='--')
plt.legend()
plt.grid(True)
plt.show()

在这里插入图片描述

    当 β = 0 \beta=0 β=0时,Swish函数变为线性函数 x / 2 x/2 x/2。当 β = 1 \beta=1 β=1时 ,Swish函数在 x > 0 x>0 x>0时近似线性,在 x < 0 x<0 x<0时近似饱和,同时具有一定的非单调性,此时我们也称其为SiLU函数.当 β → + ∞ \beta\rightarrow+\infty β+时, σ ( β x ) \sigma(\beta x) σ(βx) 趋向于离散的0-1函数,此时Swish函数近似为ReLU函数,因此,Swish函数可以看作是线性函数和ReLU函数之间的非线性插值函数,其程度由参数 β \beta β控制。

Swish函数的优点为:

  • Swish函数具有与ReLU函数相似的非线性形式,在一定程度上解决了梯度消失问题;
  • Swish函数在实现时简单易用,同时可以使用标准的反向传播算法进行训练,计算速度相对较快;
  • Swish函数是可微的,可以应用于很多需要计算梯度的模型中。

Swish函数的缺点为:

  • Swish函数在输入值很小时,输出值接近于0,这使得梯度的数值变得非常小,这会影响模型的训练速度和效率;
  • Swish函数在输入值很大的时候,可能会发生数值上溢,这会导致计算机无法计算出对应的输出值,因此在实现该函数时需要进行处理;
  • Swish函数具有一个额外的超参数 beta,如果不选择合适的 beta,可能会影响模型的效果和训练速度。

4.GELU函数

    GELU(Gaussian Error Linear Unit,高斯误差线性单元)也是一种通过门控机制来调整其输出的激活函数,和Swish比较类似。其定义为: G E L U ( x ) = x P ( X ≤ x ) GELU(x)=xP(X\leq x) GELU(x)=xP(Xx)
其中 P ( X ≤ x ) P(X\leq x) P(Xx) 为高斯分布 N ( μ , σ 2 ) N(\mu,\sigma^2) N(μ,σ2) ,其中 μ , σ \mu,\sigma μ,σ 为超参数,一般设 μ = 0 , σ = 1 \mu=0,\sigma=1 μ=0σ=1。由于高斯分布的累积分布函数为S型函数,因此GELU函数可以用Tanh函数或者Logistic函数来近似。表示为:
G E L U ( x ) ≈ 0.5 x ( 1 + t a n h ( 2 π ( x + 0.044715 x 3 ) ) ) ≈ x σ ( 1.702 x ) GELU(x)\approx0.5x(1+tanh(\sqrt{\frac{2}{\pi}}(x+0.044715x^3)))\\ \approx x\sigma(1.702x) GELU(x)0.5x(1+tanh(π2 (x+0.044715x3)))xσ(1.702x)
使用Python绘制GELU函数的实现代码及绘图结果如下:

#GELU
import numpy as np
import matplotlib.pyplot as plt

# 定义GELU函数
def gelu(x):
    return 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * np.power(x, 3))))

# 生成输入值范围
x = np.linspace(-5, 5, 100)

# 计算GELU函数的输出值
y = gelu(x)

# 绘制GELU数图像
plt.plot(x, y, label='Softplus', color='b')
plt.xlabel('Input')
plt.ylabel('Output')
plt.title('GELU Function')
plt.axhline(0, color='black', linewidth=0.5, linestyle='--')
plt.axvline(0, color='black', linewidth=0.5, linestyle='--')
plt.grid(True)
plt.show()

在这里插入图片描述

    当使用Logistic函数来近似时,GELU相当于一种特殊的Swish函数。

GELU函数的优点为:

  • GELU函数具有平滑的非线性形式,可以避免ReLU函数在输入为负时出现的零梯度问题;
  • GELU函数在实践中表现良好,它在一些自然语言处理和图像处理任务中取得了较好的性能;
  • GELU函数是可微的,可以使用标准的反向传播算法进行训练,并且对于深度神经网络的训练是有效的。

GELU函数的缺点为:

  • GELU函数的计算比ReLU函数更加复杂,包含指数和双曲正切运算,因此在一些实际的应用中,GELU函数的计算成本可能会比较高;
  • GELU函数的输出在接近于零的区域附近非常接近线性,这可能导致网络在这个区域内收敛速度较慢;
  • GELU函数的性能可能对超参数的选择比较敏感,如果不选择合适的超参数,可能会影响模型的效果和训练速度。
  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值