0


从零实现深度学习框架——神经元与常见激活函数

引言

本着“凡我不能创造的,我就不能理解”的思想,本系列文章会基于纯Python以及NumPy从零创建自己的深度学习框架,该框架类似PyTorch能实现自动求导。

要深入理解深度学习,从零开始创建的经验非常重要,从自己可以理解的角度出发,尽量不使用外部完备的框架前提下,实现我们想要的模型。本系列文章的宗旨就是通过这样的过程,让大家切实掌握深度学习底层实现,而不是仅做一个调包侠。
本系列文章首发于微信公众号:JavaNLP

我们已经了解了线性回归和逻辑回归,本文来学习深度学习中神经网络的基础构建——神经元,以及常见的激活函数。

神经元

神经网络和逻辑回归很像,但神经网络更强大。而神经网络是由很多个神经元(Neuron)组成的。一个神经元将实数集作为输入,然后应用某种运算,产生一个实数输出。

神经元示意图

在神经元内部,如上图所示,神经元首先计算输入的加权和

  1. i
  2. w
  3. i
  4. x
  5. i
  6. \sum_i w_i x_i
  7. iwixi​,然后加上偏置项
  8. b
  9. b
  10. b。给定输入
  11. x
  12. 1
  13. ,
  14. ,
  15. x
  16. n
  17. x_1,\cdots,x_n
  18. x1​,⋯,xn​,每个输入对应一个权重,得到加权和
  19. z
  20. z
  21. z
  22. z
  23. =
  24. b
  25. +
  26. i
  27. w
  28. i
  29. x
  30. i
  31. (1)
  32. z = b + \sum_i w_i x_i \tag 1
  33. z=b+i∑​wixi​(1)

通常使用向量的形式描述更加方便。这样

  1. z
  2. z
  3. z由向量
  4. w
  5. w
  6. w和标量
  7. b
  8. b
  9. b,以及输入向量
  10. x
  11. x
  12. x来描述:
  13. z
  14. =
  15. w
  16. x
  17. +
  18. b
  19. (2)
  20. z =w \cdot x +b \tag 2
  21. z=wx+b(2)

注意这里得到的

  1. z
  2. z
  3. z只是一个实数(标量)。

最后,我们不是直接使用

  1. z
  2. z
  3. z作为输出,神经元内部应用一个非线性函数
  4. f
  5. f
  6. f
  7. z
  8. z
  9. z​上:
  10. y
  11. =
  12. a
  13. =
  14. f
  15. (
  16. z
  17. )
  18. y = a = f(z)
  19. y=a=f(z)

这里的非线性函数称为激活函数,该函数的输出值称为激活值

  1. a
  2. a
  3. a,我们已经见过的一种激活函数是Sigmoid函数:
  4. y
  5. =
  6. σ
  7. (
  8. z
  9. )
  10. =
  11. 1
  12. 1
  13. +
  14. e
  15. z
  16. (3)
  17. y = \sigma(z) = \frac{1}{1 + e^{-z}} \tag 3
  18. y=σ(z)=1+ez1​(3)

这里神经元的输出

  1. y
  2. y
  3. y和激活值
  4. a
  5. a
  6. a相同,但在神经网络中,我们通常用
  7. y
  8. y
  9. y​表示整个网络最终的输出。把
  10. (
  11. 2
  12. )
  13. (2)
  14. (2)代入
  15. (
  16. 3
  17. )
  18. (3)
  19. (3),得到神经元的输出:
  20. y
  21. =
  22. σ
  23. (
  24. w
  25. x
  26. +
  27. b
  28. )
  29. =
  30. 1
  31. 1
  32. +
  33. exp
  34. (
  35. (
  36. w
  37. x
  38. +
  39. b
  40. )
  41. )
  42. (4)
  43. y = \sigma(w\cdot x + b) = \frac{1}{1 + \exp(-(w\cdot x + b))} \tag 4
  44. y=σ(wx+b)=1+exp(−(wx+b))1​(4)

除了Sigmoid之外,还有很多其他比较常见的激活函数。

常见激活函数

激活函数(activation function)通过计算加权和并加上偏置来确定神经元是否应该被激活,大多数激活函数都是非线性的。所有

ReLU

最常用的激活函数是修正线性单元(Rectified linear unit,ReLU),提供了一种非常简单的非线性变换。给定元素

  1. x
  2. x
  3. xReLU函数被定义为该元素与
  4. 0
  5. 0
  6. 0的最大值:
  7. ReLU
  8. (
  9. x
  10. )
  11. =
  12. max
  13. (
  14. 0
  15. ,
  16. x
  17. )
  18. (5)
  19. \text{ReLU}(x) = \max(0, x) \tag 5
  20. ReLU(x)=max(0,x)(5)

ReLU函数通过将相应的激活值设为

  1. 0
  2. 0
  3. 0,仅保留正元素并丢弃所有负元素。我们可以画出函数的图形感受一下:
  1. from metagrad.functions import*from metagrad.utils import plot
  2. if __name__ =='__main__':
  3. x = Tensor.arange(-8.0,8.0,0.1, requires_grad=True)
  4. y = relu(x)
  5. plot(x.numpy(), y.numpy(),'x','relu(x)', random_fname=True, figsize=(5,2.5))

ReLU函数图像

当输入为负时,ReLU函数的导数为

  1. 0
  2. 0
  3. 0,当输入为正时,ReLU函数的导数为
  4. 1
  5. 1
  6. 1。当输入为
  7. 0
  8. 0
  9. 0时,我们让其导数也为
  10. 0
  11. 0
  12. 0
  1. y.backward(Tensor.ones_like(x))
  2. plot(x.numpy(), x.grad.numpy(),'x','grad of relu', figsize=(5,2.5))

ReLU的导数图像

由于ReLU的简单性,没有包含

  1. e
  2. x
  3. e^x
  4. ex,导致它的计算效率极高。同时它的梯度要么为
  5. 0
  6. 0
  7. 0,要么为
  8. 1
  9. 1
  10. 1,这使得优化变现得更好,减轻了困扰神经网络的梯度消息问题。

ReLU导数的函数图形如上图所示,我们可以看到,在

  1. x
  2. <
  3. 0
  4. x < 0
  5. x<0的一侧,梯度值永远是
  6. 0
  7. 0
  8. 0。因此,在反向传播的过程中,可能有些神经元的权重不会被更新(因为导数为
  9. 0
  10. 0
  11. 0)。这可能会导致永不激活的死节点(神经元)。这个问题可以被ReLU的变种:Leaky ReLU解决。

Leaky ReLU

Leaky ReLU是ReLU的改进版本,主要用于解决上面跳到的死节点问题,通过给所有负值赋予一个小的正斜率来解决

  1. Leaky ReLu
  2. (
  3. x
  4. )
  5. =
  6. m
  7. a
  8. x
  9. (
  10. a
  11. x
  12. ,
  13. x
  14. )
  15. (6)
  16. \text{Leaky ReLu}(x) = max(ax, x) \tag 6
  17. Leaky ReLu(x)=max(ax,x)(6)

通常这里的

  1. a
  2. =
  3. 0.01
  4. a=0.01
  5. a=0.01,为了看出效果。在画图时让
  6. a
  7. =
  8. 0.1
  9. a=0.1
  10. a=0.1,我们看一下它的图形:
  1. y = leaky_relu(x)
  2. plot(x.numpy(), y.numpy(),'x','leaky relu(x)', random_fname=True, figsize=(5,2.5))

Leaky ReLU函数图像

Leaky ReLU的优点与ReLU相同,同时对于负输入,其导数也变成了一个非零值(即

  1. a
  2. a
  3. a)。

Leaky ReLU的导数图像

从上图可以看到,对于

  1. x
  2. <
  3. 0
  4. x < 0
  5. x<0的一侧,它们也有非零的导数。不至于出现死节点,但是由于
  6. a
  7. a
  8. a通常很小,导致在在此侧的模型参数学习缓慢。

除了Leaky ReLU外,类似地还有两种变体,分别是Parametric ReLU和Randomized Leaky ReLU。

Parametric ReLU称为参数化的ReLU,即令Leaky ReLU中的

  1. a
  2. a
  3. a变成了一个可学习的参数。

而Randomized Leaky ReLU让

  1. a
  2. a
  3. a取自一个连续均匀概率分布。

Exponential Linear Unit

ELU(Exponential Linear Unit)也是ReLU的一种变体,类似Leaky ReLU修改在

  1. x
  2. <
  3. 0
  4. x < 0
  5. x<0侧的斜率,但在负区域不是一条直线,而是一条对数曲线。
  6. ELU
  7. (
  8. x
  9. )
  10. =
  11. max
  12. (
  13. 0
  14. ,
  15. x
  16. )
  17. +
  18. min
  19. (
  20. 0
  21. ,
  22. α
  23. (
  24. exp
  25. (
  26. x
  27. )
  28. 1
  29. )
  30. )
  31. (7)
  32. \text{ELU}(x) = \max(0,x) + \min(0, \alpha *(\exp(x)-1)) \tag 7
  33. ELU(x)=max(0,x)+min(0,α∗(exp(x)−1))(7)

通常

  1. α
  2. =
  3. 1
  4. \alpha=1
  5. α=1,我们画出ELU的图像:
  1. y = elu(x)
  2. plot(x.numpy(), y.numpy(),'x','elu(x)', random_fname=True, figsize=(5,2.5))

ELU的函数图像

ELU在负值部分缓慢变得平滑,直到输出等于

  1. α
  2. -\alpha
  3. −α,且
  4. α
  5. α
  6. α是一个可调整的参数,它控制着ELU负值部分在何时饱和。但是引入了
  7. e
  8. x
  9. e^x
  10. ex。其导数的图像为:

ELU的导数图像

SoftPlus

SoftPlus函数与ReLU函数接近,但比较平滑,也是单边抑制的。

  1. SoftPlus
  2. (
  3. x
  4. )
  5. =
  6. 1
  7. β
  8. log
  9. (
  10. 1
  11. +
  12. exp
  13. (
  14. β
  15. x
  16. )
  17. )
  18. (8)
  19. \text{SoftPlus}(x) = \frac{1}{\beta} * \log(1 + \exp(\beta * x)) \tag 8
  20. SoftPlus(x)=β1​∗log(1+exp(β∗x))(8)

其中

  1. β
  2. \beta
  3. β默认为
  4. 1
  5. 1
  6. 1,随着
  7. β
  8. β
  9. β的增加,该函数越来越像ReLU

我们看一下默认情况下的函数图像:

  1. y = softplus(x, beta=10)
  2. plot(x.numpy(), y.numpy(),'x','softplus(x)', random_fname=True, figsize=(5,2.5))

SoftPlus的函数图像

  1. β
  2. =
  3. 10
  4. \beta=10
  5. β=10时,我们看一下函数图像:
  1. y = softplus(x, beta=10)
  2. plot(x.numpy(), y.numpy(),'x',r'softplus(x) with $\beta$ = 10', random_fname=True, figsize=(5,2.5))

SoftPlus设置β=10

其导数为:

  1. d
  2. d
  3. x
  4. Swish
  5. (
  6. x
  7. )
  8. =
  9. d
  10. d
  11. x
  12. (
  13. 1
  14. β
  15. log
  16. (
  17. 1
  18. +
  19. exp
  20. (
  21. β
  22. x
  23. )
  24. )
  25. )
  26. =
  27. 1
  28. β
  29. exp
  30. (
  31. β
  32. x
  33. )
  34. β
  35. 1
  36. +
  37. exp
  38. (
  39. β
  40. x
  41. )
  42. =
  43. 1
  44. 1
  45. +
  46. exp
  47. (
  48. β
  49. x
  50. )
  51. =
  52. σ
  53. (
  54. β
  55. x
  56. )
  57. \begin{aligned} \frac{d}{dx}\text{Swish}(x) &= \frac{d}{dx} \left(\frac{1}{\beta} \log(1 + \exp(\beta x)) \right) \\ &= \frac{1}{\beta} \frac{\exp(\beta x) \beta}{1 + \exp(\beta x)}\\ &= \frac{1}{1 + \exp(-\beta x)} \\ &= \sigma(\beta x) \end{aligned}
  58. dxdSwish(x)​=dxd​(β1log(1+expx)))=β11+expx)expx)β​=1+exp(−βx)1​=σ(βx)​

  1. β
  2. =
  3. 1
  4. \beta=1
  5. β=1,其导数就是
  6. σ
  7. (
  8. x
  9. )
  10. \sigma(x)
  11. σ(x)。我们来看下其导数图像:

SoftPlus的导数图像

Swish

Swish在更深层次的模型上显示出比ReLU更好的性能。Swish的输入从负无穷到正无穷。函数定义为

  1. Swish
  2. =
  3. x
  4. σ
  5. (
  6. x
  7. )
  8. =
  9. x
  10. 1
  11. +
  12. exp
  13. (
  14. x
  15. )
  16. (9)
  17. \text{Swish} = x * \sigma(x) = \frac{x}{1 + \exp(-x)} \tag 9
  18. Swish=x∗σ(x)=1+exp(−x)x​(9)

相当于是对输入

  1. x
  2. x
  3. x进行了门控(通过
  4. σ
  5. \sigma
  6. σ函数),我们看一下它的图像:
  1. y = swish(x)
  2. plot(x.numpy(), y.numpy(),'x','swish(x)', random_fname=True, figsize=(5,2.5))

Swish的函数图像

它的曲线都是光滑的,且处处可导。当

  1. x
  2. x
  3. x增大时,函数值趋于无穷大;当
  4. x
  5. x
  6. x减小时,函数值趋于常数。

Swish函数的导数为下面的公式:

  1. d
  2. d
  3. x
  4. Swish
  5. (
  6. x
  7. )
  8. =
  9. d
  10. d
  11. x
  12. x
  13. σ
  14. (
  15. x
  16. )
  17. =
  18. σ
  19. (
  20. x
  21. )
  22. +
  23. σ
  24. (
  25. x
  26. )
  27. x
  28. =
  29. σ
  30. (
  31. x
  32. )
  33. +
  34. σ
  35. (
  36. x
  37. )
  38. (
  39. 1
  40. σ
  41. (
  42. x
  43. )
  44. )
  45. x
  46. \begin{aligned} \frac{d}{dx}\text{Swish}(x) &= \frac{d}{dx} x \sigma(x) \\ &= \sigma(x) + \sigma(x)^\prime x \\ &= \sigma(x) + \sigma(x)(1 - \sigma(x)) x \end{aligned}
  47. dxdSwish(x)​=dxdxσ(x)=σ(x)+σ(x)′x=σ(x)+σ(x)(1−σ(x))x

其导数的图像为:

Swish的导数图像

Swish的特性:

  • 无上边界:不像sigmoid和tanh函数,Swish没有上边界的,因为它避免了在接近零的梯度中缓慢的训练时间——像sigmoid或tanh这样的函数是有界的,因此需要小心地初始化网络,以保持在这些函数的界限内。
  • 曲线的平滑性:平滑性在泛化和优化中起着重要的作用。与ReLU不同,Swish是一个平滑的函数,这使得它对初始化权值和学习率不那么敏感。
  • 有下边界:这有助于增强正则化效果( x x x左侧慢慢接近于 0 0 0,一定程度过滤掉一部分信息,起到正则化的效果)。

Sigmoid

Sigmoid函数将输入压缩为区间

  1. (
  2. 0
  3. ,
  4. 1
  5. )
  6. (0,1)
  7. (0,1)上的输出。因此,Sigmoid函数通常称为挤压函数(squashing function):
  8. sigmoid
  9. (
  10. x
  11. )
  12. =
  13. 1
  14. 1
  15. +
  16. exp
  17. (
  18. x
  19. )
  20. (10)
  21. \text{sigmoid}(x) = \frac{1}{1 + \exp(-x)} \tag {10}
  22. sigmoid(x)=1+exp(−x)1​(10)

当我们想要将输出看成二分类的概率时,sigmoid此时最常用。然而,sigmoid在隐藏层中较少使用,它通常被更简单、更容易训练的ReLU所取代。

下面我们画出sigmoid函数。

  1. y = sigmoid(x)
  2. plot(x.numpy(), y.numpy(),'x','sigmoid(x)', random_fname=True, figsize=(5,2.5))

Sigmoid的函数图像

其导数计算如下:

  1. d
  2. d
  3. x
  4. sigmoid
  5. (
  6. x
  7. )
  8. =
  9. d
  10. d
  11. x
  12. 1
  13. 1
  14. +
  15. e
  16. x
  17. =
  18. 0
  19. ×
  20. (
  21. 1
  22. +
  23. e
  24. x
  25. )
  26. 1
  27. ×
  28. (
  29. 1
  30. +
  31. e
  32. x
  33. )
  34. (
  35. 1
  36. +
  37. e
  38. x
  39. )
  40. 2
  41. =
  42. (
  43. e
  44. x
  45. )
  46. (
  47. 1
  48. +
  49. e
  50. x
  51. )
  52. 2
  53. =
  54. 1
  55. +
  56. e
  57. x
  58. 1
  59. (
  60. 1
  61. +
  62. e
  63. x
  64. )
  65. 2
  66. =
  67. 1
  68. 1
  69. +
  70. e
  71. x
  72. 1
  73. (
  74. 1
  75. +
  76. e
  77. x
  78. )
  79. 2
  80. =
  81. 1
  82. 1
  83. +
  84. e
  85. x
  86. (
  87. 1
  88. 1
  89. 1
  90. +
  91. e
  92. x
  93. )
  94. =
  95. σ
  96. (
  97. x
  98. )
  99. (
  100. 1
  101. σ
  102. (
  103. x
  104. )
  105. )
  106. \begin{aligned} \frac{d}{dx}\text{sigmoid}(x) &= \frac{d}{dx} \frac{1}{1 + e^{-x}} \\ &= \frac{0 \times (1+e^{-x}) - 1\times (1+e^{-x})^\prime}{(1 + e^{-x})^2} \\ &= \frac{- (-e^{-x})}{(1+e^{-x})^2} \\ &= \frac{1 + e^{-x} - 1}{(1+e^{-x})^2} \\ &= \frac{1}{1 + e^{-x}} - \frac{1}{(1 + e^{-x})^2} \\ &= \frac{1}{1 + e^{-x}} \left( 1 - \frac{1}{1 + e^{-x}} \right) \\ &= \sigma(x)(1 - \sigma(x)) \end{aligned}
  107. dxdsigmoid(x)​=dxd1+ex1​=(1+ex)20×(1+ex)−1×(1+ex)′​=(1+ex)2−(−ex)​=(1+ex)21+ex1​=1+ex1​−(1+ex)21​=1+ex1​(11+ex1​)=σ(x)(1−σ(x))​

其导数的图像为:

Sigmoid的导数图像

在深层网络中,Sigmoid存在三个问题:

  • 饱和的神经元会让梯度消失,即在较大的正数或负数作为输入的时候,梯度就会变成零,使得神经元基本不能更新。
  • 其输出不是以 0 0 0为中心的,而是 0.5 0.5 0.5。我们知道 d L d w = d L d z x \frac{dL}{dw} = \frac{dL}{dz}x dwdL​=dzdL​x,在深层网络中,由于上一层使用的是Sigmoid激活函数,导致该层的输入都是正数。即该层 w w w的梯度取决于 d L d z \frac{dL}{dz} dzdL​,要么都是正的,要么都是负的,出现了zig zag问题。如下图所示,假设最佳更新路线是蓝线所示,由于zig zag问题,使其优化变成缓慢。
  • 指数计算耗时

zig zag更新路径解释,来自cs231n

Tanh

tanh是sigmoid函数的变种,它的函数值变成范围从

  1. 1
  2. -1
  3. 1
  4. +
  5. 1
  6. +1
  7. +1,即变成了以
  8. 0
  9. 0
  10. 0为中心的。
  11. tanh
  12. (
  13. x
  14. )
  15. =
  16. e
  17. x
  18. e
  19. x
  20. e
  21. x
  22. +
  23. e
  24. x
  25. (11)
  26. \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} \tag{11}
  27. tanh(x)=ex+exexex​(11)

我们来画出该函数的图像:

  1. y = tanh(x)
  2. plot(x.numpy(), y.numpy(),'x','tanh(x)', random_fname=True, figsize=(5,2.5))

tanh函数图像

tanh函数具有平滑可微性,和将离群值映射到均值的良好性质。

为什么说tanh函数是sigmoid函数的变种呢?我们来推导一下:

  1. tanh
  2. (
  3. x
  4. )
  5. =
  6. e
  7. x
  8. e
  9. x
  10. e
  11. x
  12. +
  13. e
  14. x
  15. =
  16. e
  17. x
  18. +
  19. e
  20. x
  21. e
  22. x
  23. e
  24. x
  25. e
  26. x
  27. +
  28. e
  29. x
  30. =
  31. 1
  32. +
  33. 2
  34. e
  35. x
  36. e
  37. x
  38. +
  39. e
  40. x
  41. =
  42. 1
  43. 2
  44. e
  45. 2
  46. x
  47. +
  48. 1
  49. =
  50. 1
  51. 2
  52. σ
  53. (
  54. 2
  55. x
  56. )
  57. =
  58. 1
  59. 2
  60. (
  61. 1
  62. σ
  63. (
  64. 2
  65. x
  66. )
  67. )
  68. =
  69. 1
  70. 2
  71. +
  72. 2
  73. σ
  74. (
  75. 2
  76. x
  77. )
  78. =
  79. 2
  80. σ
  81. (
  82. 2
  83. x
  84. )
  85. 1
  86. \begin{aligned} \tanh(x) &= \frac{e^x - e^{-x}}{e^x + e^{-x}} \\ &= \frac{e^x + e^{-x} - e^{-x} - e^{-x} }{e^x + e^{-x}} \\ &= 1 + \frac{-2e^{-x}}{e^x + e^{-x}} \\ &= 1 - \frac{2}{e^{2x}+ 1}\\ &= 1 - 2\sigma(-2x) \\ &= 1 - 2(1 - \sigma(2x)) \\ &= 1 - 2 + 2\sigma(2x) \\ &= 2\sigma(2x) - 1 \end{aligned}
  87. tanh(x)​=ex+exexex​=ex+exex+exexex​=1+ex+ex2ex​=1e2x+12​=12σ(−2x)=12(1−σ(2x))=12+2σ(2x)=2σ(2x)−1

因此,我们可以看到tanh只是sigmoid函数的缩放版本。

tanh函数的导数是:

  1. d
  2. d
  3. x
  4. tanh
  5. (
  6. x
  7. )
  8. =
  9. d
  10. d
  11. x
  12. e
  13. x
  14. e
  15. x
  16. e
  17. x
  18. +
  19. e
  20. x
  21. =
  22. (
  23. e
  24. x
  25. e
  26. x
  27. )
  28. (
  29. e
  30. x
  31. +
  32. e
  33. x
  34. )
  35. (
  36. e
  37. x
  38. e
  39. x
  40. )
  41. (
  42. e
  43. x
  44. +
  45. e
  46. x
  47. )
  48. (
  49. e
  50. x
  51. +
  52. e
  53. x
  54. )
  55. 2
  56. =
  57. (
  58. e
  59. x
  60. +
  61. e
  62. x
  63. )
  64. 2
  65. (
  66. e
  67. x
  68. e
  69. x
  70. )
  71. 2
  72. (
  73. e
  74. x
  75. +
  76. e
  77. x
  78. )
  79. 2
  80. =
  81. 1
  82. (
  83. e
  84. x
  85. e
  86. x
  87. e
  88. x
  89. +
  90. e
  91. x
  92. )
  93. 2
  94. =
  95. 1
  96. tanh
  97. 2
  98. (
  99. x
  100. )
  101. \begin{aligned} \frac{d}{dx}\tanh(x) &= \frac{d}{dx} \frac{e^x - e^{-x}}{e^x + e^{-x}} \\ &= \frac{(e^x - e^{-x})^\prime(e^x + e^{-x}) -(e^x - e^{-x})(e^x + e^{-x})^\prime}{(e^x + e^{-x})^2} \\ &= \frac{(e^x + e^{-x})^2 - (e^x - e^{-x})^2}{(e^x + e^{-x})^2} \\ &= 1 - \left ( \frac{e^x - e^{-x}}{e^x + e^{-x}} \right)^2 \\ &= 1 - \tanh^2(x) \end{aligned}
  102. dxdtanh(x)​=dxdex+exexex​=(ex+ex)2(exex)′(ex+ex)−(exex)(ex+ex)′​=(ex+ex)2(ex+ex)2−(exex)2​=1−(ex+exexex​)2=1tanh2(x)​

其中

  1. d
  2. d
  3. x
  4. e
  5. x
  6. =
  7. e
  8. x
  9. \frac{d}{dx} e^x = e^x
  10. dxdex=ex
  11. d
  12. d
  13. x
  14. e
  15. x
  16. =
  17. e
  18. x
  19. \frac{d}{dx}e^{-x} = - e^{-x}
  20. dxdex=−ex

其导数图像如下所示:

tanh的导数图像

可以看到,当输入接近于

  1. 0
  2. 0
  3. 0时,tanh函数的导数接近于最大值
  4. 1
  5. 1
  6. 1。而输入在任一方向上越远离
  7. 0
  8. 0
  9. 0点,导数越接近
  10. 0
  11. 0
  12. 0

Tanh的缺点类似Sigmoid,不过它是以

  1. 0
  2. 0
  3. 0为中心的,避免了zig zag问题。

如何选择激活函数

我们看了这么多激活函数,到底要如何选择呢?

在深层网络中,首先要尝试ReLU,它具有速度快的优点,如果效果欠佳;

那么尝试Leaky ReLU;

或者tanh这种以零为中心的;

另外,在RNN中常用sigmoid或tanh,作为门控或概率值。

完整代码

完整代码笔者上传到了程序员最大交友网站上去了,地址: 👉 https://github.com/nlp-greyfoss/metagrad

References

  1. DIVE INTO DEEP LEARNING
  2. Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification

本文转载自: https://blog.csdn.net/yjw123456/article/details/122775512
版权归原作者 愤怒的可乐 所有, 如有侵权,请联系我们删除。

“从零实现深度学习框架&mdash;&mdash;神经元与常见激活函数”的评论:

还没有评论