当我这样创建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)

08-20 00:12