考虑 example code。
我想知道如何在 RNN 上的这个网络上应用梯度裁剪,那里有可能发生梯度爆炸。
tf.clip_by_value(t, clip_value_min, clip_value_max, name=None)
这是一个可以使用的示例,但我在哪里介绍呢?在RNN的def中
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 什么是要剪裁的?
我必须为此定义自己的优化器还是有更简单的选择?
梯度裁剪需要在计算梯度之后,但在应用它们更新模型参数之前进行。在您的示例中,这两件事都由 AdamOptimizer.minimize()
方法处理。
为了剪裁渐变,您需要按照 this section in TensorFlow's API documentation 中的说明显式计算、剪裁和应用它们。具体来说,您需要将对 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)
尽管看起来很流行,但您可能希望通过其全局规范来裁剪整个渐变:
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))
clip_by_global_norm()
的好例子!这在 tensorflow 文档中也被描述为 the correct way to perform gradient clipping
:tensorflow.org/versions/r1.2/api_docs/python/tf/…
tf.global_norm(gradients)
可视化以查看它的通常范围,然后将其剪裁一点以防止异常值扰乱训练。
opt.minimize()
,还是会像其他答案的某些评论中所建议的那样调用诸如 opt.run()
之类的不同名称?
optimizer.minimize(loss)
只是计算和应用梯度的简写。您可以使用 sess.run(optimize)
运行我的答案中的示例。
tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)
,那么您的 optimize
将替换我的 train_op
对吗?现在我的train_op = optimizer.minimize(loss, global_step=global_step))
,所以我正在努力确保我做出相应的调整......
tf.keras 很容易!
optimizer = tf.keras.optimizers.Adam(clipvalue=1.0)
此优化器会将所有渐变裁剪为 [-1.0, 1.0]
之间的值。
请参阅 docs。
optimizer.apply_gradients
,我们需要在调用此方法之前剪切梯度。在这种情况下,我们需要 gradients = [(tf.clip_by_value(grad, -1., 1.), var) for grad, var in gradients]
后跟 .apply_graidents
。
clipnorm
和显然是 global_clipnorm
: optimizer = tf.keras.optimizers.Adam(global_clipnorm=5.0)
这实际上是正确的 explained in the documentation.:
调用 minimize() 负责计算梯度并将它们应用于变量。如果您想在应用梯度之前对其进行处理,您可以分三步使用优化器: 使用 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()
)是 here。
opt.minimize()
,还是会像其他答案的某些评论中所建议的那样调用诸如 opt.run()
之类的不同名称?
opt.apply_gradients(...)
分配给像 train_step
这样的变量(就像您对 opt.minimize()
所做的那样。在您的主循环中,您像往常一样调用它来训练 sess.run([train_step, ...], feed_dict)
tf.clip_by_global_norm(list_of_tensors)
)。
对于那些想了解渐变裁剪(按规范)的想法的人:
每当梯度范数大于特定阈值时,我们都会裁剪梯度范数,使其保持在阈值内。此阈值有时设置为 5
。
设梯度为 g,max_norm_threshold 为 j。
现在,如果 ||g|| > j ,我们这样做:
g = ( j * g ) / ||g||
这是在 tf.clip_by_norm
中完成的实现
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
梯度裁剪基本上有助于在梯度爆炸或消失的情况下。假设你的损失太高,这将导致指数梯度流过网络,这可能会导致 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)
不定期副业成功案例分享
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
,然后优化器的迭代以optimizer.run()
完成,但在这种情况下使用optimizer.run()
似乎不起作用?optimizer.apply_gradients(capped_gvs)
需要分配给某些东西x = optimizer.apply_gradients(capped_gvs)
然后在您的会话中您可以训练为x.run(...)
UserWarning: Converting sparse IndexedSlices to a dense Tensor with 148331760 elements. This may consume a large amount of memory.
所以我的稀疏渐变以某种方式转换为密集。知道如何克服这个问题吗?tf.clip_by_global_norm
,正如@danijar 所建议的那样