文章目录
Batch Normalization
使数据分布为均值为0方差为1
批量归一化、批量标准化『https://baike.baidu.com/item/%E6%89%B9%E6%A0%87%E5%87%86%E5%8C%96/22778547』
Standardization(标准化) ???这里不确定
ref
提出前提
随着网络深度的加深,在参数更新的时候,每层的输入值的数据分布会发生变化,导致ICS(Internal Covariate Shift)问题。
ICS问题会:
- 上层网络需要不停调整来适应输入数据分布的变化,导致网络学习速度的降低
- 网络的训练过程容易陷入梯度饱和区,减缓网络收敛速度
反正,网络的训练过程容易陷入梯度饱和区,减缓网络收敛速度。
解决上面的问题:
一、白化(whitening)
主要是PCA白化与ZCA白化。白化是对输入数据分布进行变换,进而达到以下两个目的:
-
使得输入特征分布具有相同的均值与方差。
其中PCA白化保证了所有特征分布均值为0,方差为1;而ZCA白化则保证了所有特征分布均值为0,方差相同;
-
去除特征之间的相关性。
白化带来的问题:不理解
- 计算成本大
- 改变了网络每一层的分布,丢失了数据的表达能力。就是学到的参数信息会被白话操作丢失。
提出Batch Normalization
整体解释:
n
o
r
m
a
l
i
z
a
i
t
o
n
=
x
−
μ
σ
2
+
ϵ
normalizaiton = {x - \mu \over \sqrt{\sigma^2+\epsilon}}
normalizaiton=σ2+ϵx−μ
(
ϵ
\epsilon
ϵ防止方差是0而计算出错)
每个batch下,单独对该批量样本的每种特征进行normalizaiton,控制每种特征的特征值分布是均值为0,方差为1的分布
Batch就是mini-batch,因为有时候内存不能一次全装下
计算步骤:
-
假设batch的值为 m m m,即每次输入 m m m个样本参与训练
-
计算这个batch的第 j j j个特征(即对应当前关注层的第 j j j个神经元)的均值 μ j \mu_j μj
μ j = 1 m ∑ i = 1 m Z j ( i ) \mu_j={1\over m}\sum^m_{i=1}Z_j^{(i)} μj=m1i=1∑mZj(i) -
计算这个batch的第 j j j个特征(即对应当前关注层的第 j j j个神经元)的方差 σ j 2 \sigma^2_j σj2
σ j 2 = 1 m ∑ i = 1 m ( Z j ( i ) − μ j ) 2 \sigma^2_j = {1\over m}\sum^m_{i=1}(Z^{(i)}_j-\mu_j)^2 σj2=m1i=1∑m(Zj(i)−μj)2 -
Z j ( i ) Z_j^{(i)} Zj(i)是该batch的第 i i i个样本在当前关注层的第 j − 1 j-1 j−1个神经元的输出值,也即当前关注层的第 j j j个神经元的输入值
-
更新
Z ^ j = Z j − μ j σ j 2 + ϵ \hat{Z}_j={Z_j-\mu_j \over \sqrt{\sigma^2_j+\epsilon}} Z^j=σj2+ϵZj−μj
以上只是解决了分布的问题,还有数据表达能力的缺失的问题
引入可学习机制(做线性变换):
增加参数
γ
\gamma
γ和
β
\beta
β,
Z
~
j
=
γ
j
Z
^
j
+
β
j
\tilde{Z}_j = \gamma_j\hat{Z}_j+\beta_j
Z~j=γjZ^j+βj
当
γ
\gamma
γ和
β
\beta
β对应方差
σ
2
\sigma^2
σ2和均值
μ
\mu
μ时,得到最初的原始特征
Z
j
Z_j
Zj
测试阶段如何使用BN
以训练数据的整体方差和均值来作为测试阶段中BN的均值和方差,具体说:
训练时,记录每个batch的方差和均值,使用**均值和方差的无偏估计**:
μ
t
e
s
t
=
E
(
μ
b
a
t
c
h
)
\mu_{test}=E(\mu_{batch})
μtest=E(μbatch)
σ t e s t 2 = m m − 1 E ( σ b a t c h 2 ) \sigma^2_{test}={m \over m-1}E(\sigma^2_{batch}) σtest2=m−1mE(σbatch2)
这个最后的结果,对应的应该就是running_mean
和running_var
。
然后对测试数据做归一化:
B
N
(
X
t
e
s
t
)
=
γ
X
t
e
s
t
−
μ
t
e
s
t
σ
t
e
s
t
2
+
ϵ
+
β
BN(X_{test}) = \gamma{X_{test}-\mu_{test}\over\sqrt{\sigma^2_{test}+\epsilon}}+\beta
BN(Xtest)=γσtest2+ϵXtest−μtest+β
另外,除了采用整体样本的无偏估计外。吴恩达在Coursera上的Deep Learning课程指出可以对train阶段每个batch计算的mean/variance采用指数加权平均来得到test阶段mean/variance的估计。指数加权
计算实例
BN公式总结
x u p d a t e = x − E ( x ) V a r ( x ) + e p s ∗ γ + β x_{update} = {x-E(x)\over\sqrt{Var(x)+eps}}*\gamma+\beta xupdate=Var(x)+epsx−E(x)∗γ+β
拉平成一维来计算均值和方差
pytorch BN
ref
pytorch中BatchNorm1d、BatchNorm2d、BatchNorm3d
【PyTorch】详解pytorch中nn模块的BatchNorm2d()函数
使用说明
pytorch中BatchNorm1d、BatchNorm2d、BatchNorm3d
- BN的参数
num_features
都是和输入数据的C
相对应 - BN不改变数据维度
BN | 输入维度 | num_features (等于E、Var个数) |
---|---|---|
1d | (N, C)或者(N, C, L) | C |
2d | (N, C, H, W) | C |
3d | (N, C, D, H, W) | C |
计算和验证实例
PyTorch基础——torch.nn.BatchNorm2d
-
batch大于1的情况
-
E ( x ) E(x) E(x)就是把 H × W H\times W H×W维的特征==拉直成一维==再计算
-
V a r ( x ) Var(x) Var(x)也是一样
-
t = torch.tensor([[[1,2,3.0],[2,3,5]],[[1,2,3.0],[1,4,5]]]) q = t.view(-1) print(t) print(q) print(torch.mean(t),torch.mean(q)) print(torch.var(t, unbiased=False),torch.var(q, unbiased=False))# unbiased参数需要设置为False,否则 # 计算出方差为无偏估计,与当前结果不同 # 第二、第三通道相同设置
- 有图示说明,图上的feature1,2应该是sample1,2
【PyTorch】详解pytorch中nn模块的BatchNorm2d()函数
-
一个计算和验证BN2d的例子,batch=1的情况
-
C
后面的被认定为特征,输出BN的权重就可以发现-
在2d中, ( H × W ) (H\times W) (H×W)代表特征,拥有 H W HW HW个数值来表征这个特征
解释:(1)对于第 j j j个特征 C j C_j Cj来说,假设在该batch中包含 m m m个样本,所以需要统计 m m m份的维度为 ( H × W ) (H\times W) (H×W)的数据的均值和方差。(2)怎么算这个方差和均值?就是对 ( H × W ) (H\times W) (H×W)维度的数据拉成一维的,再像BN1d那样作计算。因此,求得的均值和方差的个数,分别等于
C
的值。
-
详细过程
Pytorch BN(BatchNormal)计算过程与源码分析和train与eval的区别
-
BN层的输出将作为下一层激活层的输入
-
PyTorch 源码解读之 BN,『四种Norm的图示』
-
running_var = running_var * (1 - eaf) + eaf * inputs_var * n / (n - 1) running_mean = running_mean * (1 - eaf) + eaf * inputs_mean
-
方差的计算,做的是有偏估计
BN2d验证代码
import torch.nn as nn
import torch
torch.random.manual_seed(0)
input = torch.randn(2, 3, 3, 4)
# With Learnable Parameters
m = nn.BatchNorm2d(3)
# Without Learnable Parameters
# m = nn.BatchNorm1d(100, affine=False)
output = m(input)
print(input)
print(m.weight)
print(m.bias)
print(m.running_mean)
print(output)
##########################################################################################################
## wrong
##########################################################################################################
print('=' * 100)
"""
使用先在`C`上求和再求均值和方差,均值是对的,但是方差是错误的
"""
input_c = (input[0][0] + input[1][0]) / 2
# input_c = input[0][0]
print(input_c)
firstDimenMean = torch.Tensor.mean(input_c)
firstDimenVar = torch.Tensor.var(input_c, False) # false表示贝塞尔校正不会被使用
print(firstDimenMean)
print(firstDimenVar)
batchnormone = ((input_c[0][0] - firstDimenMean) / (torch.pow(firstDimenVar, 0.5) + m.eps)) \
* m.weight[0] + m.bias[0]
print(batchnormone)
#######################################################################################################
## the computation is correct!!!
#######################################################################################################
print('=' * 100)
input_c1 = input[:, 0, :, :]
print(input_c1)
"""
注意: `torch.Tensor.var(input_c1, Ture)`和`torch.var(input_c1)`
本质是展平后作计算
"""
firstDimenMean = torch.Tensor.mean(input_c1)
firstDimenVar = torch.Tensor.var(input_c1, False) # false表示贝塞尔校正不会被使用
print(firstDimenMean)
print(firstDimenVar)
batchnormone = ((input_c1 - firstDimenMean) / (torch.pow(firstDimenVar, 0.5) + m.eps)) \
* m.weight[0] + m.bias[0]
print(batchnormone)
########################################################################################################
"""
验证均值和方差的计算,是否是通过展平后计算的
"""
print('=' * 100)
print(input_c1.flatten())
print(torch.mean(input_c1.flatten()))
print(torch.var(input_c1.flatten()))
print(torch.mean(input_c1))
print(torch.var(input_c1))
torch.Tensor.var(input_c1, Ture)
和torch.var(input_c1)
的区别
# t = torch.tensor([[[1,2,3.0],[2,3,5]],[[1,2,3.0],[1,4,5]]])
t = torch.tensor([[[-1.1258, -1.1524, -0.2506, -0.4339],
[0.8487, 0.6920, -0.3160, -2.1152],
[0.3223, -1.2633, 0.3500, 0.3081]],
[[0.4397, 0.1124, 0.6408, 0.4412],
[-0.1023, 0.7924, -0.2897, 0.0525],
[0.5229, 2.3022, -1.4689, -1.5867]]])
q = t.view(-1)
print(t)
print(q)
print(torch.mean(t), torch.mean(q))
print(torch.var(t), torch.var(q))
print(torch.Tensor.mean(t),torch.Tensor.mean(q))
print(torch.Tensor.var(t, False),torch.Tensor.var(q, False)) # BN的方式
print(torch.Tensor.var(t, True),torch.Tensor.var(q, True))
分析:torch.Tensor.var(input_c1, Ture)
==torch.var(input_c1)
# 运行结果
tensor([[[-1.1258, -1.1524, -0.2506, -0.4339],
[ 0.8487, 0.6920, -0.3160, -2.1152],
[ 0.3223, -1.2633, 0.3500, 0.3081]],
[[ 0.4397, 0.1124, 0.6408, 0.4412],
[-0.1023, 0.7924, -0.2897, 0.0525],
[ 0.5229, 2.3022, -1.4689, -1.5867]]])
tensor([-1.1258, -1.1524, -0.2506, -0.4339, 0.8487, 0.6920, -0.3160, -2.1152,
0.3223, -1.2633, 0.3500, 0.3081, 0.4397, 0.1124, 0.6408, 0.4412,
-0.1023, 0.7924, -0.2897, 0.0525, 0.5229, 2.3022, -1.4689, -1.5867])
tensor(-0.0950) tensor(-0.0950)
tensor(0.9611) tensor(0.9611)
tensor(-0.0950) tensor(-0.0950)
tensor(0.9211) tensor(0.9211)
tensor(0.9611) tensor(0.9611)
解释:
关键在与var()
的unbiased
这个参数,在Batch Normalization原理中有提到。
总结:
训练阶段,用的是有偏估计,unbiased
设置为True
测试阶段,用的有偏估计,在『测试阶段如何使用BN』有写,这部分不想验证了,搞了两天,累了
有偏估计、无偏估计
在数学上,方差的有偏估计和无偏估计:
通俗的说,就是我们拿不到所有的样本来做统计,所以只是用手上现有的数据的分布来替代整体数据分布,当手上数据无限多时,等价于整体数据。
从现有数据分布做统计,就是有偏的。