【问题标题】:How to update model parameters with accumulated gradients?如何使用累积梯度更新模型参数?
【发布时间】:2017-02-10 10:23:40
【问题描述】:

我正在使用 TensorFlow 构建深度学习模型。 TensorFlow 的新手。

由于某种原因,我的模型的批大小有限,那么这个有限的批大小会使模型具有很高的方差。

所以,我想使用一些技巧来增大批量大小。我的想法是存储每个 mini-batch 的梯度,例如 64 个 mini-batch,然后将梯度相加,使用这 64 个 mini-batch 训练数据的平均梯度来更新模型的参数。

这意味着对于前 63 个 mini-batch,不更新参数,在第 64 个 mini-batch 之后,只更新一次模型的参数。

但由于 TensorFlow 是基于图形的,有谁知道如何实现这个想要的功能?

非常感谢。

【问题讨论】:

  • sync replicas optimizer 是您要找的吗?
  • 看来我可以存储所有中间梯度,然后计算梯度的平均值,然后更新模型参数。
  • 同步副本优化器似乎适用于多个 GPU 并行训练。我会研究它,看看我是否可以利用它。

标签: python tensorflow gradient


【解决方案1】:

我在这里找到了解决方案:https://github.com/tensorflow/tensorflow/issues/3994#event-766328647

opt = tf.train.AdamOptimizer()
tvs = tf.trainable_variables()
accum_vars = [tf.Variable(tf.zeros_like(tv.initialized_value()), trainable=False) for tv in tvs]                                        
zero_ops = [tv.assign(tf.zeros_like(tv)) for tv in accum_vars]
gvs = opt.compute_gradients(rmse, tvs)
accum_ops = [accum_vars[i].assign_add(gv[0]) for i, gv in enumerate(gvs)]
train_step = opt.apply_gradients([(accum_vars[i], gv[1]) for i, gv in enumerate(gvs)])

在训练循环中:

while True:
    sess.run(zero_ops)
    for i in xrange(n_minibatches):
        sess.run(accum_ops, feed_dict=dict(X: Xs[i], y: ys[i]))
    sess.run(train_step)

但是这段代码看起来不是很干净漂亮,有人知道如何优化这些代码吗?

【讨论】:

  • 在 keras 中可以吗?
  • 所以您将sess.run(train_step) 留在了循环之外。所以这意味着在计算最后一批的梯度之后会发生权重更新,对吗?如果我们把它放在循环中,它会在每个 epoch 之后发生,对吧?
【解决方案2】:

我有同样的问题,只是想通了。

首先获取符号梯度,然后将累积梯度定义为 tf.Variables。 (似乎tf.global_variables_initializer() 必须在定义grads_accum 之前运行。否则我会出错,不知道为什么。)

tvars = tf.trainable_variables()
optimizer = tf.train.GradientDescentOptimizer(lr)
grads = tf.gradients(cost, tvars)

# initialize
tf.local_variables_initializer().run()
tf.global_variables_initializer().run()

grads_accum = [tf.Variable(tf.zeros_like(v)) for v in grads] 
update_op = optimizer.apply_gradients(zip(grads_accum, tvars)) 

在训练中,您可以在每批累积梯度(保存在gradients_accum),并在运行第 64 批后更新模型:

feed_dict = dict()
for i, _grads in enumerate(gradients_accum):
    feed_dict[grads_accum[i]] = _grads
sess.run(fetches=[update_op], feed_dict=feed_dict) 

你可以参考tensorflow/tensorflow/python/training/optimizer_test.py的用法,特别是这个函数:testGradientsAsVariables()

希望对您有所帮助。

【讨论】:

  • 我认为这段代码与问题无关。在什么时候总结了grandients,即累积?此外,在您引用的示例中,梯度不会累积;它们是根据 w.r.t 计算的。到两个独立的输入。
【解决方案3】:

之前的解决方案不计算累积梯度的平均值,这可能会导致训练不稳定。我已经修改了上面的代码,应该可以解决这个问题。

# Fetch a list of our network's trainable parameters.
trainable_vars = tf.trainable_variables()

# Create variables to store accumulated gradients
accumulators = [
    tf.Variable(
        tf.zeros_like(tv.initialized_value()),
        trainable=False
    ) for tv in trainable_vars
]

# Create a variable for counting the number of accumulations
accumulation_counter = tf.Variable(0.0, trainable=False)

# Compute gradients; grad_pairs contains (gradient, variable) pairs
grad_pairs = optimizer.compute_gradients(loss, trainable_vars)

# Create operations which add a variable's gradient to its accumulator.
accumulate_ops = [
    accumulator.assign_add(
        grad
    ) for (accumulator, (grad, var)) in zip(accumulators, grad_pairs)
]

# The final accumulation operation is to increment the counter
accumulate_ops.append(accumulation_counter.assign_add(1.0))

# Update trainable variables by applying the accumulated gradients
# divided by the counter. Note: apply_gradients takes in a list of 
# (grad, var) pairs
train_step = optimizer.apply_gradients(
    [(accumulator / accumulation_counter, var) \
        for (accumulator, (grad, var)) in zip(accumulators, grad_pairs)]
)

# Accumulators must be zeroed once the accumulated gradient is applied.
zero_ops = [
    accumulator.assign(
        tf.zeros_like(tv)
    ) for (accumulator, tv) in zip(accumulators, trainable_vars)
]

# Add one last op for zeroing the counter
zero_ops.append(accumulation_counter.assign(0.0))

此代码的使用方式与@weixsong 提供的方式相同。

【讨论】:

    【解决方案4】:

    如果我不在 sess.run(train_step) 中再次提供 feed_dict,您发布的方法似乎会失败。我不知道为什么需要 feed_dict,但有可能再次运行所有累加器,并重复最后一个示例。在我的情况下,这是我必须做的:

                self.session.run(zero_ops)
                for i in range(0, mini_batch):
    
                    self.session.run(accum_ops, feed_dict={self.ph_X: imgs_feed[np.newaxis, i, :, :, :], self.ph_Y: flow_labels[np.newaxis, i, :, :, :], self.keep_prob: self.dropout})
    
                self.session.run(norm_acums, feed_dict={self.ph_X: imgs_feed[np.newaxis, i, :, :, :], self.ph_Y: flow_labels[np.newaxis, i, :, :, :], self.keep_prob: self.dropout})
                self.session.run(train_op, feed_dict={self.ph_X: imgs_feed[np.newaxis, i, :, :, :], self.ph_Y: flow_labels[np.newaxis, i, :, :, :], self.keep_prob: self.dropout})
    

    为了标准化梯度,我知道这只是将累积的梯度除以批量大小,所以我只添加一个新的操作

    norm_accums = [accum_op/float(batchsize) for accum_op in accum_ops]
    

    有人遇到过同样的 feed_dict 问题吗?

    *更新 正如我所怀疑的那样,它使用批处理中的最后一个示例再次运行所有图。 这个小代码测试一下

    import numpy as np
    import tensorflow as tf
    ph = tf.placeholder(dtype=tf.float32, shape=[])
    var_accum = tf.get_variable("acum", shape=[], 
    initializer=tf.zeros_initializer())
    acum = tf.assign_add(var_accum, ph)
    divide = acum/5.0
    init = tf.global_variables_initializer()
        with tf.Session() as sess:
        sess.run(init)
        for i in range(5):
             sess.run(acum, feed_dict={ph: 2.0})
    
    c = sess.run([divide], feed_dict={ph: 2.0})
    #10/5 = 2
    print(c)
    #but it gives 2.4, that is 12/5, so sums one more time
    

    我想出了如何解决这个问题。所以,张量流有条件操作。我放 一个分支中的累积和另一个分支中的归一化和更新的最后累积。我的代码是一团糟,但是为了快速检查我说我让一个使用示例的小代码。

    import numpy as np
    import tensorflow as tf
    
    ph = tf.placeholder(dtype=tf.float32, shape=[])
    #placeholder for conditional braching in the graph
    condph = tf.placeholder(dtype=tf.bool, shape=[])
    
    var_accum = tf.get_variable("acum", shape=[], initializer=tf.zeros_initializer())
    
    accum_op = tf.assign_add(var_accum, ph)
    
    #function when condition of condph is True
    def truefn():
       return accum_op
    #function when condtion of condph is False
    def falsefn():
       div = accum_op/5.0
       return div
    
    #return the conditional operation
    cond = tf.cond(condph, truefn, falsefn)
    
    init = tf.global_variables_initializer()
    
    with tf.Session() as sess:
       sess.run(init)
       for i in range(4):
           #run only accumulation
           sess.run(cond, feed_dict={ph: 2.0, condph: True})
       #run acumulation and divition
       c = sess.run(cond, feed_dict={ph: 2.0, condph: False})
    
    print(c)
    #now gives 2
    

    *重要提示:忘记一切都不起作用。优化器放弃了失败。

    【讨论】:

      【解决方案5】:

      Tensorflow 2.0 Compatible Answer:根据上面weixsong的Answer和Tensorflow Website中的解释,下面提到的是Tensorflow 2.0版本中累积梯度的代码:

      def train(epochs):
        for epoch in range(epochs):
          for (batch, (images, labels)) in enumerate(dataset):
             with tf.GradientTape() as tape:
              logits = mnist_model(images, training=True)
              tvs = mnist_model.trainable_variables
              accum_vars = [tf.Variable(tf.zeros_like(tv.initialized_value()), trainable=False) for tv in tvs]
              zero_ops = [tv.assign(tf.zeros_like(tv)) for tv in accum_vars]
              loss_value = loss_object(labels, logits)
      
             loss_history.append(loss_value.numpy().mean())
             grads = tape.gradient(loss_value, tvs)
             #print(grads[0].shape)
             #print(accum_vars[0].shape)
             accum_ops = [accum_vars[i].assign_add(grad) for i, grad in enumerate(grads)]
      
      
      
          optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables))
          print ('Epoch {} finished'.format(epoch))
      
      # call the above function    
      train(epochs = 3)
      

      完整的代码可以在这个Github Gist找到。

      【讨论】:

        【解决方案6】:

        您可以使用 Pytorch 代替 Tensorflow,因为它允许用户在训练期间累积梯度

        【讨论】:

          猜你喜欢
          • 2019-04-19
          • 2021-09-02
          • 1970-01-01
          • 1970-01-01
          • 2021-01-04
          • 2018-03-28
          • 2021-09-26
          • 2016-10-09
          • 2020-09-15
          相关资源
          最近更新 更多