【问题标题】:How to feed the list of gradients, or (grad, variable name) pairs, to my model如何将梯度列表或(梯度,变量名)对提供给我的模型
【发布时间】:2018-04-24 21:51:26
【问题描述】:

这与上一个问题有关:How to partition a single batch into many invocations to save memory,也与How to train a big model with relatively large batch size on a single GPU using Tensorflow?有关;但是,我仍然找不到确切的答案。例如,另一个相关问题tensorflow - run optimizer op on a large batch 的答案对我不起作用(顺便说一句。它没有被接受并且那里没有更多的 cmets)。

我想尝试模拟更大的批量,但只使用一个 GPU。 所以,我需要计算每个较小批次的梯度,在几个这样的较小批次中聚合/平均它们,然后再应用。

(基本上,它就像同步分布式 SGD,但在单个设备/GPU 上,串行执行。当然,分布式 SGD 的加速优势会丢失,但更大的批量大小本身可能会收敛到更大的精度和更大的步长,正如最近的几篇论文所指出的那样。)

为了保持较低的内存需求,我应该使用小批量执行标准 SGD,在每次迭代后更新梯度,然后调用 optimizer.apply_gradients()(其中 optimizer 是已实现的优化器之一)。

所以,一切看起来都很简单,但当我去实现它时,它实际上并不是那么微不足道。

例如,我想使用一个Graph,计算每次迭代的梯度,然后在处理多个批次时,对梯度求和并将它们传递给我的模型。但是列表本身不能输入到sess.runfeed_dict 参数中。另外,直接传递梯度并不完全有效,我得到了TypeError: unhashable type: 'numpy.ndarray'(我认为原因是我不能传递numpy.ndarray,只有tensorflow变量)。 我可以为渐变定义一个占位符,但为此我需要先构建模型(以指定可训练变量等)。

总而言之,请告诉我有一个更简单的方法来实现它。

【问题讨论】:

    标签: tensorflow


    【解决方案1】:

    没有比你已经被告知的更简单的方法了。这种方式一开始可能看起来很复杂,但实际上非常简单。您只需使用低级 API 手动计算每个批次的梯度,对它们进行平均,然后手动将平均梯度提供给优化器以应用它们。

    我将尝试提供一些精简的代码来说明如何做到这一点。我将使用点作为实际代码的占位符,这取决于问题。你通常会做的事情是这样的:

    import tensorflow as tf
    [...]
    input = tf.placeholder(...)
    [...]
    loss = ...
    [...]
    # initialize the optimizer
    optimizer = tf.train.AdamOptimizer(LEARNING_RATE)
    # define operation to apply the gradients
    minimize = optimizer.minimize(loss)
    [...]
    if __name__ == '__main__':
        session = tf.Session(config=CONFIG)
        session.run(tf.global_variables_initializer())
        for step in range(1, MAX_STEPS + 1):
            data = ...
            loss = session.run([minimize, loss],
                               feed_dict={input: data})[1]
    

    您现在想要做的是,对多个批次进行平均以保留内存是这样的:

    import tensorflow as tf
    [...]
    input = tf.placeholder(...)
    [...]
    loss = ...
    [...]
    # initialize the optimizer
    optimizer = tf.train.AdamOptimizer(LEARNING_RATE)
    
    # grab all trainable variables
    trainable_variables = tf.trainable_variables()
    
    # define variables to save the gradients in each batch
    accumulated_gradients = [tf.Variable(tf.zeros_like(tv.initialized_value()),
                                         trainable=False) for tv in
                             trainable_variables]
    
    # define operation to reset the accumulated gradients to zero
    reset_gradients = [gradient.assign(tf.zeros_like(gradient)) for gradient in
                       accumulated_gradients]
    
    # compute the gradients
    gradients = optimizer.compute_gradients(loss, trainable_variables)
    
    # Note: Gradients is a list of tuples containing the gradient and the
    # corresponding variable so gradient[0] is the actual gradient. Also divide
    # the gradients by BATCHES_PER_STEP so the learning rate still refers to
    # steps not batches.
    
    # define operation to evaluate a batch and accumulate the gradients
    evaluate_batch = [
        accumulated_gradient.assign_add(gradient[0]/BATCHES_PER_STEP)
        for accumulated_gradient, gradient in zip(accumulated_gradients,
                                                  gradients)]
    
    # define operation to apply the gradients
    apply_gradients = optimizer.apply_gradients([
        (accumulated_gradient, gradient[1]) for accumulated_gradient, gradient
        in zip(accumulated_gradients, gradients)])
    
    # define variable and operations to track the average batch loss
    average_loss = tf.Variable(0., trainable=False)
    update_loss = average_loss.assign_add(loss/BATCHES_PER_STEP)
    reset_loss = average_loss.assign(0.)
    [...]
    if __name__ == '__main__':
        session = tf.Session(config=CONFIG)
        session.run(tf.global_variables_initializer())
    
        data = [batch_data[i] for i in range(BATCHES_PER_STEP)]
        for batch_data in data:
            session.run([evaluate_batch, update_loss],
                        feed_dict={input: batch_data})
    
        # apply accumulated gradients
        session.run(apply_gradients)
    
        # get loss
        loss = session.run(average_loss)
    
        # reset variables for next step
        session.run([reset_gradients, reset_loss])
    

    如果您填补空白,这应该可以运行。但是,我可能在将其剥离并粘贴到此处时犯了错误。对于一个可运行的示例,您可以查看 project 我目前正在自己​​研究。

    我还想明确指出,这与一次评估所有批次数据的损失不同,因为您对梯度进行平均。当您的损失在低统计数据下效果不佳时,这一点尤其重要。以直方图的卡方为例,计算具有低 bin 计数的卡方直方图的平均梯度,不如只计算一个所有 bin 一次填满的直方图的梯度。

    【讨论】:

    • 谢谢。是的,我想评估整个(更大)批次的损失和梯度。因此,据我了解,这实际上并没有模拟同步 SGD。我有超过 200 个班级,所以我猜较小的 mini-batches(如 16 或 32 个样本)明显是非 i.i.d。这是我想使用更大批量大小的原因之一。
    • 对不起,我不明白你的意思。我认为这正是您所要求的。
    【解决方案2】:

    您需要将渐变作为传递给apply_gradients 的值。它可以是占位符,但使用通常的compute_gradients/apply_gradients 组合可能更容易:

    # Some loss measure
    loss = ...
    optimizer = ...
    gradients = optimizer.compute_gradients(loss)
    # gradients is a list of pairs
    _, gradient_tensors = zip(*gradients)
    # Apply gradients as usual
    train_op = optimizer.apply_gradients(gradients)
    
    # On training
    # Compute some gradients
    gradient_values = session.run(gradient_tensors, feed_dict={...})
    # gradient_values is a sequence of numpy arrays with gradients
    
    # After averaging multiple evaluations of gradient_values apply them
    session.run(train_op, feed_dict=dict(zip(gradient_tensors, gradient_values_average)))
    

    如果您也想在 TensorFlow 中计算梯度的平均值,则需要一些专门为此的额外代码,可能是这样的:

    # Some loss measure
    loss = ...
    optimizer = ...
    gradients = optimizer.compute_gradients(loss)
    # gradients is a list of pairs
    _, gradient_tensors = zip(*gradients)
    # Apply gradients as usual
    train_op = optimizer.apply_gradients(gradients)
    
    # Additional operations for gradient averaging
    gradient_placeholders = [tf.placeholder(t.dtype, (None,) + t.shape)
                             for t in gradient_tensors]
    gradient_averages = [tf.reduce_mean(p, axis=0) for p in gradient_placeholders]
    
    # On training
    gradient_values = None
    # Compute some gradients
    for ...:  # Repeat for each small batch
        gradient_values_current = session.run(gradient_tensors, feed_dict={...})
        if gradient_values is None:
            gradient_values = [[g] for g in gradient_values_current]
        else:
            for g_list, g in zip(gradient_values, gradient_values_current):
                g_list.append(g)
    # Stack gradients
    gradient_values = [np.stack(g_list) for g_list in gradient_values)
    # Compute averages
    gradient_values_average = session.run(
        gradient_averages, feed_dict=dict(zip(gradient_placeholders, gradient_values)))
    
    # After averaging multiple gradients apply them
    session.run(train_op, feed_dict=dict(zip(gradient_tensors, gradient_values_average)))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-01
      • 2018-08-23
      • 1970-01-01
      • 1970-01-01
      • 2021-09-11
      • 1970-01-01
      • 2020-10-31
      相关资源
      最近更新 更多