1.37.Batch Normalization,批规范化
1.37.1.Batch Norm介绍
1.37.2.Intuitive explanation
1.37.3.Intuitive explanation
1.37.4.Feature scaling
1.37.5.BatchNorm1d、BatchNorm2d、BatchNorm3d
1.37.5.1.nn.BatchNorm1d(num_features)
1.37.5.2.nn.BatchNorm2d(num_features)
1.37.5.3.nn.BatchNorm3d(num_features)
1.37.6.Batch Norm
1.37.7.Pipeline
1.37.8.参考博文
Batch Normalization(简称为BN)[2],中文翻译成批规范化,是在深度学习中普遍使用的一种技术,通常用于解决多层神经网络中间层的协方差偏移(Internal Covariate Shift)问题,类似于网络输入进行零均值化和方差归一化的操作,不过是在中间层的输入中操作而已。
使用sigmoid会出现梯度消失的情况,在实际训练中,引入了BatchNorm操作,可以将输入值限定在(γ = 1 , β)之间
如下图,如果不进行Batch Norm,如果输入weight差别过大,在两个方向进行梯度下降,会出现梯度下降不平衡,在训练过程中不能稳定的收敛,在实际应用过程中也不能稳定的输出label结果,因此Normalization是很重要的
Image Normalization
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
Batch Normalization
1.对小批量(mini-batch)的2d或3d输入进行批标准化(Batch Normalization)操作
2.num_features:来自期望输入的特征数,该期望输入的大小为'batch_size x num_features [x width]'意思即输入大小的形状可以是'batch_size x num_features' 和 'batch_size x num_features x width' 都可以。(输入输出相同)输入Shape:(N, C)或者(N, C, L)输出Shape:(N, C)或者(N,C,L)eps:为保证数值稳定性(分母不能趋近或取0),给分母加上的值。默认为1e-5。momentum:动态均值和动态方差所使用的动量。默认为0.1。affine:一个布尔值,当设为true,给该层添加可学习的仿射变换参数。
3.在每一个小批量(mini-batch)数据中,计算输入各个维度的均值和标准差。gamma与beta是可学习的大小为C的参数向量(C为输入大小)在训练时,该层计算每次输入的均值与方差,并进行移动平均。移动平均默认的动量值为0.1。在验证时,训练求得的均值/方差将用于标准化验证数据。
4.例子
>>> # With Learnable Parameters
>>> m = nn.BatchNorm1d(100) #num_features指的是randn(20, 100)中(N, C)的第二维C
>>> # Without Learnable Parameters
>>> m = nn.BatchNorm1d(100, affine=False)
>>> input = autograd.Variable(torch.randn(20, 100)) #输入Shape:(N, C)
>>> output = m(input) #输出Shape:(N, C)
1.对小批量(mini-batch)3d数据组成的4d输入进行批标准化(Batch Normalization)操作
2.num_features: 来自期望输入的特征数,该期望输入的大小为'batch_size x num_features x height x width'(输入输出相同)输入Shape:(N, C,H, W)输出Shape:(N, C, H, W)eps: 为保证数值稳定性(分母不能趋近或取0),给分母加上的值。默认为1e-5。momentum: 动态均值和动态方差所使用的动量。默认为0.1。affine: 一个布尔值,当设为true,给该层添加可学习的仿射变换参数。
3.在每一个小批量(mini-batch)数据中,计算输入各个维度的均值和标准差。gamma与beta是可学习的大小为C的参数向量(C为输入大小)在训练时,该层计算每次输入的均值与方差,并进行移动平均。移动平均默认的动量值为0.1。在验证时,训练求得的均值/方差将用于标准化验证数据。
4.例子
>>> # With Learnable Parameters
>>> m = nn.BatchNorm2d(100) #num_features指的是randn(20, 100, 35, 45)中(N, C,H, W)的第二维C
>>> # Without Learnable Parameters
>>> m = nn.BatchNorm2d(100, affine=False)
>>> input = autograd.Variable(torch.randn(20, 100, 35, 45)) #输入Shape:(N, C,H, W)
>>> output = m(input)
1.对小批量(mini-batch)4d数据组成的5d输入进行批标准化(Batch Normalization)操作
2.num_features:
来自期望输入的特征数,该期望输入的大小为'batch_size x num_features depth x height x width'
(输入输出相同)输入Shape:(N, C,D, H, W)输出Shape:(N, C, D, H, W)eps: 为保证数值稳定性(分母不能趋近或取0),给分母加上的值。默认为1e-5。momentum: 动态均值和动态方差所使用的动量。默认为0.1。affine: 一个布尔值,当设为true,给该层添加可学习的仿射变换参数。3.在每一个小批量(mini-batch)数据中,计算输入各个维度的均值和标准差。gamma与beta是可学习的大小为C的参数向量(C为输入大小)在训练时,该层计算每次输入的均值与方差,并进行移动平均。移动平均默认的动量值为0.1。在验证时,训练求得的均值/方差将用于标准化验证数据。
4.例子>>> # With Learnable Parameters>>> m = nn.BatchNorm3d(100) #num_features指的是randn(20, 100, 35, 45, 10)中(N, C, D, H, W)的第二维C>>> # Without Learnable Parameters>>> m = nn.BatchNorm3d(100, affine=False) #num_features指的是randn(20, 100, 35, 45, 10)中(N, C, D, H, W)的第二维C>>> input = autograd.Variable(torch.randn(20, 100, 35, 45, 10)) #输入Shape:(N, C, D, H, W) >>> output = m(input)
目前已知的Normalization的方法有4种,对于输入数据为[N,C,(H * W)] (N代表tensor数据,C代表通道,H代表高,W代表宽)
下面图形中表示的就是4种常见的BatchNorm,其中第一种比较常见。
Batch Norm: Batch Norm:对每一个批次(N个tensor)的每个通道分别计算均值mean和方差var,如[10,4,9] 最终输出是[0,1,2,3]这样的1*4的tensor
Layer Norm: 对于每一个tensor的所有channels进行均值和方差计算
Instance Norm: 对于每个tensor的每个channels分别计算
Group Norm: 引用了group的概念,比如BGR表示一个组----不常见
pytorch中的实现:
# -*- coding: UTF-8 -*-import torch
import torch.nn as nnx = torch.rand(100, 16, 784)
# 这里直接将28*28变为一维的784
layer = nn.BatchNorm1d(16)
# 一维直接使用.BatchNorm1d即可
# 因为Batch Norm的参数直接是由channel数量得来的,
# 因此这里直接给定了channel的数量为16,后续会输出16个channel的统计信息
out = layer(x)
# 进行前向计算
print(layer.running_mean)"""
输出结果:
tensor([0.0501, 0.0500, 0.0501, 0.0500, 0.0499, 0.0501, 0.0501, 0.0500, 0.0498,0.0498, 0.0500, 0.0501, 0.0501, 0.0500, 0.0501, 0.0500])
"""
可以自行对上述结果进行验证,该结果的平均值恰好为0.5
Batch Normalization的规范化写法为:
首先第一步先统计了当前规范化的均值和方差。接下来进行Normalize的操作,即将x值减去均值再除以根号下方差的平方与一个很小的误差。最后再进行缩放,缩放成一个适宜的分布。
网友的解释,如下:
注意到这里的最后一步也称之为仿射(affine),引入这一步的目的主要是设计一个通道,使得输出output至少能够回到输入input的状态(当γ = 1 , β = 0时)使得BN的引入至少不至于降低模型的表现,这是深度网络设计的一个套路。
整个过程见流程图,BN在输入后插入,BN的输出作为规范后的结果输入的后层网络中。
代码示例:
# -*- coding: UTF-8 -*-import torch
import torch.nn as nnx = torch.rand(1, 16, 28, 28)
# 这里是28 * 28的数据
layer = nn.BatchNorm2d(16)
# 二维直接使用.BatchNorm2d
# 因为Batch Norm的参数直接是由channel数量得来的,
# 因此这里直接给定了channel的数量为16,后续会输出16个channel的统计信息
out = layer(x)
# 进行前向计算
print(layer.running_mean)
"""
输出结果:
tensor([0.0503, 0.0499, 0.0493, 0.0490, 0.0477, 0.0502, 0.0476, 0.0498, 0.0504,0.0506, 0.0504, 0.0506, 0.0500, 0.0486, 0.0511, 0.0489])
"""# 进行权值计算并输出
print(layer.weight)
"""
输出结果:
Parameter containing:
tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],requires_grad=True)
"""
这里的weight即为σ值
这里还可以设置一些参数,如添加;training=True(表明当前的模式), affine=True(设置参数自动更新学习)。
Batch Norm同样需要手动给予参数
layer.eval()
# 调用不同的模式,以完成参数是否自动更新学习
nn.BatchNorm1d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
Batch Norm具有相当优异的使用效果,如下图所示:
使用了Batch Norm后,收敛速度加快、精度提高。上右图可看出尖峰的偏差对比左侧变小了很多。
再如案例:
# -*- coding: UTF-8 -*-import torch
import torch.nn as nnm = nn.BatchNorm2d(2, affine=True) #权重w和偏重将被使用
input = torch.randn(1, 2, 3, 4)
output = m(input)print("输入图片:")
print(input)
"""
输出结果:
tensor([[[[-1.7310, -1.5597, 0.8319, 0.9386],[ 0.8572, 1.6315, -0.2010, 0.1398],[-0.2074, 0.4479, -0.1920, -1.4322]],[[ 1.1964, -0.4497, -0.0277, -0.2446],[-1.0605, -1.1224, -1.1656, 0.2643],[ 0.1333, 2.2977, -1.0742, 1.3960]]]])
"""print("归一化权重:")
print(m.weight)
"""
输出结果:
tensor([1., 1.], requires_grad=True)
"""print("归一化的偏重:")
print(m.bias)
"""
输出结果:
tensor([0., 0.], requires_grad=True)
"""print("归一化的输出:")
print(output)
"""
输出结果:
tensor([[[[-1.6394, -1.4734, 0.8449, 0.9483],[ 0.8694, 1.6199, -0.1564, 0.1740],[-0.1625, 0.4726, -0.1476, -1.3498]],[[ 1.1003, -0.4288, -0.0368, -0.2383],[-0.9962, -1.0537, -1.0939, 0.2345],[ 0.1128, 2.1234, -1.0090, 1.2858]]]],grad_fn=
"""
print(output.size())
"""
输出结果:
torch.Size([1, 2, 3, 4])
"""# i = torch.randn(1,1,2)
print("输入的第一个维度:")
print(input[0][0])
"""
输出结果:
tensor([[-1.7310, -1.5597, 0.8319, 0.9386],[ 0.8572, 1.6315, -0.2010, 0.1398],[-0.2074, 0.4479, -0.1920, -1.4322]])
"""firstDimenMean = torch.Tensor.mean(input[0][0])
firstDimenVar = torch.Tensor.var(input[0][0],False) #Bessel's Correction贝塞尔校正不会被使用print(m.eps)
"""
输出结果:
1e-05
"""print("输入的第一个维度平均值:")
print(firstDimenMean)
"""
输出结果:
tensor(-0.0397)
"""print("输入的第一个维度方差:")
print(firstDimenVar)
"""
输出结果:
tensor(1.0643)
"""bacthnormone = ((input[0][0][0][0] - firstDimenMean)/(torch.pow(firstDimenVar+m.eps,0.5))) * m.weight[0] + m.bias[0]
print(bacthnormone)
"""
输出结果:
tensor(-1.6394, grad_fn=
"""
https://blog.csdn.net/loseinvain/article/details/86476010
https://cloud.tencent.com/developer/article/1549349
https://www.pianshen.com/article/4348414318/
https://www.jianshu.com/p/6358d261ade8