【问题标题】:TensorFlow average gradients over several batches多个批次的 TensorFlow 平均梯度
【发布时间】:2018-02-09 17:55:03
【问题描述】:

这可能与Tensorflow: How to get gradients per instance in a batch? 重复。反正我也问了,因为一直没有满意的答案,而且这里的目标有点不一样。

我有一个非常大的网络,可以安装在我的 GPU 上,但我可以提供的最大批量大小是 32。任何比这更大的都会导致 GPU 内存不足。我想使用更大的批次以获得更准确的梯度近似值。

具体而言,假设我想通过依次输入 3 批 32 来计算大量 96 的梯度。我知道的最好方法是使用Optimizer.compute_gradients()Optimizer.apply_gradients()。这是一个如何工作的小例子

import tensorflow as tf
import numpy as np

learn_rate = 0.1

W_init = np.array([ [1,2,3], [4,5,6], [7,8,9] ], dtype=np.float32)
x_init = np.array([ [11,12,13], [14,15,16], [17,18,19] ], dtype=np.float32)

X = tf.placeholder(dtype=np.float32, name="x")
W = tf.Variable(W_init, dtype=np.float32, name="w")
y = tf.matmul(X, W, name="y")
loss = tf.reduce_mean(y, name="loss")

opt = tf.train.GradientDescentOptimizer(learn_rate)
grad_vars_op = opt.compute_gradients(loss)

sess = tf.Session()
sess.run(tf.global_variables_initializer())

# Compute the gradients for each batch
grads_vars1 = sess.run(grad_vars_op, feed_dict = {X: x_init[None,0]})
grads_vars2 = sess.run(grad_vars_op, feed_dict = {X: x_init[None,1]})
grads_vars3 = sess.run(grad_vars_op, feed_dict = {X: x_init[None,2]})

# Separate the gradients from the variables
grads1 = [ grad for grad, var in grads_vars1 ]
grads2 = [ grad for grad, var in grads_vars2 ]
grads3 = [ grad for grad, var in grads_vars3 ]
varl   = [ var  for grad, var in grads_vars1 ]

# Average the gradients
grads  = [ (g1 + g2 + g3)/3 for g1, g2, g3 in zip(grads1, grads2, grads3)]

sess.run(opt.apply_gradients(zip(grads,varl)))

print("Weights after 1 gradient")
print(sess.run(W))

现在这一切都非常丑陋和低效,因为前向传播是在 GPU 上运行的,而平均梯度发生在 CPU 上,然后再次在 GPU 上应用它们。

此外,此代码会引发异常,因为gradsnp.arrays 的列表,要使其正常工作,必须为每个渐变创建一个tf.placeholder

我确定应该有更好、更有效的方法来做到这一点?有什么建议吗?

【问题讨论】:

    标签: machine-learning tensorflow backpropagation gradient-descent tensorflow-gpu


    【解决方案1】:

    您可以创建trainable_variables 的副本并累积批量梯度。这里有几个简单的步骤可以遵循

    ...
    opt = tf.train.GradientDescentOptimizer(learn_rate)
    
    # constant to scale sum of gradient
    const = tf.constant(1/n_batches)
    # get all trainable variables
    t_vars = tf.trainable_variables()
    # create a copy of all trainable variables with `0` as initial values
    accum_tvars = [tf.Variable(tf.zeros_like(tv.initialized_value()),trainable=False) for t_var in t_vars]                                        
    # create a op to initialize all accums vars
    zero_ops = [tv.assign(tf.zeros_like(tv)) for tv in accum_tvars]
    
    # compute gradients for a batch
    batch_grads_vars = opt.compute_gradients(loss, t_vars)
    # collect the (scaled by const) batch gradient into accumulated vars 
    accum_ops = [accum_tvars[i].assign_add(tf.scalar_mul(const, batch_grad_var[0]) for i, batch_grad_var in enumerate(batch_grads_vars)]
    
    # apply accums gradients 
    train_step = opt.apply_gradients([(accum_tvars[i], batch_grad_var[1]) for i, batch_grad_var in enumerate(batch_grads_vars)])
    # train_step = opt.apply_gradients(zip(accum_tvars, zip(*batch_grads_vars)[1])
    
    while True:
       # initialize the accumulated gards
       sess.run(zero_ops)
    
       # number of batches for gradient accumulation 
       n_batches = 3
       for i in xrange(n_batches):
           sess.run(accum_ops, feed_dict={X: x_init[:, i]})
    
       sess.run(train_step)
    

    【讨论】:

    • 不错的解决方案。在 train_step 和 train_step 列表推导中执行 zip 而不是枚举和索引(可能也更具可读性)会稍微更 Pythonic。
    • 确实是不错的解决方案。我是否正确地认为所有操作都将在 GPU 上执行?
    • assign_op 取决于定义变量的位置,cpu/gpu。您可以在 gpus 上计算其余部分。
    • 不错的解决方案!但是看起来应该多一步来平均梯度。
    • 两个相当关键的问题: 1. 这通常不起作用:如果您使用任何作用于批处理的东西(如 BatchNorm),那么它在数学上并不等效。 2. 我根据这个想法写了一些代码,尽管准确地复制了渐变,但它似乎并没有真正起作用。 gist.github.com/Multihuntr/b8cb68316842ff68cab3062740a2a730我认为我没有犯任何逻辑错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-23
    • 1970-01-01
    相关资源
    最近更新 更多