【问题标题】:Conceptual understanding of GradientTape.gradientGradientTape.gradient 的概念理解
【发布时间】:2020-06-25 03:32:18
【问题描述】:

背景

在 Tensorflow 2 中,存在一个名为 GradientTape 的类,用于记录对张量的操作,然后可以对结果进行微分并提供给一些最小化算法。比如from the documentation我们有这个例子:

x = tf.constant(3.0)
with tf.GradientTape() as g:
  g.watch(x)
  y = x * x
dy_dx = g.gradient(y, x) # Will compute to 6.0

gradient 方法的 docstring 意味着第一个参数不仅可以是张量,还可以是张量列表:

 def gradient(self,
               target,
               sources,
               output_gradients=None,
               unconnected_gradients=UnconnectedGradients.NONE):
    """Computes the gradient using operations recorded in context of this tape.

    Args:
      target: a list or nested structure of Tensors or Variables to be
        differentiated.
      sources: a list or nested structure of Tensors or Variables. `target`
        will be differentiated against elements in `sources`.
      output_gradients: a list of gradients, one for each element of
        target. Defaults to None.
      unconnected_gradients: a value which can either hold 'none' or 'zero' and
        alters the value which will be returned if the target and sources are
        unconnected. The possible values and effects are detailed in
        'UnconnectedGradients' and it defaults to 'none'.

    Returns:
      a list or nested structure of Tensors (or IndexedSlices, or None),
      one for each element in `sources`. Returned structure is the same as
      the structure of `sources`.

    Raises:
      RuntimeError: if called inside the context of the tape, or if called more
       than once on a non-persistent tape.
      ValueError: if the target is a variable or if unconnected gradients is
       called with an unknown value.
    """

在上面的例子中,很容易看出ytarget是要微分的函数,x是取“梯度”的因变量。

根据我有限的经验,gradient 方法似乎返回一个张量列表,sources 的每个元素一个,每个梯度都是一个与 @ 的相应成员形状相同的张量987654332@.

问题

如果target 包含要区分的单个1x1“张量”,则上述gradients 行为的描述是有意义的,因为从数学上讲,梯度向量应该与函数的域具有相同的维度。

但是,如果target 是张量列表,则gradients 的输出仍然是相同的形状。为什么会这样?如果target 被认为是一个函数列表,那么输出不应该类似于雅可比矩阵吗?我该如何从概念上解释这种行为?

【问题讨论】:

    标签: python deep-learning neural-network tensorflow2.0


    【解决方案1】:

    这就是tf.GradientTape().gradient() 的定义方式。它与tf.gradients() 具有相同的功能,只是后者不能在急切模式下使用。来自tf.gradients()docs

    它返回一个长度为len(xs)的张量列表,其中每个张量是sum(dy/dx) for y in ys

    其中xssourcesystarget

    示例 1

    假设target = [y1, y2]sources = [x1, x2]。结果将是:

    [dy1/dx1 + dy2/dx1, dy1/dx2 + dy2/dx2]
    

    示例 2

    计算每样本损失(张量)与减少损失(标量)的梯度

    Let w, b be two variables. 
    xentropy = [y1, y2] # tensor
    reduced_xentropy = 0.5 * (y1 + y2) # scalar
    grads = [dy1/dw + dy2/dw, dy1/db + dy2/db]
    reduced_grads = [d(reduced_xentropy)/dw, d(reduced_xentropy)/db]
                  = [d(0.5 * (y1 + y2))/dw, d(0.5 * (y1 + y2))/db] 
                  == 0.5 * grads
    

    上述sn-p的Tensorflow示例:

    import tensorflow as tf
    
    print(tf.__version__) # 2.1.0
    
    inputs = tf.convert_to_tensor([[0.1, 0], [0.5, 0.51]]) # two two-dimensional samples
    w = tf.Variable(initial_value=inputs)
    b = tf.Variable(tf.zeros((2,)))
    labels = tf.convert_to_tensor([0, 1])
    
    def forward(inputs, labels, var_list):
        w, b = var_list
        logits = tf.matmul(inputs, w) + b
        xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
            labels=labels, logits=logits)
        return xentropy
    
    # `xentropy` has two elements (gradients of tensor - gradient
    # of each sample in a batch)
    with tf.GradientTape() as g:
        xentropy = forward(inputs, labels, [w, b])
        reduced_xentropy = tf.reduce_mean(xentropy)
    grads = g.gradient(xentropy, [w, b])
    print(xentropy.numpy()) # [0.6881597  0.71584916]
    print(grads[0].numpy()) # [[ 0.20586157 -0.20586154]
                            #  [ 0.2607238  -0.26072377]]
    
    # `reduced_xentropy` is scalar (gradients of scalar)
    with tf.GradientTape() as g:
        xentropy = forward(inputs, labels, [w, b])
        reduced_xentropy = tf.reduce_mean(xentropy)
    grads_reduced = g.gradient(reduced_xentropy, [w, b])
    print(reduced_xentropy.numpy()) # 0.70200443 <-- scalar
    print(grads_reduced[0].numpy()) # [[ 0.10293078 -0.10293077]
                                    #  [ 0.1303619  -0.13036188]]
    

    如果您为批次中的每个元素计算损失 (xentropy),则每个变量的最终梯度将是批次中每个样本的所有梯度的总和(这是有道理的)。

    【讨论】:

    • 这很有意义,并且是对文档的一个很好的解释,但它仍然引出了 为什么 梯度不是雅可比列表的问题。目前我有一个场景,我确实想要一个雅可比矩阵而不是偏导数的总和......
    • 啊,我看到现在有 tf.python.ops.gradients.jacobian。我还没有让它在 tf 1.12 中工作,但至少它存在......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-07
    • 1970-01-01
    相关资源
    最近更新 更多