Post

Xnorm and Code

Xnorm

规范化(Normalization)的核心是为了让不同层的输入取值范围或者分布能够比较一致。

在堆叠式的神经网络中,高层的网络会受到之前所有底层网络参数变化的影响,导致该高层网络的输入的分布产生较大的改变,这种现象被称为内部协变量偏移(Internal Covariate Shift)。随着网络深度的增大,这种差异会变得更加显著,从而影响模型的训练速度和最终性能。

输入分布变化较大会导致反向传播的梯度在不同层之间波动较大,这主要是因为输入分布的变化会影响到激活函数的输出和梯度的传播。如果某一层的输入分布变化较大,导致激活函数的导数在某些区域非常小(如Sigmoid在输入远离0时的导数接近0),那么反向传播的梯度会被大幅缩小,反之亦然。

1. Batchnorm

假设有$N$本书,每本书有$C$页,每页可容纳$H\times W$个字符,Batch Norm就是页为单位:假设每本书都为$C$页,首先计算$N$本书中第1页的字符$[N, H, W]$均值方差,得到统计量$\mu_1$和$\sigma_1$,然后对$N$本书的第一页利用该统计量对第一页的元素进行归一化操作,剩下的$C-1$页同理

BN是给定每一层,在一个mini-batch上进行规范化,

  • (1) 给定样本$Z_i$,其中$i=1,2,…,m$
  • (2) 计算样本的第$j$维度特征的均值$\mu_j=\frac{1}{m}\sum_{i=1}^{m}Z^i_j$
  • (3) 计算样本的第$j$维度特征的方差$\sigma_j=\frac{1}{m}\sum_{i=1}^{m}(Z^i_j-\mu_j)^2$
  • (4) 样本的每个维度减去均值,除以标准差$\hat{Z}_j^i=\frac{Z^i_j-\mu_j}{\sqrt{\sigma_j+\epsilon}}$

BN在mini-batch时不能发挥作用;在训练时,Batch Norm 需要保存每个 step 的统计信息(均值和方差)。在测试时,由于变长句子的特性,测试集可能出现比训练集更长的句子,所以对于后面位置的 step,是没有训练的统计量使用的; 注意在BatchNorm中,用于更新running_var时,使用无偏样本方差,但是在对batch进行归一化时,使用有偏样本方差,因此如果batch_size=1,更新running_var时会报错

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env python3

torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
#输入维度是(N, C, L)时,num_features应该取C;这里N是batch size,C是数据的channel,L是数据长度,输入维度是(N, L)时,num_features应该取L;
bn = nn.BatchNorm2d(c, eps=1e-10, affine=False, track_running_stats=False)
y = bn(x)
x_ = rearrange(x, 'b c h w -> (b w h) c')
mean = rearrange(x_.mean(dim=0), 'c -> 1 c 1 1')
std = rearrange(x_.std(dim=0), 'c -> 1 c 1 1')
y_ = (x-mean)/std

Training

affine设为True时,BatchNorm层才会学习参数$\gamma$和$\beta$,否则不包含这两个变量,变量名是weight和bias。对归一化后的batch进行仿射变换,即乘以模块内部的weight(初值是[1., 1., 1., 1.])然后加上模块内部的bias(初值是[0., 0., 0., 0.]),这两个变量会在反向传播时得到更新。

track_running_stats设为True时,BatchNorm层会统计全局均值running_mean(初值是[0., 0., 0., 0.])和方差running_var[1., 1., 1., 1.]),否则不含有这两个变量

momentum更新全局均值running_mean和方差running_var时使用该值进行平滑,即$x_{new}=(1-momentum)x_{cur}+momentumx_{batch}$,$x_{cur}$表示更新前的running_mean和running_var,$x_{batch}$表示当前batch的均值和无偏样本方差

Evaluating

affine设为True时,BatchNorm层对归一化后的batch进行放射变换,即乘以模块内部的weight然后加上模块内部的bias,这两个变量都是网络训练时学习到的。

track_running_stats设为True时,BatchNorm层使用在网络训练时统计出来的全局均值和无偏样本方差,否则使用batch自己的mean和无偏样本方差var

2. Layer Norm

有$N$本书,每本书有$C$页,每页可容纳$H\times W$个字符,Layer Norm就是以本为单位:首先计算第一本书中的所有字符$【H, W, C】$均值方差,得到统计量得到统计量$\mu_1$和$\sigma_1$,然后利用该统计量对第一本数进行归一化操作,剩下的$N-1$本书同理

手动实现一个 Layer Norm 层

1
2
3
4
5
6
7
8
9
10
11
12
13
class LayerNorm(nn.Moudle):
    # torch.nn.functional.layer_norm(input, normalized_shape, weight=None, bias=None, eps=1e-05)
    def __init__(self,ndim,bias):
        super(),__init__()
        self.weights=nn.Parameter(torch.ones(ndim))
        self.bias=nn.Parameter(torch.zeros(ndim)) if bias else None
    def forward(self,input):
        return F.layer_norm(input,self.weights.shape,self.weights,self.bias,1e-5)
b, c, h, w = x.shape
x_ = rearrange(x, 'b c h w -> (h w c) b')
mean = rearrange(x_.mean(dim=0), 'b -> b 1 1 1')
std = rearrange(x_.std(dim=0), 'b -> b 1 1 1')
y_ = (x-mean)/std

Layer Normalization在每个元素上应用缩放和偏置(When elementwise_affine=True)。具体来说,它使用逐元素(elementwise)缩放因子和偏置值来调整每个元素每个元素都有自己的缩放因子和偏置值,而不是整个通道/平面共享一个缩放因子和偏置值。 Layer Norm即“Layer Norm”,只在每个样本上计算所有层的均值和方差,有 Post Norm和Pre Norm。Layer Norm以样本为单位计算统计量,因此最后会得到$N$个$\mu$和$\sigma$。假设输入特征为$[N, H, W, C]$,在$N$的每个维度上对$[H, W,C]$计算其均值、方差,用于该维度上的归一化操作。

3. Instance Normalization

有$N$本书,每本书有$C$页,每页可容纳$H\times W$个字符,Instance Norm就是以每本书的每一页为单位:首先计算第1本书中第1页的所有字符$[H, W]$均值方差,得到统计量$\mu_1$和$\sigma_1$,然后利用该统计量对第1本书第1页进行归一化操作,剩下的$NC-1$页同理。

手动实现一个 Instance Norm 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# torch.nn.InstanceNorm1d(num_features, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False, device=None, dtype=None)
"""
InstanceNorm1d and LayerNorm are very similar, but have some subtle differences. InstanceNorm1d is applied on each channel of channeled data like multidimensional time series, but LayerNorm is usually applied on entire sample and often in NLP tasks. Additionally, LayerNorm applies elementwise affine transform, while InstanceNorm1d usually don’t apply affine transform.
"""
# Without Learnable Parameters

x=torch.randn((32,3,224,224))
b,c,h,w=x.shape
In=nn.InstanceNorm2d(c,eos=1e-12,affine=False,track_running_stats=False)
Y=In(x)

x_=rearrange(x,'b c h w -> b c (h w)')
mean=rearrange(x_.mean(dim=2),'b c -> b c 1 1')
std=rearrange(x_.std(dim=2),'b c -> b c 1 1 ')
y_=(x-mean)/std

print(f'diff is {Y-y_}')

4. RMSNorm

RMSNorm论文阅读

是对对LayerNorm的公式做了改造

手动实现一个 RMSNorm 层

1
2
3
4
5
6
7
8
9
10
11
12
#torch.nn.RMSNorm(normalized_shape, eps=None, elementwise_affine=True, device=None, dtype=None)
rms_norm = nn.RMSNorm([2, 3])
input = torch.randn(2, 2, 3)
Y=rms_norm(input)

b, c, h, w = x.shape
x_ = rearrange(x, 'b c h w -> (h w c) b')
# mean = rearrange(x_.mean(dim=0), 'b -> b 1 1 1')
rms=sum([(x_[i]*(x_[i])) for i in range(b) ])/b
# std = rearrange(x_.std(dim=0), 'b -> b 1 1 1')
y_ = (x)/rms
print(f'diff is {Y-y_}')
This post is licensed under CC BY 4.0 by the author.

Trending Tags