CNN数值——xavier(上)

本文收录在无痛的机器学习第一季

感谢@Express@夏龙对本文的审阅。欢迎大家多多提出宝贵意见。

上一回我们做了三个小实验。第一个是正常的实验,表现优异;第二个实验我们把初始化调整得很奇葩(为什么奇葩?),最终训练结果弱爆了;第三个实验我们把非线性函数重新换回sigmoid,模型奇迹般地回血,虽然表现不够完美,但也算是十分优异了。

于是ReLU被众人推到墙角,开始质问。

其实ReLU也是很委屈的,前面说过他的优势在与模型前向后向计算的过程中,它可以更好地传递数据,不会像sigmoid那样有梯度传递的问题,但是它又缺少了sigmoid的重要特性,那就是对数据的控制力。

我们知道sigmoid可以把任意维度的数据压缩到0到1之间,这是它最强力的一个特点。所以在使用sigmoid时,我们不用太担心数据的幅度问题,因为只要使用一个sigmoid,数据的幅度就会得到良好的控制(当然了,全是正数这件事其实也有点不太靠谱,要是像tanh那样有正有负就更好了)。而我们从上一次的实验中可以看出,采用ReLU的非线性函数,数据的维度完全没有得到控制。有的幅度到达了上千,有的依然是一个极小的小数。这说明ReLU在压缩数据幅度方面存在劣势。

于是乎我们有了以下的经验总结:

  1. sigmoid在压缩数据幅度方面有优势,对于深度网络,使用sigmoid可以保证数据幅度不会有问题,这样数据幅度稳住了就不会出现太大的失误。
  2. 但是sigmoid存在梯度消失的问题,在反向传播上有劣势,所以在优化的过程中存在不足
  3. relu不会对数据做幅度压缩,所以如果数据的幅度不断扩张,那么模型的层数越深,幅度的扩张也会越厉害,最终会影响模型的表现。
  4. 但是relu在反向传导方面可以很好地将“原汁原味”的梯度传到后面,这样在学习的过程中可以更好地发挥出来。(这个“原汁原味”只可意会,不必深究)


这么来看,sigmoid前向更靠谱,relu后向更强。这么一看似乎一切又回到了起点,到底哪个非线性函数更好呢?

要评判哪个非线性函数更好,不但要看自己本身,还要看它们和整体模型阵型的搭配情况(BP很重要!)sigmoid在学习方面存在弱点,有什么办法能帮助它呢?这个我们后面再说。那relu的数据幅度呢?有没有什么办法能够帮助它解决呢?

众人想了好久,又重新看向了初始化……

初始化:(黑人问号脸)?

xavier

大家突然想起来,刚才和relu完美配合的那个初始化叫什么来着?哦对,xavier。我们就来看看这个初始化方法的由来。xavier诞生时并没有用relu做例子,但是实际效果中xavier还是和relu很搭配的。

xavier是如何完成初始化工作的呢?它的初始化公式如下所示:

定义参数所在层的输入维度为n,输出维度为m,那么参数将以均匀分布的方式在[-\sqrt{\frac{6}{m+n}},\sqrt{\frac{6}{m+n}}]的范围内进行初始化。


那么这个公式是如何计算出来的呢?关于这个问题我们需要一段漫长的推导。在推导之前我们要强调一个关键点,就是参数的标准差,或者方差。前面我们提到了Caffe中的debug_info主要展示了数据的L1 norm,对于均值为0的数据来说,这个L1 norm可以近似表示标准差。

我们将用到以下和方差相关的定理:

假设有随机变量x和w,它们都服从均值为0,方差为\sigma的分布,那么:

  • w*x就会服从均值为0,方差为\sigma^2的分布
  • w*x+w*x就会服从均值为0,方差为2*\sigma^2的分布


以下内容主要来自论文《Understanding the difficulty of training deep feedforward neural network》的理解,这里将以我个人的理解做一下解读,如果有错欢迎来喷。

前面两个定理的变量名称是不是有点熟悉?没错,下面我们说的就是参数w和x。这里暂时将偏置项放在一边,同时我们还要把一个部分放在一边,那就是非线性部分。这篇论文心目中的理想非线性函数是tanh。为啥呢?

在大神的假想世界中,x和w都是靠近0的比较小的数字,那么它们最终计算出来的数字也应该是一个靠近0,比较小的数字。我们再看一眼tanh函数和它对应的梯度函数:


这两张图有点大,不过可以看出来,如果数值集中在0附近,我们可以发现,前向时tanh靠近0的地方斜率接近1,所以前辈告诉我们,把它想象成一个线性函数。

下面这张梯度的图像也是一样,靠近0的地方斜率接近1,所以前辈又一次告诉我们,把它想象成一个线性函数。

什么,你不信?

把它想象成一个线性函数。

把它想象成一个线性函数。

把它想象成一个线性函数……

好了,现在这个挡在中间的非线性函数硬生生掰成一个线性函数了,为了理论的完美我们也是什么也不顾了。下面就要面对一个问题,如何让深层网络在学习过程中的表现像浅层网络?

我们的脑中迅速回忆起我们接触过的浅层模型——logistic regression,SVM。为了它们的表现能够更好,我们都会把特征做初始化,细心处理,比方说做白化处理,使他的均值方差保持好,然后用浅层模型一波训练完成。现在我们采用了深层模型,输入的第一层我们是可以做到数据的白化的——减去均值,除以一个标准差。但是里面层次的数据,你总不好伸手进入把它们也搞白化吧!(当然,后来真的有人伸进去了,还做得不错)那我们看看如果在中间层不做处理会发生什么?

我们假设所有的输入数据x满足均值为0,方差为\sigma_x的分布,我们再将参数w以均值为0,方差为\sigma_w的方式进行初始化。我们假设第一次是大家喜闻乐见的卷积层,卷积层共有n个参数(n=channel*kernel_h*kernel_w),于是为了计算出一个线性部分的结果,我们有:

z_j=\sum^n_i{w_i*x_i}

这个公式的下标不准确,大家姑且这么看了,也就是说,线性输出部分的一个结果值,实际上是由n个乘加计算出来的,那么下面是一道抢答题,按照我们刚才对x和w的定义,加上前面我们说过的两个方差计算公式,这个z会服从一个什么分布呢?

均值肯定还是0嘛,没得说。

方差好像积累了一大堆东西:n*\sigma_x*\sigma_w

然后我们通过那个靠意念构建的具有“线性特征”的非线性层,奇迹般地发现一切都没有变化,那么下一层的数据就成了均值为0,方差为n*\sigma_x*\sigma_w的“随机变量”(姑且称之为随机变量吧)。

为了更好地表达,我们将层号写在变量的上标处,于是就有:

\sigma^2_x=n^1*\sigma^1_x*\sigma^1_w

我们将卷积层和全连接层统一考虑成n个参数的一层,于是接着就有:

\sigma^3_x=n^2*\sigma^2_x*\sigma^2_w

如果我们是一个k层的网络(这里主要值卷积层+全连接层的总和数),我们就有

\sigma^k_x=n^{k-1}*\sigma^{k-1}_x*\sigma^{k-1}_w=n^{k-1}*n^{k-2}*\sigma^{k-2}_x*\sigma^{k-2}_w*\sigma^{k-1}_w

继续把这个公式展开,就会得到它的最终形态:

\sigma^k_x=\sigma^1_x*\prod_{i=1}^{k-1}n^i*\sigma^i_w

可以看出,后面的那个连乘实际上看着就像个定时炸弹(相信看到这,我应该能成功地吸引大家的注意力,帮助大家把非线性函数线性化的事情忘掉了……),如果n^i*\sigma^i_w总是大于1,那么随着层数越深,数值的方差会越来越大,反过来如果乘积小于1,那么随着层数越深,数值的方差就会越来越小。

越来越大,就容易Hold不住导致溢出,越来越小,就容易导致数据差异小而不易产生有力的梯度。这就是深层模型的一大命门。

公式推到这里,我们不妨回头看看这个公式:

\sigma^2_x=n^1*\sigma^1_x*\sigma^1_w

你一定会有这样一个想法(一定会有!),如果\sigma_x^2=\sigma_x^1,接着我们保证每一层输入的方差都保持一致,那么数值的幅度不就可以解决了么?于是乎:

\sigma^1_w=\frac{1}{n^1}

我们用均值为1,方差为上式的那个数字做初始化,不就可以解决了?

不错,从理论上讲是这个思路,不过,这只是这个思路的开始……

广告时间

更多精彩尽在《深度学习轻松学:核心算法与视觉实践》

编辑于 2017-11-22

文章被以下专栏收录