本文原载于https://imlogm.github.io,转载请注明出处~
摘要:BN最早出现在Google的Inception(v2)网络中,它的效果让人瞠目结舌。那么,BN的原理是什么?为什么会有这么好的效果?
关键字:深度学习, BN, 优化
1. 从数据预处理谈起
如果你有相关的机器学习经验的话,应该知道在进行模型训练前需要进行数据预处理。而数据预处理中非常重要的一步是“归一化”(Normalization)。
为什么需要归一化呢?其一,因为机器学习的训练本质上是让模型学习到数据的分布。如果训练集的分布与测试集的分布有很大不同,那么训练出来的模型泛化能力就差,人们自然希望先把训练集和测试集分布调整到相近再训练;其二,实践表明,一个好的数据分布,有利于加速模型的训练及减少不收敛出现的概率,人们自然希望把数据调整到“好的分布”再训练。
怎样的分布算是好的分布?如图1左小图,数据分布的中心不在原点,数据分布的x方向明显长于y方向,这不是一个好的分布;如图1右小图,数据分布的中心在原点,数据分布的各个方向长度都接近1,这是一个好的分布。当然,并不是说图1的分布不能用于训练,只要训练集和测试集的分布相同,无论怎样差的分布,理论上都能训练出具有优秀泛化能力的模型;只是分布越差,训练起来越困难。
在数据预处理中归一化常用的方法是主成分分析( PCA )与 白化( whitening )。它们的作用,就是把数据变到“好的分布”下。如果不了解这两项技术,可以看这篇文章:主成分分析( PCA )与 白化( whitening )-Pony_s
2. 预处理还不够
也许数据预处理在机器学习领域还够用,但是到了深度学习领域,就显得力不从心了:因为深度网络很深,每一层过后,数据的分布都会产生一定的变化;浅层的微小变化经过一层层放大,到了深层就是很大的变化了。
论文把这样现象叫做“Internal Covariate Shift”。
如果能有一种方法,能够在每一层的输出上进行数据归一化就好了。Batch Normalization正是要解决这个问题。
3. Batch Normalization
公式:
$$\widehat{x}^{(k)}=\frac{x^{(k)}-E[x^{(k)}]}{\sqrt{Var[x^{(k)}]}}$$
如何理解这个公式?结合图1,先看分子部分,$E[x^{(k)}]$表示的是$x^{(k)}$的期望,也就是平均值。$x^{(k)}-E[x^{(k)}]$,每个数据减去整体平均值,那么得到数据关于原点对称,这步的作用就是使数据分布关于原点对称。
再看分母部分,$Var[x^{(k)}]$表示求数据的方差,$\sqrt{Var[x^{(k)}]}$表示数据的标准差。分子除分母之后,数据的分布在各个方向上的标准差都为1,所以这步的作用是使数据分布在各个方向的标准差都为1。
4. 没有那么简单
事实情况还没有那么简单。要知道并不是无脑在每一层的输出都加上上面的公式就能得到好的结果。万一某一些层就是要特殊的数据分布怎么办?我们不如把这个问题交给深度网络自身。
$$y^{(k)}=\gamma ^{(k)}\widehat{x}^{(k)}+\beta ^{(k)}$$
公式中$\gamma ^{(k)}$和$\beta ^{(k)}$都是通过网络学习得到的。不难想到,当:
$$\gamma ^{(k)}=\sqrt{Var[x^{(k)}]}\space ,\space \space \beta ^{(k)}=E[x^{(k)}]$$
相当于没有加入归一化。
关于Batch Normalization的进一步展开,可以看这篇文章:Batch Normalization 学习笔记-hjimce的专栏
5. BN加在什么地方
论文给出的位置是加在激活函数前,也就是“conv -> bn -> relu”,残差网络ResNet的代码也用的这种方式。当然,我也看到过加在激活函数后的,也就是“conv -> relu -> bn”。
你也可以看下这个回答:CNN中batch normalization应该放在什么位置?-lystdo的回答
6. BN是万能的?
首先,提醒初学者一个容易掉进的坑:
如果BN的期望和标准差是每个mini-batch各自计算得出的,那么batch_size不要设太小,以免每个mini-batch得到的期望和标准差波动太大。
其次,不得不说,BN的效果确实显著:训练速度加快了,收敛精度也提高了。但是还是有一些情况不能使用BN:NTIRE2017夺冠的EDSR去掉了Batch Normalization层就获得了提高为什么?-pby5的回答