Batch Normalization
背景介绍:Problem & Solution
1. Internal Covariate Shift
在训练神经网络时,由于weights的改变,每一个神经元的激活输出分布也会发生变化,这种现象叫做Internal Covariate Shift。上一层neuron的输出分布的频繁变化,会加大后面neurons的学习的难度,因为它们需要相应地改变weights,以适应这种分布变化。特别是当网络加深时,前几层的weights即使只有微小的改变,传递到后面也会有很大的影响。
训练模型时,为了得到更好的结果,我们需要注意让训练集的分布尽量与测试集的分布一致,而这一点对于训练过程也适用。如果在训练时,每一个神经元的输入分布保持不变,那么神经元就能更快地收敛,不用因为输入分布的变化而频繁更新权值。
2. Solution
为了加快模型的收敛,Internal Covariate Shift问题需要得到解决。即在训练过程中,每个神经元的输入分布需要尽可能稳定,以便神经元能尽快学习到和其他神经元的联系和一些非线性特征,而不是频繁update只为适应新的输入分布。
一个经典的方案是白化(whitening)输入,即让输入集合是均值为0,方差为1的标准分布,并且其中输入的各维度之间不相关。这个方法计算复杂,也不利于反向传播。
借鉴完全白化(whitening)每一层的输入的思想,加以简单化,得到了batch normalization方法。
batch normalization
简化的目标是让得到的BN transform尽量少地增加计算量和额外的参数,同时处处可导,以便做反向传播。
1. 维度独立标准化
在完全白化的过程中,为了使输入向量的各个维度之间不相关,需要计算向量的协方差矩阵和反向平方根,十分消耗计算资源。为了降低计算量,BN以每个feature为单位,做标量的标准化,只需计算标量的mean和variance即可。
公式
假设一个layer的输入是一个d维的向量:
x ⃗ = ( x 1 , x 2 , . . . , x d ) \vec{x}=(x^1, x^2,...,x^d) x=(x1,x2,...,xd)
对其中的每一个维度独立地做标准化:
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[xk]xk−E[xk]
相对于完全白化来说,此时的输入向量的个维度之间依然相关,但是这种方法依然可以加速模型收敛速度,稳定输入分布。
仿射变换
单纯按上述方法做标准化会产生一个问题,它有可能会缩小输入的值域,进而限制了模型的表达力。比如说,在按维度标准化以后,输入的各个维度可能全部密集集中在0附近。对于激活函数例如sigmoid和Relu来说,输出范围会被限制在0附近值所对应的小部分值域,而无法达到标准化之前的值域范围。
为了保证BN不会缩小输出值域,在标准化后,可以加入一个仿射变换,保证输出的范围可以不被BN所限制,对于每个标准化之后的feature
x
k
x^k
xk:
y ^ k = γ k x ^ k + β k \hat{y}^k = \gamma^k\hat{x}^k+\beta^k y^k=γkx^k+βk
这里的 γ k , β k \gamma^k, \beta^k γk,βk是为每个神经元另外加上的两个可学习参数,会在反向传播时一起被update。当 γ k = V a r [ x k ] , β k = E [ x k ] \gamma^k=Var[x^k], \beta^k=E[x^k] γk=Var[xk],βk=E[xk]时,模型有能力还原BN之前的输入,因此不会因为加了标准化而限制了模型表达力。
2. 以mini-batch为标准化单位
motivation
为了能和SGD过程相匹配,标准化每一个feature时,可以使用mini-batch的信息来代表整个数据集的分布。注意,这里我们默认mini-batch与其所在的整个数据集是同一分布的。
算法
注意:每个activation均有它自己独立的
γ
\gamma
γ和
β
\beta
β。
3. 反向传播
注意:在反向传播的时候, μ β \mu_{\beta} μβ 和 σ β \sigma_{\beta} σβ的计算中也使用到了 x i x_i xi,因此使用chain rule计算 ∂ l ∂ x i \frac{\partial l}{\partial x_i} ∂xi∂l偏导时,需要用到 ∂ μ β ∂ x i \frac{\partial\mu_{\beta}}{\partial x_i} ∂xi∂μβ和 ∂ σ β 2 ∂ x i \frac{\partial\sigma_{\beta}^2}{\partial x_i} ∂xi∂σβ2:
∂
l
∂
x
i
=
∂
l
∂
x
^
i
∗
∂
x
^
i
∂
x
i
+
∂
l
∂
σ
β
2
∗
∂
σ
β
2
∂
x
i
+
∂
l
∂
μ
β
∗
∂
μ
β
∂
x
i
\frac{\partial l}{\partial x_i}=\frac{\partial l}{\partial\hat{x}_i}*\frac{\partial\hat{x}_i}{\partial x_i}+\frac{\partial l}{\partial\sigma_{\beta}^2}*\frac{\partial\sigma_{\beta}^2}{\partial x_i}+\frac{\partial l}{\partial \mu_{\beta}}*\frac{\partial \mu_{\beta}}{\partial x_i}
∂xi∂l=∂x^i∂l∗∂xi∂x^i+∂σβ2∂l∗∂xi∂σβ2+∂μβ∂l∗∂xi∂μβ(如何由chian rule推导得来??请参考Back Propagation in Batch Normalization Layer,下图variable间的依赖关系也可以解释)
效果
1. gradient stability
当learning rate太大时,有可能会产生梯度爆炸或消失的问题(???how),或者导致模型陷入局部最优。而 BN可以帮助缓解这些问题。
-
B N ( W u ⃗ ) = B N ( ( a W ) u ⃗ ) BN(W\vec{u}) = BN((aW)\vec{u}) BN(Wu)=BN((aW)u) :
这个性质说明加了BN以后,parameters的scale不会影响到模型的训练。即使learning rate设置得过大, BN也可以减弱它的负面影响。 -
∂ B N ( ( a W ) u ⃗ ) ∂ ( a W ) = ∂ B N ( W u ⃗ ) ∂ ( a W ) = 1 a ∂ B N ( W u ⃗ ) ∂ W \frac{\partial BN((aW)\vec{u})}{\partial(aW)}=\frac{\partial BN(W\vec{u})}{\partial(aW)}=\frac{1}{a}\frac{\partial BN(W\vec{u})}{\partial W} ∂(aW)∂BN((aW)u)=∂(aW)∂BN(Wu)=a1∂W∂BN(Wu):
对于变大的weights,其对应的gradient会相应减小,可以使weights的增长速度更加稳定。
2. regularization
即使是同一个sample和固定的网络,所属于的mini-batch不同也会导致它对应的输出不同,这为模型加入了一些noise,可以起到dropout的效果,降低neuron间的依赖性。与此同时,加入BN后,上一层weight的改变对下一层weight的影响会被抑制,也能降低神经元间的依赖性。
使用方法
在训练的时候,可以通过计算mini-batch的mean与variance来做BN,并且动态更新相关 γ \gamma γ和 β \beta β参数。但是testing的时候,mean与variance如何得到呢?
during testing / deployment
当训练完成以后,固定所有的参数,再对所有的batch做一次forward propagation。得到每个neuron在每个mini-batch上的mean与variance,最后求平均即是最后每个neuron的BN layer的
μ
β
\mu_{\beta}
μβ 和
σ
β
\sigma_{\beta}
σβ。
testing时直接用计算结果来标准化每个feature,再加上之后的仿射变换,BN layer的计算可以总结为:
y ^ k = γ k V a r [ x k ] + ϵ x k + β k − γ E [ x k ] V a r [ x k ] + ϵ \hat{y}^k = \frac{\gamma^k}{\sqrt{Var[x^k]+\epsilon}}x^k+\beta^k- \frac{\gamma E[x^k]}{\sqrt{Var[x^k]+\epsilon}} y^k=Var[xk]+ϵγkxk+βk−Var[xk]+ϵγE[xk]
CNN 上的应用
BN 的初衷是使每个神经元每次输出的feature所对应的分布稳定,因此在CNN中,BN应该以每个filter的每次输出作为标准化的最小单位。即,每一个filter卷积后所对应的所有输出,都应该被用来计算mean与variance。假设如下场景:
输入: b a t c h _ s i z e ∗ w e i g h t ∗ h e i g h t ∗ n _ c h a n n e l s batch\_size * weight * height * n\_channels batch_size∗weight∗height∗n_channels
filters: 3 ∗ 3 ( ∗ n _ c h a n n e l s ) ∗ n _ f i l t e r s 3*3(* n\_channels)*n\_filters 3∗3(∗n_channels)∗n_filters ( stride=1)
输出: b a t c h _ s i z e ∗ w e i g h t ∗ h e i g h t ∗ n _ f i l t e r s batch\_size * weight * height * n\_filters batch_size∗weight∗height∗n_filters
对于每一个 f i l t e r i filter_i filteri的输出结果做BN:
f i l t e r i filter_i filteri对应的输出的数目: n u m s = b a t c h _ s i z e ∗ ( w e i g h t ∗ h e i g h t ∗ 1 ) nums =batch\_size * (weight * height *1) nums=batch_size∗(weight∗height∗1)
m e a n f i l t e r i = 1 n u m s ∑ b s = 1 b a t c h _ s i z e ∑ r = 1 h e i g h t ∑ c = 1 w e i g h t x r , c i t h f e a t u r e m a p o f s a m p l e b s mean_{filter_i}=\frac{1}{nums}\sum_{bs=1}^{batch\_size}\sum_{r=1}^{height}\sum_{c=1}^{weight}x_{\ r,c}^{\ i^{th}feature\ map\ of\ sample^{bs}} meanfilteri=nums1∑bs=1batch_size∑r=1height∑c=1weightx r,c ithfeature map of samplebs
v a r i a n c e f i l t e r i = 1 n u m s ∑ b s = 1 b a t c h _ s i z e ∑ r = 1 h e i g h t ∑ c = 1 w e i g h t ( x r , c i t h f e a t u r e m a p o f s a m p l e b s − m e a n f i l t e r i ) 2 variance_{filter_i}=\frac{1}{nums} \sum_{bs=1}^{batch\_size}\sum_{r=1}^{height}\sum_{c=1}^{weight}(x_{\ r,c}^{\ i^{th}feature\ map\ of\ sample^{bs}}-mean_{filter_i})^2 variancefilteri=nums1∑bs=1batch_size∑r=1height∑c=1weight(x r,c ithfeature map of samplebs−meanfilteri)2
BN layer的位置
BN的位置也会影响训练效果。对于如下的一个神经元:
z
=
g
(
W
x
⃗
+
b
)
z=g(W\vec x+b)
z=g(Wx+b)
g
(
.
)
:
g(.):
g(.): 激活函数,一般为非线性。
BN的输入有两种选择:
- z z z (等效于标准化模型输入 x ⃗ \vec x x)
- W x ⃗ + b W\vec x+b Wx+b (等效于 W x ⃗ W\vec x Wx)
文中倾向于把BN layer放置在非线性激活函数之前。因为:( W x ⃗ W\vec x Wx更有可能符合对称的密集的,更接近高斯的分布。而 z z z因为是非线性函数的输出,它的分布会随着训练剧烈改变,BN layer并不能有效改善它存在的Internal Covariate Shift问题。)扩起来因为我没理解。。
tensorflow中的具体实现
Reference
https://arxiv.org/abs/1502.03167
https://machinelearningmastery.com/batch-normalization-for-training-of-deep-neural-networks/