在单个tensorflow sess.run()中使用多个梯度下降步骤

3
我想在单个sess.run()调用中执行多步梯度下降。每次调用的输入是固定的,因此我只需要传递一次。
我该如何做呢?我有一个想法,但我不确定它是否会在每个步骤重新计算梯度(而是应用第一个梯度N次)。我希望避免多次调用tf.gradients()。将grads_and_vars包含在依赖项中是否足够?
N=5
fit_op_i = fit_op_0 = optimizer.apply_gradients(grads_and_vars)
for i in range(N):
    with tf.control_dependencies([fit_op_i]):
        fit_op_i = optimizer.apply_gradients(grads_and_vars)
fit_op_N = fit_op_i

与需要多次调用sess.run()的答案相关的问题: 在TensorFlow中多次运行train op


1
既然所有的梯度都只是向量,为什么不将它们相加以获得参数的最终更新,并执行 optimizer.apply_gradients 呢? - Jie.Zhou
1
@Jie.Zhou 梯度向量是局部于参数空间中的位置的。它们必须通过在参数空间中的不同点评估梯度来获得。我同意你可以添加它们,但需要一些设置来评估空间中不同点处的梯度以获取每个梯度分量。 - eqzx
2
这还不够。中间张量的值将被重复使用,因此您需要将整个计算图复制N次,并添加足够的控制依赖项以确保所有有效的执行顺序与您的期望相匹配。 - Yaroslav Bulatov
1个回答

3
为了实现这一点,我们可以定义一系列唯一的前向-后向传递过程,并指定操作之间的依赖关系,然后将它们[1]tf.group组合在一起,在单个会话运行中执行。
我的示例为拟合50个二维高斯斑点定义了一个感知器层。该代码在tensorboard中生成以下图形: enter image description here 为了测试正确性,我使用相同的初始化值进行了两次训练。第一次使用单个前向-后向传递步骤,第二次使用3个步骤作为单个操作组合:
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    for i in range(12):
        loss_val = loss_op.eval(feed_dict={x:x_train, y:y_train})
        print(i, '-->', "{0:.3f}".format(loss_val))
        _ = sess.run(train_op, feed_dict={x:x_train, y:y_train})
        # loss_val = loss_op.eval(feed_dict={x:x_train, y:y_train})
        # print(i, '-->', "{0:.3f}".format(loss_val))
        # _ = sess.run(applied_grads, feed_dict={x:x_train, y:y_train})
# 3-steps     # 1-step    
# 0 --> 0.693 # 0 --> 0.693 ---
# 1 --> 0.665 # 1 --> 0.683
# 2 --> 0.638 # 2 --> 0.674
# 3 --> 0.613 # 3 --> 0.665 ---
# 4 --> 0.589 # 4 --> 0.656
# 5 --> 0.567 # 5 --> 0.647
# 6 --> 0.547 # 6 --> 0.638 ---
# 7 --> 0.527 # 7 --> 0.630
# 8 --> 0.509 # 8 --> 0.622
# 9 --> 0.492 # 9 --> 0.613 ---
# ...

这显然对应着3个步骤。 完整示例:

from sklearn.datasets import make_blobs
import tensorflow as tf
import numpy as np
tf.reset_default_graph()

times_to_apply = 3 # number of steps to perform

with tf.name_scope('x'):
    x = tf.placeholder(tf.float32, shape=(None, 2))
with tf.name_scope('y'):
    y = tf.placeholder(tf.int32, shape=(50))

logits = tf.layers.dense(inputs=x,
                         units=2,
                         name='NN',
                         kernel_initializer=tf.initializers.ones,
                         bias_initializer=tf.initializers.zeros)

optimizer = tf.train.GradientDescentOptimizer(0.01)


with tf.name_scope('loss-step-1'):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss_op = tf.reduce_mean(xentropy)

with tf.name_scope('optimizer-step-1'):
    grads_and_vars = optimizer.compute_gradients(loss_op)
    applied_grads = optimizer.apply_gradients(grads_and_vars)

all_grads_and_vars = [grads_and_vars]
all_applied_grads = [applied_grads]
all_loss_ops = [loss_op]

for i in range(times_to_apply - 1):
    with tf.control_dependencies([all_applied_grads[-1]]):
        with tf.name_scope('loss-step-' + str(i + 2)):
            xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
            all_loss_ops.append(tf.reduce_mean(xentropy))
    with tf.control_dependencies([all_loss_ops[-1]]):
        with tf.name_scope('optimizer-step-' + str(i + 2)):
           all_grads_and_vars.append(optimizer.compute_gradients(all_loss_ops[-1]))
           all_applied_grads.append(optimizer.apply_gradients(all_grads_and_vars[-1]))

train_op = tf.group(all_applied_grads)

[1] @eqzx是完全正确的。没有必要将操作分组在一起。为了达到相同的效果,我们可以仅执行最终优化器步骤,并使用明确定义的依赖项。


这看起来是一个不错的解决方案!添加最近毕业生的依赖项以重新计算损失。我认为组可能并不必要,另一种方法是仅评估最终损失(如果您只关心那么多步骤后的损失)。 - eqzx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接