TL;DR
WN(Weight Normalization):
BN(Batch Normalization):批样本单通道,对象为NHW
LN(Layer Normalization):单样本全通道,对象为CHW
IN(Instance Normalization):单样本单通道,对象为HW
GN(Group Normalization):单样本批通道,对象为GH*W
权重归一化的重要目的是,避免模型过拟合。
在深层网络训练的过程中,由于网络中参数变化而引起内部结点数据分布发生变化的这一过程,被称作内部协变量转移Internal Covariate Shift(ICF)。
假如我们要训练一个神经网络分类器,我们从训练数据中拿到两个明显分布不同的两个batch。当我们用这两个batch分别去训练神经网络的时候,就会由于训练batch分布的剧烈波动导致收敛速度慢,甚至是神经网络性能的下降。这就是在输入数据上的Covariate shift。
通常我们可以通过增大batch的大小,并充分对数据进行shuffle来保证每个batch的分布尽量接近原始数据的分布,从而减少Covariate shift带来的负面影响。
同样的对于神经网络的每一层的输入数据同样存在类似的问题,这就是Internal Covariate Shift。但是在训练过程中,神经网络的参数会不断地更新,会导致神经网络内部的输入输出分布的剧烈波动,我们并不能像处理输入数据那样去处理神经网络内部每一层的输入输出。
BN的提出就是为了解决这个问题的。将每一层的输入分布都归一化到均值为0,方差为1上,减少了所谓的 Internal Covariate Shift,从而稳定乃至加速了训练。
但是我们仔细思考,均值和方差相同,数据分布就一定相同或者相近吗,显然是不是的。不管哪一层的输入都不可能严格满足正态分布,从而单纯地将均值方差标准化无法实现标准分布 N(0, 1)
;其次,就算能做到 N(0, 1)
,这种诠释也无法进一步解释其他归一化手段(如 Instance Normalization、Layer Normalization)起作用的原因。
个人感觉。BN 的作用更像是多种因素的复合结果,比如对于我们主流的激活函数来说, [?1,1] 基本上都是非线性较强的区间,所以将输入弄成均值为 0、方差为 1,也能更充分地发挥激活函数的非线性能力,不至于过于浪费神经网络的拟合能力。
再比如我们对神经网络的训练数据,特别是图像数据进行预处理的时候都要进行所谓的“白化”操作,最常见的白化操作便采用PCA对数据进行白化操作,最后达到的效果如图所示:
但是对于神经网络内部来说,做这样的白化操作代价过大,而且PCA操作也不是一个可微分的操作。所以我们用正则化操作来替代白化操作,也可以达到类似的效果,如图所示:
feature map: 包含 N 个样本,每个样本通道数为C
,高为H
,宽为W
。对其求均值和方差时,将在 N、H、W 上操作,而保留通道C
的维度。
具体来说,就是把第 1 个样本的第 1 个通道,加上第 2 个样本第 1 个通道 ...... 加上第 N 个样本第 1 个通道,求平均,得到通道 1 的均值。对所有通道都施加一遍这个操作,就得到了所有通道的均值和方差。
?最后我们将每个 pixel 对应的值减去均值,除以方差,就得到了规范化的结果。在此基础上,BN 还增加了两个可训练的参数 γ,β。所以最终的表达式为:
我们知道BN在每一层计算的均值与方差都是基于当前batch中的训练数据,但是这就带来了一个问题:我们在预测阶段,有可能只需要预测一个样本或很少的样本,没有像训练样本中那么多的数据,
此时均值与方差的计算一定是有偏估计,这个时候我们该如何进行计算呢?
利用BN训练好模型后,我们保留了每组mini-batch训练数据在网络中每一层的均值与方差。此时我们使用整个样本的统计量来对Test数据进行归一化,具体来说使用均值与方差的无偏估计:
得到每个特征的均值与方差的无偏估计后,我们对test数据采用同样的normalization方法:
作用:
BN的思考
import torch
class BatchNorm2d(torch.nn.Module):
def __init__(self, channel, eps=1e-5, affine=True, momentum=0.9):
super().__init__()
# 初始化训练参数
self.gamma = torch.nn.Parameter(torch.ones(1, channel, 1, 1))
self.beta = torch.nn.Parameter(torch.zeros(1, channel, 1, 1))
self.eps = eps
self.affine = affine
self.momentum = momentum
self.register_buffer(‘running_mean‘, torch.zeros(channel))
self.register_buffer(‘running_var‘, torch.ones(channel))
def forward(self, input):
# input shape must be (N, C, H, W)
if self.training:
means = input.mean((0, 2, 3), keepdim=True)
vars = ((input-means)**2).sum((0, 2, 3), keepdim=True)
self.running_mean = self.momentum * self.running_mean + (1-self.momentum) * means
self.running_var = self.momentum * self.running_var + (1-self.momentum) * vars
else:
means = self.running_mean
vars = self.running_var
output = (input - means) / torch.sqrt(vars + self.eps)
if self.affine:
output = output* self.gamma + self.beta
return output
LN一般只用于RNN的场景下,在CNN中LN规范化效果不如BN,WN,GN,IN的
LN与BN的区别
InstanceNormalization主要是针对CNN设计,并且是为了弥补BN的对batch的依赖。
InstanceNormalization对于图像生成任务效果明显优于BN,在图像分类上不如BN。
GN在要求batch比较小的场景下或者目标检测,视频分类等任务由于BN
参考博客:
深入理解Batch Normalization
深度学习-Normalization
原文:https://www.cnblogs.com/Tsingwaa/p/14586091.html