batch normalization(BN)
面试前一定要看原论文
目录:
- 提出原因
- 作用
- 前向传播白化操作
- BN原理
- 细节(两个参数和卷积层BN)
- bn缺点
- 源码解读
- reference
正文
-
提出原因(易错,bn提出不是为了解决梯度问题!!是为了解决Internal Covariate Shift)
训练的本质是学习数据分布。如果训练数据与测试数据的分布不同会降低模型的泛化能力。因此,应该在开始训练前对所有输入数据做归一化处理。
而在神经网络中,因为每个隐层的参数不同,会使下一层的输入发生变化,从而导致每一批数据的分布也发生改变;致使网络在每次迭代中都需要拟合不同的数据分布,增大了网络的训练难度(需要调小学习率和小心的初始化)与过拟合的风险。
-
作用:(要会详细解释为什么)
BN 是一种正则化方法(减少泛化误差),主要作用有:
1). 加速网络的训练(缓解梯度消失,支持更大的学习率)
2). 防止过拟合
3). 降低参数初始化要求,打乱样本顺序
4). 移除或使用较低的dropout。 dropout是常用的防止overfitting的方法,而导致overfit的位置往往在数据边界处,如果初始化权重就已经落在数据内部,overfit现象就可以得到一定的缓解。论文中最后的模型分别使用10%、5%和0%的dropout训练模型,与之前的40%-50%相比,可以大大提高训练速度。
5). 降低L2权重衰减系数。 还是一样的问题,边界处的局部最优往往有几维的权重(斜率)较大,使用L2衰减可以缓解这一问题,现在用了Batch Normalization,就可以把这个值降低了,论文中降低为原来的5倍
6). 取消Local Response Normalization层。 由于使用了一种Normalization,再使用LRN就显得没那么必要了。而且LRN实际上也没那么work。
具体解释:
1). 加速网络的训练(缓解梯度消失,支持更大的学习率)
指较少的学习步骤获得相同的精度。
a)较大的学习率往往导致反向传播时造成梯度问题,困在最小处,batchnorm可以避免神经网络层中很小的参数变动在层数加深的过程中会积聚造成很大的影响;比如,在饱和激活函数,可以避免输入数据分布在饱和区域,而使得其尽量分布在线性区域;
b)另外,BN对参数scale有很强的适应性,在学习率很大时,可能使得参数的scale很大,在梯度下降时会放大梯度值,这可能使得模型爆炸;对于一个scaler a,
所以,大的权值会导致小的梯度,BN会使参数的变化更为稳定。-
防止过拟合
同样一个样本的输出不再仅仅取决于样本本身,也取决于跟这个样本属于同一个mini-batch的其它样本。同一个样本跟不同的样本组成一个mini-batch,它们的输出是不同的(仅限于训练阶段,在inference阶段是没有这种情况的)。我把这个理解成一种数据增强:同样一个样本在超平面上被拉扯,每次拉扯的方向的大小均有不同。不同于数据增强的是,这种拉扯是贯穿数据流过神经网络的整个过程的,意味着神经网络每一层的输入都被数据增强处理了。
-
-
前向传播减小Internal Covariate Shift:
之前的研究表明如果在图像处理中对输入图像进行白化(Whiten)操作的话——所谓白化,就是对输入数据分布变换到0均值,单位方差的正态分布——那么神经网络会较快收敛,那么BN作者就开始推论了:图像是深度神经网络的输入层,做白化能加快收敛,那么其实对于深度网络来说,其中某个隐层的神经元是下一层的输入,意思是其实深度神经网络的每一个隐层都是输入层,不过是相对下一层来说而已,那么能不能对每个隐层都做白化呢?这就是启发BN产生的原初想法,而BN也确实就是这么做的,可以理解为对深层神经网络每个隐层神经元的激活值做简化版本的白化操作。
那么能不能只在在前向过程应用这些normalization,而在反向传播忽视它呢,答案是否定的。论文中给出了一个例子:
那么网络的输出、损失将没有任何变化。但是,在下一次梯度下降更新参数后, 𝑏又会有Δ𝑏的变化。那么随着训练继续𝑏会趋于无穷,但loss不变。
鉴于以上问题,作者提出,normalization过程中,要把optimization过程考虑进来。
既然白化计算过程比较复杂,那我们就简化一点,比如我们可以尝试单独对每个特征进行normalizaiton就可以了,让每个特征都有均值为0,方差为1的分布就OK。
另一个问题,既然白化操作减弱了网络中每一层输入数据表达能力,那我就再加个线性变换操作,让这些数据再能够尽可能恢复本身的表达能力就好了。
因此,基于上面两个解决问题的思路,作者提出了Batch Normalization,下一部分来具体讲解这个算法步骤。
-
原理( 注意训练和测试的区别 )
1) 训练时,
计算batch均值,方差,归一化,缩放平移(为了恢复原分布)。
BN层计算公式:
反向传播计算相关参数:2) 测试时:
测试时均值和方差采取训练时的移动平均,具体如下:
a) 当测试或预测时,每次可能只会传入单个数据,此时模型会使用全局统计量代替批统计量;
b) 训练每个 batch 时,都会得到一组(均值,方差);
c) 所谓全局统计量,就是对这些均值和方差求其对应的数学期望;具体公式如下:
其中 μ_i 和 σ_i 分别表示第 i 轮 batch 保存的均值和标准差;m 为 batch_size,系数 m/(m-1) 用于计算无偏方差估计(原文称该方法为移动平均(moving averages))
此时BN(x)改变为:
完整算法如下: - 细节:
-
卷积层BN:
对卷积层来说,批量归一化发生在卷积计算之后、应用激活函数之前。如果卷积计算输出多个通道,我们需要对这些通道的输出分别做批量归一化,且每个通道都拥有独立的拉伸和偏移参数,且均为标量。设小批量中有 m个样本。在单个通道上,假设卷积计算输出的高和宽分别为p和q。我们需要对该通道中 m x p x q个元素同时做批量归一化。对这些元素做标准化计算时,我们使用相同的均值和方差,即该通道中m x p x q个元素的均值和方差。即BN层计算量为m x p x q
-
BN两个参数的作用
用来恢复参数分布,BN在归一化时会破坏参数分布。这样的normalization可能会改变这一层网络的表征能力,比如对于一个sigmoid激活函数,可能就把限制在了它的线性区域,失去了非线性激活的能力。因此我们要赋予normalization能够进行单位变换(identity transform)的能力。这可以通过在后面再加一层缩放和平移实现
-
对于不同的激活函数:
对于sigmoid函数而言,拉回到非饱和区域。
对于relu函数而言,gamma作用不大,beta很重要,否则0均值1方差会导致一半的数值在负半轴失活。
-
-
bn缺点
batch大小对其效果影响很大,batch 很小时,其梯度不够稳定,收敛变慢,极端情况就是 sgd
- 源码解读
def Batchnorm_simple_for_train(x, gamma, beta, bn_param):
"""
param:x : 输入数据,设shape(B,L)
param:gama : 缩放因子 γ
param:beta : 平移因子 β
param:bn_param : batchnorm所需要的一些参数
eps : 接近0的数,防止分母出现0
momentum : 动量参数,一般为0.9, 0.99, 0.999
running_mean :滑动平均的方式计算新的均值,训练时计算,为测试数据做准备
running_var : 滑动平均的方式计算新的方差,训练时计算,为测试数据做准备
"""
running_mean = bn_param['running_mean'] #shape = [B]
running_var = bn_param['running_var'] #shape = [B]
results = 0. # 建立一个新的变量
x_mean=x.mean(axis=0) # 计算x的均值
x_var=x.var(axis=0) # 计算方差
x_normalized=(x-x_mean)/np.sqrt(x_var+eps) # 归一化
results = gamma * x_normalized + beta # 缩放平移
# BN 的移动平均值计算公式 mean = 0.1 * 当前mean + 0.9 * 以前的mean
running_mean = momentum * running_mean + (1 - momentum) * x_mean
running_var = momentum * running_var + (1 - momentum) * x_var
#记录新的值
bn_param['running_mean'] = running_mean
bn_param['running_var'] = running_var
return results , bn_param
def Batchnorm_simple_for_test(x, gamma, beta, bn_param):
"""
param:x : 输入数据,设shape(B,L)
param:gama : 缩放因子 γ
param:beta : 平移因子 β
param:bn_param : batchnorm所需要的一些参数
eps : 接近0的数,防止分母出现0
momentum : 动量参数,一般为0.9, 0.99, 0.999
running_mean :滑动平均的方式计算新的均值,训练时计算,为测试数据做准备
running_var : 滑动平均的方式计算新的方差,训练时计算,为测试数据做准备
"""
running_mean = bn_param['running_mean'] #shape = [B]
running_var = bn_param['running_var'] #shape = [B]
results = 0. # 建立一个新的变量
x_normalized=(x-running_mean )/np.sqrt(running_var +eps) # 归一化
results = gamma * x_normalized + beta # 缩放平移
return results , bn_param
-
reference:
github面经大全
batch normalization论文解读
卷积神经网络batch normalization
batch normalization知乎
todo:
-
为什么采取移动平均
因为全局平均值无法得到,计算量太大,只能使用移动平均值。
-
为什么训练时不采取移动平均?
使用 BN 的目的就是为了保证每批数据的分布稳定,使用全局统计量反而违背了这个初衷;
BN 的作者认为在训练时采用移动平均可能会与梯度优化存在冲突;
-
BN为什么可以加速训练:
1)把越来越偏的分布强制拉回比较标准的分布,这样使得激活输入值落在非线性函数对输入比较敏感的区域,这样输入的小变化就会导致损失函数较大的变化,意思是这样让梯度变大,避免梯度消失问题产生,而且梯度变大意味着学习收敛速度快,能大大加快训练速度
2)归一化了每层和每维度的scale,所以可以整体使用一个较高的学习率,而不必像以前那样迁就小scale的维度;
3)归一化后使得更多的权重分界面落在了数据中,降低了overfit的可能性,因此一些防止overfit但会降低速度的方法,例如dropout和权重衰减就可以不使用或者降低其权重