TensorFlow笔记5:神经网络中的激活函数(activation function)

Jan 15,2018   3526 words   13 min

Tags: DeepLearning

在学习深度学习神经网络时,经常会在网络中看到“激活函数”,一般用于某一层的输出数据上,但一直不太明白为什么要有这个东西。 因此这篇博客参考了网上各种资源学习了一下,这里简单做一个总结。主要从以下几个方面介绍:是什么、为什么要用(有什么效果)、常用的激活函数。

一、为什么要用激活函数

简而言之一句话,因为线性模型的表达能力不够,引入激活函数是为了添加非线性因素,解决线性模型所不能解决的问题。

下面来具体说说如何理解,部分内容参考这个网页

如下图所示,是一个单层感知机的示意图,它可以划出一条线,把平面分开。 因此很容易,如果组合多个感知机,自然可以获得更强的分类能力。可以理解为能分割出的图形长得更加“复杂”。 在上图中,最终输出的y可以用如下公式表示。

\[y = w_{2-1}y_1+w_{2-2}y_2+w_{2-3}y_3\]

其中y1、y2、y3分别为单个感知机的输出,w1、w2、w3分别为加和的权重。

\[\left\{\begin{matrix} y_1 = w_{1-11}x_1+w_{1-12}x_2+b_{1-1} \\ y_2 = w_{1-21}x_1+w_{1-22}x_2+b_{1-2} \\ y_3 = w_{1-31}x_1+w_{1-32}x_2+b_{1-3} \end{matrix}\right.\]

但由上面的公式和图可以很明显的发现,最终输出的y与输入仍然是线性关系,只是函数关系更加复杂了。 虽然说可以用很多线性关系逼近非线性,这不是我们想要的最好的结果。我们希望可以获取到一个非线性关系,这样会大大提高分类能力。 我们直接使用step activation function所能获得的分类器, 只能还是线性的,最多不过是复杂的线性组合罢了。 当然你可以说我们可以用无限条直线去逼近一条曲线。 可以是可以,不过比起用non-linear的activation function来说觉得有些低效。

因此聪明的人们自然而然的就想到了,在每一层计算完了之后,对输出的结果加一个激活函数,如下图中的σ()。 而激活函数在这里被定义成了右边这个复杂的式子,这是一个真真正正的非线性函数。当将其扩展到多层后,如下所示,就形成了非线性的网络结构。 与之前没有加激活函数的多层感知机相比,这里每个感知机输出都加上了激活函数,这样最终可以形成一个很复杂的函数模型。 而这个函数模型相比于刚刚那个线性的,表达能力更加强大了。运用这个非线性网络,我们就有可能训练得到类似于下面的这种平滑的分类面。 不是所有的关系都可以用线性模型表达,都是线性可分的。例如下面这个图。 在不改变输入维数的情况下想用线性分类器对它分类几乎是无解的。当然对于这种情况有两种解决办法,一种就是利用线性变换,也就是SVM里的一种思想。 将当前数据映射到另一个空间中去。它在二维线性不可分,也许在三维就线性可分了。例如下面这个经典的动图就说明了这个问题。 第二种就是在不改变输入的情况下在每一层的输出后面都加个非线性函数,构建非线性分类器。在神经网络的训练中,我们采用的就是第二种方法。 而要实现非线性模型,激活函数就成了最佳选择。

因此,综上所述,为了提高网络的表达能力,最好的办法就是使用激活函数,使线性网络变成非线性的。

二、激活函数是什么

上面说了使用激活函数的原因,那么下面就需要了解激活函数本身的相关内容了。 说白了,激活函数就是一个普通函数,我们可以暂时不管它为什么叫这个名字。知道给一个输入就可以得到一个输出。 借用别人的话是,不要误解是指这个函数去激活什么,而是指如何把“激活的神经元的特征”通过函数把特征保留并映射出来,这是神经网络能解决非线性问题关键。 激活函数一般会满足一下性质:

  • 非线性:当激活函数是线性的时候,一个两层的神经网络就可以逼近基本上所有的函数了。如果使用的是恒等激活函数,那么其实整个网络跟单层神经网络是等价的。
  • 可微性:当优化方法是基于梯度的时候,这个性质是必须的。
  • 单调性:当激活函数是单调的时候,单层网络能够保证是凸函数。
  • f(x)≈x:当激活函数满足这个性质的时候,如果参数的初始化是random的很小的值,那么神经网络的训练将会很高效;如果不满足这个性质,那么就需要很用心的去设置初始值。
  • 输出值范围:当激活函数输出值有限时,基于梯度的优化方法会更加稳定,因为特征的表示受有限权值的影响更显著;当激活函数的输出是无限时,模型的训练会更加高效,不过在这种情况小,一般需要更小的learning rate。

满足以上这些性质的函数都可以叫激活函数。因此你完全也可以自己提一个激活函数,效果怎么样就另说了。

三、常用的激活函数

下面介绍一些常用的激活函数以及其使用的注意事项。

1.Sigmoid

上图左边便是Sigmoid函数,表达式如下。

\[f(x)=\frac{1}{1+e^{-x}}\]

它能够把输入的连续实值“压缩”到0和1之间。 如果是非常大的负数,那么输出就是0;如果是非常大的正数,输出就是1。 但它也有如下缺点:

  • Sigmoids saturate and kill gradients.这就是我们常常提到的梯度消失问题。当输入非常大或者非常小的时候(saturation),这些神经元的梯度是接近于0的,从图中可以看出梯度的趋势。所以,需要尤其注意参数的初始值来尽量避免saturation的情况。如果初始值很大的话,大部分神经元可能都会处在saturation的状态而把gradient kill掉,这会导致网络变的很难学习。
  • Sigmoid的output不是0均值。这是不可取的,因为这会导致后一层的神经元将得到上一层输出的非0均值的信号作为输入。如果是按batch去训练,那么那个batch可能得到不同的信号,所以这个问题还是可以缓解一下的。因此,非0均值这个问题虽然会产生一些不好的影响,不过跟上面提到的kill gradients问题相比还是要好很多的。
2.tanh

tanh是上图中的右图,可以看出,tanh跟sigmoid还是很像的,实际上tanh是sigmoid的变形。

\[tanh(x) = 2sigmoid(2x)-1\]

与sigmoid不同的是,tanh是0均值的。因此实际应用中,tanh会比sigmoid更好。 tanh在特征相差明显时的效果会很好,在循环过程中会不断扩大特征效果显示出来,但在特征相差比较复杂或是相差不是特别大时,需要更细微的分类判断的时候,sigmoid效果就好了。 同时sigmoid和tanh作为激活函数的话,注意要对input进行归一化,否则激活后的值都会进入平坦区,使隐层的输出全部趋同。但是ReLU并不需要输入归一化来防止它们达到饱和。

3.ReLU

中文名称叫修正线性单元(Rectified linear unit,ReLU),函数图像如下。 其表达式如下。

\[f(x) = max(0,x)\]

或者写为:

\[ReLU(x) = \left\{\begin{matrix} x\qquad x\geq 0\\ 0\qquad x< 0 \end{matrix}\right.\]

它是一个分段线性函数。输入信号<0时,输出都是0,>0的情况下,输出等于输入。 当输入数据是二维时,ReLU效果如下。 相比于sigmoid/tanh,ReLU只需要一个阈值就可以得到激活值,而不用去算一大堆复杂的运算。 ReLU也有缺点,就是训练的时候很”脆弱”,很容易就”die”了。 一个非常大的梯度流过一个ReLU神经元,更新过参数之后,这个神经元再也不会对任何数据有激活现象了。 如果这个情况发生了,那么这个神经元的梯度就永远都会是0。 实际操作中,如果你的learning rate很大,那么很有可能你网络中的40%的神经元都”dead”了。 如果你设置了一个合适的较小的learning rate,这个问题发生的情况其实也不会太频繁。 同时需要注意,通常来说,很少会把各种激活函数串起来在一个网络中使用的。 更多与ReLU有关的内容可以参考这两篇博客:博客1博客2

4.Leaky ReLUs

就是用来解决这个 “dying ReLU” 的问题的,公式如下。

\[ReLU(x) = \left\{\begin{matrix} x\qquad x\geq 0\\ \alpha x\qquad x< 0 \end{matrix}\right.\]

这里的α是一个很小的常数。这样既修正了数据分布,又保留了一些负轴的值,使得负轴信息不会全部丢失。

以上便是关于激活函数的相关笔记。此外还有更多的激活函数,需要可以百度。

本文作者原创,未经许可不得转载,谢谢配合

返回顶部