如何区分并记住常见的几种 Normalization 算法

参考链接:

https://zhuanlan.zhihu.com/p/69659844

https://www.cnblogs.com/cxq1126/p/13299859.html

目录

Batch Normalization (BN)

举例说明:

Tip:

Layer Normalization (LN)

Instance Normalization (IN)

Group Normalization (GN)

总结 


神经网络中有各种归一化算法:Batch Normalization (BN)Layer Normalization (LN)Instance Normalization (IN)Group Normalization (GN)。从公式看它们都差不多,如 (1) 所示:无非是减去均值,除以标准差,再施以线性映射。

Batch Normalization (BN)

# coding=utf8
import torch
from torch import nn

# track_running_stats=False,求当前 batch 真实平均值和标准差,
# 而不是更新全局平均值和标准差
# affine=False, 只做归一化,不乘以 gamma 加 beta(通过训练才能确定)
# num_features 为 feature map 的 channel 数目
# eps 设为 0,让官方代码和我们自己的代码结果尽量接近
bn = nn.BatchNorm2d(num_features=3, eps=0, affine=False, track_running_stats=False)

# 乘 10000 为了扩大数值,如果出现不一致,差别更明显
x = torch.rand(10, 3, 5, 5) * 10000
official_bn = bn(x)

# 把 channel 维度单独提出来,而把其它需要求均值和标准差的维度融合到一起
x1 = x.permute(1, 0, 2, 3).contiguous().view(3, -1)

mu = x1.mean(dim=1).view(1, 3, 1, 1)
# unbiased=False, 求方差时不做无偏估计(除以 N-1 而不是 N),和原始论文一致
# 个人感觉无偏估计仅仅是数学上好看,实际应用中差别不大
std = x1.std(dim=1, unbiased=False).view(1, 3, 1, 1)

my_bn = (x - mu) / std

diff = (official_bn - my_bn).sum()
print('diff={}'.format(diff))  # 差别是 10-5 级的,证明和官方版本基本一致

举例说明:

输入数据是6张3通道784个像素点的数据,将其分到三个通道上,在每个通道上也就是[6, 784]的数据,然后分别得到和通道数一样多的统计数据均值μ和方差σ,将每个像素值减去μ除以σ也就变换到了接近N(0,1)的分布,后面又使用参数β和γ将其变换到接近N(β,γ)的分布。

μσ只是样本中的统计数据,是没有梯度信息的,不过会保存在运行时参数里。而γβ属于要训练的参数,他们是有梯度信息的。

import torch
from torch import nn

x = torch.rand(100, 16, 784)     #100张16通道784像素点的数据,均匀分布

layer = nn.BatchNorm1d(16)       #传入通道数,因为H和W已经flatten过了,所以用1d
out = layer(x)

print(layer.running_mean)
#tensor([0.0499, 0.0501, 0.0501, 0.0501, 0.0501, 0.0502, 0.0500, 0.0499, 0.0499,
#        0.0501, 0.0500, 0.0500, 0.0500, 0.0501, 0.0500, 0.0500])
print(layer.running_var)
#tensor([0.9083, 0.9083, 0.9083, 0.9084, 0.9083, 0.9083, 0.9084, 0.9083, 0.9083,
#        0.9083, 0.9083, 0.9083, 0.9084, 0.9084, 0.9083, 0.9083])
import torch
from torch import nn

x = torch.rand(1, 16, 7, 7)     #1张16通道的7*7的图像

layer = nn.BatchNorm2d(16)      #传入通道数(必须和上面的通道数目一致)
out = layer(x)

print(out.shape)                #torch.Size([1, 16, 7, 7])
print(layer.running_mean)
print(layer.running_var)
print(layer.weight.shape)       #torch.Size([16])对应上面的γ
print(layer.bias.shape)         #torch.Size([16])对应上面的β
print(vars(layer))              #查看网络中一个层上的所有参数
# {'training': True,
#   '_parameters':
#       OrderedDict([('weight', Parameter containing:
#                               tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], requires_grad=True)),
#                   ('bias', Parameter containing:
#                               tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], requires_grad=True))]),
#   '_buffers':
#       OrderedDict([('running_mean', tensor([0.0527, 0.0616, 0.0513, 0.0488, 0.0484, 0.0510, 0.0590, 0.0459, 0.0448, 0.0586, 0.0535, 0.0464, 0.0581, 0.0481, 0.0420, 0.0549])),
#                   ('running_var', tensor([0.9089, 0.9075, 0.9082, 0.9079, 0.9096, 0.9098, 0.9079, 0.9086, 0.9081, 0.9075, 0.9052, 0.9081, 0.9093, 0.9075, 0.9086, 0.9073])),
#                   ('num_batches_tracked', tensor(1))]),
# '_backward_hooks': OrderedDict(),
# '_forward_hooks': OrderedDict(),
# '_forward_pre_hooks': OrderedDict(),
# '_state_dict_hooks': OrderedDict(),
# '_load_state_dict_pre_hooks': OrderedDict(),
# '_modules': OrderedDict(),
# 'num_features': 16,
# 'eps': 1e-05,
# 'momentum': 0.1,
# 'affine': True,
# 'track_running_stats': True}

Tip:

layer.weight和layer.bias是当前batch上的;

如果在定义层时使用了参数affine=False,那么就是固定γ=1和β=0不自动学习,这时参数layer.weight和layer.bias将是None。

 

Layer Normalization (LN)

import torch
from torch import nn

x = torch.rand(10, 3, 5, 5)*10000

# normalization_shape 相当于告诉程序这本书有多少页,每页多少行多少列
# eps=0 排除干扰
# elementwise_affine=False 不作映射
# 这里的映射和 BN 以及下文的 IN 有区别,它是 elementwise 的 affine,
# 即 gamma 和 beta 不是 channel 维的向量,而是维度等于 normalized_shape 的矩阵
ln = nn.LayerNorm(normalized_shape=[3, 5, 5], eps=0, elementwise_affine=False)

official_ln = ln(x)

x1 = x.view(10, -1)
mu = x1.mean(dim=1).view(10, 1, 1, 1)
std = x1.std(dim=1,unbiased=False).view(10, 1, 1, 1)

my_ln = (x-mu)/std

diff = (my_ln-official_ln).sum()

print('diff={}'.format(diff)) # 差别和官方版本数量级在 1e-5

 

Instance Normalization (IN)

import torch
from torch import nn


x = torch.rand(10, 3, 5, 5) * 10000

# track_running_stats=False,求当前 batch 真实平均值和标准差,
# 而不是更新全局平均值和标准差
# affine=False, 只做归一化,不乘以 gamma 加 beta(通过训练才能确定)
# num_features 为 feature map 的 channel 数目
# eps 设为 0,让官方代码和我们自己的代码结果尽量接近
In = nn.InstanceNorm2d(num_features=3, eps=0, affine=False, track_running_stats=False)

official_in = In(x)

x1 = x.view(30, -1)
mu = x1.mean(dim=1).view(10, 3, 1, 1)
std = x1.std(dim=1, unbiased=False).view(10, 3, 1, 1)

my_in = (x-mu)/std

diff = (my_in-official_in).sum()
print('diff={}'.format(diff)) # 误差量级在 1e-5

 

Group Normalization (GN)

Group Normalization (GN) 适用于占用显存比较大的任务,例如图像分割。对这类任务,可能 batchsize 只能是个位数,再大显存就不够用了。而当 batchsize 是个位数时,BN 的表现很差,因为没办法通过几个样本的数据量,来近似总体的均值和标准差。GN 也是独立于 batch 的,它是 LN 和 IN 的折中。正如提出该算法的论文展示的:

import torch
from torch import nn


x = torch.rand(10, 20, 5, 5)*10000

# 分成 4 个 group
# 其余设定和之前相同
gn = nn.GroupNorm(num_groups=4, num_channels=20, eps=0, affine=False)
official_gn = gn(x)

# 把同一 group 的元素融合到一起
x1 = x.view(10, 4, -1)
mu = x1.mean(dim=-1).reshape(10, 4, -1)
std = x1.std(dim=-1).reshape(10, 4, -1)

x1_norm = (x1-mu)/std
my_gn = x1_norm.reshape(10, 20, 5, 5)

diff = (my_gn-official_gn).sum()

print('diff={}'.format(diff)) # 误差在 1e-4 级

总结 

除了上面这些归一化方法,还有基于它们发展出来的算法,例如 Conditional BatchNormalization 和 AdaIN,可以分别参考下面的博客:

https://zhuanlan.zhihu.com/p/61248211

https://zhuanlan.zhihu.com/p/57875010

 

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值