问题:如何在TensorFlow中应用梯度裁剪?
考虑示例代码。
我想知道如何在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,要裁剪的内容是什么?
我是否需要为此定义自己的优化器,还是有一个更简单的选择?
回答 0
在计算梯度之后,但在应用梯度更新模型参数之前,需要进行梯度修剪。在您的示例中,这两种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)
回答 1
尽管看起来很流行,但您可能希望通过其全局范数来裁剪整个渐变:
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))
回答 2
调用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()
)在此处。
回答 3
对于那些想了解梯度裁剪的想法(按规范)的人:
每当梯度范数大于特定阈值时,我们都会修剪梯度范数,以使其保持在阈值之内。此阈值有时设置为5
。
令梯度为g,max_norm_threshold为j。
现在,如果|| g || > j,我们这样做:
g =( j * g)/ || G ||
这是在 tf.clip_by_norm
回答 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
回答 5
梯度修剪基本上可以在梯度爆炸或消失的情况下起到帮助作用。说您的损失太高,将会导致指数梯度流经网络,可能导致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)