前言
BN(Batch Normalization)在如今的神经网络中应用广泛,涵盖图像分类、语义分割和目标检测等各个领域,对于防止过拟合起到了巨大的作用。在TensorFlow中,使用BN时通常要设置一个参数——is_training,作为模型处在训练模式或者测试模式的标志。倘若这个参数使用不得当,就会出现推理时精度异常,会出现如下情况:
①batch_size = 1时,无论输入数据是否打乱,推理精度都是极低;
②batch_size 较大时,且输入数据没有打乱时,精度极低;
③当batch_size较大时,且输入数据随机打乱时,精度略高,但会出现重复性误差,即同一批数据,两次推理结果可能不同。
这些情况是常见的BN参数设置错误问题,当读者看完下面对BN的解读,就可以知道出现这些情况的原因,从而更加有效地去运用BN。
BN训练及推理
在BN论文中,算法的计算过程如下所示:
μ
B
\mu_{\mathcal{B}}
μB、
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2、
γ
\gamma
γ、
β
\beta
β和
y
i
y_{i}
yi分别为神经网络中当前层所有节点在batch这个维度的均值、方差、尺度、偏置和输出,m为batch大小。
μ
B
\mu_{\mathcal{B}}
μB、
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2为当前Batch的统计值,
γ
\gamma
γ、
β
\beta
β为可训练的参数,初始值为1和0。
举两个例子:
①在一个一维的BP神经网络中,某一层的维度为[10,2000],那么这层的
μ
B
\mu_{\mathcal{B}}
μB、
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2、
γ
\gamma
γ、
β
\beta
β和
y
i
y_{i}
yi的维度分别为[1,2000]、[1,2000]、[1,2000]、[1,2000]和[10,2000]。
②在二维的卷积神经网络中,对于其中的某一层,可以认为一个通道为一个节点,其对应的均值、方差都为2维(point-wise),而尺度和偏置都为一维(channel-wise)。对于输入维度为[10,300,300,100]的这样一个输入,这层的
μ
B
\mu_{\mathcal{B}}
μB、
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2、
γ
\gamma
γ、
β
\beta
β和
y
i
y_{i}
yi的维度分别为[1,300,300,100]、[1,300,300,100]、[1,100]、[1,100]和[10,300,300,100]。
代码如下:
mean = np.mean(x, axis=0)
var = np.var(x, axis=0)
out_ = (x - mean) / np.sqrt(var + eps)
out = gamma * out_ + beta
这便是训练时输出的由来。但是我们知道,在推理时,batch的大小可能是1,这时候如果还是用这样的方式来计算均值和方差显然不make sense。
那么应该怎么做?论文中的做法见下图:
γ
\gamma
γ、
β
\beta
β使用训练得到的参数,而
μ
B
\mu_{\mathcal{B}}
μB、
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2则是对所有batch计算出来的
μ
B
\mu_{\mathcal{B}}
μB、
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2 做无偏估计的值,也就是TensorFlow中的moving_mean和moving_variance,然后代入上面的公式进行计算。
但是这种做法有一个极大的弊端:需要耗费大量的内存空间用于存储每一个batch中每一层的
μ
B
\mu_{\mathcal{B}}
μB、
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2。所以一般采用如下公式进行计算:
r
μ
B
i
=
β
r
μ
B
i
−
1
+
(
1
−
β
)
μ
B
r \mu_{B i}=\beta r \mu_{B i-1}+(1-\beta) \mu_{B}
rμBi=βrμBi−1+(1−β)μB
r
σ
B
i
2
=
β
r
σ
B
i
−
1
2
+
(
1
−
β
)
σ
B
2
r \sigma_{B i}^{2}=\beta r \sigma_{B i-1}^{2}+(1-\beta) \sigma_{B}^{2}
rσBi2=βrσBi−12+(1−β)σB2
r μ B i r \mu_{B i} rμBi是迭代了 i i i次的moving_mean, r σ B i 2 r \sigma_{B i}^{2} rσBi2则是迭代了 i i i次moving_variance。β是人为设置的参数。
预测异常的原因
①:batch_size = 1时,无论输入数据是否打乱,推理精度都是极低;
解释:batch_size=1时,仅仅对一个样本进行推理时,此时的
μ
B
\mu_{\mathcal{B}}
μB为样本本身,
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2为0,此时的
x
^
\widehat{x}
x
为异常值,自然推理出现问题。
②:batch_size 较大时,且输入数据没有打乱时,精度极低;
解释:当数据没有打乱时,通常在一个batch中所有样本为同类,这样计算出的
μ
B
\mu_{\mathcal{B}}
μB和
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2与moving_mean和moving_variance有较大的差距,使得推理出错。
③当batch_size较大时,且输入数据随机打乱时,精度略高,但会出现重复性误差,即同一批数据,两次推理结果可能不同。
解释:当输入数据打乱时,在一个batch中所有样本类别不完全相同,使得计算出来
μ
B
\mu_{\mathcal{B}}
μB和
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2可能接近moving_mean和moving_variance,最终推理结果精度略高;由于在导入模型参数时重新随机打乱数据,同一个样本在推理时使用的
μ
B
\mu_{\mathcal{B}}
μB和
σ
B
2
\sigma_{\mathcal{B}}^{2}
σB2不同,自然结果也就不同。
总结
①对于BN,训练时对样本分布敏感,应在训练前把训练样本打乱。
②TensorFlow中,在训练时应当设置is_training为True,而测试时应当设为False;PyTorch也要注意使用net.train()或者net.test()切换模型。