机器学习中归一化方法很多,如L1/L2归一化、dropout、批量归一化、层归一化、实例归一化、Group归一化等。由于篇幅的限制,这里主要介绍深度学习中两种常用的归一化方法,即批量归一化(Batch Normalization)和层归一化(Layer Normalization)。
批量归一化和层归一化方法在深度学习中应用非常普遍,涉及的数据包括一维或多维。为更好地理解这些归一化方法,我们首先从直观的图形入手,然后再说明其背后的原理,最后通过具体代码实现这些归一化方法。
(1)输入数据为一维的情况
1)批量归一化如图2-47所示。
批量归一化对每个批量样本中的每个特征值分别进行均值和方差的计算。
2)层归一化图2-48所示。
层归一化对每个批量样本中的每个样本分别进行均值和方差计算。
(2)输入数据为多维的情况
这里以输入数据为4维(Batch、Channel、Hight、Width)为例,多维数据常用归一化方法如图2-49所示。
图2-47 批量归一化
图2-48 层归一化
图2-49 输入数据为多维的几种常用归一化方法
在图2-49中, N 表示批量大小, C 表示通道数量, H 表示图像的高, W 表示图像的宽。
1)批量归一化。批次(Batch)方向做归一化,计算 NHW 的均值,对小批量(Small Batch Size)效果不好。批量归一化的主要缺点是对批量的大小比较敏感,由于每次计算均值和方差是在一个批次上,因此如果批量太小,则计算的均值、方差不足以代表整个数据分布。
2)层归一化。通道(Channel)方向做归一化,计算 CHW 的均值,主要对RNN作用明显。例如层归一化在Transformer模型中被广泛使用。
3)实例归一化(Instance Normalization)。一个通道内做归一化,计算 HW 的均值,主要用于风格化迁移。在图像风格化中,因为生成结果主要依赖于某个图像实例,所以对整个批次归一化并不适合,相反,对每个图像实例做归一化更具优势,如图2-50所示,实例归一化可以加速模型收敛,并且保持每个图像实例之间的独立性。
图2-50 在图像风格化中两种归一化方法的效果比较
1.批量归一化的数学表达式
假设批量大小为 m ,一批样本中的某个特征为 x 1 , x 2 ,…, x m 。在神经网络中,可认为对应某个神经元,该神经元对应的输入值为{ x i }。
该批次中,这个特征的均值和方差为:
其中, μ 称为平移参数(shift parameter), σ 称为缩放参数(scale parameter)。通过这两个参数进行平移和缩放变换得到:
为了保证模型的表达能力不会因规范化而下降,可以通过一个附加的再缩放参数和再平移变换,将上一步得到的 进一步变换为:
其中, γ 初始值一般设为1, β 初始值一般设为0,这两个参数在训练过程中将不断进行学习更新。引入参数 γ 和 β 是为了尽量与底层神经网络的学习结果保持一致,将规范化后的数据进行再平移和再缩放,使得每个神经元对应的输入范围是针对该神经元量身定制的一个确定范围(均值为 β 、方差为 γ 2 )。
这种改写方式除了充分利用底层学习的能力外,还有另一方面的意义,即保证获得非线性的表达能力。sigmoid等激活函数在神经网络中有着重要的作用,通过区分饱和区和非饱和区,使得神经网络的数据变换具有非线性计算能力。第一步的规范化会将几乎所有数据映射到激活函数的非饱和区(线性区),仅利用到了线性变化能力,从而降低了神经网络的表达能力。而进行再变换,则可以将数据从线性区变换到非线性区,恢复模型的表达能力。
2.层归一化的数学表达式
批量归一化比较适合于批量比较大的情况,这样批量数据分布与整体分布较接近。在进行训练之前,要做好充分的打乱(shuffle),否则效果会大打折扣。此外,批量归一化对于输入数据不一致的情况可能会产生影响。
为克服批量归一化在这些方面的不足,人们提出层归一化方法。与批量归一化不同,层归一化是对一个样本的所有特征的规范化。它综合考虑一层所有维度的输入,计算该层的平均输入值和输入方差,然后用同一个规范化操作来转换各个维度的输入。假设一个样本的特征数为 n (或对应神经网络层所有的输入神经元的个数),则层归一化的表示式为:
与批量归一化不同,层归一化中 γ 、 β 一般为标量。
层归一化针对单个训练样本进行,不依赖于其他数据,因此可以避免批量归一化中受小批量数据分布影响的问题,可以用于小批量场景、动态网络场景和循环神经网络等,特别是自然语言处理领域,如在Transformer中得到广泛使用。此外,层归一化不需要保存批量的均值和方差,节省了额外的存储空间。
3.实例归一化的数学表达式
批量归一化注重对每个批次进行归一化,保证数据分布一致,因为判别模型中结果取决于数据整体分布。但是在图像风格化中,生成结果主要依赖于某个图像实例,所以对整个批次归一化不适合,因此,实例归一化更具优势,实例归一化的公式如下:
1.用Python实现批量归一化
(1)定义批量归一化的函数
这里假设输入为4维数据,即[Batch,Channel,Higt,Width]。
(2)把批量归一化函数封装在一个类中
(3)定义一个输入
(4)测试
运行结果如下:
2.用PyTorch实现批量归一化
运行结果与使用Python定义的函数一致。
说明:函数nn.BatchNorm2d中各参数。
参数说明如下。
● num_features(int):输入的特征数,这里指通道数,即[B,C,H,W]中的C。
● eps:为保证数值稳定性(分母不能趋近或取0),给分母加上的值(对应公式中的 ε )。默认为1e-5。
● momentum:动态均值和动态方差所使用的动量。默认为0.1。
● affine:布尔值,默认值为True,当设为True,给该层添加可学习的再变换参数,即使用公式中的可学习参数 γ 、 β 。
● track_running_stats:布尔值,当设为True时,记录训练过程中的均值和方差。
输入形状:[B,C,H,W]
输出形状:[B,C,H,W]
3.用Python实现层归一化
层归一化可用于图像或NLP处理,对图像处理的输入假设为4维,即[N,C,H,W]。对于NLP处理,其输入为3维,即[batch_size, time_steps, embedding_dim]。
(1)输入为图像的情况
1)定义层归一化函数。
2)把层归一化函数封装在类中。
3)测试。
运行结果如下:
4)直接使用PyTorch提供的模块:
运行结果与使用Python实现的运行结果完全一致。
(2)输入为语句的情况
运行结果如下:
4.用Python实现实例归一化
实例归一化可用对图像处理的输入假设为4维,即[N,C,H,W]。
(1)定义Instance Norm函数
(2)把函数包装在PyTorch类中
(3)测试
(4)直接使用PyTorch提供的模块
运行结果与自定义Instance Norm的运行结果完全一致。