Layer Normalization详解

2023-12-03发布于深度学习 | 最后更新于2023-12-04 12:12:00

Pytorch Normalization

总体步骤与Batch Normalization相同,只有在第二步标准化中计算均值和方差时涉及的元素范围不同

Layer Normalization

pytorch中Layer Normalization的方法只有一种,并没有像Batch Normalization一样分为1d、2d、3d,这和LN计算均值和方差的范围有关。

对应的Normalization类为torch.nn.LayerNorm

标准化的公式仍然为:

$$ \hat{x_i}=\frac{x_i-\mathrm{E(X)}}{\sqrt{\mathrm{Var(X)}+\epsilon}} $$

此处,我们结合官方文档中给出的两个例子进行理解:

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]
    ] # 第二个句子
]
$$ \begin{align*} \mathrm{E}_1&=\frac{1}{12}(8+3+1+6+\dots +9+1)=\frac{49}{12},&\mathrm{Var}_1&\approx 7.41\\ \mathrm{E}_2&=\frac{1}{12}(6+2+2+5+\dots +2+8)=5,&\mathrm{Var}_2&=6 \end{align*} $$

对于第一个句子中的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

$$ \begin{align*} \mathrm{E}_1&\approx 6.22,&\mathrm{Var}_1&\approx 5.40\\ \mathrm{E}_2&\approx 5.06,&\mathrm{Var}_2&\approx5.16 \end{align*} $$

验证如下

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\)