Batch-Normalization (BN)
通过使用当前batch数据的均值mean和方差variance对它们进行标准化处理,可以使得模型的训练更加快速和稳定。实践中会把BN一般放在非线性激活函数的上一层或者下一层。
原理
训练时
BN之所以称之为batch normalization,就是因为normalization是沿batch_size维度进行的。
设某一个神经元对一个batch
内的\(n\)个样本的输出为分别为\(Z^{(i)}, i=1, \cdots, n\),BN层通过计算
得到它们的均值 \(\mu\) 和方差 \(\delta\). 然后通过
\[Z_{norm}^{(i)} = \frac{Z^{(i)} - \mu}{\sqrt{\delta^2 - \epsilon}} \tag{3}\]将原来该神经元的输出(可能是任意分布)转化到均值为0,方差为1的标准正态分布上。如图3所示。
最后,再对\(Z_{norm}^{(i)}\)做一个线性变换
\[\hat{Z} = \gamma \times Z_{norm}^{(i)} + \beta \tag{4}\]\(\gamma\) 和 \(\beta\) 是可学习的参数,可以在正态分布的基础上调整,选择其最优的正态分布。
上面的解释是基于神经元neuron的输出展开的,但是对于2D CNN中卷积得到的特征图,batch normalization是如何作用的呢?
如图4,就是将某一个channel对应的batchsize个维度为(H, W)的tensor求解出一对均值方差,对这batchsize个(H, W)的tensor,其每一个pixel value减去该均值并除以该方差。参考
Q: bn 不是对batch维度做归一化嘛,为什么h,w维度也要做?
A: 因为CNN中卷积核是共享的,所以一张特征图应该被看作一个”神经元”的输出,因此归一化的时候N,H,W应该放在一起归一化。参考(评论区)
注意,BN层操作是channel-wise的:一个channel对应一对均值和方差,一对\(\gamma\) 和 \(\beta\) 。这也是在Pytorch中定义BatchNorm2d层时,需要传入的是channel_num的缘故。参考和参考
测试时
训练时,BN层可以为每一个batch
输入数据计算均值和方差,但是测试时,BN层使用的均值和方差不是根据输入样本计算,而是使用训练过程中计算得到的值,直接从(3)式开始计算。可以参考这个demo code.
效果
可以看到,使用BN可以容忍更高的学习率,更快更好地收敛。
QA
Q:经常见到的mean = [0.485, 0.456, 0=406] , std = [0.229, 0.224, 0.225]
是什么?
A:前面的(0.485,0.456,0.406)表示均值,分别对应的是RGB三个通道;后面的(0.229,0.224,0.225)则表示的是标准差,这些值是在ImageNet数据集计算出来的,所以很多人都使用它们。不过在有了BN之后,它们已经不必要了。
Q:对于RGB图而言,均值不应该是接近于128吗?为什么会是接近于0.5的数呢?
A: 这是因为使用了pytorch
的transforms.ToTensor()对图片进行了归一化处理(另外对于PIL Image or numpy.ndarray类型的输入,还将尺寸由 (H x W x C)
转化为了 (C x H x W)
)。如
1
2
3
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
Q:上面说BN层在训练和测试时使用方法不同,那么模型如何区分自己是在训练还是在测试呢?
A:使用model.train()
或者model.train(True)
将model
设为训练状态;使用model.eval()
或者model.train(False)
将model
设为测试状态。当状态变化时,目前只有dropout
和batchnorm
两种层会受到影响(参考)。batchnorm
层在不同状态下的表现如前所述;对于dropout
层,当处在测试状态时,该层输出值等于输入值(identity map
),相当于disabled
。
Tips:
dropout(p)
在训练时有一个scaled by factor
\(\frac{1}{1-p}\)操作,目的是使得某一神经元输出在测试时的期望=训练时的期望,更进一步解释可参考这里
Q:Batch Normalization的一些局限?
A:因为测试时BN层使用的均值和方差是训练过程中计算得到的值,因此当测试集和训练集数据分布差别较大时,会得到不好的测试效果。
Q:BN层应放在非线性激活函数前面还是后面?
A:BN作者本意是为了通过BN层,使得任何的参数值,都可以用所期望的分布(应该就是指正态分布)来生成激活值,因此将BN层放在了非线性激活函数前面一层。后来很多网络结构,比如ResNet,mobilenet-v2也都沿用了这一准则。不过也有工作表示将BN层放在了非线性激活函数后面一层会取得更好的效果,但是没有给出有力的解释。reddit上对于这一问题有个激烈的讨论。
Q:Group Normlization?
A: