文章目录
深度神经网络训练过程中,各网络层参数在不断变化,每层网络的输入分布不断变化 ,不同的输入分布可能需重新训练,此外,我们也不得不使用 较小的参数初始化、较小的学习率 训练模型,避免网络输出陷入饱和区,造成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”现象,其实这个协变量移位的概念可以扩展至整个学习系统之外,如子网络或者神经网络中的一层:
式中
可以是任意变换,参数
通过最小化损失
学习得到。
如果将
看作为子网络的输入,则参数
的过程可看作为(batch size=
,learning rate=
)
这完全等价于以
作为输入训练独立的网络
,我们都知道 训练集和测试集具有相同分布是模型可有效训练的前提,其实这也适用于训练子网络或子层,如果能保持每次训练时,输入
的分布相同,则在学习参数
时,就不必补偿因
分布变化带来的影响。
如何解决深层网络训练的梯度消失问题?考虑sigmoid激活函数
随着输入绝对值
的增大,梯度
趋于0,意味着
的所有维度,除非该维度绝度值较小,否则该维度流向
的梯度将消失,模型训练减缓。由于输入
受前面所有层参数的影响,改变前面层参数,很小可能将
的许多维度推向饱和区,这种现象在深层网络中尤其明显,在实践过程中,使用 ReLU激活函数并小心地初始化参数,可以较好的避免饱和问题。
如何使得网络层的每次输入分布一致且避免输出饱和,从而加速训练?基于此,提出 Batch Normalization 方法,将输入分布规范化为标准正太分布,解决以上问题。
怎样使用BN?
参照图像处理领域以 白化(whitening, 像素值线性变化至0均值、1方差) 加速训练的方式,Batch Normalization对神经网络的每一层或一些层的输入执行 “白化” 操作,固定输入分布,从而降低Internal Covariate Shift的影响。
白化在数据预处理过程中的目的:
- 去除特征间相关性,使得特征独立;
- 使得所有特征具有相同的均值和方差,特征同分布;
对包含规范化的网络层使用SGD训练会出现什么问题?对于包含Batch Norm的优化过程,优化器在使用SGD更新模型参数时,可能会以一种要求规范化更新(去规范化) 的形式更新参数,从而尽可能地加快训练过程!
举例来说,对于一个简单的网络层,输入为
,偏差为
,以下列方式得到规范化后输入
:
假如期望
和偏差
无依赖关系,则偏差
的更新公式为
因此,
从更新前后结果可以看到,
值的更新不会影响网络输出,当然也不会降低总体损失! 当我们在梯度下降过程之外进行参数规范化时,
值可能无限制增长,导致模型blows up! 发生这一问题的原因在于,优化器未考虑到某些参数进行了规范化,也就是说损失函数中不包含某些参数的规范化项。
如何让优化器考虑到所有参数的规范化?如果对于任意参数,网络总是能以期望的分布产生激活值,也就是说 损失函数对模型参数的梯度应考虑模型参数的规范化,或者说模型参数的规范化依赖于模型参数。
令向量
表示网络层输入,
是训练样本集,规范化输入
可表示为
如果
是其它网络层的输出,则规划范项
不仅依赖于
,而且依赖于
中的每一个训练样本,因为经过多次迭代,每个训练样本都对
产生过作用!在使用BP算法优化参数时,需分别计算
关于
和
的雅可比矩阵,如果忽略后项,很可能造成上述 模型爆炸 的现象。计算规范化项对训练集的雅可比矩阵计算量太大,Batch Norm算法基于整个训练集的统计信息,对一个训练样本进行规范化。
如何有效地实现BN?
第一种简单化处理:对输入向量
的每一维独立地进行标准化(0均值、1方差规范化)
其中,期望和方差是在整个训练集中计算得到的。
此外,对每层网络都进行简单的标准化,可能会降低模型的非线性表达能力,如对sigmoid的输入标准化,目的是把一些绝对值较大的非线性区值拉回到线性区,增大导数值,因此最终大多数点被约束在[-2, 2]区间,相当于仅利用与sigmoid函数的近似线性部分:
Batch Normlization对规范化后的值在进行缩放和平移解决这个问题:
模型可以学习变量
和
,以恢复模型的非线性表达能力,每个神经元都有自己独特的伸缩、平移参数。特别地,当
等于标准差、
等于期望时,模型等价于没有进行规范化,这赋予了模型自我控制规范化的自由度! 换一种角度来看,引入这两个变量之后,使得优化器仅通过修改这两个参数 去规范化,而不是调整可能会降低网络稳定性的网络权重参数。
第二种简单化处理:在每个mini-batch内部估计每次激活的均值和方差。 可看作为 规范化后的值,易证整个Norm过程可微,因此可以使用BP更新网络参数。
BN的实现见算法1。
如何训练和推理使用BN的网络?
在训练过程中,若mini-batch的样本数大于1,我们都可以使用batch norm有效地训练网络,但在推理过程中,mini-batch可能仅有一个样本,因此,使用以下公式规范化输入
与训练过程不同的是,上式中期望和方差都是训练集总体的统计量,若迭代次数为
,则 无偏统计量
推理和训练过程见算法2。
BN作用在神经元的输入侧还是输出侧?
Batch Normalization 可以应用于网络的任一组激活 ,我们考虑element-wise非线性变化
为非线性映射,如ReLU或者sigmiod。
我们可以将BN作用于非线性变换之前,即规范化 ,同样 可以将BN作用于 ,但是 很可能是另一非线性函数的输出,在训练过程中其分布形状可能会发生改变,而且约束其一、二阶矩并不能消除协变量转移???
相比较,
更有可能具有对称性、非稀疏分布性,即更加具有“高斯特性”,因此规范化此部分,很可能获得我们想要的稳定分布。如果我们规范化
,则 可以忽略偏差项
,因为它的作用会在均值相减时被抵消,因此网络层的规范化可表示为
为什么BN网络可以使用更高的学习率?
传统深层网络使用较大的学习率训练模型,可能 造成梯度消失、梯度爆炸或陷入局部极小点,而BN可阻止输入进入激活函数的饱和区,从而避免这些问题。
从另一个角度来看,伸缩变化网络权重参数,不影响BN输出,对于确定的标量
:
梯度为
为什么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批标准化