Tensorflow:如何在批处理中获得每个实例的渐变?

时间:2017-07-26 10:49:47

标签: python tensorflow

我正在查看此笔记本中的政策渐变示例:https://github.com/ageron/handson-ml/blob/master/16_reinforcement_learning.ipynb

相关代码在这里:

X = tf.placeholder(tf.float32, shape=[None, n_inputs])

hidden = tf.layers.dense(X, n_hidden, activation=tf.nn.elu, kernel_initializer=initializer)
logits = tf.layers.dense(hidden, n_outputs)
outputs = tf.nn.sigmoid(logits)  # probability of action 0 (left)
p_left_and_right = tf.concat(axis=1, values=[outputs, 1 - outputs])
action = tf.multinomial(tf.log(p_left_and_right), num_samples=1)

y = 1. - tf.to_float(action)
cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=logits)
optimizer = tf.train.AdamOptimizer(learning_rate)
grads_and_vars = optimizer.compute_gradients(cross_entropy)
gradients = [grad for grad, variable in grads_and_vars]
gradient_placeholders = []
grads_and_vars_feed = []
for grad, variable in grads_and_vars:
    gradient_placeholder = tf.placeholder(tf.float32, shape=grad.get_shape())
    gradient_placeholders.append(gradient_placeholder)
    grads_and_vars_feed.append((gradient_placeholder, variable))
training_op = optimizer.apply_gradients(grads_and_vars_feed)

...
# Run training over a bunch of instances of inputs
            for step in range(n_max_steps):
                action_val, gradients_val = sess.run([action, gradients], feed_dict={X: obs.reshape(1, n_inputs)})
...
# Then weight each gradient by the action values, average, and feed them back into training_op to apply_gradients()

以上工作正常,因为每个run()都会返回不同的渐变。

我想批处理所有这些,并将输入数组输入run()而不是一次输入一个输入(我的环境与示例中的环境不同,所以对我来说是有意义的批量,并提高性能)。即:

action_val, gradients_val = sess.run([action, gradients], feed_dict={X: obs_array})

其中obs_array的形状为[n_instances, n_inputs]

问题是optimizer.compute_gradients(cross_entropy)似乎返回单个渐变,即使cross_entropy是1d张量的形状[None,1]。 action_val确实返回1d张量的操作,正如预期的那样 - 批处理中每个实例一个操作。

有没有办法让我获得一系列渐变,批量中每个实例一个?

2 个答案:

答案 0 :(得分:2)

  

问题在于optimizer.compute_gradients(cross_entropy)似乎返回单个渐变,即使cross_entropy是形状[None, 1]的1d张量。

这种情况在设计上发生,因为每个张量的梯度项都会自动聚合。根据默认的optimizer.compute_gradients聚合方法,梯度计算操作(例如AddN和低级原语tf.gradients)构成所有梯度操作的总和。对于大多数随机梯度下降的情况,这是好的。

最后,不幸的是,必须在一个批次上进行梯度计算。当然,除非构建自定义渐变函数,否则扩展TensorFlow API以提供没有完全聚合的渐变计算。更改implementation of tf.gradients以执行此操作似乎并不是非常简单。

您可能希望为强化学习模型使用的一个技巧是并行执行多个会话运行。根据{{​​3}},Session API支持多个并发步骤,并将利用现有资源进行并行计算。问题FAQ显示了如何执行此操作。

答案 1 :(得分:0)

我提出的一个弱解决方案是创建一个梯度操作数组,批处理中每个实例一个,然后我可以同时运行所有这些:

X = tf.placeholder(tf.float32, shape=[minibatch_size, n_inputs])

hidden = tf.layers.dense(X, n_hidden, activation=tf.nn.elu, kernel_initializer=initializer)
hidden2 = tf.layers.dense(hidden, n_hidden, activation=tf.nn.elu, kernel_initializer=initializer)
logits = tf.layers.dense(hidden2, n_outputs)
outputs = tf.nn.sigmoid(logits)  # probability of action 0
p_left_and_right = tf.concat(axis=1, values=[outputs, 1 - outputs])
action = tf.multinomial(tf.log(p_left_and_right), num_samples=1)

y = 1. - tf.to_float(action)
cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=logits)
optimizer = tf.train.AdamOptimizer(learning_rate)

# Calculate gradients per batch instance - for minibatch training
batch_gradients = []
for instance_cross_entropy in tf.unstack(cross_entropy):
    instance_grads_and_vars = optimizer.compute_gradients(instance_cross_entropy)
    instance_gradients = [grad for grad, variable in instance_grads_and_vars]
    batch_gradients.append(instance_gradients)

# Calculate gradients for just one instance - for single instance training
grads_and_vars = optimizer.compute_gradients(cross_entropy)
gradients = [grad for grad, variable in grads_and_vars]

# Create gradient placeholders
gradient_placeholders = []
grads_and_vars_feed = []
for grad, variable in grads_and_vars:
    gradient_placeholder = tf.placeholder(tf.float32, shape=grad.get_shape())
    gradient_placeholders.append(gradient_placeholder)
    grads_and_vars_feed.append((gradient_placeholder, variable))

# In the end we only apply a single set of averaged gradients
training_op = optimizer.apply_gradients(grads_and_vars_feed)

...

while step < len(obs_array) - minibatch_size:
    action_array, batch_gradients_array = sess.run([action, batch_gradients], feed_dict={X: obs_array[step:step+minibatch_size]})
    for action_val, gradient in zip(action_array, batch_gradients_array):
    action_vals.append(action_val)
    current_gradients.append(gradient)
    step += minibatch_size

要点是我需要为占位符X指定批量大小,我不能将其保持开放状态,否则unstack不知道有多少元素要取消堆叠。我卸载cross_entropy以获得每个实例的cross_entropy,然后我为每个实例调用compute_gradients。在训练期间,我运行([action,batch_gradients],feed_dict = {X:obs_array [step:step + minibatch_size]}),这为每个批次提供了单独的渐变。

这一切都很好,但它并没有给我带来很大的性能提升。我只获得2倍的最大加速。将批量大小增加到5以上只是线性地缩放run()的运行时间,并且没有增益。

令人遗憾的是,Tensorflow可以快速计算和聚合数百个实例的渐变,但逐个请求渐变的速度要慢得多。可能需要深入挖掘源...