对于 Batch Normalization 的知识最原始的出处来源于《Batch Normalization:Accelerating Deep Network Trainning by Reducing Internal Covariate Shift》这篇论文。
文章开始前,先讲一下 Batch Normalization 有什么好处。
- 加速神经网络的训练过程。
- 减少对 Dropout 的依赖
- 可以用比较大的学习率去训练网络,然后不用在意权重参数的初始化问题。
其实,最重要的就是第一点,Batch Norm 可以加速神经网络的训练
它号称可以加快神经网络训练时间达到 14 倍之多。
上图 a,揭露的是有 Batch Norm 和没有的训练速度对比,数据集是 MNIST。b、c 说明的是经过 Batch Norm 后数据的分布更稳定。
所以,如果你之前没有尝试过,你就可以把它尝试一下。
怎么使用呢?
一般的机器学习框架如 Tensorflow 和 Pytorch 等等,都有现成的 API 可供调用。
你只需要将它添加到激活函数前面或者后面就可以了。
如果,你想了解它的原理和理论的话,可以继续阅读下面的内容。
Batch Normalization
训练神经网络,SGD 是最广为人知的方法,它结合方向传播的机制,在每一步训练过程中动态更新网络的权重数值。
W ← : W − η ∗ ∇ W W\leftarrow :W - \eta*\nabla{W} W←:W−η∗∇W
W 代表权重, η \eta η 代表学习率, ∇ W \nabla{W} ∇W 是每次训练时对应的 W 的梯度,所以学习率和梯度越大,W 变化也就越快。
我们都知道深度神经网络,难于训练。这是因为随着前向传播的进行,每个层的 input 值的分布都会发生变化,这种现象被称为 internal covariate shift,这也导致了人们需要认真处理好参数的初始化和用较小的学习率去训练网络,而学习率越小,训练的时间就越长。
如果大家对 internal covariate shift 概念有些模糊的话,那么怎么样去理解呢?
大家看下面这张图。
神经网络是一层接一层的,每一层的输出可以当作是前一层的输入。比如 L1 层的输出就是 L2 的输入,L2 的输出就是 L3 的输入。
如果输入的值的分布区间变化太大的话,那么每一次训练的时候,每一层都需要去适应新的输入值的分布,这使得模型比较难以稳定下来,而神经网络训练的过程其实就是一个模型参数逐渐稳定的过程。
比如,如果输入代表一个人的某 4 个特征,第一次训练时是 144、2、3、10,神经网络用一些变量去拟合。
但第二次训练时变成了 2,100,3,28,这个时候神经网络需要重新拟合,但因为某些数值变化太大,所以神经网络相对上一次就改动很大。
第三次训练时输入又变成了 20、7、3、18,神经网络又需要做很大的改动。
如果这样持续下去,神经网络要花很长的时间才能稳定下来。
所以,得找一个办法去约束输入的值的分布,让它们不能总是发生急剧的变化,即使要变化,也让它尽量在一个可控的范围内,这样神经网络更新权重时不会那么动荡。
于是,有人提出了自己的解决方法,这就是 Batch Normalization,它号称可以加快神经网络训练时间达到 14 倍之多。
神经网络的训练一般和 2 个参数有关:梯度、学习率。这两个数值越大,训练速度就越快。
所以我们希望以比较大的学习率进行训练,但学习率过高的话,网络反而收敛不了,所以,我们又希望能想办法控制反向传播时的梯度,让它们尽可能高。
我们看看大名鼎鼎的 Sigmoid 激活函数图像。
我们都知道输入值越大或者越小,它越靠近 1 和 -1,从函数图形上可以显著看到在梯度的变化,只有在 0 的时候梯度最大,然后向两极逐渐减少到 0,梯度接近为 0 的时候,训练会变得很慢很慢。
假设输入是 [1.0,2.3,130,244],前面 2 个还好,后面的就容易将整体的输出值推向 Sigmoid 图像中梯度的饱和区间,所以我们常对输入进行标准化,让输入值分布在 0 ~ 1 之间,这有利于网络的训练。
但是,光对输入层进行标准化仍然不够,后面的每一层对应的输入值怎么办呢?
所以,Batch Normalization 就在神经网络的中间层对输入做标准化。
概率论教科书告诉我们,把 X 标准化的公式如下:
W = X − μ σ W = \tfrac{X - \mu}{\sigma} W=σX−μ
μ \mu μ 是 X 变量的期望, σ \sigma σ 是标准差,而 W 就是经过标准化后得到的新的输入变量,这样它的值约束在期望附近的范围,W 内部数值也符合期望为 0,方差为 1。其实,也可以称这一过程为白化。
W 也被称为白噪声,在各个频率内能量大致相同的信号被称为白噪声。我们知道白色的光其实是许多光的混合,白光包含了各个频率成分的光,但各个频率的光能量大致相同,所以才会用白噪声去形容这种频率能量分布大致相同的随机信号。
已经有论文证明将输入进行白噪声化,可以加速神经网络的训练,所以问题是怎么去运用,最简便的方法就是将每一层的输入都进行白噪声化,但是这计算量比较大,Batch Norm 给出了自己的简化方案。
简化1:针对 X 的每个神经元做单独的标准化
假如输入是 x = ( x ( 1 ) , x ( 2 ) , x ( 3 ) , x ( 4 ) ) x=(x^{(1)},x^{(2)},x^{(3)},x^{(4)}) x=(x(1),x(2),x(3),x(4)),那么可以说 x 有 4 个 feature,其实就算 4 个神经元,论文中称它们为 activation。
我们只需要针对这 4 个 activation 做标准化,而不是整个 4 个 activation 一起做标准化,因为后者开销太大了。
需要注意的是,这个时候说的标准化是面向整个训练集样本的
x
^
(
k
)
=
x
(
k
)
−
E
[
x
(
k
)
]
V
a
r
[
x
(
k
)
]
\hat{x}^{(k)} = \frac{x^{(k)}-E[x^{(k)}]}{\sqrt{Var[x^{(k)}]}}
x^(k)=Var[x(k)]x(k)−E[x(k)]
但 Batch Norm 不仅仅是做标准化而已,它还加入了缩放和平移,这通过引入了 2 个参数 γ \gamma γ 和 β \beta β,这使得标准化的结果能够更灵活。
y ( k ) = γ ( k ) x ^ ( k ) + β ( k ) y^{(k)} = \gamma^{(k)}\hat{x}^{(k)}+\beta^{(k)} y(k)=γ(k)x^(k)+β(k)
试想一下极端情况,有时候未经标准化的输入其实就是最理想的状态,强行进行标准化只会让结果变得更加糟糕,所以 Batch Norm 得避免出现这种情况,而 γ β \gamma \beta γβ 可以解决这个问题。
如果 γ ( k ) = V a r [ x ( k ) ] \gamma^{(k)}=\sqrt{Var[x^{(k)}]} γ(k)=Var[x(k)] 同时 β ( k ) = E [ x ( k ) ] \beta^{(k)}=E[x^{(k)}] β(k)=E[x(k)],这样正好可以把 x ^ ( k ) \hat{x}^{(k)} x^(k) 复原成 x ( k ) x^{(k)} x(k)。
听起来很美妙,其实还有一个问题。
我们平常训练时,都是 mini-batch 的方式训练的,但是上面的公式无论方差还是期望,都是基于整个训练集上的数据做的处理,显然这是不切实际的,因此,整个算法需要调整和进行再一步的简化。
你可以想一下,如果训练集有 10 万条数据,为了某个 activation 做标准化,你需要将所有的的数据先读取出来,进行均值和方差计算,这样好吗?
简化2:基于 mini-batch 范围计算期望和方差
实际上 Batch Norm 的 batch 就是 mini-batch 的 batch,所以它是批量化处理的。算法如下图:
相比较于前面的期望和方差计算,现在的 normalization 是针对 mini-batch 的,并不是针对整个数据集的,这使得期望和方差的计算可行和可靠。
上面的 m 是指 mini-batch 的 batch 大小,所以 normalization 某个位置的 activation,均值化的是这一批次同样位置的 activation 的值,它的分母是 m。
需要注意的是 γ \gamma γ 和 β \beta β ,并不是超参数,而是训练的过程学习过来的。
现在有个问题,如果 γ \gamma γ 和 β \beta β 能够被学习,那么,它们一定能够计算出梯度,因为有梯度,所以它们才能够不断被更新。
事实上,可以通过链式法则推导出它们的梯度的。
具体更详细的推导,有兴趣的同学可以自行结合前面的公式验证一下。
既然 γ \gamma γ 和 β \beta β 能够求得梯度,那么也就无需怀疑它们能够学习这一结论了。
如何在 Batch Norm 过的网络中训练和推导?
我们知道神经网络训练的过程当中,参数是不断更新的。
神经网络在推导的过程中,因为不涉及训练,所以它就不需要更新了。
下面是它的算法截图。
神经网络的训练还是按照前面的算法进行。
不同的地方是推导阶段,Batch Norm 对于期望和方差的取值有所改变。
这里,期望和方差是之前训练的时候针对多个 mini-batch 移动平均得到的结果。
我们需要注意的一点是,在普通的神经网络层中每一个 activation 对应一对 γ \gamma γ 和 β \beta β。
在卷积神经网络中,情况有些不同。
Batch Norm 在卷积神经网络上的操作
在全连接层,也是仿射变化中。
如果输入是 x = ( x 1 , x 2 , x 3 ) x=(x1,x2,x3) x=(x1,x2,x3),那么针对这 3 个 activation 单独做 normalization 就好了。
但是,在卷积神经网络中,有一点点不同,因为卷积过程相当于权值共享,并且它的输出也有多个 featuremap。
如果,让 batch norm 仍然符合基本的卷积规律,那么应该怎么做呢?
在每一个 mini-batch 中,针对每一个 featuremap 做整体的 normalization,而不是针对 featuremap 上的单个 activation 做 normalization.
这样做的好处是,把整个 featuremap 当成一个 activation 来看待,每一个 featuremap 对应一对 γ \gamma γ和 β \beta β , 所以 γ \gamma γ和 β \beta β 数量减少了。
如果一个 featuremap 的 size 是 [n,m,p,q],n 是 channel 数量,m 是 mini-batch size,p 和 q 是高和宽,所以它有 nmp*q 个神经元。如果针对每个 activation 做 normalization 那么要保存太多的参数了。
而针对单个 featuremap 做 normalization 自然可以减少好多参数。