【问题标题】:Multiple steps of gradient descent in single tensorflow sess.run()单个张量流 sess.run() 中的多步梯度下降
【发布时间】:2019-04-09 02:56:01
【问题描述】:

我想对单个 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() 调用的相关问题: Run train op multiple times in tensorflow

【问题讨论】:

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

标签: tensorflow


【解决方案1】:

为了实现这一点,我们可以定义一系列独特的正向反向传播,并在操作之间指定依赖关系,然后tf.group 将它们一起[1] 以在单个会话运行中执行。

我的示例定义了一个感知器层,用于拟合 50 个二维高斯斑点。该代码在 tensorboard 中生成以下图表:

为了测试正确性,我用相同的初始化值训练了两次。第一次使用单个前向反向传播步骤,第二次使用 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 是绝对正确的。没有必要将操作分组在一起。为了达到同样的效果,我们可以只执行具有明确定义的依赖关系的最终优化器步骤。

【讨论】:

  • 这看起来是个不错的解决方案!添加对最近毕业生的依赖以重新计算损失。我认为分组可能没有必要,另一种方法是只评估最终损失(如果您只关心经过那么多步骤后的损失)
猜你喜欢
  • 2017-03-15
  • 1970-01-01
  • 2016-08-20
  • 2013-10-29
  • 2022-09-28
  • 1970-01-01
  • 2017-06-05
  • 2017-11-27
  • 1970-01-01
相关资源
最近更新 更多