Layer Normalization详解
2023-12-03发布于深度学习 | 最后更新于2023-12-04 12:12:00
总体步骤与Batch Normalization相同,只有在第二步标准化中计算均值和方差时涉及的元素范围不同
Layer Normalization
pytorch中Layer Normalization的方法只有一种,并没有像Batch Normalization一样分为1d、2d、3d,这和LN计算均值和方差的范围有关。
对应的Normalization类为torch.nn.LayerNorm
标准化的公式仍然为:
此处,我们结合官方文档中给出的两个例子进行理解:
NLP
假设一个Batch中有2个句子,每个句子由3个词组成,词向量的维数为4。如此,计算的是各个句子中所有数值的均值和方差,即最终算得2个均值和2个方差,每个均值/方差都统计了12个数(即一个句子中的所有值)。
下面对于下面这组具体数据进行手工计算:
[
[
[8, 3, 1, 6], # 第一个句子的第一个词
[3, 3, 2, 7],
[1, 5, 9, 1]
], # 第一个句子
[
[6, 2, 2, 5],
[5, 8, 5, 9],
[2, 6, 2, 8]
] # 第二个句子
]
对于第一个句子中的12个数值都减去\(\frac{49}{12}\),再除以\(\sqrt{7.41}\);第二个句子同样处理即可得到结果。使用numpy验证如下:
data_np = np.array([[[8, 3, 1, 6],
[3, 3, 2, 7],
[1, 5, 9, 1]],
[[6, 2, 2, 5],
[5, 8, 5, 9],
[2, 6, 2, 8]]])
data_torch = torch.from_numpy(data_np)
data_torch = data_torch.float()
ln = nn.LayerNorm((3, 4), elementwise_affine=False)
means = np.repeat(data_np.mean((-1, -2)), 12).reshape(2, 3, 4)
vars = np.repeat(data_np.var((-1, -2)), 12).reshape(2, 3, 4)
ln(data_torch)
tensor([[[ 1.4388, -0.3980, -1.1327, 0.7041],
[-0.3980, -0.3980, -0.7653, 1.0715],
[-1.1327, 0.3368, 1.8062, -1.1327]],
[[ 0.4082, -1.2247, -1.2247, 0.0000],
[ 0.0000, 1.2247, 0.0000, 1.6330],
[-1.2247, 0.4082, -1.2247, 1.2247]]])
(data_np - means) / np.sqrt(vars)
array([[[ 1.4389, -0.398 , -1.1327, 0.7041],
[-0.398 , -0.398 , -0.7653, 1.0715],
[-1.1327, 0.3368, 1.8062, -1.1327]],
[[ 0.4082, -1.2247, -1.2247, 0. ],
[ 0. , 1.2247, 0. , 1.633 ],
[-1.2247, 0.4082, -1.2247, 1.2247]]])
Image
假设一个batch中有2张图片,每张图片都采用RGB三通道,图片的大小为2x3
[
[
[
[7, 8, 6],
[9, 9, 7]
], # 第一张图片的第一个通道
[
[7, 7, 9],
[3, 5, 5]
],
[
[9, 7, 7],
[3, 2, 2]
]
], # 第一张图片
[
[
[3, 5, 5],
[5, 2, 4]
], # 第二张图片的第一个通道
[
[8, 1, 9],
[6, 8, 2]
],
[
[4, 4, 9],
[6, 5, 5]
]
] # 第二张图片
]
最终会得到2个均值和2个方差,都是对\(3\times 2\times 3=18\)个值进行的计算。第一组数值为7, 8, 6, 9, ..., 7, 3, 2, 2
,第二组数值为3, 5, 5, 5, ..., 9, 6, 5, 5
验证如下
data_np = np.array([[
[[7, 8, 6], [9, 9, 7]],
[[7, 7, 9], [3, 5, 5]],
[[9, 7, 7], [3, 2, 2]]
],
[
[[3, 5, 5], [5, 2, 4]],
[[8, 1, 9], [6, 8, 2]],
[[4, 4, 9], [6, 5, 5]]
]])
data_torch = torch.from_numpy(data_np)
data_torch = data_torch.float()
ln = nn.LayerNorm((3, 2, 3), elementwise_affine=False)
means = np.repeat(data_np.mean((-1, -2, -3)), 18).reshape(2, 3, 2, 3)
vars = np.repeat(data_np.var((-1, -2, -3)), 18).reshape(2, 3, 2, 3)
ln(data_torch)
tensor([[[[ 0.3349, 0.7654, -0.0957],
[ 1.1959, 1.1959, 0.3349]],
[[ 0.3349, 0.3349, 1.1959],
[-1.3873, -0.5262, -0.5262]],
[[ 1.1959, 0.3349, 0.3349],
[-1.3873, -1.8178, -1.8178]]],
[[[-0.9046, -0.0244, -0.0244],
[-0.0244, -1.3447, -0.4645]],
[[ 1.2958, -1.7847, 1.7358],
[ 0.4156, 1.2958, -1.3447]],
[[-0.4645, -0.4645, 1.7358],
[ 0.4156, -0.0244, -0.0244]]]])
(data_np - means) / np.sqrt(vars)
array([[[[ 0.3349, 0.7654, -0.0957],
[ 1.1959, 1.1959, 0.3349]],
[[ 0.3349, 0.3349, 1.1959],
[-1.3873, -0.5262, -0.5262]],
[[ 1.1959, 0.3349, 0.3349],
[-1.3873, -1.8178, -1.8178]]],
[[[-0.9046, -0.0244, -0.0244],
[-0.0244, -1.3447, -0.4645]],
[[ 1.2958, -1.7847, 1.7358],
[ 0.4156, 1.2958, -1.3447]],
[[-0.4645, -0.4645, 1.7358],
[ 0.4156, -0.0244, -0.0244]]]])
torch.nn.LayerNorm
此处讨论一下LayerNorm类的一些内容。其输入维度为\((N,*)\),输出维度与输入保持一致。
LayerNorm初始化的第一个参数称为normalized_shape
,指出了要标准化维度的大小信息,需要注意的是,必须从输入维度大小的后面开始截取。例如对于输入大小\((2, 3, 4, 5)\),normalized_shape
参数可以指定为(5)
、(4, 5)
、(3, 4, 5)
或(2, 3, 4, 5)
,不能跳着来,也不能从前面开始。显然,LayerNorm会对此参数指出的范围计算统计量。
和BatchNorm不同的是,要指定是否使用放缩和平移,用的不是affine
参数,而是elementwise_affine
。
总结
总而言之,Layer Normalization会以一个特征向量为范围计算统计量。例如对于输入大小\((2, 3, 4, 5)\),normalized_shape
参数与计算统计量用到的数值个数对应关系为:
- (5)
:\(5\)
- (4, 5)
:\(4\times 5=20\)
- (3, 4, 5)
:\(3\times 4\times 5=60\)
- (2, 3, 4, 5)
:\(2\times 3\times 4\times 5=120\)