常用 Normalization 方法与PyTorch接口
简介
-
因为神经网络里主要有两类实体:神经元或者连接神经元的边,所以按照规范化操作涉及对象的不同可以分为两大类,一类是对第L层每个神经元的激活值或者说对于第L+1层网络神经元的输入值进行Normalization操作,比如
- BatchNorm/LayerNorm/InstanceNorm/GroupNorm等方法都属于这一类;
- 另外一类是对神经网络中连接相邻隐层神经元之间的边上的权重进行规范化操作,比如Weight Norm就属于这一类。广义上讲,一般机器学习里看到的损失函数里面加入的对参数的的L1/L2等正则项,本质上也属于这第二类规范化操作。L1正则的规范化目标是造成参数的稀疏化,就是争取达到让大量参数值取得0值的效果,而L2正则的规范化目标是有效减小原始参数值的大小。有了这些规范目标,通过具体的规范化手段来改变参数值,以达到避免模型过拟合的目的。
-
目前神经网络中常见的第一类的Normalization方法主要有:Batch Normalization(BN,2015年)、Layer Normalization(LN,2016年)、Instance Normalization(IN,2017年)、Group Normalization(GN,2018年)。它们都是从激活函数的输入来考虑、做文章的,以不同的方式对激活函数的输入进行 Norm 的。我们将输入的 feature map shape 记为[N, C, H, W],其中N表示batch size,即N个样本;C表示通道数;H、W分别表示特征图的高度、宽度。这几个方法主要的区别就是在:
- N是在batch上,对N、H、W做归一化,而保留通道 C 的维度。BN主要缺点是对batchsize的大小比较敏感,由于每次计算均值和方差是在一个batch上,所以如果batchsize太小,则计算的均值、方差不足以代表整个数据分布。BN适用于固定深度的前向神经网络,如CNN,不适用于RNN;
- LN在通道方向上,对C、H、W归一化,主要对RNN效果明显;
- 一个channel内做归一化,算H*W的均值,用在风格化迁移;因为在图像风格化中,生成结果主要依赖于某个图像实例,所以对整个batch归一化不适合图像风格化中,因而对HW做归一化。可以加速模型收敛,并且保持每个图像实例之间的独立。
- GN将channel分组,然后再做归一化。如果使用场景约束BatchSize必须设置很小,无疑此时考虑使用GroupNorm;而其它任务情形应该优先考虑使用BatchNorm。
-
如果把特征图比喻成一摞书,这摞书总共有 N 本,每本有 C 页,每页有 H 行,每行 有W 个字符。
- BN 求均值时,相当于把这些书按页码一一对应地加起来(例如第1本书第36页,第2本书第36页…),再除以每个页码下的字符总数:N×H×W,因此可以把 BN 看成求“平均书”的操作(注意这个“平均书”每页只有一个字),求标准差时也是同理。
- LN 求均值时,相当于把每一本书的所有字加起来,再除以这本书的字符总数:C×H×W,即求整本书的“平均字”,求标准差时也是同理。
- IN 求均值时,相当于把一页书中所有字加起来,再除以该页的总字数:H×W,即求每页书的“平均字”,求标准差时也是同理。
- GN 相当于把一本 C 页的书平均分成 G 份,每份成为有 C/G 页的小册子,求每个小册子的“平均字”和字的“标准差”。
-
不论是哪个方法,其基本计算步骤都如上所述,大同小异,最主要的区别在于神经元集合S的范围怎么定,不同的方法采用了不同的神经元集合定义方法。
-
为什么这些Normalization需要确定一个神经元集合S呢?原因很简单,这类深度学习的规范化目标是将神经元的激活值限定在均值为0方差为1的正态分布中。而为了能够对网络中某个神经元的激活值 规范到均值为0方差为1的范围,必须有一定的手段求出均值和方差,而均值和方差是个统计指标,要计算这两个指标一定是在一个集合范围内才可行,所以这就要求必须指定一个神经元组成的集合,利用这个集合里每个神经元的激活来统计出所需的均值和方差,这样才能达到预定的规范化目标。
Batch Normalization, BN
论文链接:https://arxiv.org/pdf/1502.03167.pdf
-
BN的主要思想: 针对每个神经元,使数据在进入激活函数之前,沿着通道计算每个batch的均值、方差,‘强迫’数据保持均值为0,方差为1的正态分布,避免发生梯度消失。具体来说,就是把第1个样本的第1个通道,加上第2个样本第1个通道 … 加上第 N 个样本第1个通道,求平均,得到通道 1 的均值(注意是除以 N×H×W 而不是单纯除以 N,最后得到的是一个代表这个 batch 第1个通道平均值的数字,而不是一个 H×W 的矩阵)。求通道 1 的方差也是同理。对所有通道都施加一遍这个操作,就得到了所有通道的均值和方差。
-
BN的使用位置: 第一种是原始BN论文提出的,放在激活函数之前;另外一种是后续研究提出的,放在激活函数之后,不少研究表明将BN放在激活函数之后效果更好。
-
BN算法过程:
- 沿着通道计算每个batch的均值 μ \mu μ
- 沿着通道计算每个batch的方差 σ 2 {\sigma}^2 σ2
- 做归一化
- 加入缩放和平移变量
α
\alpha
α和
β
\beta
β
其中 ε \varepsilon ε是一个很小的正值,比如 1 0 − 8 10^{-8} 10−8 。加入缩放和平移变量的原因是:保证每一次数据经过归一化后还保留原有学习来的特征,同时又能完成归一化操作,加速训练。 这两个参数是用来学习的参数。
-
BN的作用:
- 允许较大的学习率;
- 减弱对初始化的强依赖性
- 保持隐藏层中数值的均值、方差不变,让数值更稳定,可以加快模型收敛速度;
- 引入的随机噪声能够起到对模型参数进行正则化的作用,有利于增强模型泛化能力
-
BN存在的问题:
- 每次是在一个batch上计算均值、方差,如果batch size太小,则计算的均值、方差不足以代表整个数据分布;
- batch size太大:会直接固定梯度下降的方向,导致很难更新;
- 对于有些像素级图片生成任务来说,BN效果不佳;
- RNN等动态网络使用BN效果不佳且使用起来不方便;
- 训练时和推理时统计量不一致;
-
pytorch BatchNorm:
torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
torch.nn.BatchNorm3d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
参数:
num_features: 来自期望输入的特征数,该期望输入的大小为’batch_size x num_features [x width]’
eps: 为保证数值稳定性(分母不能趋近或取0),给分母加上的值。默认为1e-5。
momentum: 动态均值和动态方差所使用的动量。默认为0.1。
affine: 布尔值,当设为true,给该层添加可学习的仿射变换参数。
track_running_stats:布尔值,当设为true,记录训练过程中的均值和方差;
Layer Normalization, LN
论文链接:https://arxiv.org/pdf/1607.06450v1.pdf
- 针对BN不适用于深度不固定的网络(sequence长度不一致,如RNN),Layer Normalization这种在同隐层内计算统计量的模式就比较符合RNN这种动态网络,目前在RNN中貌似也只有LayerNorm相对有效,但Layer Normalization目前看好像也只适合应用在RNN场景下,在CNN等环境下效果是不如BatchNorm或者GroupNorm等模型的。从目前现状看,动态网络中的Normalization机制是非常值得深入研究的一个领域。
- LN中同层神经元的输入拥有相同的均值和方差,不同的输入样本有不同的均值和方差。
- Layer Normalization (LN) 的一个优势是不需要批训练,在单条数据内部就能归一化。LN不依赖于batch size和输入sequence的长度。
- pytorch LayerNorm
torch.nn.LayerNorm(normalized_shape, eps=1e-05, elementwise_affine=True)
参数:
normalized_shape: 输入尺寸[∗×normalized_shape[0]×normalized_shape[1]×…×normalized_shape[−1]]
eps: 为保证数值稳定性(分母不能趋近或取0),给分母加上的值。默认为1e-5。
elementwise_affine: 布尔值,当设为true,给该层添加可学习的仿射变换参数。
实现公式:
Instance Normalization, IN
论文链接:https://arxiv.org/pdf/1607.08022.pdf
- 从上述内容可以看出,Layer Normalization在抛开对Mini-Batch的依赖目标下,为了能够统计均值方差,很自然地把同层内所有神经元的响应值作为统计范围,那么我们能否进一步将统计范围缩小?对于CNN明显是可以的,因为同一个卷积层内每个卷积核会产生一个输出通道,而每个输出通道是一个二维平面,也包含多个激活神经元,自然可以进一步把统计范围缩小到单个卷积核对应的输出通道内部。对于图中某个卷积层来说,每个输出通道内的神经元会作为集合S来统计均值方差。对于RNN或者MLP,如果在同一个隐层类似CNN这样缩小范围,那么就只剩下单独一个神经元,输出也是单值而非CNN的二维平面,这意味着没有形成集合S,所以RNN和MLP是无法进行Instance Normalization操作的。
- IN针对图像像素做normalization,最初用于图像的风格化迁移。在图像风格化中,生成结果主要依赖于某个图像实例,feature map 的各个 channel 的均值和方差会影响到最终生成图像的风格。所以对整个batch归一化不适合图像风格化中,因而对H、W做归一化。可以加速模型收敛,并且保持每个图像实例之间的独立。
- pytorch InstanceNorm
torch.nn.InstanceNorm1d(num_features, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
torch.nn.InstanceNorm2d(num_features, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
torch.nn.InstanceNorm3d(num_features, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
参数:
num_features: 来自期望输入的特征数
eps: 为保证数值稳定性(分母不能趋近或取0),给分母加上的值。默认为1e-5。
momentum: 动态均值和动态方差所使用的动量。默认为0.1。
affine: 布尔值,当设为true,给该层添加可学习的仿射变换参数。
rack_running_stats:布尔值,当设为true,记录训练过程中的均值和方差;
实现公式:
Group Normalization, GN
论文链接:https://arxiv.org/pdf/1803.08494.pdf
- 从上面的Layer Normalization和Instance Normalization可以看出,这是两种极端情况,Layer Normalization是将同层所有神经元作为统计范围,而Instance Normalization则是CNN中将同一卷积层中每个卷积核对应的输出通道单独作为自己的统计范围。那么,有没有介于两者之间的统计范围呢?通道分组是CNN常用的模型优化技巧,所以自然而然会想到对CNN中某一层卷积层的输出或者输入通道进行分组,在分组范围内进行统计。这就是Group Normalization的核心思想,是Facebook何凯明研究组2017年提出的改进模型。
- GN是为了解决BN对较小的mini-batch size效果差的问题。GN适用于占用显存比较大的任务,例如图像分割。对这类任务,可能 batch size 只能是个位数,再大显存就不够用了。而当 batch size 是个位数时,BN 的表现很差,因为没办法通过几个样本的数据量,来近似总体的均值和标准差。
- 具体方法:GN 计算均值和标准差时,把每一个样本 feature map 的 channel 分成 G 组,每组将有 C/G 个 channel,然后将这些 channel 中的元素求均值和标准差。各组 channel 用其对应的归一化参数独立地归一化。
- 理论上MLP和RNN也可以引入这种模式,但是还没有看到相关研究,不过从道理上考虑,MLP和RNN这么做的话,分组内包含神经元太少,估计缺乏统计有效性,猜测效果不会太好。
- pytorch GroupNorm
torch.nn.GroupNorm(num_groups, num_channels, eps=1e-05, affine=True)
参数:
num_groups:需要划分为的groups
num_features: 来自期望输入的特征数,该期望输入的大小为’batch_size x num_features [x width]’
eps: 为保证数值稳定性(分母不能趋近或取0),给分母加上的值。默认为1e-5。
momentum: 动态均值和动态方差所使用的动量。默认为0.1。
affine: 布尔值,当设为true,给该层添加可学习的仿射变换参数。
实现公式:
备注
- 另外,还需要注意它们的映射参数 α \alpha α和 β \beta β 的区别:对于 BN,IN,GN, 其 α \alpha α和 β \beta β 都是维度等于通道数 C 的向量。而对于 LN,其 α \alpha α和 β \beta β 都是维度等于normalized_shape 的矩阵。
- BN 和 IN 可以设置参数:momentum和track_running_stats来获得在整体数据上更准确的均值和标准差。LN 和 GN 只能计算当前 batch 内数据的真实均值和标准差。