简单的分享一下,很多人在做网络推理的时候,并不会考虑batchnorm的作用,拿别人的网络来训练,训练完了之后预测的时候也直接预测。这是不对的,因为batchnorm本身在预测的时候没有任何作用,它实在训练的时候起到了一个归一话加速收敛的作用。
batchnorm合并的必要性
那么为什么要将batchnorm合并呢?一般放置在卷积层(conv层)或者全连接层之后,将数据归一化并加速了训练拟合速度。但是bn层虽然在深度学习模型训练时起到了一定的积极作用,但是在预测时因为凭空多了一些层,影响了整体的计算速度并占用了更多内存或者显存空间。所以我们设想如果能将bn层合并到相邻的卷积层或者全连接层之后就好了,于是就有了这篇文章所提到的工作。
batchnorm合并的方法
batchnorm以后iban其实放置在conv或者pooling之后。合并的方式就得分开进行。
- 如果是在conv之后
思考一下,假如是在conv之后,原来bn层的参数,也就是均值和方差,可以直接合并到卷积参数里面去,这样计算的时候就不用再进行这一步的计算了。看看代码是如何实现的:
mean = bn[0].data
var = bn[1].data
scalef = bn[2].data
scales = scale[0].data
shift = scale[1].data
if scalef != 0:
scalef = 1. / scalef
mean = mean * scalef
var = var * scalef
rstd = 1. / np.sqrt(var + 1e-5)
if bn_maps[key]["type"] == "Convolution":
rstd1 = rstd.reshape((channels,1,1,1))
scales1 = scales.reshape((channels,1,1,1))
wt = wt * rstd1 * scales1
else:
rstd1 = rstd.reshape((1, channels,1,1))
scales1 = scales.reshape((1, channels,1,1))
wt = wt * rstd1 * scales1
bias = (bias - mean) * rstd * scales + shift
nobn.params[key][0].data[...] = wt
nobn.params[key][1].data[...] = bias
这个是caffe的合并操作。这个代码在MobileNet-SSD里面有:https://github.com/chuanqi305/MobileNet-SSD/blob/master/merge_bn.py
2. 如果是在pooling之后
pooling之后的操作差不多,具体大家可以探索一下。
这样你的bn曾合并之后速度可以增加很多,有其实bn层很多的网络,将bn层合并是一个不可或缺的操作,否则你推理的速度将会大大的减慢。