Table of Contents
参考:
BatchNormalization、LayerNormalization、InstanceNorm、GroupNorm、SwitchableNorm总结
PyTorch学习之归一化层(BatchNorm、LayerNorm、InstanceNorm、GroupNorm)
归一化层,目前主要有这几个方法:
Batch Normalization(2015年)、Layer Normalization(2016年)、Instance Normalization(2017年)、Group Normalization(2018年)、Switchable Normalization(2018年);
区别:
将输入的图像shape记为[N, C, H, W],这几个方法主要的区别就是在:
- BatchNorm:batch方向做归一化,算NHW的均值,对小batchsize效果不好;BN主要缺点是对batchsize的大小比较敏感,由于每次计算均值和方差是在一个batch上,所以如果batchsize太小,则计算的均值、方差不足以代表整个数据分布
- LayerNorm:channel方向做归一化,算CHW的均值,主要对RNN作用明显;
- InstanceNorm:一个channel内做归一化,算H*W的均值,用在风格化迁移;因为在图像风格化中,生成结果主要依赖于某个图像实例,所以对整个batch归一化不适合图像风格化中,因而对HW做归一化。可以加速模型收敛,并且保持每个图像实例之间的独立。
- GroupNorm:将channel方向分group,然后每个group内做归一化,算(C//G)HW的均值;这样与batchsize无关,不受其约束。
- SwitchableNorm是将BN、LN、IN结合,赋予权重,让网络自己去学习归一化层应该使用什么方法。
BatchNorm
torch.nn.BatchNorm2d
(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
- num_features: 特征图数量
- eps: 为保证数值稳定性(分母不能趋近或取0),给分母加上的值。默认为1e-5。
- momentum: 动态均值和动态方差所使用的动量。默认为0.1。
- affine: 布尔值,当设为true,给该层添加可学习的仿射变换参数。
- track_running_stats:布尔值,当设为true,记录训练过程中的均值和方差;
均值:相当于 Figure 2 中 N个长方体的对应位置元素相加除以N
InstanceNorm
torch.nn.InstanceNorm2d
(num_features, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)
相当于N=1的BatchNorm
LocalResponseNorm
torch.nn.LocalResponseNorm
(size, alpha=0.0001, beta=0.75, k=1.0)
size:用于归一化的邻居通道数
alpha:乘积因子,Default: 0.0001
beta :指数,Default: 0.75
k:附加因子,Default: 1
原文链接:https://blog.csdn.net/LoseInVain/article/details/86476010
Pytorch中的BatchNorm的API主要有:
torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
一般来说pytorch中的模型都是继承nn.Module类的,都有一个属性trainning指定是否是训练状态,训练状态与否将会影响到某些层的参数是否是固定的,比如BN层或者Dropout层。通常用model.train()指定当前模型model为训练状态,model.eval()指定当前模型为测试状态。
同时,BN的API中有几个参数需要比较关心的,一个是affine指定是否需要仿射,还有个是track_running_stats指定是否跟踪当前batch的统计特性。容易出现问题也正好是这三个参数:trainning,affine,track_running_stats。如果affine=False则γ=1,β=0,并且不能学习被更新。一般都会设置成affine=True
track_running_stats=True表示跟踪整个训练过程中的batch的统计特性,得到方差和均值,而不只是仅仅依赖与当前输入的batch的统计特性。相反的,如果track_running_stats=False那么就只是计算当前输入的batch的统计特性中的均值和方差了。当在推理阶段的时候,如果track_running_stats=False,此时如果batch_size比较小,那么其统计特性就会和全局统计特性有着较大偏差,可能导致糟糕的效果。
一般来说,trainning和track_running_stats有四种组合
- trainning=True, track_running_stats=True。这个是期望中的训练阶段的设置,此时BN将会跟踪整个训练过程中batch的统计特性。
- trainning=True, track_running_stats=False。此时BN只会计算当前输入的训练batch的统计特性,可能没法很好地描述全局的数据统计特性。
- trainning=False, track_running_stats=True。这个是期望中的测试阶段的设置,此时BN会用之前训练好的模型中的(假设已经保存下了)running_mean和running_var并且不会对其进行更新。一般来说,只需要设置model.eval()其中model中含有BN层,即可实现这个功能。
- trainning=False, track_running_stats=False 效果同2,只不过是位于测试状态,这个一般不采用,这个只是用测试输入的batch的统计特性,容易造成统计特性的偏移,导致糟糕效果。
代码演示:
参考https://blog.csdn.net/bigFatCat_Tom/article/details/91619977
用pytorch封装好的BN运算一下
import torch
import torch.nn as nn
a = torch.randn(2,3,4,4)
print(a)
'''
输出:
tensor([[[[-0.2083, -0.0617, 1.3143, 2.6126],
[ 0.5093, -0.5015, -1.2195, 0.9299],
[-0.6965, 1.0784, 0.8513, 0.4423],
[ 0.8713, 0.1331, -0.3746, -0.1559]],
[[-2.2629, 0.0863, -2.0906, -1.5406],
[ 0.1329, -0.0482, 1.5541, 0.9241],
[-0.1211, -0.4665, 0.8098, 2.0917],
[-1.2141, 0.1769, -2.0404, -0.9025]],
[[ 0.6931, -0.3296, 0.4235, 0.2623],
[-0.9637, -0.0713, -0.8088, 2.1761],
[ 0.8186, -1.6996, 1.4698, -0.9832],
[ 0.8413, -0.0220, -0.5528, -1.0645]]],
[[[ 0.6976, -0.6083, -2.0900, -1.7487],
[-1.0340, -1.8326, -0.4567, 0.8283],
[-0.1331, -0.9351, -0.1814, 1.3032],
[ 0.6202, 0.4430, -0.3147, 2.2516]],
[[-1.0134, 0.1929, -0.4350, 1.1158],
[ 0.7481, -1.2626, -1.0841, 2.3136],
[ 1.3261, 0.5567, -0.6275, -1.0899],
[-0.5978, -0.7278, -0.0906, 0.3606]],
[[ 0.1453, 0.0271, 0.6967, -0.9279],
[-0.2625, 0.8887, 0.4750, 1.9671],
[-0.9255, -0.7932, -1.5779, -0.4061],
[-1.2149, -0.7441, 0.8653, -0.5020]]]])
'''
m = nn.BatchNorm2d(3)
out_a = m(a)
print(m.weight)
print(m.bias)
print(out_a)
'''
输出
Parameter containing:
tensor([0.3354, 0.4789, 0.3222], requires_grad=True)
Parameter containing:
tensor([0., 0., 0.], requires_grad=True)
tensor([[[[-0.0880, -0.0421, 0.3883, 0.7945],
[ 0.1365, -0.1797, -0.4043, 0.2681],
[-0.2407, 0.3145, 0.2435, 0.1155],
[ 0.2497, 0.0188, -0.1400, -0.0716]],
[[-0.8787, 0.1045, -0.8066, -0.5764],
[ 0.1240, 0.0482, 0.7187, 0.4551],
[ 0.0177, -0.1269, 0.4072, 0.9437],
[-0.4398, 0.1424, -0.7855, -0.3094]],
[[ 0.2553, -0.0888, 0.1645, 0.1103],
[-0.3021, -0.0019, -0.2500, 0.7542],
[ 0.2975, -0.5497, 0.5166, -0.3087],
[ 0.3051, 0.0147, -0.1639, -0.3361]]],
[[[ 0.1954, -0.2131, -0.6766, -0.5698],
[-0.3463, -0.5961, -0.1657, 0.2363],
[-0.0645, -0.3153, -0.0796, 0.3849],
[ 0.1712, 0.1158, -0.1212, 0.6815]],
[[-0.3558, 0.1491, -0.1137, 0.5353],
[ 0.3814, -0.4600, -0.3853, 1.0366],
[ 0.6233, 0.3013, -0.1942, -0.3878],
[-0.1818, -0.2362, 0.0304, 0.2193]],
[[ 0.0710, 0.0312, 0.2565, -0.2901],
[-0.0662, 0.3211, 0.1819, 0.6839],
[-0.2893, -0.2448, -0.5088, -0.1146],
[-0.3867, -0.2283, 0.3132, -0.1468]]]],
grad_fn=<NativeBatchNormBackward>)
'''
自己计算一下,验证运算结果:
# 针对每个通道做均值和方差,用0,1,2分别表示3个通道,此处只验证一下0通道
print(a[:, 0, :, :])
mean1 = torch.mean(a[:, 0, :, :])
var1 = torch.var(a[:, 0, :, :], False) # false表示bias校正不会被使用
print(mean1)
print(var1)
print(m)
'''
输出
tensor([[[-0.2083, -0.0617, 1.3143, 2.6126],
[ 0.5093, -0.5015, -1.2195, 0.9299],
[-0.6965, 1.0784, 0.8513, 0.4423],
[ 0.8713, 0.1331, -0.3746, -0.1559]],
[[ 0.6976, -0.6083, -2.0900, -1.7487],
[-1.0340, -1.8326, -0.4567, 0.8283],
[-0.1331, -0.9351, -0.1814, 1.3032],
[ 0.6202, 0.4430, -0.3147, 2.2516]]])
tensor(0.0729)
tensor(1.1494)
BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
'''
cal_out = (a[0][0] - mean1) / (var1.pow(0.5) + m.eps) * m.weight[0] + m.bias[0]
print(cal_out)
'''
输出
tensor([[-0.0880, -0.0421, 0.3883, 0.7945],
[ 0.1365, -0.1797, -0.4043, 0.2681],
[-0.2407, 0.3145, 0.2435, 0.1155],
[ 0.2497, 0.0188, -0.1400, -0.0716]], grad_fn=<AddBackward0>)
'''