一、概述
这一集讲了选择合适的activation function
。
预处理数据。
Weight
的初始化。
Batch Normalization
的运用。
如何监督整个学习过程以便提早发现问题。
以及如何优化hyper parameter
。
二、Neural Networks
讲激活函数之前讲了很多关于神经网络的历史。体会最深的就是,一个事物的兴起,不是自身力量就足够的。如果没有摩尔定律,没有大数据,神经网络的发展还不会被提上时代的议程。
激活函数,用来在线性的输入之后产生非线性的输出。非线性代表着解决更加复杂问题的能力。虽然说线性也能解决高复杂度问题,比如素描中画足够多的直线是可以在中间截出一个圆。但是远不如用非线性函数来得快和准确,并且其复杂性也让实践变得很难。
下图是一些激活函数。
- Sigmoid 历史上,使用最多的是
Sigmoid
。它接收一个实数,然后产生一个[0, 1]
的输出。 历史上使用最多,因为Sigmoid function
可以用来表示一个neuron
是否被激活
这样的的概念。 但是Sigmoid
有三个问题: 第一个问题,饱和的neuron会杀死
backpropagation过程中的gradient
。这个问题被称作vanishing gradient problem
。 上图中,解释了vanishing gradient problem
。 当x = 10
和x = -10
的时候,就是一个sigmoid
就产生了一个saturated neuron
(饱和神经元)。这个neuron
的值接近0
,或者接近1
。如上图,在x = 10
和x = -10
的地方,y
基本上是0
和1
。Sigmoid function
的导数是sigmoid * (1 - sigmoid)
。 想象一下,sigmoid
无限接近于1
的时候,dsigmoid = ->1 * (1 - ->1) = -> 0
(用->表示极限了…)。 另外,sigmoid
无限接近于0
的时候,dsigmoid = ->0 * (1 - ->0) = -> 0
。 两种情况,在链式法则下,所有的数乘以0
都等于0
,因此gradient
就被杀死了。之后的gradient
都等于0
。 第二个问题,Sigmoid function的输出不是趋向于0的(zero-centered)。 这个翻译可能不是很正确,原文写出来了zero-centered
,结合解释体会一下。 上图中,如果使用Sigmoid
,当input x
总是正的时候,w
的gradients
会是怎样的? 答案是,w
的gradients
要么都是正的,要么都是负的。 上图中,如果x
都是正数,那么sigmoid
的输出y
在[0.5,1]
。 那么dsigmoid = [0.5, 1] * (1 - [0.5, 1]) ~= [0, 0.5]
总是> 0
的。 假设最后整个神经网络的输出是正数,最后w
的gradient
就是正数;如果输出是负数,最后w
的gradient
就是负数。如下图。 这样的gradients
造成的问题就是,如上图中,你的w
要么全是正的(x
是正的),要么全是负的(x
是负的)。 假设最优的一个w
向量应该是在上图中第四象限,那么要将你的w
优化到最优状态,就必须走这样的之字形(zig-zag),因为你的w
要么只能往下走(负数),要么只能往右走(正的)。达成优化的效率十分低下,模型拟合的过程就会十分缓慢。 如果要深究的话,查找有关费雪矩阵(Fisher Matrices
)以及自然梯度(Natural Gradient
)的相关论文,证明过程是挺复杂的。 因此,在input
和activation function
的处理中,最好都追求0趋向
的数据。好的数据预处理和选用属性更佳的激活函数,就可以达到这样的效果。(貌似Sigmoid
不好的地方就在于它的求导函数让gradient
的值很蛋疼)。 第三个问题,exp()函数是消耗计算资源的。 虽然说Convnet
中大部分的计算资源都将被大量的convolution
占用,但相比其他activation function
,exp()
都是不小的一笔计算开支。 - 选择其他Ativation Function 1) tanh
`tanh`解决了`sigmoid`的第二个`数据非0趋向`的问题。
到这里,还是理解不了这个zero-centered
这个问题。 我猜是不是因为sigmoid
的区间只在[0,1]
,那么输出的分布是不均匀的,输出都是大于等于0
的数,所以称为non zero-centered
。 而像tanh
,输出区间分布在[-1, 1]
,分布能比较均匀,因此是zero-centerred
。 但是,reLU
呢?区间是[0,x]
啊… 然后google了一下,大家都在说zero-centered
的作用,但是没有解释zero-center
的意思…看了一些文章之后大概有了一点感觉。 点这里看Quora这位大哥对于zero-center的举例 看了这个之后,我觉得zero-center
应该是activation function
应该有[0,0]
这个值。 看sigmoid
,是没有[0,0]
点的,因此就算你的dataset
预处理得离0
(normalized
)有多好,但是sigmoid
还是不能给你一个良好的输出(这个输出永远大于0)。 然后结合Quora
中那位大哥对于perceptron
的解释,觉得对于神经网络做出决定来说,确切的0
和确切的非0
是很重要的。中间的其他输出值都是杂项,是噪音,会干扰最终的决定,也会阻碍拟合的进程(噪音越严重,w
的优化过程就越缓慢)。 这样想来,tanh
也不行。下面还有负数的输出,同时也没有解决vanishing gradient
的问题。![](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNTE5MTQ0NjM5NTA3?x-oss-process=image/format,png) 如上图,`tanh`的输出区间是`[-1, 1]`。虽然比`sigmoid`好一些,但是在`饱和神经元`的情况下,还是没有解决`vanishing gradient`的问题。
2) ReLu 12年的时候Krizhevsky
提出了使用max(0,x)
这样的函数让神经网络的拟合更加迅速。ReLu
解决了vanishing gradient
的问题,至少在正区间内,神经元不会饱和了。 这里有疑问,x无限接近于0的时候,不就饱和了么… 那backprop的时候,gradient还是接近0的…再体会一下。 同时,解决了浪费计算资源的问题,ReLu
只是做一个门槛比较(threshholding
),计算效率高。 最后,使用ReLu
让神经网络拟合更快。 但是看到这里懵了,说ReLu
不是zero-centered
… 刚才有google了一些文章,讲relu
的,看到了relu
容易die
的缺点,差不多就是一旦learning_rate
没有设置好,导致第一次update w
的时候让input
到了negative regime
(负数那边),那么这个relu
节点就死亡了,再也不会被激活(因为relu
的导数drelu = 1 if x > 0 else 0
,如果流过去的input
是0
,流回来的gradient
就是0
,w
不会被更新),导致w
不会被更新,也导致这个neuron
不再学习了。如下图。 但是关于这个zero-centered
,课后再查资料吧…估计zero-centered
就是中心点要在[0,0]
上… 3) Leaky Relu 为了解决relu neuron will die
的问题,leaky relu
把负数区域的斜率调整了一些。如图。 这里提到了这个0.01
可以是任意数值,因此有了Prelu
,将这个x
之前的系数设置成变量alpha
,这个变量同样可以通过backprop
来学习,不断进行调整。 4) ELU 这个activation function
有relu
所有的有点,并且输出的均值是接近0
的。 但是可以看到,计算的时候是需要exp()
的,产生了效率问题。 5) Maxout "Neuron" 有优点但是并不常见,如果使用这个激活函数,意味着每个neuron
将有两组w
,感觉增加了神经网络的负担。 - 小结 使用
relu
,但是小心选择learning_rate
。 尝试其他的函数,但是都不常用。 不要使用sigmoid
。
三、Data Preprocessing
在预处理dataset
时,可以看到下图中的zero-centered
和normalized data
。
另外,在实践中,也有看到下面PCA
主成分析和Whitening
的。
不过,在针对图片的神经网络中,不太会用到PCA
或者Whitening
,或者是Normalization
,常用的就是centered
。
根据视频所说,per-channel mean
的方式更加常用。因为只需要考虑颜色通道的3
个数值,而不用考虑图片整体的像素矩阵
。
四、Weight Initialization
问题:为什么不能将w初始化成0?
所有的neuron的输出都将是一样的,backprop时的gradient也将是一样的,不利于学习。
关于W
的初始化,有下面几种方式的演进。
- 小随机数 这种方式适用在规模较小的神经网络中,但同时也很容易造成激活之后输出结果的不均匀分布。 下面会用图示来说明这个问题。 为了验证输出结果分布不均的问题,先创建了一个
10
层的每层有500
个neuron
的神经网络;使用tanh
作为非线性激活函数;W
的初始化就用上图中的公式。代码如图。 下面要做的就是,要统计一下上面这个网络每一层在tanh activation
之后的输出,并且看一下每层的输出的平均数和标准方差。画图如下。 通过上图可以看到,刚开始的时候,每一层的输出的平均值是一直是0
;再来看std
,从1
开始,一直下降,然后突然就栽到一直保持0
的状态。 然后看下直方图,第一层还挺正常的,数据还能均匀分布在[-1,1]
的区间内。之后就迅速收窄,所有的tanh
输出都是接近0
了。 只要tanh
的输出接近0
,那么在backprop
的时候,gradient
几乎也是0
,因此不仅W
得不到良好的更新调整,整个网络就像是在一个死循环一样。 这也是vanishing gradient problem
的表现。 那么,如果将0.01
调整成1.0
会如何呢?调整如下图。 可以看到,这是另一个极端。看直方图,几乎所有的neuron
都饱和了,意味着tanh
的输出要么是1
,要么是-1
。 在backprop
的时候,所有的gradient
都是0
,那么什么都得不到更新,网络也不会进行任何学习。Weight Initialization
的选择要十分小心。在上述情况下,那么W
的值应该介于1
和0.01
之间。可以不断尝试在这中间找一个合适的值。 看一下学者对初始化的见解。 下图是10年提出的Xavier Initialization
。 用图中的公式初始化,可看到直方图的分布是很均匀的,没有过于饱和的地方,网络也能在合理的backprop
中良好学习。 虽然看上图的std
还是会降到0
。Andrej解释是因为这个学术论文是没有考虑tanh
作为非线性函数的。即便如此,这样的下降看起来不像之前那样迅速,神经网络也能正常进行学习。 再继续,如果将tanh
换成relu
,应该好似用下面的初始化方式。如图。 - Batch Normalization 之前所强调的,就是非线性之后的一个正态分布是很重要的。 下面的方法,就是怎么得到一个正态分布的非线性输出。 学者提出,既然那么想要正态分布,那就做一个正态分布即可。 上图中,
batch norm
的公式将对输入的每一个维度作normalization
。 这样的操作之后,所有的输出的数值就能成一个正态分布了。 通常,会将batch normalization layers
插入到网络中,确保每一个input x
的每一个特征维度,都能产生一个unit gaussian activation
。如图。 问题:需要给tanh函数传递unit gaussian的输入吗?本来,在没有高斯分布的时候,tanh层的输入可以是任意可以调整的(backprop回来的gradient调整了w,下次进入tanh的输入和上次会有不同,并且分布是相对随机的,因此神经网络可以在这种随机分布情况下,通过更新w来控制tanh的输出的区间);但是一旦在tanh前加了一层BN(Batch Normalization)层,那么进入tanh的输入总是高斯分布的,网络本身就没有任何办法调整tanh的输出区间了。 为了解决这个问题,就用到了下图中的方法。 在使用BN
之后,网络可以选择对x
增加一个系数γ
,并可以选择增加一个量β
。保证输出的值在横向和竖向都可以动态调整。 在使用了这个方式之后,网络就能在BN
之后,对tanh
的输入进行控制,如果需要的话。它可以选择缩放这个输入,或者上移,下移。 值得注意的是,公式中这个γ
和β
是可以通过bakprop
来学习的。因此,有了这两个变量之后,BN
就可以变成一个恒等函数(identity function
),意思就是可有可无了,但是是由网络自身来控制。 一旦神经网络学会了γ
和β
,它可以选择将BN
的输出还原回BN
的输入,就像BN
根本不存在一样。 网络可以通过学习,自己决定是否使用BN
。如果使用BN
是有好处的,那就保留,反之,则通过γ
和β
重置BN
的变换。 整个BN
的过程如下。Batch Normalization
的好处有如下几点: 它能优化流经网络的gradient,让其不会vanish消失掉,也不会直接爆炸。它能让我们设置更高的learning_rate,网络能在更短的时间内完成训练。它让我们不再那么依赖Weight Initialization。这是最重要的一点。它的作用像是之前提到过的regularization。可以减少之后要用到的dropout的量。可以这样想,Andrej提到了BN的作用就是将原本单独的一个input x,在BN之后,就和这个batch中其他的input捆绑到了一起。在weight regularization中,原理选择出分布更加均衡的weight来让网络的训练结果更加generalize。类似的,这里的input在被捆绑到一起之后,能够让神经网络更好地意识到每个特征之间的关系,比起单独单干的一个input,这样的训练方式可能可以取得更好的效果。(纯属胡说八道也说不定,以后再体会)
五、检查网络的学习过程
这里讲了一下从处理数据到完成训练一个神经网络的的流程。一旦发现哪一部有问题,可以提前停止,进行调整。
- 预处理数据 第一步就是预处理数据,如下图。
- 选择神经网络结构 选择适当的网络结构。
- 计算一次,确保loss在正常的区间 先将
regularization
设置为0.0
。 前面的视频说过,如果W
初始化没有问题,那么第一次的loss
应该在-log(num_classes - 1)
,大约2.3
。 然后提高一些regularization
的值,比如到1e3
,正常情况下,loss
也应该有所提高。loss
也有所提高,说明一切正常。 - 小批量数据 之后,可以先选取很小一部分数据来训练,这里只选取了
20
个sample
。 这一步是确保创建的网络能够overfit
小批量的数据。如下图。 这里,loss
接近0
,training acc
等于1
,成功overfitting
了这20
个sample
- 完整训练 这一步,首先从很小的regularization开始,先找到适当的learning_rate,可以使
loss
下降。 如果loss
没有下降,可能是learning_rate
设置太小。
六、Hyperparameter Optimization
- 从粗糙到精细 可以先用一小部分的数据,像上面使用的
20
个,找到一个大致的能让网络学习起来的参数。 然后慢慢微调。 如果在调试过程中看到loss
大于了原有loss
的三倍,直接停止训练,重新调整参数。 寻找参数的方式如下图。 - 微调 找到最初的参数之后,要进行微调。微调的时候,可以调整随机的区间,然后看哪一组数据的效果最好。 上图中,最下面红框中的准确率有
%53
,但是要特别当心。因为产生这个准确率的reg
和lr
都逼近边界值(10和1)。 说明了在机器学习中要也别当心边界值,边界值上的好效果可能意味着有内在的问题。当然也可能没有。 - 可视化训练过程以及观察数值比例 将
loss
的下降可视化,如果平稳下降,没有出现奇葩情况,说明一切正常。如图。 最后,也可以检查W
的更新值和W
的比值。正常应该再1e-3
左右。
七、总结
这一集介绍了激活函数;介绍了预处理数据的几种方式;学习了如何初始化Weight
;学习了Batch Normalization
的作用;如何检查一个神经网络的参数是否正常,以及如何优化hyper parameter
。