以前使用 Caffe 的时候没注意这个, 现在使用预训练模型来动手做时遇到了. 在 slim 中的自带模型中 inception, resnet, mobilenet 等都自带 BN 层, 这个坑在《实战 Google 深度学习框架》第二版这本书 P166 里只是提了一句, 没有做出解答.
书中说训练时和测试时使用的参数 is_training 都为 True, 然后给出了一个链接供参考. 本人刚开始使用时也是按照书中的做法没有改动, 后来从保存后的 checkpoint 中加载模型做预测时出了问题: 当改变需要预测数据的 batchsize 时预测的 label 也跟着变, 这意味着 checkpoint 里面没有保存训练中 BN 层的参数, 使用的 BN 层参数还是从需要预测的数据中计算而来的. 这显然会出问题, 当预测的 batchsize 越大, 假如你的预测数据集和训练数据集的分布一致, 结果就越接近于训练结果, 但如果 batchsize=1, 那 BN 层就发挥不了作用, 结果很难看.
那如果在预测时 is_traning=false 呢, 但 BN 层的参数没有从训练中保存, 那使用的就是随机初始化的参数, 结果不堪想象.
所以需要在训练时把 BN 层的参数保存下来, 然后在预测时加载, 参考几位大佬的博客, 有了以下训练时添加的代码:
- update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
- with tf.control_dependencies(update_ops):
- train_step = tf.train.AdamOptimizer(learning_rate=0.0001).minimize(loss)
- # 设置保存模型
- var_list = tf.trainable_variables()
- g_list = tf.global_variables()
- bn_moving_vars = [g for g in g_list if 'moving_mean' in g.name]
- bn_moving_vars += [g for g in g_list if 'moving_variance' in g.name]
- var_list += bn_moving_vars
- saver = tf.train.Saver(var_list=var_list, max_to_keep=5)
这样就可以在预测时从 checkpoint 文件加载 BN 层的参数并设置 is_training=False.
最后要说的是, 虽然这么做可以解决这个问题, 但也可以利用预测数据来计算 BN 层的参数, 不是说一定要保存训练时的参数, 两种方案可以作为超参数来调节使用, 看哪种方法的结果更好.
感谢几位大佬的博客解惑:
- https://blog.csdn.net/dongjbstrong/article/details/80447110?utm_source=blogxgwz0
- http://www.cnblogs.com/hrlnw/p/7227447.html
来源: https://www.cnblogs.com/baijing1/p/9842321.html