前言
- 数据规一化的作用:
- 调整数据分布,增加网络泛化度.
- 若每批数据分布不同,神经网络需要不断调整模型权重去适应不同的数据分布,因此可以增加训练速度.即缓解Internal Covariate Shift问题,将数据分布拉到激活函数的非饱和区,具有权重/数据伸缩不变性的特点.
- 起到缓解梯度消失/爆炸,加速训练,正则化的效果.
- 内部协方差转移internal convarite shift:
- 简设模型函数 y = f ( θ , x ) y=f(\theta,x) y=f(θ,x),训练时,每层网络的输入数据分布会不断变化,导致参数 θ \theta θ变化和y变化.
- 处理维度(B,C,H,W)数据时,
- BN:在batch上,对(C,H,W)做归一化.
- LN:在Channel上,对(B,H,W)做归一化.
- GN:在Channel上对C分组,即对(G,H,W)做归一化,其中G*K=C,将K个GN结果合并为最终归一化结果.
- IN:在图像像素上,对(H,W)做归一化.
- SN:是将BN LN IN结合,赋予不同权重,让网络自己去学习归一化层应该使用什么方法.
- WN:对模型权重进行归一化.
1.Batch Normalization(BN)
过程
- 预处理:
x ( k ) = x ( k ) − E [ x ( k ) ] V a r [ x ( k ) ] x^{(k)}=\frac{x^{(k)}-E[x^{(k)}]}{Var[x^{(k)}]} x(k)=Var[x(k)]x(k)−E[x(k)] - 变换重构:
x ( k ) = γ ( k ) x ( k ) + β ( k ) x^{(k)}=\gamma^{(k)}x^{(k)}+\beta^{(k)} x(k)=γ(k)x(k)+β(k)
变换重构可以保留原始的特征分布,其中 γ \gamma γ和 β \beta β是可训练参数.
代码实现
训练和推理时不一样,训练时会导入bn_param即上一个批次的均值方差,并和本批次进行平均,最终得到所有批次的均值方差(训练不固定).然后,推理时会用到这个最终结果并使用(推理固定).
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]
momentun = bn_param['momentun'] #shape = [B]
results = 0. # 建立一个新的变量
x_mean=x.mean(axis=0) # 计算x的均值
x_var=x.var(axis=0) # 计算方差
running_mean = momentum * running_mean + (1 - momentum) * x_mean
running_var = momentum * running_var + (1 - momentum) * x_var
x_normalized=(x - running_mean)/np.sqrt(running_var + eps) # 归一化
results = gamma * x_normalized + beta # 缩放平移
#记录新的值
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
优点
- 可减少对初始参数化的依赖.
- 允许更大的学习率,大幅提升训练速度,收敛更快.
- 改善正则化dropout,减轻其参数选择.
- 将模型中间层的输入分布从饱和区拉到了非饱和区,减小了梯度弥散,提升了训练收敛速度.
缺点
- 不适用于RNN,因为RNN的输入可能会是不同长度的序列(NLP任务),有个补全操作是会填充mask,因此有无效信息.此外RNN是基于TimeStep计算,如果BN会导致需要保存每个TS下的均值方差,效率低占用内存.(RNN适合使用LN)
- 不适用于GAN这种生成类任务,因为BN会使得每个批次的数据之间的差异性减少,即引入噪声.(可用IN)
- 不适用于batch少的情况,因为无法以少量样本的数据分布去代表大部分.
- 不适用于训练集和测试集数据分布差距大的情况.
BN与过拟合梯度消失问题
BN核心在于通过对参数搜索空间进行约束来增加模型的鲁棒性,因为压缩了参数的搜索空间,因此带来了一系列提升,例如加速训练,缓解过拟合和梯度消失问题.
BN能够让数据曲线变得更平滑一点,这样对于拟合函数,就可以尽大可能提升VC维.
BN主要解决的还是数据分布问题,对于过拟合和梯度消失只能起到附加缓解作用.
为什么需要BN
机器学习领域有个很重要的假设:IID独立同分布假设,就是假设训练数据和测试数据是满足相同分布的,这是通过训练数据获得的模型能够在测试集获得好的效果的一个基本保障。
BatchNorm就是在深度神经网络训练过程中使得每一层神经网络的输入保持相同分布的。
对于深度学习这种包含很多隐层的网络结构,在训练过程中,因为各层参数老在变,所以每个隐层都会面临covariate shift的问题,也就是在训练过程中,隐层的输入分布老是变来变去。
因为深层神经网络在做非线性变换前的激活输入值(就是那个x=WU+B,U是输入)随着网络深度加深或者在训练过程中,其分布逐渐发生偏移或者变动,之所以训练收敛慢,一般是整体分布逐渐往非线性函数的取值区间的上下限两端靠近(对于Sigmoid函数来说,意味着激活输入值WU+B是大的负值或正值),所以这导致后向传播时低层神经网络的梯度消失,这是训练深层神经网络收敛越来越慢的本质原因,而BN就是通过一定的规范化手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0方差为1的标准正太分布而不是正态分布,其实就是把越来越偏的分布强制拉回比较标准的分布,这样使得激活输入值落在非线性函数对输入比较敏感的区域,这样输入的小变化就会导致损失函数较大的变化,意思是这样让梯度变大,避免梯度消失问题产生,而且梯度变大意味着学习收敛速度快,能大大加快训练速度。
2.Layer Normalization(LN)
代码
注意axis=-1意思是对样本最后一个轴取均值和方差.
def call(self, inputs, training=None):
mean = K.mean(inputs, axis=-1, keepdims=True)
variance = K.mean(K.square(inputs - mean), axis=-1, keepdims=True)
std = K.sqrt(variance + self.epsilon)
outputs = (inputs - mean) / std
if self.scale:
outputs *= self.gamma
if self.center:
outputs += self.beta
return outputs
好处
- 无所谓Batch大小过于小,也不用考虑输入的长度是否一致.
- 不用对训练和测试采用不同的方法.
- 一定程度上缓解过拟合.
坏处
将LN添加到CNN之后,实验结果发现LN破坏了卷积学习到的特征,模型无法收敛,所以在CNN之后使用BN是一个更好的选择.
LN和BN的区别
L
N
(
左
)
和
B
N
(
右
)
对
比
示
意
图
LN(左)和BN(右)对比示意图
LN(左)和BN(右)对比示意图
- BN对一个神经元的batch所有样本进行标准化,LN对一个样本同一层所有神经元进行标准化.
- BN训练和推理取方差和均值方法不一样,LN一致.BN在对每个批次时会导入前一个批次保存的BN均值方差,并和此次结果做momentum,并保存给下一个批次使用.
- BN适用于CNN,LN适用于RNN和batch小的情况.
- 在BN和LN都能使用的场景中,BN的效果一般优于LN.因为不同数据,同一特征得到的归一化特征更不容易损失信息.
3.Instance Normalization(IN)
适用于风格迁移,超分别率图像生成这类任务.
代码
def Instancenorm(x, gamma, beta):
# x_shape:[B, C, H, W]
results = 0.
eps = 1e-5
x_mean = np.mean(x, axis=(2, 3), keepdims=True)
x_var = np.var(x, axis=(2, 3), keepdims=True0)
x_normalized = (x - x_mean) / np.sqrt(x_var + eps)
results = gamma * x_normalized + beta
return results
BN与IN的区别
BN注重对每个batch进行归一化,保证数据分布一致,因为判别模型中结果取决于数据整体分布。
但是图像风格化中,生成结果主要依赖于某个图像实例,所以对整个batch归一化不适合图像风格化中,因而对HW做归一化。可以加速模型收敛,并且保持每个图像实例之间的独立。
4.Group Normalization(GN)
主要是针对Batch Normalization对小batchsize效果差,GN将channel方向分group,然后每个group内做归一化,算(C//G)HW的均值,这样与batchsize无关,不受其约束。
def GroupNorm(x, gamma, beta, G=16):
# x_shape:[B, C, H, W]
results = 0.
eps = 1e-5
x = np.reshape(x, (x.shape[0], G, x.shape[1]/16, x.shape[2], x.shape[3]))
x_mean = np.mean(x, axis=(2, 3, 4), keepdims=True)
x_var = np.var(x, axis=(2, 3, 4), keepdims=True0)
x_normalized = (x - x_mean) / np.sqrt(x_var + eps)
results = gamma * x_normalized + beta
return results
5.Switchable Normalization(SN)
主要在于模型越复杂,要考虑太多不同归一化操作,可以使用SN来自动确认合适的归一化方法,也就是设置不同权重.
好处
- 归一化虽然提高模型泛化能力,然而归一化层的操作是人工设计的。在实际应用中,解决不同的问题原则上需要设计不同的归一化操作,并没有一个通用的归一化方法能够解决所有应用问题;
- 一个深度神经网络往往包含几十个归一化层,通常这些归一化层都使用同样的归一化操作,因为手工为每一个归一化层设计操作需要进行大量的实验。
- SN使用可微分学习,为一个深度网络中的每一个归一化层确定合适的归一化操作。
代码
def SwitchableNorm(x, gamma, beta, w_mean, w_var):
# x_shape:[B, C, H, W]
results = 0.
eps = 1e-5
# IN
mean_in = np.mean(x, axis=(2, 3), keepdims=True)
var_in = np.var(x, axis=(2, 3), keepdims=True)
# LN
mean_ln = np.mean(x, axis=(1, 2, 3), keepdims=True)
var_ln = np.var(x, axis=(1, 2, 3), keepdims=True)
# BN
mean_bn = np.mean(x, axis=(0, 2, 3), keepdims=True)
var_bn = np.var(x, axis=(0, 2, 3), keepdims=True)
# 加权
mean = w_mean[0] * mean_in + w_mean[1] * mean_ln + w_mean[2] * mean_bn
var = w_var[0] * var_in + w_var[1] * var_ln + w_var[2] * var_bn
# SN归一化
x_normalized = (x - mean) / np.sqrt(var + eps)
results = gamma * x_normalized + beta
return results
6.Weight Normalization(WN)
WN是在权值的维度上做的归一化。
计算
WN将权值向量
ω
\omega
ω在其欧氏范数和其方向上解耦成了参数向量
v
v
v和参数标量
g
g
g:
w
=
g
∣
∣
v
∣
∣
v
w=\frac{g}{||v||}v
w=∣∣v∣∣gv
可以训练优化这两个参数
代码
import torch.nn as nn
import torch.nn.functional as F
# 以一个简单的单隐层的网络为例
class Model(nn.Module):
def __init__(self, input_dim, output_dim, hidden_size):
super(Model, self).__init__()
# weight_norm
self.dense1 = nn.utils.weight_norm(nn.Linear(input_dim, hidden_size))
self.dense2 = nn.utils.weight_norm(nn.Linear(hidden_size, output_dim))
def forward(self, x):
x = self.dense1(x)
x = F.leaky_relu(x)
x = self.dense2(x)
return x
WN与BN的区别
- WN是在权值的维度上做的归一化;而BN是对batch的数据进行归一化.
- WN不会和BN一样将数据归一化,因此也要注意网络参数初始化.
- WN也是和样本量无关的,所以可以应用在batchsize较小以及RNN等动态网络中;而BN不适用于这两种情况.
- WN在生成模型,强化学习等噪声敏感的环境中WN的效果也要优于BN;BN不适用于生成类任务.
- WN的计算效率也要优于要计算归一化统计量的BN.
优化:Mean-Only BN
基于WN的动机,文章提出了Mean-Only BN。这种方法是一个只进行减均值而不进行除方差的BN,动机是考虑到BN的除方差操作会引入额外的噪声,实验结果表明WN+Mean-Only BN虽然比标准BN收敛得慢,但它们在测试集的精度要高于BN。
参考
https://zhuanlan.zhihu.com/p/88347589
https://blog.youkuaiyun.com/flash_zhj/article/details/107161877
https://www.cnblogs.com/LXP-Never/p/11566064.html
https://zhuanlan.zhihu.com/p/55102378