论文:Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
从论文名字就可以看出,BatchNorm技术是用来加速网络训练的,手段就是通过“Reducing Internal Covariate Shift(减小内部协变量偏移)”,什么叫做“Internal Covariate Shift”呢? 看一下下图所示的sigmoid函数图:
可以看出当x值在0附近时,其导数较大,靠近两边时导数很小。而在深度神经网络中,如果不加以约束,则激活函数的输入值很容易向两边偏移,导致导数变小,这样会使得网络训练变慢,而且逐层累乘后还会产生梯度消失现象。所以normalization的作用相当于数据在经过每层时都将它拉回为一个统一分布。batch normalization过程如下:
其中xi是线性激活值。首先求出均值和方差,之后再减去均值、除以方差。参数应该是为了防止因为方差接近0而使得除以0无限大的问题,所以在pytorch中是一个比较小的值(1e-5)。为了防止网络表达性下降,又在normaliza只后添加一个逆变换,即将x乘以一个缩放值并加上一个偏移量,其中和都是可学习参数。
因此,normalization其实更像是另一个神经网络层,因为有可学习参数存在,但是参数量相对来说是极少的,与线性变换中y=wx+b的b参数量一致,为神经元个数。
另外,也有实验表明batch normalization放在非线性激活层之后也有同样甚至更好的加速效果,这样的话似乎上述原理很难解释为什么BatchNorm能够加速训练,毕竟Normalization都是与激活函数无关的,这里有一个更容易理解的解释:BatchNorm为什么能缓解梯度消失问题
BatchNorm的缺点:
1.需要较大的batch以体现整体数据分布
2.训练阶段需要保存每个batch的均值和方差,以求出整体均值和方差在infrence阶段使用
3.不适用于可变长序列的训练,如RNN
LayerNorm克服了以上BatchNorm的缺点,在特征维度进行归一化,对每个Batch有一个均值和方差,因此不依赖于batch大小,即使batch为1也能使用。
LayerNorm只是归一化的维度与BatchNorm有所区别,但是其他区别不大。LayerNorm中也存在和可学习参数,并且和是在特征维度进行,而不是在Batch维度。
例如,input是batch×seq_len×hidden,则Layer首先在hidden维度求出batch×seq_len个标准差和均值,再使用它们进行归一化,但和只有hidden个,因此LayerNorm归一化之后的缩放是再特征维度上进行。
a=array([[[-0.66676328, -0.95822262, 1.2951657 , 0.67924618],
[-0.46616455, -0.39398589, 1.95926177, 2.36355916],
[-0.39897415, 0.80353481, -1.46488175, 0.55339737]],
[[-0.66223895, -0.16435625, -1.96494932, -1.07376919],
[ 1.30338369, -0.19603094, -1.43136723, -1.0207508 ],
[ 0.8452505 , -0.08878595, -0.5211611 , 0.10511936]]])
u=np.mean(a, axis=(2,))
s = np.std(a, axis=(2,))
y = a-u[...,None]
y = y/s[...,None]
print(y)
########################输出###################################
array([[[-0.80954074, -1.12241971, 1.29657224, 0.63538821],
[-1.0214588 , -0.96610083, 0.83874033, 1.14881929],
[-0.30472338, 1.04125172, -1.49779981, 0.76127147]],
[[ 0.46047519, 1.21440667, -1.51218696, -0.16269489],
[ 1.56757537, 0.13400543, -1.04708279, -0.65449801],
[ 1.53885365, -0.35203004, -1.2273397 , 0.04051609]]])
import torch.nn.functional as F
input = torch.tensor(a)
y = F.layer_norm(input,(4,))
print(y)
#####################输出################
tensor([[[-0.8095, -1.1224, 1.2966, 0.6354],
[-1.0215, -0.9661, 0.8387, 1.1488],
[-0.3047, 1.0412, -1.4978, 0.7613]],
[[ 0.4605, 1.2144, -1.5122, -0.1627],
[ 1.5676, 0.1340, -1.0471, -0.6545],
[ 1.5388, -0.3520, -1.2273, 0.0405]]])
的确是只在特征维度进行缩放了。
w = torch.tensor([1,1,2,2])
b = torch.tensor([1,1,1,1])
y = F.layer_norm(input,(4,),w,b)
print(y)
#########################输出######################
tensor([[[ 0.1905, -0.1224, 3.5931, 2.2708],
[-0.0215, 0.0339, 2.6775, 3.2976],
[ 0.6953, 2.0412, -1.9956, 2.5225]],
[[ 1.4605, 2.2144, -2.0243, 0.6746],
[ 2.5676, 1.1340, -1.0942, -0.3090],
[ 2.5388, 0.6480, -1.4546, 1.0810]]])