批标准化详解(Batch Normalization for Reducing Internal Covariate Shift)

深度神经网络训练过程中,各网络层参数在不断变化,每层网络的输入分布不断变化 ,不同的输入分布可能需重新训练,此外,我们也不得不使用 较小的参数初始化、较小的学习率 训练模型,避免网络输出陷入饱和区,造成BP算法的梯度消失,深度模型一般难以训练。

作者称这种内部网络层输入分布变化的现象为Internal Covariate Shift,作者提出Batch Normalization, BN算法解决Internal Covariate Shift和输出饱和问题 ,使训练速度加快,算法的特点是:

  • 允许使用较大的学习率训练模型,允许使用较大的参数初始化模型,经过BN处理后的输入,基本不会处于激活函数饱和区
  • 自带正则化,与dropout类似,它为每个隐藏层的输入增加了噪声,这意味着我们可使用更少的dropout
  • 模型收敛速度显著加快,ImageNet数据集分类任务使用BN,以7%的训练步数达到相同的表现

为什么要使用BN?

使用mini-batch SGD训练模型的好处?在batch使用SGD是深度网络模型训练的主流方法,如Adagrad等,这种方法是模型在每个step在batch上的损失更新模型参数,随着batch size增加,训练效果也会增加,此外,通过batch方式训练,也可以利用到现代的计算平台实现并行计算,如GPU的核心数为2的n次方,将batch size设置为2的n次方,往往可以获得更快的训练速度。

使用SGD训练模型存在的问题?由于每层网络的输入受前面所有层网络的参数影响,这可能导致前层网络参数较小变化,多数神经元位于非饱和区(梯度爆炸),后层网络变化很大,又或者前层网络较大变化,某些神经元陷入饱和区(梯度消失),后层网络变化很小(前面波涛汹涌,后面波澜不惊)。

为什么要规范化网络层输入分布?当每层网络输入分布变化,意味着每层网络要不断地适应新的分布,这是一种“covariate shift”现象,其实这个协变量移位的概念可以扩展至整个学习系统之外,如子网络或者神经网络中的一层:
= F 2 ( F 1 ( u , Θ 1 ) , Θ 2 ) \ell=F_2(F_1(u, \Theta_1), \Theta_2)
式中 F 1 , F 2 F_1,F_2 可以是任意变换,参数 Θ 1 , Θ 2 \Theta_1,\Theta_2 通过最小化损失 \ell 学习得到。

如果将 x = F 1 ( u , Θ 1 ) x=F_1(u,\Theta_1) 看作为子网络的输入,则参数 Θ 2 \Theta_2 的过程可看作为(batch size= m m ,learning rate= α \alpha
= F 2 ( x , Θ 2 ) , Θ 2 Θ 2 α m i = 1 m F 2 ( x i , Θ 2 ) Θ 2 \ell = F_2(x, \Theta_2),\quad \Theta_2 \leftarrow \Theta_2 - \frac{\alpha}{m}\sum_{i=1}^m \frac{\partial F_2(x_i,\Theta_2)}{\partial \Theta_2}
这完全等价于以 x x 作为输入训练独立的网络 F 2 F_2 ,我们都知道 训练集和测试集具有相同分布是模型可有效训练的前提,其实这也适用于训练子网络或子层,如果能保持每次训练时,输入 x x 的分布相同,则在学习参数 Θ 2 \Theta_2 时,就不必补偿因 x x 分布变化带来的影响

如何解决深层网络训练的梯度消失问题?考虑sigmoid激活函数
z = g ( W u + b ) , g ( x ) = 1 1 + e x z=g(Wu+b),\quad g(x)=\frac{1}{1+e^{-x}}
随着输入绝对值 x |x| 的增大,梯度 g ( x ) g'(x) 趋于0,意味着 x = W u + b x=Wu+b 的所有维度,除非该维度绝度值较小,否则该维度流向 u u 的梯度将消失,模型训练减缓。由于输入 x x 受前面所有层参数的影响,改变前面层参数,很小可能将 x x 的许多维度推向饱和区,这种现象在深层网络中尤其明显,在实践过程中,使用 ReLU激活函数并小心地初始化参数,可以较好的避免饱和问题。

如何使得网络层的每次输入分布一致且避免输出饱和,从而加速训练?基于此,提出 Batch Normalization 方法,将输入分布规范化为标准正太分布,解决以上问题。


怎样使用BN?

参照图像处理领域以 白化(whitening, 像素值线性变化至0均值、1方差) 加速训练的方式,Batch Normalization对神经网络的每一层或一些层的输入执行 “白化” 操作,固定输入分布,从而降低Internal Covariate Shift的影响。

扫描二维码关注公众号,回复: 11597987 查看本文章

白化在数据预处理过程中的目的:

  • 去除特征间相关性,使得特征独立
  • 使得所有特征具有相同的均值和方差,特征同分布

对包含规范化的网络层使用SGD训练会出现什么问题?对于包含Batch Norm的优化过程,优化器在使用SGD更新模型参数时,可能会以一种要求规范化更新(去规范化) 的形式更新参数,从而尽可能地加快训练过程!

举例来说,对于一个简单的网络层,输入为 u u ,偏差为 b b ,以下列方式得到规范化后输入 x ^ \hat x
x ^ = x E [ x ] , x = u + b , E [ x ] = 1 N i = 1 N x i , X = { x 1... N } \hat x=x -\Bbb E[x], \quad x=u+b, \quad \Bbb E[x]=\frac{1}{N}\sum_{i=1}^N x_i,\quad \mathcal X=\{x_{1...N}\}
假如期望 E [ x ] \Bbb E[x] 和偏差 b b 无依赖关系,则偏差 b b 的更新公式为
b b + Δ b = b x ^ x ^ b = b x ^ b\leftarrow b +\Delta b = b - \frac{\partial\ell}{\partial\hat x}\frac{\partial\hat x}{\partial b}=b-\frac{\partial\ell}{\partial\hat x}
因此,
u + ( b + Δ b ) E [ u + ( b + Δ b ) ] = u + b E [ u + b ] u+(b+\Delta b)-\Bbb E[u+(b+\Delta b)]=u+b-\Bbb E[u+b]
从更新前后结果可以看到, b b 值的更新不会影响网络输出,当然也不会降低总体损失! 当我们在梯度下降过程之外进行参数规范化时, b b 值可能无限制增长,导致模型blows up! 发生这一问题的原因在于,优化器未考虑到某些参数进行了规范化,也就是说损失函数中不包含某些参数的规范化项

如何让优化器考虑到所有参数的规范化?如果对于任意参数,网络总是能以期望的分布产生激活值,也就是说 损失函数对模型参数的梯度应考虑模型参数的规范化,或者说模型参数的规范化依赖于模型参数。

令向量 x x 表示网络层输入, X \mathcal X 是训练样本集,规范化输入 x x 可表示为
x ^ = Norm ( x , X ) \hat x =\text{Norm}(x,\mathcal X)
如果 x x 是其它网络层的输出,则规划范项 x ^ \hat x 不仅依赖于 x x ,而且依赖于 X \mathcal X 中的每一个训练样本,因为经过多次迭代,每个训练样本都对 x x 产生过作用!在使用BP算法优化参数时,需分别计算 Norm \text{Norm} 关于 x x X \mathcal X 的雅可比矩阵,如果忽略后项,很可能造成上述 模型爆炸 的现象。计算规范化项对训练集的雅可比矩阵计算量太大,Batch Norm算法基于整个训练集的统计信息,对一个训练样本进行规范化。


如何有效地实现BN?

第一种简单化处理:对输入向量 x x 的每一维独立地进行标准化(0均值、1方差规范化)
x ^ ( k ) = x ( k ) E [ x ( k ) ] V a r [ x ( k ) ] \hat x^{(k)}=\frac{x^{(k)}-\Bbb E[x^{(k)}]}{\sqrt{Var [x^{(k)}]}}
其中,期望和方差是在整个训练集中计算得到的。

此外,对每层网络都进行简单的标准化,可能会降低模型的非线性表达能力,如对sigmoid的输入标准化,目的是把一些绝对值较大的非线性区值拉回到线性区,增大导数值,因此最终大多数点被约束在[-2, 2]区间,相当于仅利用与sigmoid函数的近似线性部分

Batch Normlization对规范化后的值在进行缩放和平移解决这个问题:
y ( k ) = γ ( k ) x ^ ( k ) + β ( k ) y^{(k)}=\gamma^{(k)}\hat x^{(k)}+\beta^{(k)}
模型可以学习变量 γ \gamma β \beta ,以恢复模型的非线性表达能力,每个神经元都有自己独特的伸缩、平移参数。特别地, γ \gamma 等于标准差、 β \beta 等于期望时,模型等价于没有进行规范化,这赋予了模型自我控制规范化的自由度 换一种角度来看,引入这两个变量之后,使得优化器仅通过修改这两个参数 去规范化,而不是调整可能会降低网络稳定性的网络权重参数。

第二种简单化处理:在每个mini-batch内部估计每次激活的均值和方差。 y i y_i 可看作为 x i x_i 规范化后的值,易证整个Norm过程可微,因此可以使用BP更新网络参数。

BN的实现见算法1


如何训练和推理使用BN的网络?

在训练过程中,若mini-batch的样本数大于1,我们都可以使用batch norm有效地训练网络,但在推理过程中,mini-batch可能仅有一个样本,因此,使用以下公式规范化输入
x ^ = x E [ x ] Var [ x ] + ϵ \hat x = \frac{x-\Bbb E[x]}{\sqrt{\text{Var}[x]+\epsilon}}
与训练过程不同的是,上式中期望和方差都是训练集总体的统计量,若迭代次数为 m m ,则 无偏统计量
E [ x ] = E B [ μ B ] , Var [ x ] = m m 1 E B [ σ B 2 ] \Bbb E[x]=\Bbb E_{\mathcal B}[\mu_{\mathcal B}],\quad \text{Var}[x]=\frac{m}{m-1}\cdot \Bbb E_{\mathcal B}[\sigma^2_{\mathcal B}]

推理和训练过程见算法2


BN作用在神经元的输入侧还是输出侧?

Batch Normalization 可以应用于网络的任一组激活 ,我们考虑element-wise非线性变化
z = g ( W u + b ) z=g(Wu+b)
g ( ) g(·) 为非线性映射,如ReLU或者sigmiod。

我们可以将BN作用于非线性变换之前,即规范化 x = W u + b x=Wu+b ,同样 可以将BN作用于 u u ,但是 u u 很可能是另一非线性函数的输出,在训练过程中其分布形状可能会发生改变,而且约束其一、二阶矩并不能消除协变量转移???

相比较, W u + b Wu+b 更有可能具有对称性、非稀疏分布性,即更加具有“高斯特性”,因此规范化此部分,很可能获得我们想要的稳定分布。如果我们规范化 W u + b Wu+b ,则 可以忽略偏差项 b b ,因为它的作用会在均值相减时被抵消,因此网络层的规范化可表示为
z = g ( BN ( W u ) ) z=g(\text{BN}(Wu))


为什么BN网络可以使用更高的学习率?

传统深层网络使用较大的学习率训练模型,可能 造成梯度消失、梯度爆炸或陷入局部极小点,而BN可阻止输入进入激活函数的饱和区,从而避免这些问题。

从另一个角度来看,伸缩变化网络权重参数,不影响BN输出,对于确定的标量 a a
BN ( W u ) = BN ( ( a W ) u ) \text{BN}(Wu)=\text{BN}((aW)u)
梯度为
BN ( ( a W ) u ) u = BN ( W u ) u BN ( ( a W ) u ) a W = 1 a BN ( W u ) W \begin{aligned} \frac{\partial \text{BN}((aW)u)}{\partial u} & =\frac{\partial \text{BN}(Wu)}{\partial u}\\[2ex] \frac{\partial \text{BN}((aW)u)}{\partial aW} & =\frac{1}{a}·\frac{\partial \text{BN}(Wu)}{\partial W} \end{aligned}


为什么BN网络自带正则化效果?

BM相当于对神经元的输入进行平移和缩放,而平移和缩放的大小仅在当前mini-batch中计算得到等价于对神经元的输入增加了噪音,dropout也有增加噪音的作用。

The stochasticity from the batch statistics serves as a regularizer during training. (From Layer Normalization)

换一种角度来看,神经元的输入值是经过在mini-batch中标准化后得到的,具有样本结合性,神经网络不再需要为单一训练样本生成一个特定值,这种效应有利于模型的泛化,在 BN网络中Dropout似乎可以减少强度或者移除


BN在Tensorflow中的实现

主要与原论文的区别在于,每次迭代以滑动平均的方式将mini-batch的均值和方差更新到总体!

以下仅展示BN的实际过程,batch_size=8,hidden_size=3

import tensorflow as tf

x = tf.reshape(tf.range(24, dtype=tf.float32), shape=(8, 3))
"""
<tf.Tensor: id=1269, shape=(8, 3), dtype=float32, numpy=
array([[ 0.,  1.,  2.],
       [ 3.,  4.,  5.],
       [ 6.,  7.,  8.],
       [ 9., 10., 11.],
       [12., 13., 14.],
       [15., 16., 17.],
       [18., 19., 20.],
       [21., 22., 23.]], dtype=float32)>

"""

"""
axis: Integer, the axis that should be normalized (typically the features axis). 
For instance, after a `Conv2D` layer with `data_format="channels_first"`, set `axis=1` in `BatchNormalization`.
"""
bn = tf.keras.layers.BatchNormalization(axis=1)

# training
bn(x, training=True)
"""
<tf.Tensor: id=1397, shape=(8, 3), dtype=float32, numpy=
array([[-1.5275091 , -1.5275091 , -1.5275091 ],
       [-1.0910779 , -1.0910779 , -1.0910779 ],
       [-0.65464675, -0.65464675, -0.65464675],
       [-0.21821558, -0.21821558, -0.21821558],
       [ 0.21821558,  0.21821558,  0.21821558],
       [ 0.65464675,  0.65464675,  0.65464675],
       [ 1.0910779 ,  1.0910779 ,  1.0910779 ],
       [ 1.5275091 ,  1.5275091 ,  1.5275091 ]], dtype=float32)>
"""

# inference
bn(x)
"""
<tf.Tensor: id=1433, shape=(8, 3), dtype=float32, numpy=
array([[-0.08679464,  0.73155487,  1.5499043 ],
       [ 2.3930523 ,  3.2114017 ,  4.0297513 ],
       [ 4.872899  ,  5.6912484 ,  6.5095983 ],
       [ 7.352746  ,  8.171096  ,  8.989445  ],
       [ 9.832593  , 10.650943  , 11.469292  ],
       [12.31244   , 13.13079   , 13.949139  ],
       [14.792287  , 15.610637  , 16.428986  ],
       [17.272135  , 18.090483  , 18.908833  ]], dtype=float32)>
"""

Reference

1. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
2. 详解深度学习中的Normalization,BN/LN/WN
3.【深度学习】深入理解Batch Normalization批标准化

猜你喜欢

转载自blog.csdn.net/sinat_34072381/article/details/106158014