如何在TensorFlow中应用梯度裁剪?


96

考虑示例代码

我想知道如何在RNN上的该网络上应用梯度剪切,而梯度可能会爆炸。

tf.clip_by_value(t, clip_value_min, clip_value_max, name=None)

这是一个可以使用的示例,但是在哪里介绍呢?在RNN中

    lstm_cell = rnn_cell.BasicLSTMCell(n_hidden, forget_bias=1.0)
    # Split data because rnn cell needs a list of inputs for the RNN inner loop
    _X = tf.split(0, n_steps, _X) # n_steps
tf.clip_by_value(_X, -1, 1, name=None)

但这没有意义,因为张量_X是输入,而不是grad,要裁剪的内容是什么?

我是否需要为此定义自己的优化器,还是有一个更简单的选择?

Answers:


143

在计算梯度之后,但在应用梯度更新模型参数之前,需要进行梯度修剪。在您的示例中,这两种AdamOptimizer.minimize()方法均由该方法处理。

为了裁剪您的渐变,您需要按照TensorFlow API文档本节中的说明显式计算,裁剪和应用它们。具体来说,您需要minimize()用以下类似的方法代替对方法的调用:

optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
gvs = optimizer.compute_gradients(cost)
capped_gvs = [(tf.clip_by_value(grad, -1., 1.), var) for grad, var in gvs]
train_op = optimizer.apply_gradients(capped_gvs)

4
Styrke,感谢您的帖子。您是否知道下一步实际上要运行优化程序的迭代?通常,将优化器实例化为optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) ,然后对优化器进行迭代,optimizer.run()optimizer.run()在这种情况下使用似乎无效?
applecider

6
好的optimizer.apply_gradients(capped_gvs),需要将其分配给某些东西,x = optimizer.apply_gradients(capped_gvs)然后在您的课程中您可以训练为x.run(...)
applecider 16'Apr

3
向@ remi-cuingnet喊出漂亮的编辑建议。(不幸的是,这被草率的评论者拒绝了)
Styrke,2016年

这给了我UserWarning: Converting sparse IndexedSlices to a dense Tensor with 148331760 elements. This may consume a large amount of memory.某种方式,我的稀疏渐变被转换为密集。任何想法如何克服这个问题?
佩卡

8
实际上,以夹梯度(根据tensorflow文档,计算机科学家和逻辑)以正确的方式是tf.clip_by_global_norm,如通过@danijar建议
gdelab

116

尽管看起来很流行,但您可能希望通过其全局范数来裁剪整个渐变:

optimizer = tf.train.AdamOptimizer(1e-3)
gradients, variables = zip(*optimizer.compute_gradients(loss))
gradients, _ = tf.clip_by_global_norm(gradients, 5.0)
optimize = optimizer.apply_gradients(zip(gradients, variables))

分别裁剪每个渐变矩阵会更改其相对比例,但是也可以:

optimizer = tf.train.AdamOptimizer(1e-3)
gradients, variables = zip(*optimizer.compute_gradients(loss))
gradients = [
    None if gradient is None else tf.clip_by_norm(gradient, 5.0)
    for gradient in gradients]
optimize = optimizer.apply_gradients(zip(gradients, variables))

在TensorFlow 2中,磁带计算梯度,优化器来自Keras,我们不需要存储更新操作,因为它会自动运行而不将其传递给会话:

optimizer = tf.keras.optimizers.Adam(1e-3)
# ...
with tf.GradientTape() as tape:
  loss = ...
variables = ...
gradients = tape.gradient(loss, variables)
gradients, _ = tf.clip_by_global_norm(gradients, 5.0)
optimizer.apply_gradients(zip(gradients, variables))

10
很好的例子clip_by_global_norm()the correct way to perform gradient clipping在tensorflow文档中也对此进行了描述:tensorflow.org/versions/r1.2/api_docs/python/tf/…–
MZHm

9
@Escachator这是经验性的,将取决于您的模型以及可能的任务。我要做的是将梯度范数可视化tf.global_norm(gradients)以查看其正常范围,然后在该范围之上进行一些裁剪以防止离群值干扰训练。
danijar '17

1
您会继续打电话opt.minimize()还是会打电话给opt.run()其他答案,例如其他答案中的一些建议?
reese0106

3
@ reese0106不,optimizer.minimize(loss)只是计算和应用渐变的简写。您可以使用在我的答案中运行示例sess.run(optimize)
danijar

1
因此,如果我tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)在实验功能中使用,那么您optimize将替换我的train_op正确代码吗?现在是我的,train_op = optimizer.minimize(loss, global_step=global_step))所以我试图确保我进行相应的调整...
reese0106 '18

10

实际上在文档中对此做了正确解释。

调用minimum()既要计算梯度,又要将其应用于变量。如果要在应用渐变之前对其进行处理,则可以分三步使用优化器:

  • 使用compute_gradients()计算梯度。
  • 根据需要处理渐变。
  • 使用apply_gradients()应用处理后的渐变。

在他们提供的示例中,他们使用以下3个步骤:

# Create an optimizer.
opt = GradientDescentOptimizer(learning_rate=0.1)

# Compute the gradients for a list of variables.
grads_and_vars = opt.compute_gradients(loss, <list of variables>)

# grads_and_vars is a list of tuples (gradient, variable).  Do whatever you
# need to the 'gradient' part, for example cap them, etc.
capped_grads_and_vars = [(MyCapper(gv[0]), gv[1]) for gv in grads_and_vars]

# Ask the optimizer to apply the capped gradients.
opt.apply_gradients(capped_grads_and_vars)

MyCapper是限制渐变的任何函数。有用的功能列表(除外tf.clip_by_value())在此处


您会继续打电话opt.minimize()还是会打电话给opt.run()其他答案,例如其他答案中的一些建议?
reese0106 '18

@ reese0106不,您需要将其分配opt.apply_gradients(...)给一个变量train_step,例如(就像您想要的那样opt.minimize()。)在主循环中,您像往常一样将其称为训练sess.run([train_step, ...], feed_dict)
dsalaj

请记住,梯度定义为模型中所有参数的损耗导数的向量。TensorFlow将其表示为Python列表,其中包含每个变量及其渐变的元组。这意味着要裁剪梯度范数,就不能单独裁剪每个张量,需要立即考虑列表(例如,使用tf.clip_by_global_norm(list_of_tensors))。
danijar

8

对于那些想了解梯度裁剪的想法(按规范)的人:

每当梯度范数大于特定阈值时,我们都会修剪梯度范数,以使其保持在阈值之内。此阈值有时设置为5

令梯度为g,max_norm_threshold为j

现在,如果|| g || > j,我们这样做:

g =( j * g)/ || G ||

这是在 tf.clip_by_norm


如果我需要手动选择阈值,是否有任何常用方法可以做到这一点?
ningyuwhut

这是某些论文中提出的一种黑魔法。否则,您必须进行大量实验,然后找出哪个更有效。
kmario23

4

IMO最好的解决方案是用TF的估算器装饰器包装优化器tf.contrib.estimator.clip_gradients_by_norm

original_optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
optimizer = tf.contrib.estimator.clip_gradients_by_norm(original_optimizer, clip_norm=5.0)
train_op = optimizer.minimize(loss)

这样,您只需要定义一次,而不必在每次梯度计算后运行它。

文档:https : //www.tensorflow.org/api_docs/python/tf/contrib/estimator/clip_gradients_by_norm


2

梯度修剪基本上可以在梯度爆炸或消失的情况下起到帮助作用。说您的损失太高,将会导致指数梯度流经网络,可能导致Nan值。为了克服这个问题,我们将梯度裁剪在特定范围内(-1到1或根据条件的任何范围)。

clipped_value=tf.clip_by_value(grad, -range, +range), var) for grad, var in grads_and_vars

其中grads _and_vars是渐变对(您可以通过tf.compute_gradients计算)及其变量。

裁剪后,我们只需使用优化器即可应用其值。 optimizer.apply_gradients(clipped_value)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.