tf-slim批处理规范:训练/推理模式之间的行为不同

Dom*_*ack 5 machine-learning deep-learning tensorflow tf-slim batch-normalization

我正在尝试基于流行的苗条实现训练tensorflow模型,mobilenet_v2并且正在观察无法解释与批处理规范化相关的行为(我认为)。

问题总结

推理模式下的模型性能最初有所提高,但经过很长一段时间后才开始产生琐碎的推理(全部接近零)。当在训练模式下运行时,即使在评估数据集上,也将继续保持良好的性能。批处理归一化衰减/动量速率会影响评估性能。

下面提供了更多详细的实现细节,但是我可能会在文本墙中迷失大多数人,因此,这里有一些图片会让您感兴趣。

下面的曲线来自我bn_decay在训练时调整参数的模型。

0-370k :(bn_decay=0.997默认)

370k-670k: bn_decay=0.9

670k +: bn_decay=0.5

失利 (橙色)训练(在训练模式下)和(蓝色)评估(在推理模式下)的损失。低是好的。

评估指标 推理模式下评估数据集上模型的评估指标。高就是好。

我试图提供一个最小的例子来说明问题-在MNIST上进行分类-但失败了(即分类效果很好,并且我所遇到的问题没有出现)。对于无法进一步简化工作,我深表歉意。

实施细节

我的问题是2D姿态估计,目标是将高斯集中在关节位置。它本质上与语义分段相同,除了不使用softmax_cross_entropy_with_logits(labels, logits)I用法tf.losses.l2_loss(sigmoid(logits) - gaussian(label_2d_points))(我使用术语“ logits”来描述我学习的模型的未激活输出,尽管这可能不是最好的术语)。

推论模型

在对输入进行预处理之后,我的logits函数是对基本mobilenet_v2的作用域调用,其后是单个未激活的卷积层,以使过滤器数量合适。

from slim.nets.mobilenet import mobilenet_v2

def get_logtis(image):
    with mobilenet_v2.training_scope(
            is_training=is_training, bn_decay=bn_decay):
        base, _ = mobilenet_v2.mobilenet(image, base_only=True)
    logits = tf.layers.conv2d(base, n_joints, 1, 1)
    return logits
Run Code Online (Sandbox Code Playgroud)

培训操作

我已经尝试过tf.contrib.slim.learning.create_train_op,还有自定义培训操作:

def get_train_op(optimizer, loss):
    global_step = tf.train.get_or_create_global_step()
    opt_op = optimizer.minimize(loss, global_step)
    update_ops = set(tf.get_collection(tf.GraphKeys.UPDATE_OPS))
    update_ops.add(opt_op)
    return tf.group(*update_ops)
Run Code Online (Sandbox Code Playgroud)

我使用的是tf.train.AdamOptimizerlearning rate=1e-3

训练循环

我正在使用tf.estimator.EstimatorAPI进行培训/评估。

行为

最初的培训进行得很好,预期绩效会大大提高。这符合我的期望,因为快速训练了最后一层以解释预训练的基础模型输出的高级特征。

然而,很长的时间段(步骤60K的batch_size与8,〜在GTX-1070 8小时)后,我的模型开始输出接近零的值(〜1E-11)当在推理模式下运行,即,is_training=False。当以* training模式, i.e.is_training = True` 运行时,即使在评估集上,完全相同的模型也会继续改进。我已经从视觉上验证了这是。

经过一些实验后,我将bn_decay(批量归一化衰减/动量速率)从默认值0.997更改0.9为约370k步长(也尝试过0.99,但这并没有太大的区别),并观察到了准确的改进。目视检查推理模式下的推理后~1e-1,在预期位置的顺序推断值中显示出清晰的峰值,这与训练模式下的峰值位置一致(尽管该值要低得多)。这就是为什么精度会显着提高,但损失(尽管波动较大)却没有太大改善的原因。

经过更多的训练后,这些影响消失了,并恢复为全零推断。

bn_decay在步骤〜670k 进一步将其降至0.5。这导致损失和准确性均得到改善。我可能要等到明天才能看到长期影响。

损耗和评估指标图如下所示。请注意,评估指标基于对数的argmax,high为良好。损失是基于实际值,低是好的。橙色用于is_training=True训练集,而蓝色用于is_training=False评估集。大约8的损耗与所有零输出一致。

其他注意事项

  • 我还尝试了关闭Dropout(即始终使用来运行Dropout层is_training=False),但没有发现任何区别。
  • 我已经尝试过所有版本的tensorflow从1.7to 1.10。没有不同。
  • bn_decay=0.99从一开始就从预先训练的检查点开始训练模型。与使用default的行为相同bn_decay
  • 其他批次大小为16的实验在质量上具有相同的行为(尽管由于内存限制,我无法同时评估和训练,因此定量分析批次大小为8)。
  • 我使用相同的损失和tf.layersAPI 训练了不同的模型,并从头开始训练。他们工作得很好。
  • 从头开始训练(而不是使用预先训练的检查点)会导致类似的行为,尽管花费的时间更长。

摘要/我的想法:

  • 我相信这不是过度拟合/数据集问题。在使用时,该模型会对评估集做出明智的推断is_training=True,包括峰的位置和幅度。
  • 我有信心,不运行更新操作不是问题。我以前没有使用slim过,但是除了使用外,arg_scope它与tf.layers我广泛使用的API 看起来并没有太大不同。我还可以检查移动平均值,并观察它们随着训练的进行而变化。
  • 临时bn_decay值会显着影响结果。我接受的值0.5太低了,但是我的想法已经用完了。
  • 我曾尝试换出slim.layers.conv2d层为tf.layers.conv2d具有momentum=0.997(即势头默认值衰减一致)和行为是一样的。
  • 使用预训练权重和Estimator框架的最小示例适用于MNIST的分类而无需修改bn_decay参数。

我已经研究了tensorflow和模型github存储库上的问题,但除此之外并没有发现太多问题。我目前正在尝试以较低的学习率和更简单的优化器(MomentumOptimizer)进行试验,但这更多是因为我的想法已经用尽,而不是因为我认为问题出在这里。

可能的解释

  • 我最好的解释是,我的模型参数正在以某种方式快速循环,以使移动统计信息无法跟上批次统计信息。我从未听说过这种行为,也无法解释为什么模型在经过更多时间后会恢复为不良行为,但这是我的最佳解释。
  • 移动平均代码中可能存在一个错误,但是对于其他情况,包括简单的分类任务,它对我来说都非常有效。在提出更简单的示例之前,我不想提出任何问题。

无论如何,我的想法已经用完了,调试周期很长,为此我已经花了太多时间。乐意提供更多详细信息或按需运行实验。也很乐意发布更多代码,尽管我担心这会使更多人害怕。

提前致谢。

Dom*_*ack 1

1e-4使用 Adam 将学习率降低到使用 Momentum 优化器(使用learning_rate=1e-3momentum=0.9)都解决了这个问题。我还发现这篇文章表明该问题跨越多个框架,并且由于优化器和批量归一化之间的相互作用而成为某些网络的未记录的病态。我不认为这是优化器由于学习率太高而未能找到合适的最小值的简单情况(否则训练模式下的性能会很差)。

我希望这可以帮助其他遇到同样问题的人,但我还远远没有满意。我绝对很高兴听到其他解释。