torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None) 

基本原理
在卷积神经网络的卷积层之后总会添加BatchNorm2d进行数据的归一化处理,这使得数据在进行Relu之前不会因为数据过大而导致网络性能的不稳定,BatchNorm2d()函数数学原理如下:

                                                       

BatchNorm2d()内部的参数如下:

1.num_features:一般输入参数为batch_size*num_channels*height*width (N,C,H,W),即为其中特征的数量

2.eps:分母中添加的一个值,目的是为了计算的稳定性,默认为:1e-5

3.momentum:用于计算running_mean和running_var,默认为0.1

4.affine:当设为true时,会给定可以学习的系数矩阵gamma和beta

5.track_running_stats:一个布尔值,当设置为True时,模型追踪running_mean和running_variance,当设置为False时,模型不跟踪统计信息,并在训练和测试时都使用测试数据的均值和方差来代替,默认为True

在mini-batch上计算每个维度的均值和标准差, gamma 和 beta 是可学习参数向量, gamma 默认设置为1, beta 默认设置为0 。其中,标准差是通过有偏估计来计算的。等同于torch.var(input,unbiased=False) 。

通常在默认情况下,训练过程中BatchNorm层会保持对其计算的均值和方差的估计,然后在测试时使用这个均值和方差进行标准化。Running estimates保持默认动量为0.1 。

如果参数track_running_stats被设置为False,BatchNorm层将不会进行running estimates,Batch统计的均值和方差将会使用测试数据的均值和方差来代替。

NOTE:

BatchNorm中的momentum参数与优化器中的momentum和传统概念中的momentum不同。

在BatchNorm中,Running statistics的更新规则是:

X_new = ( 1 - momentum ) * X_old + momentum * X_current

X是估计量的统计;X_current是新估计的值。

因为Batch Normalization是在Channel维度上进行的,在(N,H,W)的切片上计算统计信息,

所以通常称为空间批归一化。

BatchNorm:

输入:(N,C,H,W)

输出:(N,C,H,W)

输入输出形状相同

训练阶段:

        1.当track_running_stats=True,running_mean和running_var会跟踪不同batch数据的mean和variance,但是仍然是用每个batch的mean和variance做normalization
        2.当track_running_stats=False,此时running_mean和running_var不跟踪不同batch数据的statistics,但是仍然用每个batch的mean和variance做normalization
测试阶段:

        1.当track_running_stats=True,则使用训练阶段跟踪估计的running_mean和running_var做normalization
        2.当track_running_stats=False,训练阶段没有跟踪不同batch数据的statistics,因此使用每个batch的mean和variance做normalization

代码演示

#encoding:utf-8
import torch
import torch.nn as nn
#num_features - num_features from an expected input of size:batch_size*num_features*height*width
#eps:default:1e-5 (公式中为数值稳定性加到分母上的值)
#momentum:动量参数,用于running_mean and running_var计算的值,default:0.1
m=nn.BatchNorm2d(2,affine=True) #affine参数设为True表示weight和bias将被使用
input=torch.randn(1,2,3,4)
output=m(input)
 
print(input)
print(m.weight)
print(m.bias)
print(output)
print(output.size())

具体的输出如下:

tensor([[[[ 1.4174, -1.9512, -0.4910, -0.5675],
          [ 1.2095,  1.0312,  0.8652, -0.1177],
          [-0.5964,  0.5000, -1.4704,  2.3610]],
         [[-0.8312, -0.8122, -0.3876,  0.1245],
          [ 0.5627, -0.1876, -1.6413, -1.8722],
          [-0.0636,  0.7284,  2.1816,  0.4933]]]])
Parameter containing:
tensor([0.2837, 0.1493], requires_grad=True)
Parameter containing:
tensor([0., 0.], requires_grad=True)
tensor([[[[ 0.2892, -0.4996, -0.1577, -0.1756],
          [ 0.2405,  0.1987,  0.1599, -0.0703],
          [-0.1824,  0.0743, -0.3871,  0.5101]],
         [[-0.0975, -0.0948, -0.0347,  0.0377],
          [ 0.0997, -0.0064, -0.2121, -0.2448],
          [ 0.0111,  0.1232,  0.3287,  0.0899]]]],
       grad_fn=<NativeBatchNormBackward>)
torch.Size([1, 2, 3, 4])

分析:输入是一个1*2*3*4 四维矩阵,gamma和beta为一维数组,是针对input[0][0],input[0][1]两个3*4的二维矩阵分别进行处理的,我们不妨将input[0][0]的按照上面介绍的基本公式来运算,看是否能对的上output[0][0]中的数据。首先我们将input[0][0]中的数据输出,并计算其中的均值和方差。

print("输入的第一个维度:")
print(input[0][0]) #这个数据是第一个3*4的二维数据
#求第一个维度的均值和方差
firstDimenMean=torch.Tensor.mean(input[0][0])
firstDimenVar=torch.Tensor.var(input[0][0],False)   #false表示贝塞尔校正不会被使用
print(m)
print('m.eps=',m.eps)
print(firstDimenMean)
print(firstDimenVar)

输出结果如下:

输入的第一个维度:
tensor([[ 1.4174, -1.9512, -0.4910, -0.5675],
        [ 1.2095,  1.0312,  0.8652, -0.1177],
        [-0.5964,  0.5000, -1.4704,  2.3610]])
BatchNorm2d(2, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
m.eps= 1e-05
tensor(0.1825)
tensor(1.4675)

我们可以通过计算器计算出均值和方差均正确计算。最后通过公式计算input[0][0][0][0]的值,代码如下:

batchnormone=((input[0][0][0][0]-firstDimenMean)/(torch.sqrt(firstDimenVar+m.eps)))\
    *m.weight[0]+m.bias[0]
print(batchnormone)

输出结果如下:

tensor(0.2892, grad_fn=<AddBackward0>)

结果值等于output[0][0][0][0]。ok,代码和公式完美的对应起来了。

ps:上面计算方差时有一个贝塞尔校正系数,具体可以通过如下链接参考:https://www.jianshu.com/p/8dbb2535407e

从公式上理解即在计算方差时一般的计算方式如下:

通过贝塞尔校正的样本方差如下:

目的是在总体中选取样本时能够防止边缘数据不被选到。详细的理解可以参考上面的链接。 

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐