如何在 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)

But this doesn't make sense as the tensor _X is the input and not the grad what is to be clipped?

我必须为此定义自己的优化器,还是有一个更简单的选项?

93321 次浏览

梯度裁剪需要发生在计算梯度之后,但在应用它们来更新模型的参数之前。在您的示例中,这两件事都由 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)

尽管看起来很流行,你可能还是想用它的全球标准来修剪整个梯度:

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))

这实际上是正确的 在文件中解释。:

最小化()调用同时负责计算梯度和 如果你想处理渐变 在应用它们之前,你可以通过以下三个步骤来使用优化器:

  • 使用 computer _ gradients ()计算渐变。
  • 按照您的意愿处理渐变。
  • 使用 application _ gradients ()应用处理过的渐变。

在这个例子中,他们使用了以下三个步骤:

# 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())是 给你

对于那些想要理解渐变剪切(按规范)的想法的人:

当梯度范数大于特定阈值时,我们剪切梯度范数,使其保持在阈值内。这个阈值有时被设置为 5

设梯度为 ,max _ norm- 阈值为 J

现在,如果我们做到了:

G = (J * )/| | | |

This is the implementation done in tf.clip_by_norm

梯度裁剪基本上有助于在爆炸或消失的情况下梯度。假设你的损失太高,这将导致指数梯度流过网络,这可能会导致 Nan 值。为了克服这一点,我们剪辑在一个特定的范围内的梯度(-1至1或任何范围根据条件)。

Clipping _ value = tf.clip _ by _ value (grad,-range,+ range) ,var)表示 grad,在 grads _ 和 _ vars 中表示 var

where grads _and_vars are the pairs of gradients (which you calculate via tf.compute_gradients) and their variables they will be applied to.

剪切之后,我们只需使用优化器应用它的值。 optimizer.apply_gradients(clipped_value)

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

对 TF.Keras 来说很简单!

optimizer = tf.keras.optimizers.Adam(clipvalue=1.0)

This optimizer will clip all gradients to values between [-1.0, 1.0].

看看 医生

Method 1

if you are training your model using your custom training loop then the one update step will look like

'''
for loop over full dataset
X -> training samples
y -> labels
'''
optimizer = tf.keras.optimizers.Adam()
for x, y in train_Data:
with tf.GradientTape() as tape:
prob = model(x, training=True)
# calculate loss
train_loss_value = loss_fn(y, prob)
        

# get gradients
gradients = tape.gradient(train_loss_value, model.trainable_weights)
# clip gradients if you want to clip by norm
gradients = [(tf.clip_by_norm(grad, clip_norm=1.0)) for grad in gradients]
# clip gradients via values
gradients = [(tf.clip_by_value(grad, clip_value_min=-1.0, clip_value_max=1.0)) for grad in gradients]
# apply gradients
optimizer.apply_gradients(zip(gradients, model.trainable_weights))

方法2

或者您也可以简单地替换上面代码中的第一行,如下所示

# for clipping by norm
optimizer = tf.keras.optimizers.Adam(clipnorm=1.0)
# for clipping by value
optimizer = tf.keras.optimizers.Adam(clipvalue=0.5)

如果使用的是 model.compile-> model.fit管道,第二种方法也可以工作。