【TensorFlow2.x系列第3篇】TensorFlow2.0-深度学习中的激活函数

对于激活函数的应用是比较熟悉和频繁的,但一直以来没有系统性地把深度学习中地激活函数做一个总结,因此小知同学特地对常用的激活函数做一个比较全面的总结,在讲述理论的同时,也根据激活函数的公式进行编程实现

激活函数概念

所谓激活函数(Activation Function),就是在人工神经网络的神经元上运行的函数,负责将神经元的输入映射到输出端。

什么是激活函数

激活函数(Activation functions)对于人工神经网络模型去学习、理解非常复杂和非线性的函数来说具有十分重要的作用。它们将非线性特性引入到我们的网络中。如图1,在神经元中,输入的 inputs 通过加权,求和后,还被作用了一个函数,这个函数就是激活函数。引入激活函数是为了增
加神经网络模型的非线性。没有激活函数的每层都相当于矩阵相乘。就算你叠加了若干层之后,无非还是个矩阵相乘罢了。

为什么使用激活函数

如果不用激活函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合,这种情况就是最原始的感知机(Perceptron)。
如果使用的话,激活函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。

个人认知与理解

我觉得机器学习的传统算法与深度学习算法的比较重要的区别是:

  1. 从广义上来讲,机器学习的传统算法一般只能使用一种目标模型函数,比如逻辑回归使用logistic函数、只能解决单维度问题;而深度学习可以在不同神经层使用不同或者多种激活函数、因此拥有多种或者不同函数的特性,所以解决问题具有多维度、线性、非线性等处理能力
  2. 深度学习的激活函数使得深度学习算法既能解决简单的线性问题、也能处理复杂的非线性问题
  3. 数据中的特征往往具有不同的特性、特征与不同模型之间也有较大的辨识差异,机器学习的传统算法的单一模型可能只能对部分特征产生重要作用,而深度学习的多种激活函数则比较全面、多维度对特征进行学习

常用的激活函数

  1. sigmoid 函数
  2. tanh 函数
  3. relu 函数
  4. leaky relu 函数
  5. elu 函数
  6. softmax 函数

饱和激活函数与非饱和激活函数

  1. 饱和函数是指当自变量 x 达到某个值(或者说趋于无穷小、无穷大)的时候,因变量 y 就不再发生变化,而是趋于某一个固定的值
    • sigmoid 函数就是一个饱和激活函数,当自变量 z 趋于无穷小时,因变量 y 趋于 0;当自变量 z 趋于无穷大时,因变量 y 趋于 1
    • tanh 函数就是一个饱和激活函数,当自变量 z 趋于无穷小时,因变量 y 趋于 -1;当自变量 z 趋于无穷大时,因变量 y 趋于 1
  2. 饱和函数是指当自变量 x 达到某个值(或者说趋于无穷小、无穷大)的时候,因变量 y 就依然发生变化,并不是趋于某一个固定的值
    • relu 函数就是一个非饱和激活函数,当自变量 z 小于 0 时,因变量 y 等于 0;但当自变量 z 大于 0 时,因变量 y 是一个 z 的变化值
    • relu 的变种激活函数
  3. 非饱和激活函数的优势
    • 首先,“非饱和激活函数”能解决所谓的“梯度消失”问题
    • 其次,它能加快收敛速度
import warnings
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
warnings.filterwarnings('ignore')
%matplotlib inline

x = np.linspace(-10, 10, 100)

1.sigmoid激活函数(饱和函数)

  1. sigmoid函数计算公式
    σ ( z ) = 1 1 + e z , e 2.7183 \sigma(z) =\frac{1}{1+e^{-z}},e\approx 2.7183
  2. sigmoid函数的导数公式
    σ ( z ) = σ ( z ) ( 1 σ ( z ) ) , e 2.7183 {\sigma(z)}' =\sigma(z)(1-\sigma(z)),e\approx 2.7183
  3. sigmoid函数评价
    • 优点:sigmoid函数有效地将实数域的线性问题映射到[0,1]区间的类别概率问题,实现分类;
    • 缺点:然而在深度学习算法中使用sigmoid函数有时候在反向求导传播时会导致梯度消失的现象:
      • 当z很大时, σ ( z ) \sigma(z) 趋近于1,当z很小时, σ ( z ) \sigma(z) 趋近于0
      • 其导数 σ ( z ) = σ ( z ) ( 1 σ ( z ) ) {\sigma(z)}' =\sigma(z)(1-\sigma(z)) 在z很大或很小时都会趋近于0,造成梯度消失的现象
# 手写计算函数
def sigmoid(z, mode=False):
    if mode:  # 手写公式
        L = []
        for i in range(len(z)):
            sigmoid_value = 1/(1+(2.7183)**(-z[i]))
            L.append(sigmoid_value)
        return L
    else:  # 使用numpy
        return 1/(1+np.exp(-z))
plt.plot(x, sigmoid(z=x))
# 调用 Tensorflow 的 sigmoid 函数
y_sigmoid = tf.nn.sigmoid(x)
plt.plot(x, y_sigmoid)

2.tanh激活函数(饱和函数)

  1. tanh函数计算公式
    t a n h ( z ) = e z e z e z + e z , e 2.7183 tanh(z)=\frac{e^{z}-e^{-z}}{e^{z}+e^{-z}},e\approx 2.7183
  2. tanh函数的导数公式
    t a n h ( z ) = 1 ( t a n h ( z ) ) 2 , e 2.7183 {tanh(z)}'=1-(tanh(z))^{2},e\approx 2.7183
  3. tanh函数评价
    • 优点:sigmoid函数有效地将实数域的线性问题映射到[-1,1]区间的类别概率问题,实现分类;
    • 缺点:然而在深度学习算法中使用tanh函数有时候在反向求导传播时会导致梯度消失的现象:
      • 当z很大时, tanh ( z ) \tanh(z) 趋近于1,当z很小时, t a n h ( z ) tanh(z) 趋近于-1
      • 其导数 t a n h ( z ) = 1 ( t a n h ( z ) ) 2 {tanh(z)}'=1-(tanh(z))^{2} 在z很大或很小时都会趋近于0,造成梯度消失的现象
  4. tanh与sigmoid的关系式:
    t a n h ( z ) = 2 s i g m o i d ( 2 z ) 1 tanh(z)=2sigmoid(2z)-1
def tanh(z, mode=True):
    if mode:  # 手写公式
        L = []
        for i in range(len(z)):
            exp1 = 2.7183**(z[i])
            exp2 = 2.7183**(-z[i])
            tanh_value = (exp1-exp2)/(exp1+exp2)
            L.append(tanh_value)
        return L
    else:  # 使用numpy
        return (np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x))
plt.plot(x,tanh(z=x))
# 调用 Tensorflow 的 tanh 函数
y_tanh = tf.nn.tanh(x)
plt.plot(x, y_tanh)

3.relu激活函数(非饱和函数)

修正线性单元(Rectified linear unit,ReLU)

  1. relu函数计算公式
    r e l u ( z ) = m a x ( 0 , z ) relu(z)=max(0,z)
  2. relu函数的导数公式
    r e l u ( z ) = { 1 z > 0 0 z 0 {relu(z)}'=\left\{\begin{matrix} 1 & z> 0\\ 0 & z\leq 0 \end{matrix}\right.
  3. relu函数评价
    • 优点:
      • relu函数的非饱和性可以有效地解决梯度消失的问题,提供相对较宽的激活边界
      • relu函数是阈值函数,运算简单且快速,只需通过阈值判断就可以得到激活值,而sigmoid、tanh函数都需要计算指数,运算复杂
      • ReLU的单侧抑制(负梯度都为 0)提供了网络的稀疏表达能力
    • 缺点:
      • r e l u ( z ) = m a x ( 0 , z ) relu(z)=max(0,z) 会导致负梯度的神经元产生不可逆的死亡,也称为死亡神经元,由于负梯度值都为 0,在往后的运算中都将以 0 传播
      • 如果学习率(Learning Rate)设置较大,会导致超过一定比例的神经元不可逆死亡,进而参数梯度无法更新,整个训练过程失败
def relu(x):  # 手写公式
    relu_list = []
    for i in range(len(x)):
        relu_list.append(max(0, x[i]))
    return relu_list
plt.ylim(-1,6)
plt.plot(x, relu(x))
# 调用 Tensorflow 的 relu 函数
y_relu = tf.nn.relu(x)
plt.ylim(-1,6)
plt.plot(x, y_relu)

4.leaky relu

带泄露修正线性单元(Leaky ReLU)函数是经典(以及广泛使用的)的ReLu激活函数的变体,该函数输出对负值输入有很小的坡度,它旨在解决负梯度神经元死亡的问题。由于负梯度神经元导数总是不为零,这能减少静默神经元的出现,允许基于梯度的学习(虽然会很慢),解决了Relu函数进入负区间后神经元不学习的问题。

  1. leaky relu 函数计算公式
    L r e l u ( z ) = m a x ( 0.1 z , z ) , a < 1 ( a = 0.1 ) , a Lrelu(z)=max(0.1z,z),a<1(a=0.1),其中a可以自定义
  2. leaky relu 函数的导数公式
    L r e l u ( z ) = { 1 z > 0 0.1 z 0 {Lrelu(z)}'=\left\{\begin{matrix} 1 & z>0\\ 0.1 &z\leq 0 \end{matrix}\right.
  3. leaky relu 函数评价
    • 优点:
      • 旨在解决 relu 负梯度神经元死亡的问题,函数输出对负值输入有很小的坡度
      • 解决了Relu函数进入负区间后神经元不学习的问题
    • 缺点:
      • 负梯度的 a 难以寻求合适的系数值,a 通常小于 1 且通常使用 a = 0.1
      • 负梯度的 a 的选择和确定需要一定的经验或者做实验验证,相对麻烦一些
def leaky_rule(z):  # 手写公式
    L = []
    for i in range(len(z)):
        lrule_value = max(0.1*z[i], z[i])
        L.append(lrule_value)
    return L
plt.plot(x, leaky_rule(z=x))
# 调用 Tensorflow 的 leaky_relu 函数
y_lrelu = tf.nn.leaky_relu(x)
plt.plot(x, y_lrelu)

5.elu

指数线性单元(Exponential Linear Units)

  1. elu 函数计算公式
    e l u ( z ) = { z z 0 a ( e z 1 ) z < 0 , t e n s o r f l o w , a 1 elu(z)=\left\{\begin{matrix} z &z\geq 0 \\ a(e^{z}-1) & z<0 \end{matrix}\right.,tensorflow中,a默认为1

  2. elu 函数的导数公式
    e l u ( z ) = { 1 z 0 a e z z < 0 {elu(z)}'=\left\{\begin{matrix} 1 & z\leq 0\\ ae^{z} & z<0 \end{matrix}\right.

  3. elu 函数评价

    • 融合了sigmoid和ReLU,左侧具有软饱和性,右侧无饱和性。
    • 右侧线性部分使得ELU能够缓解梯度消失,而左侧软饱能够让ELU对输入变化或噪声更鲁棒。
    • ELU的输出均值接近于零,所以收敛速度更快。
    • 在 ImageNet上,不加 Batch Normalization 30 层以上的 ReLU 网络会无法收敛,PReLU网络在MSRA的Fan-in (caffe )初始化下会发散,而 ELU 网络在Fan-in/Fan-out下都能收敛。
def elu(z, a=0.1):  # 手写公式
    L = []
    for i in range(len(z)):
        if z[i] >= 0:
            elu_value = z[i]
            L.append(elu_value)
        else:
            elu_value = a*(2.7183**(z[i]) - 1)
            L.append(elu_value)
    return L
plt.plot(x, elu(z=x, a=1))
# 调用 Tensorflow 的 elu 函数
y_elu = tf.nn.elu(x)
plt.plot(x, y_elu)

参考资料
[1] 百度百科,激活函数,https://baike.baidu.com/item/%E6%BF%80%E6%B4%BB%E5%87%BD%E6%95%B0/2520792?fr=aladdin
[2] 百面机器学习,深度神经网络中的激活函数
[3] CSDN博客,常用激活函数,https://blog.csdn.net/qq_23304241/article/details/80300149
[4] CSDN博客,ELU激活函数, https://blog.csdn.net/zrh_CSDN/article/details/81266188

发布了18 篇原创文章 · 获赞 10 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_41731978/article/details/103822468