Table of Contents
BN
feature map: 包含 N 个样本,每个样本通道数为 C,高为 H,宽为 W。对其求均值和方差时,将在 N、H、W上操作,而保留通道 C 的维度。具体来说,就是把第1个样本的第1个通道,加上第2个样本第1个通道 ...... 加上第 N 个样本第1个通道,求平均,得到通道 1 的均值(注意是除以 N×H×W 而不是单纯除以 N,最后得到的是一个代表这个 batch 第1个通道平均值的数字,而不是一个 H×W 的矩阵)。求通道 1 的方差也是同理。对所有通道都施加一遍这个操作,就得到了所有通道的均值和方差。具体公式为:
import torch
from torch import nn
import numpy as np
random_data = np.random.rand(100, 30, 64, 64) * 1000
data1 = random_data.transpose((1, 0, 2, 3)).reshape(30, -1)
mu1 = data1.mean(axis=1).reshape(1, 30, 1, 1)
std1 = data1.std(axis=1).reshape(1, 30, 1, 1)
numpy_bn = (random_data - mu1) / (std1 + 1e-8)
bn_ = nn.BatchNorm2d(num_features=30, eps=1e-8, affine=False, track_running_stats=False)
x2 = torch.from_numpy(random_data)
offical_bn = bn_(x2)
print('diff:{}'.format((offical_bn.numpy() - numpy_bn).sum()))
# diff:-7.560503308773859e-08 差距非常小
LN
BN 的一个缺点是需要较大的 batchsize 才能合理估训练数据的均值和方差,这导致内存很可能不够用,同时它也很难应用在训练数据长度不同的 RNN 模型上。Layer Normalization (LN) 的一个优势是不需要批训练,在单条数据内部就能归一化。对于 ,LN 对每个样本的 C、H、W 维度上的数据求均值和标准差,保留 N 维度。其均值和标准差公式为:
import torch
from torch import nn
import numpy as np
random_data = np.random.rand(100, 30, 64, 64) * 1000
data1 = random_data.reshape(100, -1)
mu1 = data1.mean(axis=1).reshape(100, 1, 1, 1)
std1 = data1.std(axis=1).reshape(100, 1, 1, 1)
numpy_ln = (random_data - mu1) / std1
ln = nn.LayerNorm(normalized_shape=[30, 64, 64], eps=0, elementwise_affine=False)
offical_ln = ln(torch.from_numpy(random_data))
print('diff:{}'.format((numpy_ln - offical_ln.numpy()).sum()))
# diff = 2.4112720174197834e-09
IN
Instance Normalization (IN) 最初用于图像的风格迁移。作者发现,在生成模型中, feature map 的各个 channel 的均值和方差会影响到最终生成图像的风格,因此可以先把图像在 channel 层面归一化,然后再用目标风格图片对应 channel 的均值和标准差“去归一化”,以期获得目标图片的风格。IN 操作也在单个样本内部进行,不依赖 batch。对于 ,IN 对每个样本的 H、W 维度的数据求均值和标准差,保留 N 、C 维度,也就是说,它只在 channel 内部求均值和标准差,其公式为:
import torch
from torch import nn
import numpy as np
random_data = np.random.rand(100, 30, 64, 64) * 1000
data1 = random_data.reshape(100 * 30, -1)
mu1 = data1.mean(axis=1).reshape(100, 30, 1, 1)
std1 = data1.std(axis=1).reshape(100, 30, 1, 1)
numpy_in = (random_data - mu1) / std1
ln = nn.InstanceNorm2d(num_features=30, eps=0, affine=False, track_running_stats=False)
offical_in = ln(torch.from_numpy(random_data))
print('diff:{}'.format((numpy_in - offical_in.numpy()).sum()))
#diff= 1.9767101974942072e-10
GN
Group Normalization (GN) 适用于占用显存比较大的任务,例如图像分割。对这类任务,可能 batchsize 只能是个位数,再大显存就不够用了。而当 batchsize 是个位数时,BN 的表现很差,因为没办法通过几个样本的数据量,来近似总体的均值和标准差。GN 也是独立于 batch 的,它是 LN 和 IN 的折中。GN 计算均值和标准差时,把每一个样本 feature map 的 channel 分成 G 组,每组将有 C/G 个 channel,然后将这些 channel 中的元素求均值和标准差。各组 channel 用其对应的归一化参数独立地归一化。
import torch
from torch import nn
import numpy as np
random_data = np.random.rand(100, 30, 64, 64) * 10000
data1 = random_data.reshape(100, 10, -1)
mu1 = data1.mean(axis=-1).reshape(100, 10, -1)
std1 = data1.std(axis=-1).reshape(100, 10, -1)
numpy_gn = (data1 - mu1) / std1
numpy_gn = numpy_gn.reshape(100, 30, 64, 64)
gn_ = nn.GroupNorm(num_groups=10, num_channels=30, eps=0, affine=False)
offical_gn = gn_(torch.from_numpy(random_data))
#diff:-6.625669787246119e-10
INFERENCE
1. https://zhuanlan.zhihu.com/p/69659844
2. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift https://arxiv.org/abs/1502.03167
3. Layer Normalization https://arxiv.org/abs/1607.06450
4. Instance Normalization https://arxiv.org/pdf/1607.08022.pdf
5.Group Normalization https://arxiv.org/pdf/1803.08494.pdf