本文主旨
y = x − E [ x ] V a r [ x ] + ϵ ∗ γ + β y = \frac{x - \mathrm{E}[x]}{ \sqrt{\mathrm{Var}[x] + \epsilon}} * \gamma + \beta y=Var[x]+ϵx−E[x]∗γ+β
本篇文章通过实例说明BN、IN、LN、GN是取哪些维度上的均值和方差来进行标准化的。
当处理图片时,输入数据维度为(N, C, H, W),代表(batch,channel,height,width)
假设batch=2,channel=4,height=1,width=2,可通过下图直观感受,图中的1和2为高和宽。
import numpy as np
a = np.array([[[[ 1., 5.]],
[[ 2., 6.]],
[[ 3., 7.]],
[[ 4., 8.]]],
[[[ 9., 13.]],
[[10., 14.]],
[[11., 15.]],
[[12., 16.]]]], dtype=np.float32)
print('a.shape:', a.shape)
# output:a.shape:(2, 4, 1, 2)
简要介绍
标准化:通过一部分数据得到均值和方差,经过下面的公式得到标准化后的值。各种不同的标准化方法差异主要在于选取哪些数据做均值和方差。
ϵ \epsilon ϵ 是防止分母为零而取的一个极小的数, γ \gamma γ和 β \beta β是为了将归一化的数成比例放缩和平移,可自动学习。本文重点观察不同归一化方法对均值和方差的处理。
BN
取①各批次、②单个通道、③所有值做均值和方差,得到的均值和方差维度为(C,),(以均值举例,方差也是取这些值,IN、LN、GN同),可通过上图直观感受如何取值的。
u = (1 + 5 + 9 + 13) / 4 = 7
用程序说明,手动处理的归一化结果与pytorch函数作用结果相同,并以第一个数字举例,其他数值可自行测试。
# BatchNorm2d
import torch
import torch.nn as nn
import numpy as np
import math
a = np.array([[[[ 1., 5.]],
[[ 2., 6.]],
[[ 3., 7.]],
[[ 4., 8.]]],
[[[ 9., 13.]],
[[10., 14.]],
[[11., 15.]],
[[12., 16.]]]], dtype=np.float32)
print('a.shape:', a.shape)
u = np.mean(a, axis=(0,2,3))
var = np.var(a, axis=(0,2,3))
print('均值为:', u)
print('方差为:', var)
# nn.BatchNorm2d第一个参数为通道数
batch_norm = nn.BatchNorm2d(4, eps=1e-6, affine=False)
b = torch.from_numpy(a)
c = batch_norm(b) # BatchNorm输入维度需[N,C,H,W]
print('变换后:', c)
# 验证第一个元素
print('1变换后为:', (1 - u[0]) / math.sqrt(var[0] + 1e-6))
IN
取①单个批次,②单个通道,③所有值做均值和方差,得到的均值和方差维度为(N, C)
u = (1 + 5) / 2 = 3
程序说明
# InstanceNorm2d
import torch
import torch.nn as nn
import numpy as np
import math
a = np.array([[[[ 1., 5.]],
[[ 2., 6.]],
[[ 3., 7.]],
[[ 4., 8.]]],
[[[ 9., 13.]],
[[10., 14.]],
[[11., 15.]],
[[12., 16.]]]], dtype=np.float32)
print('a.shape:', a.shape)
u = np.mean(a, axis=(2, 3))
var = np.var(a, axis=(2, 3))
print('均值为:', u)
print('方差为:', var)
# nn.InstanceNorm2d第一个参数为通道数
instance_norm = nn.InstanceNorm2d(4, eps=1e-6, affine=False)
b = torch.from_numpy(a)
c = instance_norm(b)
print('变换后:', c)
# 验证第一个元素
print('1变换后为:', (1 - u[0][0]) / math.sqrt(var[0][0] + 1e-6))
LN
LN较为特殊,nn.LayerNorm要求的输入维度为(N, *),与其他标准化所需要的(N, C, H, W)不同,并且第一个参数要求输入需要归一化的维度。通常图像数据为(N, C, H, W),而文本数据为(批次大小,序列长度,词嵌入大小)。
层归一化大多数的解释为①单个批次、②所有通道、③所有值做均值和方差,而实际上是可以自定义的。LN在文本领域通常对最后一个维度做标准化,即对每个词内部进行标准化。本实验以文本数据输入为例,即将最后两个维度合并,介绍LN处理方法,此时与实例归一化结果相同。
u = (1 + 5) / 2 = 3
程序说明
# LayerNorm
import torch
import torch.nn as nn
import numpy as np
import math
a = np.array([[[[ 1., 5.]],
[[ 2., 6.]],
[[ 3., 7.]],
[[ 4., 8.]]],
[[[ 9., 13.]],
[[10., 14.]],
[[11., 15.]],
[[12., 16.]]]], dtype=np.float32)
a = a.reshape((2, 4, 2))
print('a.shape:', a.shape)
u = np.mean(a, axis=(2))
var = np.var(a, axis=(2))
print('均值为:', u)
print('方差为:', var)
# nn.LayerNorm第一个参数为归一化的维度,通常图像为(H,W),文本为最后一维
layer_norm = nn.LayerNorm(a.shape[-1], eps=1e-6, elementwise_affine=False)
b = torch.from_numpy(a)
c = layer_norm(b)
print('变换后:', c)
# 验证第一个元素
print('1变换后为:', (1 - u[0][0]) / math.sqrt(var[0][0] + 1e-6))
GN
①单个批次、②在通道上分n组、③该组的所有值做均值和方差(以两组为例)
u = (1 + 5 + 2 + 6) / 4 = 3.5
程序说明
# GroupNorm
import torch
import torch.nn as nn
import numpy as np
import math
a = np.array([[[[ 1., 5.]],
[[ 2., 6.]],
[[ 3., 7.]],
[[ 4., 8.]]],
[[[ 9., 13.]],
[[10., 14.]],
[[11., 15.]],
[[12., 16.]]]], dtype=np.float32)
print('a.shape:', a.shape)
u = np.concatenate((np.mean(a[:,:2,:,:], axis=(1, 2, 3)), np.mean(a[:,2:,:,:], axis=(1, 2, 3))))
var = np.concatenate((np.var(a[:,:2,:,:], axis=(1, 2, 3)), np.var(a[:,2:,:,:], axis=(1, 2, 3))))
print('均值为:', u)
print('方差为:', var)
# nn.GroupNorm前两个参数代表分组数和通道数,用 通道数/分组数 得到每组对几个通道进行归一化
group_norm = nn.GroupNorm(2, 4, eps=1e-6, affine=False)
b = torch.from_numpy(a)
c = group_norm(b) # GroupNorm输入维度需[N,C,H,W]
print('变换后:', c)
# 验证第一个元素
print('1变换后为:', (1 - u[0]) / math.sqrt(var[0] + 1e-6))