当我这样创建train_op
时:
train_op = tf.contrib.layers.optimize_loss(
loss=loss,
global_step=tf.contrib.framework.get_global_step(),
learning_rate=params['learning_rate'],
optimizer='Adam'
)
我得到了一个在验证和测试集上表现良好的工作网络。
如果我只是使用
minimize()
这样的方法:optimizer = tf.train.AdamOptimizer(learning_rate=params['learning_rate'])
train_op = optimizer.minimize(
loss=loss,
global_step=tf.train.get_global_step()
)
即使经过1000个步骤后的首次验证,我也会得到更差的结果(准确性,查全率,损失),并且过一会儿似乎完全过拟合(验证损失或多或少是恒定的,并且是火车损失的100倍,但准确性和查全率)崩溃)
我创建了一个contrib的清理版本的函数,该函数在两个标记的地方与笔直的Optimizer.minimize()不同:
def make_train_op(loss, optimizer, global_step):
with tf.variable_scope(None, "OptimizeLoss", [loss, global_step]):
# ==========================================
# this part is extra comparing to minimize()
update_ops = set(tf.get_collection(tf.GraphKeys.UPDATE_OPS))
if update_ops:
with tf.control_dependencies([update_ops]):
loss = tf.identity(loss)
# ==========================================
gradients = optimizer.calculate_gradients(
loss,
tf.trainable_variables()
)
grad_updates = optimizer.apply_gradients(
gradients,
global_step=global_step,
name="train")
# ==========================================
# so is this one
with tf.control_dependencies([grad_updates]):
train_op = tf.identity(loss)
# ==========================================
return train_op
验证再次执行良好。在所有情况下,训练看起来都差不多(健康)。网络是相对简单的CNN / batchnorm / dropout / maxpool混合,具有交叉熵损失。
我的理解方式是,图形中的某些操作不会显示为损失的依存关系,但需要计算梯度。这怎么可能呢?如果这是正常情况,那么为什么这两个摘要不是核心部分?在构建模型时是否应该做一些不同的事情,以避免需要这种依赖强制?
最佳答案
问题与batchnorm更新操作有关,实际上是documented:
注意:训练时,需要更新moving_mean和moving_variance。默认情况下,更新操作位于tf.GraphKeys.UPDATE_OPS中,因此需要将它们作为依赖项添加到train_op中。例如:
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
train_op = optimizer.minimize(loss)