我正在尝试基于流行的mobilenet_v2
的{{3}}来训练张量流模型,并观察到无法解释的行为(我认为)与批量归一化有关。
最初,推理模式下的模型性能有所提高,但经过很长一段时间后才开始产生琐碎的推理(所有接近零)。当在训练模式下运行时,即使在评估数据集上,也能保持良好的性能。批处理归一化衰减/动量速率会影响评估性能。
下面提供了更多详细的实现细节,但是我可能会在文本中迷失大多数人,因此这里有一些图片会让您感兴趣。
下面的曲线来自我在训练时调整了bn_decay
参数的模型。
0-370k:bn_decay=0.997
(默认)
370k-670k:bn_decay=0.9
670k +:bn_decay=0.5
slim implementation (橙色)训练(在训练模式下)和(蓝色)评估(在推理模式下)的损失。低是好的。
我试图提供一个最小的例子来说明问题-在MNIST上进行分类-但失败了(即分类效果很好,并且没有出现我遇到的问题)。对于无法进一步简化工作,我深表歉意。
我的问题是二维姿态估计,目标是高斯人的关节位置。它本质上与语义分段相同,除了不是使用softmax_cross_entropy_with_logits(labels, logits)
而是使用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
我已经尝试过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)
我将tf.train.AdamOptimizer
与learning rate=1e-3
一起使用。
我正在使用tf.estimator.Estimator
API进行培训/评估。
最初的培训进行得很顺利,预期效果会大大提高。这符合我的期望,因为快速训练了最后一层以解释预训练的基本模型输出的高级特征。
但是,很长一段时间(GTX-1070上以batch_size 8进行60k步,〜8小时)后,我的模型在推理模式下开始输出接近零的值(〜1e-11) em>,即is_training=False
。当以* training模式, i.e.
is_training = True`运行时,即使在评估集上,相同的模型也将继续改进。我已经目视验证了。
经过一些实验,我以约370k的步长将bn_decay
(批处理规范化衰减/动量速率)从默认的0.997
更改为0.9
(也尝试了0.99
,但并没有太大的差别),并观察到准确性的即时改善。目视检查推理模式下的推理后,在预期位置的~1e-1
阶推断值中出现了清晰的峰值,这与训练模式下的峰值位置一致(尽管值低得多)。这就是为什么精度会显着提高,但损失(虽然波动较大)却没有太大改善的原因。
经过更多的训练后,这些效果消失了,并恢复为全零推断。
在步骤670k,我进一步将bn_decay
降低到0.5。这导致损失和准确性均得到改善。我可能要等到明天才能看到长期影响。
以下给出了损失和评估指标图。请注意,评估指标基于对数的argmax,high为良好。损失是基于实际值,低是好的。橙色在训练集上使用is_training=True
,而蓝色在评估集上使用is_training=False
。大约8的损耗与所有零输出一致。
is_training=False
运行dropout层),但没有发现任何区别。1.7
到1.10
的所有版本的tensorflow。没什么。bn_decay=0.99
从预先训练的检查点训练了模型。与使用默认bn_decay
的行为相同。tf.layers
API来训练不同的模型,并从头开始训练。他们工作得很好。is_training=True
一起运行时,对评估集做出了明智的推断,包括峰的位置和幅度。slim
,但是除了使用arg_scope
之外,它与我广泛使用的tf.layers
API看起来并没有太大不同。我还可以检查移动平均值,并观察它们随着训练的进行而变化。bn_decay
值 暂时影响了结果。我接受0.5
的值太低了,但是我的想法已经用完了。slim.layers.conv2d
的{{1}}层换成tf.layers.conv2d
(即与默认衰减值一致的动量),并且行为相同。momentum=0.997
框架的最小示例适用于MNIST的分类,而无需修改Estimator
参数。我已经遍历了tensorflow和模型github存储库上的问题,但是除了之外,没有发现其他问题。我目前正在尝试以较低的学习率和更简单的优化器(bn_decay
)进行试验,但这更多是因为我的想法已经用完,而不是因为我认为问题出在这里。
无论如何,我的想法用光了,调试周期很长,为此我已经花了太多时间。乐意提供更多详细信息或按需运行实验。也很高兴发布更多代码,尽管我担心这会吓跑更多人。
谢谢。
答案 0 :(得分:1)
使用Adam将学习率降低到1e-4
,并使用Momentum优化器(使用learning_rate=1e-3
和momentum=0.9
)都解决了此问题。我还发现this post暗示该问题跨越多个框架,并且由于优化程序和批处理规范化之间的相互作用而成为某些网络的未记录病理。我不认为这是简单的情况,因为学习速率过高(否则训练模式下的性能会很差),优化器无法找到合适的最小值。
我希望这可以帮助其他遇到同样问题的人,但距离满足还有很长的路要走。我当然很高兴听到其他解释。