参考来源:
脚本之家:pytorch LayerNorm 参数的用法及计算过程

LayerNormchannel 方向做归一化,算 CHW 的均值,主要对 RNN 作用明显;
LayerNorm 中不会像 BatchNorm 那样跟踪统计全局的均值方差,因此 train()eval()LayerNorm 没有影响。

LayerNorm 参数

  1. torch.nn.LayerNorm(
  2. normalized_shape: Union[int, List[int], torch.Size],
  3. eps: float = 1e-05,
  4. elementwise_affine: bool = True
  5. )

参数:

normalized_shape

  • 如果传入整数,比如 4,则被看做只有一个整数的 list,此时 LayerNorm 会对输入的最后一维进行归一化,这个 **int** 值需要和输入的最后一维一样大
    • 假设此时输入的数据维度是 [3, 4],则对 3 个长度为 4 的向量求均值方差,得到 3 个均值和 3 个方差,分别对这 3 行进行归一化(每一行的 4 个数字都是均值为 0,方差为 1);LayerNorm 中的 weightbias 也分别包含 4 个数字,重复使用 3 次,对每一行进行仿射变换(仿射变换即乘以 weight 中对应的数字后,然后加 bias 中对应的数字),并会在反向传播时得到学习。
  • 如果输入的是个 **list** 或者 **torch.Size**,比如 [3, 4]torch.Size([3, 4]),则会对网络最后的两维进行归一化,且要求输入数据的最后两维尺寸也是 [3, 4]
    • 假设此时输入的数据维度也是 [3, 4],首先对这 12 个数字求均值和方差,然后归一化这个 12 个数字;weightbias 也分别包含 12 个数字,分别对 12 个归一化后的数字进行仿射变换(仿射变换即乘以 weight 中对应的数字后,然后加 bias 中对应的数字),并会在反向传播时得到学习。
    • 假设此时输入的数据维度是 [N, 3, 4],则对着 N[3,4]做和上述一样的操作,只是此时做仿射变换时,weightbias 被重复用了 N 次。
    • 假设此时输入的数据维度是 [N, T, 3, 4],也是一样的,维度可以更多。

注意:显然 LayerNormweightbiasshape 就是传入的 normalized_shape

eps

归一化时加在分母上防止除零。

elementwise_affine

  • 如果设为 False,则 LayerNorm 层不含有任何可学习参数。
  • 如果设为 True(默认是 True)则会包含可学习参数 weightbias,用于仿射变换,即对输入数据归一化到均值 0 方差 1 后,乘以 weight,加 bias

LayerNorm 前向传播(以 normalized_shape 为一个 int 举例)

1、如下所示输入数据的 shape(3, 4),此时 normalized_shape 传入 4(输入维度最后一维的 size),则沿着最后一维(沿着最后一维的意思就是对最后一维的数据进行操作)求 E[x]Var[x],并用这两个结果把 batch 沿着最后一维归一化,使其均值为 0,方差为 1。归一化公式用到了 eps(),即
torch.nn.LayerNorm - 图1

  1. tensor = torch.FloatTensor([[1, 2, 4, 1],
  2. [6, 3, 2, 4],
  3. [2, 4, 6, 1]])

此时,E[x]=[2.0,3.75,3.25]Var[x]=[1.5000,2.1875,3.6875](有样本偏差),归一化后的值如下面所示。

  1. [[-0.8165, 0.0000, 1.6330, -0.8165],
  2. [ 1.5213, -0.5071, -1.1832, 0.1690],
  3. [-0.6509, 0.3906, 1.4321, -1.1717]]

举例说明:第 0 行第 2 列的数字 4,减去第 0 行的均值 2.0 等于 2,然后除以 (1.5+ε)0.5=(1.50001)0.5=1.224749,即 2/1.224749≈1.6330

2、如果 elementwise_affine==True,则对归一化后的 batch 进行仿射变换,即乘以模块内部的 weight(初值是 [1., 1., 1., 1.])然后加上模块内部的 bias(初值是 [0., 0., 0., 0.]),这两个变量会在反向传播时得到更新。

3、如果 elementwise_affine==False,则 LayerNorm 中不含有 weightbias 两个变量,只做归一化,不会进行仿射变换。

总结

在使用 LayerNorm 时,通常只需要指定 normalized_shape 就可以了。

补充:【Pytorch】F.layer_norm 和 nn.LayerNorm 到底有什么区别?

【背景】
最近在做视频方向,处理的是时序特征,就想着能不能用 Batch Normalization 来做视频特征 BN 层?在网上查阅资料发现,时序特征并不能用 Batch Normalization,因为一个 batch 中的序列有长有短。
此外,BN 的一个缺点是需要较大的 batchsize 才能合理估训练数据的均值和方差,这导致内存很可能不够用,同时它也很难应用在训练数据长度不同的 RNN 模型上。
Layer Normalization (LN) 的一个优势是不需要批训练,在单条数据内部就能归一化。
对于 RNN 等时序模型,有时候同一个 batch 内部的训练实例长度不一(不同长度的句子),则不同的时态下需要保存不同的统计量,无法正确使用 BN 层,只能使用 Layer Normalization
查阅 Layer Normalization(下述 LN)后发现,这东西有两种用法,一个是 **F.layer_norm**,一个是 **torch.nn.LayerNorm**,本文探究他们的区别。

F.layer_norm

用法

  1. F.layer_norm(
  2. x,
  3. normalized_shape,
  4. self.weight.expand(normalized_shape),
  5. self.bias.expand(normalized_shape)
  6. )

其中:

  • **x** 是输入的 Tensor
  • **normalized_shape** 是要归一化的维度,可以是 x 的后若干维度。
  • **self.weight.expand(normalized_shape)**,可选参数,自定义的 weight
  • **self.bias.expand(normalized_shape)**,可选参数,自定义的 bias

示例
很容易看出来,跟 F.normalize 基本一样,没有可学习的参数,或者自定义参数。具体使用示例如下:

  1. import torch.nn.functional as F
  2. input = torch.tensor(a)
  3. y = F.layer_norm(input,(4,))
  4. print(y)
  5. #####################输出################
  6. tensor([[[-0.8095, -1.1224, 1.2966, 0.6354],
  7. [-1.0215, -0.9661, 0.8387, 1.1488],
  8. [-0.3047, 1.0412, -1.4978, 0.7613]],
  9. [[ 0.4605, 1.2144, -1.5122, -0.1627],
  10. [ 1.5676, 0.1340, -1.0471, -0.6545],
  11. [ 1.5388, -0.3520, -1.2273, 0.0405]]])

添加缩放:

  1. w = torch.tensor([1,1,2,2])
  2. b = torch.tensor([1,1,1,1])
  3. y = F.layer_norm(input,(4,),w,b)
  4. print(y)
  5. #########################输出######################
  6. tensor([[[ 0.1905, -0.1224, 3.5931, 2.2708],
  7. [-0.0215, 0.0339, 2.6775, 3.2976],
  8. [ 0.6953, 2.0412, -1.9956, 2.5225]],
  9. [[ 1.4605, 2.2144, -2.0243, 0.6746],
  10. [ 2.5676, 1.1340, -1.0942, -0.3090],
  11. [ 2.5388, 0.6480, -1.4546, 1.0810]]])

nn.LayerNorm

用法

  1. torch.nn.LayerNorm(
  2. normalized_shape: Union[int, List[int], torch.Size],
  3. eps: float = 1e-05,
  4. elementwise_affine: bool = True
  5. )

**normalized_shape**: 输入尺寸, [∗×normalized_shape[0]×normalized_shape[1]×…×normalized_shape[−1]]
**eps**: 为保证数值稳定性(分母不能趋近或取 0),给分母加上的值。默认为 1e-5
**elementwise_affine**: 布尔值,当设为 True,给该层添加可学习的仿射变换参数。

示例
elementwise_affine 如果设为 False,则 LayerNorm 层不含有任何可学习参数。
如果设为 True(默认是 True)则会包含可学习参数 weightbias,用于仿射变换,即对输入数据归一化到均值 0 方差 1 后,乘以 weight,加 bias

  1. import torch
  2. input = torch.randn(2,3,2,2)
  3. import torch.nn as nn
  4. #取消仿射变换要写成
  5. #m = nn.LayerNorm(input.size()[1:], elementwise_affine=False)
  6. m1 = nn.LayerNorm(input.size()[1:])#input.size()[1:]为torch.Size([3, 2, 2])
  7. output1 = m1(input)
  8. #只normalize后两个维度
  9. m2 = nn.LayerNorm([2,2])
  10. output2 = m2(input)
  11. #只normalize最后一个维度
  12. m3 = nn.LayerNorm(2)
  13. output3 = m3(input)

总结

**F.layer_norm** 中没有可学习参数,而 **nn.LayerNorm** 有可学习参数。当 elementwise_affine 设为 False 时,nn.LayerNorm 退化为 F.layer_norm