前言
这篇文章首先回答了一个人们都忽略的问题:在神经网络中,一个小小的激活函数为何如此重要?它的作用到底是什么?然后我就目前主流的激活函数做了一个对比讨论,并提出一些建议。
激活函数的本质
这里首先引出结论:激活函数是来向神经网络中引入非线性因素的,通过激活函数,神经网络就可以拟合各种曲线。 这里举一个例子来说明:假如我的任务是:将下面的这幅图中的三角形和圆形分开,也就是一个典型的二分类问题。
这个例子比较简单,我们用肉眼能很轻松的得出结论:无法用一条直线将这两种图形完全分开。比如我这里使用一个单层感知机:
方程:
就是上图右边的直线,无论如何更改参数,它都不能很好的完成这个二分类问题,因为问题本身就是线性不可分的。
那么如何才能完美解决这个问题呢?我们试一试多感知器的表现
如果仔细观察,你就会发现,多感知器形成的方程其实也是一条直线(不信你把上面的那个式子因式分解一下),上面已经说了,该问题是线性不可分的,所以多感知器也不能解决这个问题。接下来,就让我们看看激活函数能不能解决。在每一层叠加完后,我们为输出与输入之间加上一个激活函数,此时的方程就变成了这样:
此时,我们就引入了非线性因素,这时的函数表达就极为丰富了。让我们再看一看引入激活函数后的多感知器。
这样也许就能将这个线性不可分的问题解决了。最后也许就是这个样子:
小结
从上面的分析来看,我们可以看出激活函数是神经网络在拟合函数方面超过传统机器学习方法的一个关键因素。对比传统机器学习方法中, 即使是最复杂的SVM, 在拟合方面非线性曲线差神经网络n条街。
常见的激活函数
1. sigmoid函数
-
公式
-
求导
<meta charset="utf-8">
虽然simoid函数有诸多缺陷,但依然是目前主流的激活函数之一。其主要有以下几个缺陷:
1. sigmoid 极容易导致梯度消失问题。 这一问题在RNN 的梯度消失问题 已经做了详细的讲解, 值得一提的是, sigmoid 神经元在值为 0 或 1 的时候接近饱和,这些区域,梯度几乎为 0,这比我们在上篇文章中举得最佳情况要差很多。
2. 计算费时。 在神经网络训练中,常常要计算sigmid的值, 幂计算会导致耗时增加。但这不是什么大问题,最多是换个显卡, 模型跑的时间长点而已。
3. sigmoid 函数不是关于原点中心对称的(zero-centered)。
我们来具体解释一下第三个缺陷, 讨论一下为什么原点对称对激活函数来说这么重要(看我上一篇转载)
2. tanh函数 -- 双曲正切函数
-
公式
-
求导
tanh解决了sigmoid中的 zero-centered 问题, 但是对于梯度消失问题依旧无能为力。
3.ReLU
ReLU是目前使用最频繁的一个函数,如果你不知道你的激活函数应该选择哪个,那么建议你选择ReLU试试。一般情况下,将ReLU作为你的第一选择。
-
公式
- 求导
首先,Relu一定程度上缓解了梯度问题(正区间), 这一点我在《如何解决神经网络中梯度消失,爆炸问题?》 一文中详细讲解。
其次, 计算速度非常快,这一点也可以明显比较出来。
最后, Relu加速了模型的收敛, 比sigmoid与tanh要快很多。
虽然Relu很不错, 但是其依旧有一些缺点:
Relu的输出不是zero-centered
Dead ReLU Problem。 这表示某些神经元可能永远不会被激活, 导致其相应的参数永远不能被更新。其本质是由于Relu在的小于0时其梯度为0所导致的。
我们在这里详细讲解一下Dead Relu问题, 毕竟下面两个Relu 的变体都是用来解决这个问题的。
- 首先我们假设Relu的输入是一个低方差中心在+0.1的正态分布, 此时假设现在大多数Relu的输入是正数,那么大多数输入经过Relu函数能得到一个正值, 因此此时大多数输入能够反向传播通过Relu得到一个梯度, 于是我们的Relu的输入就完成了更新。
- 假设在随机反向传播中, 有一个巨大的梯度经过了Relu且此时Relu的输入为正(Relu是打开的), 那么该梯度会引起Relu输入X的巨大变化, 假设此时输入X的分布变成了一个地方差, 中心在-0.1 的正态分布。此时的情况如下:首先, 大多数Relu的输入变为负数, 输入经过Relu函数就能得到一个0, 这也意味着大多数输入不能反向传播通过Relu得到一个梯度,导致这部分输入无法通过更新。
4. 其余激活函数
除了以上介绍的几个激活函数外, 还有一些比较小众的激活函数,如 Leaky ReLU、PReLU 或者 Maxout。
最后: 如何选择激活函数?
如何选择激活函数是一门大学问, 我的研究还不够深,做的实验还不够多, 因此很难有所定论, 这里先引用几篇博客里面和吴恩达视频中的结论。
1.除非在二分类问题中,否则请小心使用sigmoid函数。
2.可以试试 tanh,不过大多数情况下它的效果会比不上 ReLU 和 Maxout。
3.如果你不知道应该使用哪个激活函数, 那么请优先选择Relu。
4.如果你使用了Relu, 需要注意一下Dead Relu问题, 此时你需要仔细选择 Learning rate, 避免出现大的梯度从而导致过多的神经元 “Dead” 。
5.如果发生了Dead Relu问题, 可以尝试一下leaky ReLU,ELU等Relu变体, 说不定会有惊喜。