简介
神经网络为什么需要激活函数:首先数据的分布绝大多数是非线性的,而一般神经网络的计算是线性的,引入激活函数,是在神经网络中引入非线性,强化网络的学习能力。所以激活函数的最大特点就是非线性。
激活函数对神经网络的重要性自不必多言,来自丹麦技术大学的 Casper Hansen 通过公式、图表和代码实验介绍了 Sigmoid、Tanh、ReLU、Softplus、Softmax、ELU 以及更新的 Leaky ReLU、SELU、GELU、Maxout 以及Swish这些激活函数,并比较了它们的优势和短板。
在计算每一层的激活值时,我们要用到激活函数,之后才能确定这些激活值究竟是多少。根据每一层前面的激活、权重和偏置,我们要为下一层的每个激活计算一个值。但在将该值发送给下一层之前,我们要使用一个激活函数对这个输出进行缩放。本文将介绍不同的激活函数。
1、概述
激活函数是神经网络中一个至关重要的部分。在这篇长文中,我将全面介绍11种不同的激活函数,并阐述它们各自的优缺点。我会给出激活函数的方程和微分方程,还会给出它们的图示。本文的目标是以简单的术语解释这些方程以及图。
2、Sigmoid 函数
Sigmoid 函数是一个Logistic函数,意思就是说:不管输入是什么,得到的输出都在0到1之间。也就是说,你输入的每个神经元、节点或激活都会被缩放为一个介于 0到1之间的值。
sigmoid 这样的函数常被称为非线性函数,因为我们不能用线性的项来描述它。很多激活函数都是非线性或者线性和非线性的组合(有可能函数的一部分是线性的,但这种情况很少见)。
这基本上没什么问题,但值恰好为 0 或 1 的时候除外(有时候确实会发生这种情况)。为什么这会有问题?
这个问题与反向传播有关(有关反向传播的介绍请参阅我的前一篇文章)。在反向传播中,我们要计算每个权重的梯度,即针对每个权重的小更新。这样做的目的是优化整个网络中激活值的输出,使其能在输出层得到更好的结果,进而实现对成本函数的优化。
在反向传播过程中,我们必须计算每个权重影响成本函数(cost function)的比例,具体做法是计算成本函数相对于每个权重的偏导数。假设我们不定义单个的权重,而是将最后一层 L 中的所有权重 w 定义为 w^L,则它们的导数为:
注意,当求偏导数时,我们要找到 ∂a^L 的方程,然后仅微分 ∂z^L,其余部分保持不变。我们用撇号「'」来表示任意函数的导数。当计算中间项 ∂a^L/∂z^L 的偏导数时,我们有:
则sigmoid 函数的导数就为:
当我们向这个 sigmoid 函数输入一个很大的x值(正或负)时,我们得到几乎为 0 的y值——也就是说,当我们输入 w×a+b 时,我们可能得到一个接近于 0 的值。
当 x 是一个很大的值(正或负)时,我们本质上就是用一个几乎为0的值来乘这个偏导数的其余部分。
如果有太多的权重都有这样很大的值,那么我们根本就没法得到可以调整权重的网络,这可是个大问题。如果我们不调整这些权重,那么网络就只有细微的更新,这样算法就不能随时间给网络带来多少改善。对于针对一个权重的偏导数的每个计算,我们都将其放入一个梯度向量中,而且我们将使用这个梯度向量来更新神经网络。可以想象,如果该梯度向量的所有值都接近 0,那么我们根本就无法真正更新任何东西。
这里描述的就是梯度消失问题。这个问题使得 sigmoid 函数在神经网络中并不实用,我们应该使用后面介绍的其它激活函数。
Sigmoid函数的自定义实现:
3、Tanh函数
tanh为双曲正切函数,其英文读作Hyperbolic Tangent。tanh和 sigmoid 相似,都属于饱和激活函数,区别在于输出值范围由 (0,1) 变为了 (-1,1),可以把 tanh 函数看做是 sigmoid 向下平移和拉伸后的结果。
tanh公式:
从公式中,可以更加清晰看出tanh与sigmoid函数的关系(平移+拉伸)。tanh及其导数曲线:
tanh作为激活函数的特点:
相比Sigmoid函数,
1、tanh的输出范围时(-1, 1),解决了Sigmoid函数的不是zero-centered输出问题;
2、幂运算的问题仍然存在;
3、tanh导数范围在(0, 1)之间,相比sigmoid的(0, 0.25),梯度消失(gradient vanishing)问题会得到缓解,但仍然还会存在。
自定义实现:
4、梯度问题
4.1、梯度消失问题
更新特定的权重,则更新规则为:
但如果偏导数 ∂C/∂w^(L) 很小,如同消失了一般,又该如何呢?这时我们就遇到了梯度消失问题,其中许多权重和偏置只能收到非常小的更新。
可以看到,如果权重的值为 0.2,则当出现梯度消失问题时,这个值基本不会变化。因为这个权重分别连接了第一层和第二层的首个神经元:
假设这个权重的值为 0.2,给定一个学习率(具体多少不重要,这里使用了 0.5),则新的权重为:
这个权重原来的值为 0.2,现在更新为了 0.199999978。很明显,这是有问题的:梯度很小,如同消失了一样,使得神经网络中的权重几乎没有更新。这会导致网络中的节点离其最优值相去甚远。这个问题会严重妨碍神经网络的学习。
人们已经观察到,如果不同层的学习速度不同,那么这个问题还会变得更加严重。层以不同的速度学习,前面几层总是会根据学习率而变得更差。
在这个示例中,隐藏层 4 的学习速度最快,因为其成本函数仅取决于连接到隐藏层 4 的权重变化。我们看看隐藏层 1;这里的成本函数取决于连接隐藏层 1 与隐藏层 2、3、4 的权重变化。如果你看过了我前一篇文章中关于反向传播的内容,那么你可能知道网络中更前面的层会复用后面层的计算。
同时,如前面介绍的那样,最后一层仅取决于计算偏导时出现的一组变化:
最终,这就是个大问题了,因为现在权重层的学习速度不同。这意味着网络中更后面的层几乎肯定会被网络中更前面的层受到更多优化。而且问题还在于反向传播算法不知道应该向哪个方向传递权重来优化成本函数。
4.2、梯度爆炸问题
梯度爆炸问题本质上就是梯度消失问题的反面。研究表明,这样的问题是可能出现的,这时权重处于「爆炸」状态,即它们的值快速增长。
注意,这个示例也可用于展示梯度消失问题,而我是从更概念的角度选择了它,以便更轻松地解释。
本质上讲,当 01 时,我们可能遇到梯度爆炸问题。但是,当一个层遇到这个问题时,必然有更多权重满足梯度消失或爆炸的条件。
我们从一个简单网络开始。这个网络有少量权重、偏置和激活,而且每一层也只有一个节点。
这个网络很简单。权重表示为 w_j,偏置为 b_j,成本函数为 C。节点、神经元或激活表示为圆圈。
Nielsen 使用了物理学上的常用表示方式 Δ 来描述某个值中的变化(这不同于梯度符号 ∇)。举个例子,Δb_j 描述的是第 j 个偏置的值变化。
通过下式衡量变化率:
下面式子的论据和上面的偏导一样。即我们如何通过偏置的变化率来衡量成本函数的变化率?正如刚才介绍的那样,Nielsen 使用Δ来描述变化,因此我们可以说这个偏导能大致通过Δ来替代:
先从网络的起点开始,计算第一个偏置 b_1 中的变化将如何影响网络。因为我们知道,在上一篇文章中,第一个偏置 b_1 会馈入第一个激活 a_1,我们就从这里开始。我们先回顾一下这个等式:
如果 b_1 改变,我们将这个改变量表示为 Δb_1。因此,我们注意到当 b_1改变时,激活a_1也会改变——我们通常将其表示为 ∂a_1/∂b_1。
因此,我们左边有偏导的表达式,这是 b_1中与 a_1 相关的变化。但我们开始替换左边的项,先用 z_1 的 sigmoid 替换a_1:
上式表示当 b_1 变化时,激活值 a_1 中存在某个变化。我们将这个变化描述为 Δa_1。
我们将变化 Δa_1 看作是与激活值 a_1 中的变化加上变化 Δb_1 近似一样。
这里我们跳过了一步,但本质上讲,我们只是计算了偏导数,并用偏导的结果替代了分数部分。a_1 的变化导致 z_2 的变化所描述的变化 Δa_1 现在会导致下一层的输入 z_2 出现变化。
表示方式和前面一样,我们将下一个变化记为 Δz_2。我们又要再次经历前面的过程,只是这次要得到的是 z_2 中的变化:
可以使用下式替代 Δa_1:
计算这个式子。希望你清楚地明白到这一步的过程——这与计算 Δa_1 的过程一样。
这个过程会不断重复,直到我们计算完整个网络。通过替换 Δa_j 值,我们得到一个最终函数,其计算的是成本函数中与整个网络(即所有权重、偏置和激活)相关的变化。
基于此,我们再计算 ∂C/∂b_1,得到我们需要的最终式:
4.3、梯度爆炸的极端案例
据此,如果所有权重 w_j 都很大,即如果很多权重的值大于 1,我们就会开始乘以较大的值。举个例子,所有权重都有一些非常高的值,比如 100,而我们得到一些在 0 到 0.25 之间、sigmoid 函数导数的随机输出:
可以合理地相信这会远大于1,但为了方便示例展示,我们将其设为 1。
使用这个更新规则,如果我们假设 b_1 之前等于 1.56,而学习率等于 0.5。
尽管这是一个极端案例,但你懂我的意思。权重和偏置的值可能会爆发式地增大,进而导致整个网络爆炸。
现在花点时间想想网络的权重和偏置以及激活的其它部分,爆炸式地更新它们的值。这就是我们所说的梯度爆炸问题。很显然,这样的网络学不到什么东西,因此这会完全毁掉你想要解决的任务。
4.4、避免梯度爆炸:梯度裁剪/规范
解决梯度爆炸问题的基本思路就是为其设定一个规则。这部分我不会深入进行数学解释,但我会给出这个过程的步骤:
选取一个阈值——如果梯度超过这个值,则使用梯度裁剪或梯度规范;
定义是否使用梯度裁剪或规范。如果使用梯度裁剪,你就指定一个阈值,比如 0.5。如果这个梯度值超过 0.5 或 -0.5,则要么通过梯度规范化将其缩放到阈值范围内,要么就将其裁剪到阈值范围内。
但是要注意,这些梯度方法都不能避免梯度消失问题。所以我们还将进一步探索解决这个问题的更多方法。通常而言,如果你在使用循环神经网络架构(比如 LSTM 或 GRU),那么你就需要这些方法,因为这种架构常出现梯度爆炸的情况