“GradientTape”,“implicit_gradients”之间的区别是什么?

时间:2018-04-30 10:50:58

标签: tensorflow

我试图切换到TensorFlow急切模式,我发现GradientTape()上下文与implicit_gradients()implicit_value_and_gradients()之间的文档w.r.t区别有点令人困惑。它们之间的区别是什么时候最好使用哪一个? intro point in the documentation根本没有提到隐式*函数,但是tf repo中的几乎所有示例似乎都使用该方法来计算渐变。

1 个答案:

答案 0 :(得分:12)

启用eager执行时,有4种方法可以自动计算渐变(实际上,它们也可以在图形模式下工作):

  • tf.GradientTape上下文记录计算,以便您可以调用tfe.gradient()来获取在记录任何可训练变量时计算的任何张量的渐变。
  • tfe.gradients_function()接受一个函数(比如f())并返回一个渐变函数(比如说fg()),它可以计算f()关于输出的f()的渐变tfe.implicit_gradients()(或其中一部分)的参数。
  • fg()非常相似,但f()计算tfe.implicit_value_and_gradients()输出相对于这些输出所依赖的所有可训练变量的渐变。
  • fg()几乎完全相同,但f()也会返回函数tf.GradientTape的输出。

通常,在机器学习中,您需要计算与模型参数(即变量)相关的损失梯度,并且您通常也会对损失本身的值感兴趣。对于此用例,最简单和最有效的选项是tfe.implicit_value_and_gradients()tfe.implicit_value_and_gradients()(其他两个选项不会为您提供损失本身的值,因此如果您需要它,则需要额外的计算)。我个人在编写生产代码时更喜欢tf.GradientTape,而在Jupyter笔记本中进行实验时我更喜欢tf.GradientTape

修改:在TF 2.0中,似乎只剩下import tensorflow as tf import tensorflow.contrib.eager as tfe tf.enable_eager_execution() w1 = tfe.Variable(2.0) w2 = tfe.Variable(3.0) ​ def weighted_sum(x1, x2): return w1 * x1 + w2 * x2 s = weighted_sum(5., 7.) print(s.numpy()) # 31 。也许其他功能会被添加回来,但我不会指望它。

详细示例

让我们创建一个小功能来突出差异:

tf.GradientTape

使用GradientTape

s上下文中,记录所有操作,然后您可以计算任何可训练变量在上下文中计算的任何张量的梯度。例如,此代码在GradientTape上下文中计算s,然后根据w1计算s = w1 * x1 + w2 * x2的渐变。自s起,与w1相关的x1的渐变为with tf.GradientTape() as tape: s = weighted_sum(5., 7.) ​ [w1_grad] = tape.gradient(s, [w1]) print(w1_grad.numpy()) # 5.0 = gradient of s with regards to w1 = x1

tfe.gradients_function()

使用s

此函数返回另一个函数,该函数可以计算函数返回值与其参数之间的渐变。例如,我们可以使用它来定义一个函数,该函数将根据x1x2来计算grad_fn = tfe.gradients_function(weighted_sum) x1_grad, x2_grad = grad_fn(5., 7.) print(x1_grad.numpy()) # 2.0 = gradient of s with regards to x1 = w1 的渐变:

weighted_sum()

在优化的上下文中,对于我们可以调整的变量,它会更有意义地计算渐变。为此,我们可以更改w1函数以将w2tfe.gradients_function()作为参数,并告诉"w1"仅考虑名为"w2"的参数和def weighted_sum_with_weights(w1, x1, w2, x2): return w1 * x1 + w2 * x2 grad_fn = tfe.gradients_function(weighted_sum_with_weights, params=["w1", "w2"]) [w1_grad, w2_grad] = grad_fn(w1, 5., w2, 7.) print(w2_grad.numpy()) # 7.0 = gradient of s with regards to w2 = x2

tfe.implicit_gradients()

使用weighted_sum()

此函数返回另一个函数,该函数可以计算函数返回值的梯度,该值依赖于它所依赖的所有可训练变量。回到s的第一个版本,我们可以使用它来计算w1关于w2grad_fn = tfe.implicit_gradients(weighted_sum) [(w1_grad, w1_var), (w2_grad, w2_var)] = grad_fn(5., 7.) print(w1_grad.numpy()) # 5.0 = gradient of s with regards to w1 = x1 assert w1_var is w1 assert w2_var is w2 的渐变,而不必显式传递这些变量。请注意,渐变函数返回梯度/变量对列表:

w1

这个函数确实看起来是最简单和最有用的选项,因为通常我们感兴趣的是计算与模型参数(即变量)有关的损失的梯度。 注意:尝试使w1 = tfe.Variable(2., trainable=False)无法解决(weighted_sum())并重新定义grad_fn,您会看到s仅返回与{w2相关的渐变tfe.implicit_value_and_gradients() 1}}。

使用implicit_gradients()

此函数几乎与weighted_sum()相同,但它创建的函数也返回被区分的函数的结果(在本例中为grad_fn = tfe.implicit_value_and_gradients(weighted_sum) s, [(w1_grad, w1_var), (w2_grad, w2_var)] = grad_fn(5., 7.) print(s.numpy()) # 31.0 = s = w1 * x1 + w2 * x2 ):

db = firebase.FirebaseApplication("https://db.firebaseio.com")
def foo(a, b, c, d):
    """code that PUT data from a b c d to Firebase"""

当你需要函数的输出和它的渐变时,这个函数可以给你一个很好的性能提升,因为你在使用autodiff计算渐变时可以免费获得函数的输出。