层归一化(LayerNorm)

我们知道 BN 通过对输入 mini batch 样本进行 normalization,能够加快网络收敛。但是,BN 不适合用在 RNN 网络。原因是:BN 是对同一个批次所有样本的相同特征进行 normalization 操作,也就是说 Batch 样本的特征数量应该是一样的。

例如:我们在 CNN 网络中输入的 Batch 样本,每个输入图像都有相同数量的 Feature Map,这样 BN 可以对不同的样本的相同 Feature Map 进行 normalization 操作。

例如:我们在 MLP 网络中输入的 Batch 样本,每条输入样本都有相同数量的特征,这样 BN 可以对不同样本的相同特征进行 normalization 操作。

但是,如果是 RNN 网络,输入的 Batch 样本,长度是不一致的。对于一句话,每个字/词可以理解为样本的一个特征,这就导致每个样本的特征数量不同,我们就没办法在特征维度进行 normalization 操作。为了解决该问题,就提出了 Layer Normalization,LN 和 BN 不同之处就在于,对一个样本的不同特征分别进行 normalization 操作,其实也是基于时间步的 normalization 操作。

简单来说,就是对每一个字的向量表示进行标准化计算,计算公式如下:

elementwise_affine = True 时,λ 是一个数据维度的参数。例如:在 Bert 中 hidden size 为 768,所以 λ 就会有 768 维度,对应的 β 也有 768 维度。λ 和 β 相当于对归一化之后做一个线性变换的参数和偏置。

对每一个维度进行标准化之后,得到的每一个维度的数据再乘以 λ 加上 β。

Paper:https://arxiv.org/pdf/1607.06450.pdf

示例代码:

import torch.nn as nn
import torch


def test():

    batch, length, dim = 2, 3, 4
    torch.manual_seed(0)
    batch_input = torch.randint(0, 10, size=[batch, length, dim]).float()
    print(batch_input)

    layer_norm = nn.LayerNorm(normalized_shape=dim, eps=1e-5, elementwise_affine=False)
    output = layer_norm(batch_input)
    print(output)


if __name__ == '__main__':
    test()

程序执行结果:

tensor([[[4., 9., 3., 0.],
         [3., 9., 7., 3.],
         [7., 3., 1., 6.]],

        [[6., 9., 8., 6.],
         [6., 8., 4., 3.],
         [6., 9., 1., 4.]]])
tensor([[[ 0.0000,  1.5430, -0.3086, -1.2344],
         [-0.9622,  1.3471,  0.5773, -0.9622],
         [ 1.1531, -0.5241, -1.3628,  0.7338]],

        [[-0.9622,  1.3471,  0.5773, -0.9622],
         [ 0.3906,  1.4321, -0.6509, -1.1717],
         [ 0.3430,  1.3720, -1.3720, -0.3430]]])
  1. 对 [4., 9., 3., 0.] 进行 normalization,得到[ 0.0000, 1.5430, -0.3086, -1.2344]
  2. 对 [0., 3., 9., 3.] 进行 normalization,得到 [-0.9622, 1.3471, 0.5773, -0.9622]
  3. … 以此类推

第一个字 [4, 9, 3, 0] 向量的归一化计算过程如下:

   a = torch.tensor([4., 9., 3., 0.]).float()

    # 先将向量中心化
    b = a - torch.mean(a)

    # 计算方差
    # torch.var 计算时会除以 (n-1)
    # LayNorm 中计算方差时除的是 n
    var =  torch.sum(b ** 2) / 4

    # 计算标准差
    c = torch.sqrt(var + 1e-5)

    # 计算归一化后的结果
    print(b / c)

总结:

  1. LayerNorm 使用时,只需要指定 token 的向量维度即可
  2. LayerNorm 使用时,对每一个字的向量进行归一化,即:将输入的每一个字的向量表示都归一化到均值为 0 标准差为 1 的分布中

未经允许不得转载:一亩三分地 » 层归一化(LayerNorm)
评论 (0)

7 + 8 =