关于Batch Normalization 和pytorch中的使用.

关于Batch Normalization 和pytorch中的使用.

关于BatchNormalization一直没有细细地总结过其用处,但是还经常被问到这个,所以还是要总结一下。

沿着深度的方向进行



In [1]: import torch

In [2]: bn = torch.nn.BatchNorm2d(512)

In [3]: bn
Out[3]: BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

In [4]: a = torch.rand(16, 512, 4,4)

In [5]: b = bn(a)

In [6]: b.shape
Out[6]: torch.Size([16, 512, 4, 4])

In [7]: for x in bn.parameters():
   ...:     print(x.shape)
   ...:
torch.Size([512])
torch.Size([512])


从上面可以看出,比如说当前的featuremap的大小是N,C,H,W的,那么bn实际上是沿着深度方向进行的,也就是那么当前bn的参数有是C*2个,即上面展示的两个512, 因为一个是beta, 一个是gamma.另外如果上面的参数affine=False,那就没有参数可以学习了.

另外注意到经过BN之前和BN之后 feature map的大小并没有发生改变,这个是很好理解的,因为只是做了一个归一化处理,要是改变了feature的shape就不对了.

训练和测试的时候有何不同

在训练的时候, 会计算一个batch内的mean 和var, 但是因为是小batch小batch的训练的,所以会采用加权或者动量的形式来将每个batch的 mean和var来累加起来,也就是说再算当前的batch的时候,其实当前的权重只是占了0.1, 之前所有训练过的占了0.9的权重,这样做的好处是不至于因为某一个batch太过奇葩而导致的训练不稳定.

好,现在假设训练完成了, 那么在整个训练集上面也得到了一个最终的”mean 和var”, BN层里面的参数也学习完了(如果指定学习的话),而现在需要测试了,测试的时候往往会一张图一张图的去测,这时候没有batch而言了,对单独一个数据做 mean和var是没有意义的, 那么怎么办,实际上在测试的时候BN里面用的mean和var就是训练结束后的mean_finalval_final. 也可说是在测试的时候BN就是一个变换。所以在用pytorch的时候要注意这一点,在训练之前要有model.train() 来告诉网络现在开启了训练模式,在eval的时候要用”model.eval()”, 用来告诉网络现在要进入测试模式了.因为这两种模式下BN的作用是不同的.

BN的作用

  • 防止出现weight decay 或者是 weight blow up.

这个可以想像在用深层的CNN的时候,梯度传到某一层的时候可能是这种样子w^N, N可能比较大了, 如果w 过小或者是过大,就会分别出现 weight decay和 weight blowup 的情况,而BN的作用就是可以将w 弄到1附近去. 虽然1.01**365=37.780.99**365=0.0255 了,但是目前的网络还不是那么地深.

  • 尽量使得输入的分布是一致的,

因为统计学里面有一个假设是,假设源空间和目标空间的数据分布是一致的.

  • 可以使训练更加地稳定,那么稳定了之后,学习得就快了,所以也提高了训练的速度, 使得收敛变得快

  • 解决0梯度问题

一般BN都是加在relu之前的, 即”cnn-bn-relu”, 如果是这样的话”cnn-relu”, 如果所有的channel的值全部为负值的话,那么经过relu之后当前channel的值就全部为0了,那么整个输出就全部变为0了, 那么0的导数还是0,所以这样的话,这之前的就学不到东西了,就会一直处于这个阶段。但是如果加了BN的话,即使所有的值为负值,但是现在做了BN之后均值是0,还是有些是为正的. 这样经过relu之后是会有梯度反传的.

打赏,谢谢~~

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,多谢支持~

打开微信扫一扫,即可进行扫码打赏哦