Layer Normalization解析

原论文名称:Layer Normalization
原论文地址: https://arxiv.org/abs/1607.06450

之前有讲过Batch Normalization的原理,链接,今天来简单讲讲Layer Normalization。Layer Normalization是针对自然语言处理领域提出的,例如像RNN循环神经网络。为什么不使用直接BN呢,因为在RNN这类时序网络中,时序的长度并不是一个定值(网络深度不一定相同),比如每句话的长短都不一定相同,所有很难去使用BN,所以作者提出了Layer Normalization(注意,在图像处理领域中BN比LN是更有效的,但现在很多人将自然语言领域的模型用来处理图像,比如Vision Transformer,此时还是会涉及到LN)。

具体论文就不讲了,我们直接看下Pytorch官方给的关于LayerNorm的简单介绍。只看公式的话感觉和BN没什么区别,都是减均值 E ( x ) E(x) E(x),除以标准差 V a r ( x ) + ϵ \sqrt{Var(x) + \epsilon } Var(x)+ϵ ​ 其中 ϵ \epsilon ϵ是一个非常小的量(默认 1 0 − 5 10^{-5} 10−5),是为了防止分母为零。同样也有两个可训练的参数 β , γ \beta, \gamma β,γ。不同的是,BN是对一个batch数据的每个channel进行Norm处理,但LN是对单个数据的指定维度进行Norm处理与batch无关(后面有示例)。而且在BN中训练时是需要累计moving_mean和moving_var两个变量的(所以BN中有4个参数 m o v i n g _ m e a n , m o v i n g _ v a r , β , γ moving\_mean, moving\_var, \beta, \gamma moving_mean,moving_var,β,γ),但LN不需要累计只有 β , γ \beta, \gamma β,γ两个参数。

在Pytorch的LayerNorm类中有个normalized_shape参数,可以指定你要Norm的维度(注意,函数说明中the last certain number of dimensions,指定的维度必须是从最后一维开始)。比如我们的数据的shape是[4, 2, 3],那么normalized_shape可以是[3](最后一维上进行Norm处理),也可以是[2, 3](Norm最后两个维度),也可以是[4, 2, 3](对整个维度进行Norm),但不能是[2]或者[4, 2],否则会报以下错误(以normalized_shape=[2]为例):

RuntimeError: 
Given normalized_shape=[2],         
expected input with shape [*, 2],    
but got input of size[4, 2, 3]

提示我们传入的normalized_shape=[2],接着系统根据我们传入的normalized_shape推理出期待的输入数据shape应该为[*, 2]即最后的一个维度大小应该是2,但我们实际传入的数据shape是[4, 2, 3]所以报错了。

Layer Normalization解析
接着,我们再来看个示例。下面是我写的测试代码,分别使用官方的LN方法和自己实现的LN方法进行比较,看自己理解的是否正确。

import torch
import torch.nn as nn


def layer_norm_process(feature, beta=0., gamma=1., eps=1e-5):
    feature_shape = feature.shape
    feature = feature.reshape(-1, feature_shape[-1])
    for i in range(feature.shape[0]):
        # 计算均值
        mean_t = feature[i].mean()
        # 计算方差
        var = feature[i].var(unbiased=False)

        # layer norm process
        feature[i] = (feature[i] - mean_t) / torch.sqrt(var + eps)
        feature[i] = feature[i] * gamma + beta
    feature = feature.reshape(feature_shape)
    return feature


t = torch.rand(4, 2, 3)
print(t)
# 仅在最后一个维度上做norm处理
norm = nn.LayerNorm(normalized_shape=t.shape[-1], eps=1e-5)
# 官方layer norm处理
t1 = norm(t)
# 自己实现的layer norm处理
t2 = layer_norm_process(t, eps=1e-5)
print("t1:\n", t1)
print("t2:\n", t2)

首先使用torch.rand方法随机生成一个shape为[4, 2, 3]的变量t:

Layer Normalization解析

接着使用官方的方法创建一个LN层,这里t.shape[-1]指的是数据的最后一个维度3即只对最后一个维度进行Norm处理,如上图中用红色框框选出的每组数据:

# 仅在最后一个维度上做norm处理
norm = nn.LayerNorm(normalized_shape=t.shape[-1], eps=1e-5)

然后将数据传入实例化好的norm类得到以下结果:

 tensor([[[-1.2758,  1.1659,  0.1099],
         [ 0.6532, -1.4123,  0.7591]],

        [[ 1.1400,  0.1522, -1.2922],
         [ 1.0942, -1.3229,  0.2287]],

        [[-0.9757, -0.3983,  1.3741],
         [ 1.4134, -0.7379, -0.6755]],

        [[ 0.1563,  1.1389, -1.2951],
         [-1.2341,  0.0203,  1.2138]]], grad_fn=<NativeLayerNormBackward>)

然后调用自己实现的LayerNorm方法(注意, β \beta β最初为0, γ \gamma γ最初为1,后面通过训练慢慢学习调整的)得到如下结果:

 tensor([[[-1.2758,  1.1659,  0.1099],
         [ 0.6532, -1.4123,  0.7591]],

        [[ 1.1400,  0.1522, -1.2922],
         [ 1.0942, -1.3229,  0.2287]],

        [[-0.9757, -0.3983,  1.3741],
         [ 1.4134, -0.7379, -0.6755]],

        [[ 0.1563,  1.1389, -1.2951],
         [-1.2341,  0.0203,  1.2138]]])

很明显和官方得到的结果是一模一样的,这也说明了自己的理解是正确的。

上一篇:SAP Spartacus Reference App Structure


下一篇:2021-06-13