1. 前言
深度学习的目的不是搭建怎样的模型,而是在准确的前提下,用尽可能精简的模型来学习训练数据集的分布。因此,深度学习作为数据驱动的方法,对数据集的要求高。在概率论与数理统计中独立同分布是大家最喜欢的条件,但在深度学习中数据往往都不是独立同分布的,比如当数据为图片时,相邻像素点具有相关性,减少数据的相关性的方法有PCA等;Normlization则是将数据变换成 同分布。
2. Pytorch中的Normlization
2.1 BatchNorm
1). torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)
1d表示序列数据,对整个batch中的数据计算均值和方差,参数说明如下:
num_features :输入特征(通道)数
eps :常数(避免分母为零)。
momentum :动态均值和动态方差所使用的动量。
x
^
new
=
(
1
−
\hat{x}_{\text {new }}=(1-
x^new =(1− momentum
)
×
x
^
+
) \times \hat{x}+
)×x^+ momentum
×
x
t
\times x_{t}
×xt,其中X是统计估计,Xt是当前的观察值。
affine :是否添加可学习的放射变换。
track_running_stats :“True”,表示记录训练过程中的均值和方差
输入 :(N,C)或(N,C,L),其中N为batchsize,C为num_channels,L为len_sequence。
输出 :与输入相同
# With Learnable Parameters
m = nn.BatchNorm1d(100)
# Without Learnable Parameters
m = nn.BatchNorm1d(100, affine=False)
input = torch.randn(20, 100)
output = m(input)
2). torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)
对图像数据的BatchNorm ,参数说明与计算方法同BatchNorm1d。
输入 :(N,C,H,W),其中N为batchsize,C为num_channels,H为图片的高,W为图片的宽。
输出 :与输入相同。
# With Learnable Parameters
m = nn.BatchNorm2d(100)
# Without Learnable Parameters
m = nn.BatchNorm2d(100, affine=False)
input = torch.randn(20, 100, 35, 45)
output = m(input)
3). torch.nn.BatchNorm3d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)
对3D数据的BatchNorm,参数说明同BatchNorm2d。
输入 :(N,C,D,H,W),其中N为batchsize,C为num_channels,D,H,W为数据的三个维度。
输出 :与输入相同。
# With Learnable Parameters
m = nn.BatchNorm3d(100)
# Without Learnable Parameters
m = nn.BatchNorm3d(100, affine=False)
input = torch.randn(20, 100, 35, 45, 10)
output = m(input)
2.1.1 LazyBatchNorm
torch.nn.LazyBatchNorm1d(eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)
相比BatchNorm1d多增加了一个”延迟模块“,延迟初始化参数。对应的还有LazyBatchNorm2d 和 LazyBatchNorm3d。
2.1.2 SyncBatchNorm
torch.nn.SyncBatchNorm(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, process_group=None, device=None, dtype=None)
官方文档的解释:The mean and standard-deviation are calculated per-dimension over all mini-batches of the same process groups. 也就是说计算同一步的所有mini_batch的均值方差,我的理解如下:
当使用
N
N
N个GPU训练时,使用BatchNorm只能计算每个设备输入数据的均值和方差。使用SyncBatchNorm则计算
N
∗
B
a
t
c
h
S
i
z
e
N*BatchSize
N∗BatchSize 个数据计算均值和方差。SyncBatchNorm就是Pytorch提供的具有同步能力的BN层。
2.2 GroupNorm
torch.nn.GroupNorm(num_groups, num_channels, eps=1e-05, affine=True, device=None, dtype=None)
num_groups :channel分出来的组数
BatchNorm是对每一个channel进行均值和方差的计算,GroupNorm则是将channel分为几组计算均值和方差,比如说有6个通道,分为3组。
2.3 InstanceNorm
torch.nn.InstanceNorm1d(num_features, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False, device=None, dtype=None)
BatchNorm是对整个batch的每个channel计算均值和方差,InstanceNorm表示对batch中的每个数据的每个channel,分别计算均值和方差。与BatchNorm一样他也有相应的 torch.nn.InstanceNorm2d 和 torch.nn.InstanceNorm3d。
2.3.1 LazyInstanceNorm
参考 LazyBatchNorm,只是将整个batch计算均值和方差改为分别计算每个数据的均值和方差。
2.4 LayerNorm
torch.nn.LayerNorm(normalized_shape, eps=1e-05, elementwise_affine=True, device=None, dtype=None)
normalized:为list或int或torch.Size,如果输入为int则这个数等于input的最后一维;list和torch.Size时一般为(C, H, W)。
LayerNorm :计算每个样本的所有通道的均值和方差。
2.5 LocalResponseNorm
torch.nn.LocalResponseNorm(size, alpha=0.0001, beta=0.75, k=1.0)
size:用于归一化的通道数
alpha:乘积因子
beta:指数
k:附加因子
b
c
=
a
c
(
k
+
α
n
∑
c
′
=
max
(
0
,
c
−
n
/
2
)
min
(
N
−
1
,
c
+
n
/
2
)
a
c
′
2
)
−
β
b_{c}=a_{c}\left(k+\frac{\alpha}{n} \sum_{c^{\prime}=\max (0, c-n / 2)}^{\min (N-1, c+n / 2)} a_{c^{\prime}}^{2}\right)^{-\beta}
bc=ac⎝⎛k+nαc′=max(0,c−n/2)∑min(N−1,c+n/2)ac′2⎠⎞−β
3. 总结
以一个batch的图像数据 ( N , C , H , W ) (N,C,H,W) (N,C,H,W)为例。
3.1 解释
BatchNorm:在batch上对
(
N
,
H
,
W
)
(N,H,W)
(N,H,W)做归一化,对小batchsize效果不好,当batchsize太小时,计算的均值和方差不足以代表整个数据分布;
GroupNorm:将channel分组,然后对每个组再做归一化,也就是对
(
(
C
/
/
G
)
,
H
,
W
)
((C//G),H,W)
((C//G),H,W)进行归一化;
InstanceNorm:对于每一个channel内归一化,也就是对
(
H
,
W
)
(H,W)
(H,W)进行归一化,用在风格化迁移,保持每个图像实例之间的独立;
LayerNorm:在channel上对,对
(
C
,
H
,
W
)
(C,H,W)
(C,H,W)做归一化,主要对RNN作用明显。
3.2 mean与var计算公式
上述方法都是减去均值除以标准差,再进行线性变换,不同点在于计算均值和标准差的公式
y
=
x
−
E
[
x
]
Var
[
x
]
+
ϵ
∗
γ
+
β
y=\frac{x-\mathrm{E}[x]}{\sqrt{\operatorname{Var}[x]+\epsilon}} * \gamma+\beta
y=Var[x]+ϵx−E[x]∗γ+β
BatchNorm的均值与标准差
μ
c
(
x
)
=
1
N
H
W
∑
n
=
1
N
∑
h
=
1
H
∑
w
=
1
W
x
n
c
h
w
\mu_{c}(x)=\frac{1}{N H W} \sum_{n=1}^{N} \sum_{h=1}^{H} \sum_{w=1}^{W} x_{n c h w}
μc(x)=NHW1n=1∑Nh=1∑Hw=1∑Wxnchw
σ
c
(
x
)
=
1
N
H
W
∑
n
=
1
N
∑
h
=
1
H
∑
w
=
1
W
(
x
n
c
h
w
−
μ
c
(
x
)
)
2
+
ϵ
\sigma_{c}(x)=\sqrt{\frac{1}{N H W} \sum_{n=1}^{N} \sum_{h=1}^{H} \sum_{w=1}^{W}\left(x_{n c h w}-\mu_{c}(x)\right)^{2}+\epsilon}
σc(x)=NHW1n=1∑Nh=1∑Hw=1∑W(xnchw−μc(x))2+ϵ
GroupNorm的均值与标准差
μ
n
g
(
x
)
=
1
(
C
/
G
)
H
W
∑
c
=
g
C
/
G
(
g
+
1
)
C
/
G
∑
h
=
1
H
∑
w
=
1
W
x
n
c
h
w
\mu_{n g}(x)=\frac{1}{(C / G) H W} \sum_{c=g C / G}^{(g+1) C / G} \sum_{h=1}^{H} \sum_{w=1}^{W} x_{n c h w}
μng(x)=(C/G)HW1c=gC/G∑(g+1)C/Gh=1∑Hw=1∑Wxnchw
σ
n
g
(
x
)
=
1
(
C
/
G
)
H
W
∑
c
=
g
C
/
G
(
g
+
1
)
C
/
G
∑
h
=
1
H
∑
w
=
1
W
(
x
n
c
h
w
−
μ
n
g
(
x
)
)
2
+
ϵ
\sigma_{n g}(x)=\sqrt{\frac{1}{(C / G) H W} \sum_{c=g C / G}^{(g+1) C / G} \sum_{h=1}^{H} \sum_{w=1}^{W}\left(x_{n c h w}-\mu_{n g}(x)\right)^{2}+\epsilon}
σng(x)=(C/G)HW1c=gC/G∑(g+1)C/Gh=1∑Hw=1∑W(xnchw−μng(x))2+ϵ
InstanceNorm的均值与标准差
μ
n
c
(
x
)
=
1
H
W
∑
h
=
1
H
∑
w
=
1
W
x
n
c
h
w
\mu_{n c}(x)=\frac{1}{H W} \sum_{h=1}^{H} \sum_{w=1}^{W} x_{n c h w}
μnc(x)=HW1h=1∑Hw=1∑Wxnchw
σ
n
c
(
x
)
=
1
H
W
∑
h
=
1
H
∑
w
=
1
W
(
x
n
c
h
w
−
μ
n
c
(
x
)
)
2
+
ϵ
\sigma_{n c}(x)=\sqrt{\frac{1}{H W} \sum_{h=1}^{H} \sum_{w=1}^{W}\left(x_{n c h w}-\mu_{n c}(x)\right)^{2}+\epsilon}
σnc(x)=HW1h=1∑Hw=1∑W(xnchw−μnc(x))2+ϵ
LayerNorm的均值与方差
μ
n
(
x
)
=
1
C
H
W
∑
c
=
1
C
∑
h
=
1
H
∑
w
=
1
W
x
n
c
h
w
\mu_{n}(x)=\frac{1}{C H W} \sum_{c=1}^{C} \sum_{h=1}^{H} \sum_{w=1}^{W} x_{n c h w}
μn(x)=CHW1c=1∑Ch=1∑Hw=1∑Wxnchw
σ
n
(
x
)
=
1
C
H
W
∑
c
=
1
C
∑
h
=
1
H
∑
w
=
1
W
(
x
n
c
h
w
−
μ
n
(
x
)
)
2
+
ϵ
\sigma_{n}(x)=\sqrt{\frac{1}{C H W} \sum_{c=1}^{C} \sum_{h=1}^{H} \sum_{w=1}^{W}\left(x_{n c h w}-\mu_{n}(x)\right)^{2}+\epsilon}
σn(x)=CHW1c=1∑Ch=1∑Hw=1∑W(xnchw−μn(x))2+ϵ
3.3 检验
1). BatchNorm:
import torch
import torch.nn as nn
input = torch.randn(5,3,16,16)*100
BN = nn.BatchNorm2d(num_features=3, eps=1e-05, momentum=0, affine=False, track_running_stats=False)
def BN_custom(input):
# 变形(3, 5*16*16)
input1 = input.permute(1,0,2,3).contiguous().view(3, -1)
# 均值(每一个通道均值)
mu = input1.mean(dim=1).view(1,3,1,1)
# 标准差(每一个通道标准差)
std = input1.std(dim=1, unbiased=False).view(1,3,1,1)
output = (input-mu) / (std.pow(2)+1e-05).pow(1/2)
return output
err = BN(input) - BN_custom(input)
print(err.abs().sum())
输出:tensor(9.7361e-05)。
误差的一范数为9.7361e-05可以认为方法正确。
2).GroupNorm: 把相邻的通道分为一组
import torch
import torch.nn as nn
input = torch.randn(5,6,16,16)*100
GN = nn.GroupNorm(3, 6, eps=1e-05, affine=False)
def GN_custom(input):
# 变形(5, 3, 2*16*16)
input1 = input.view(5, 3, -1)
# 均值(5, 3, 1)
mu = input1.mean(dim=-1).reshape(5, 3, -1)
# 标准差(5, 3, 1)
std = input1.std(dim=-1, unbiased=False).reshape(5, 3, -1)
output = (input1-mu) / (std.pow(2)+1e-05).pow(1/2)
output = output.reshape(5, 6, 16, 16)
return output
err = GN(input) - GN_custom(input)
print(err.abs().sum())
输出:tensor(0.0003)。
误差的一范数为0.0003可以认为方法正确。
3).InstanceNorm:
import torch
import torch.nn as nn
input = torch.randn(5, 3, 16, 16)
IN = nn.InstanceNorm2d(num_features=3, eps=1e-05, momentum=0, affine=False, track_running_stats=False)
def IN_custom(input):
# 变形(5*3, 256)
input1 = input.view(15, -1)
# 均值
mu = input1.mean(dim=1).view(5, 3, 1, 1)
# 标准差
std = input1.std(dim=1, unbiased=False).view(5, 3, 1, 1)
output = (input - mu)/(std.pow(2) + 1e-05).pow(1/2)
return output
err = IN(input) - IN_custom(input)
print(err.abs().sum())
输出:tensor(0.0002)。
误差的一范数为0.0002可以认为方法正确。
4). LayerNorm:
import torch
import torch.nn as nn
input = torch.randn(5, 3, 16, 16)
LN = nn.LayerNorm(normalized_shape=[3,16,16], eps=1e-05, elementwise_affine=False)
def LN_custom(input):
# 变形(5, 3*16*16)
input1 = input.view(5, -1)
# 均值
mu = input1.mean(dim=1).view(5, 1, 1, 1)
# 标准差
std = input1.std(dim=1, unbiased=False).view(5, 1, 1, 1)
output = (input-mu)/(std.pow(2) + 1e-05).pow(1/2)
return output
err = LN(input) - LN_custom(input)
print(err.abs().sum())
输出:tensor(0.0001)。
误差的一范数为0.0001可以认为方法正确。