【问题标题】:How Can I Define Only the Gradient for a Tensorflow Subgraph?如何仅定义 TensorFlow 子图的梯度?
【发布时间】:2016-04-06 16:03:48
【问题描述】:

首先:我才刚接触 Tensorflow 几天,所以请多多包涵。

我从 cifar10 教程代码开始,现在我使用卷积和特征值分解的组合来打破符号微分。 IE。图表被构建,然后在调用train() 时,脚本以“没有为操作定义梯度[...](操作类型:SelfAdjointEig)”而停止。这并不奇怪。

有问题的子图的输入仍然只是输入特征图和正在使用的过滤器,我手头有梯度公式,考虑到子图的输入和相对于其输出的梯度。

根据我在文档中看到的内容,我可以使用 RegisterGradient 为自定义 Ops 注册渐变方法,或者使用实验性 gradient_override_map 覆盖它们。 这两个都应该让我可以访问我需要的东西。例如,searching on Github 我发现很多示例都以op.input[0] 或诸如此类的形式访问操作的输入。

我遇到的问题是我想从本质上“捷径”整个子图,而不是单个操作,所以我没有单个操作可以装饰。 由于这发生在 cifar 示例的卷积层之一中,我尝试使用该层的范围对象。 从概念上讲,进入和退出该范围图形的内容正是我想要的,所以如果我能以某种方式覆盖整个范围的渐变,那将“已经”做到这一点。

我看到tf.Graph.create_op(我认为)我可以使用它来注册一种新的操作类型,然后我可以使用上述方法覆盖该操作类型的梯度计算。但是我没有看到一种方法来定义该操作的 forward 传递而不用 C++ 编写它......

也许我完全以错误的方式接近这个? 由于我所有的前向或后向操作都可以使用 python 接口来实现,我显然希望避免在 C++ 中实现任何东西。

【问题讨论】:

  • 也许您可以覆盖不可微图顶部单个操作的梯度,然后使用tf.stop_gradient() 来阻止该子图的梯度构造? stackoverflow.com/questions/33727935/…
  • 我可以想象在本地定义一个梯度函数,然后在其中使用仍在范围内的输入。但是我如何告诉 tf 我将哪些节点的梯度作为梯度计算的输入?这对我来说感觉就像我从根本上滥用了框架:P

标签: tensorflow


【解决方案1】:

这是 Sergey Ioffe 的一个技巧:

假设你想要一组在前向模式下表现为 f(x),但在后向模式下表现为 g(x) 的操作组。您将其实现为

t = g(x)
y = t + tf.stop_gradient(f(x) - t)

因此,在您的情况下,您的 g(x) 可能是一个身份操作,使用 gradient_override_map 进行自定义渐变

【讨论】:

  • 为了理解:stop_gradient 调用负责自动渐变位,覆盖g 的渐变使我能够插入自己的渐变,t + f(x) - t 将被优化掉?
  • “t + f(x) - t”的值等于“f(x)”。在当前版本中它在计算上是等效的,但在未来的版本中它可能会被优化掉
  • 终于可以应用这个了,尽管毕竟不是同一个功能。但这并不能很好地推广到具有多个输入的“复合运算”,因为“加减”不起作用,是吗?我能想到的最好的(但毕竟不必尝试)是以某种方式使用元组而不是身份操作。但我有点不清楚图表之后的样子。无论如何,非常感谢你:)
  • 正是我需要的。也许这应该是内置的?
  • 如果你假设你可以很容易地取消(例如在 stop_gradient 中减法)向后传球对向前传球的影响,那么给定的解决方案是简洁的。但是,假设前向函数生成一组随机索引,用于对网络/损失中使用的某些特征和/或标签进行洗牌。在这种情况下,一个简单的“减法”不会取消调用随机器两次的效果。我们如何才能使第二个(向后)调用无害?
【解决方案2】:

从 TensorFlow 1.7 开始,tf.custom_gradientthe way to go

【讨论】:

    【解决方案3】:

    如何乘除而不是加减t?

    t = g(x)
    y = tf.stop_gradient(f(x) / t) * t
    

    【讨论】:

    • dy/dt 这里是 (f(x)/t)*dy - 不是我们想要的。通过左侧停止梯度不会阻止使用正向结果的乘法导数。
    【解决方案4】:

    这是适用于 TensorFlow 2.0 的方法。请注意,在 2.0 中,我们很高兴拥有 2 种不同的 autodiff 算法:GradientTape 用于渴望模式,tf.gradient 用于非渴望模式(这里称为“惰性”)。我们证明tf.custom_gradient 可以双向工作。

    import tensorflow as tf
    assert tf.version.VERSION.startswith('2.')
    import numpy as np
    from tensorflow.python.framework.ops import disable_eager_execution, enable_eager_execution
    from tensorflow.python.client.session import Session
    
    @tf.custom_gradient
    def mysquare(x):
      res = x * x
      def _grad(dy):
        return dy * (2*x)
      return res, _grad
    
    def run_eager():
      enable_eager_execution()
    
      x = tf.constant(np.array([[1,2,3],[4,5,6]]).astype('float32'))
      with tf.GradientTape() as tape:
        tape.watch(x)
        y = tf.reduce_sum(mysquare(x))
    
        dy_dx = tape.gradient(y,x)
        print('Eager mode')
        print('x:\n',x.numpy())
        print('y:\n',y.numpy())
        print('dy_dx:\n',dy_dx.numpy())
    
    
    def run_lazy():
      disable_eager_execution()
    
      x = tf.constant(np.array([[1,2,3],[4,5,6]]).astype('float32'))
      y = tf.reduce_sum(mysquare(x))
      dy_dx = tf.gradients(y,x)
    
      with Session() as s:
        print('Lazy mode')
        print('x:\n',x.eval(session=s))
        print('y:\n',y.eval(session=s))
        assert len(dy_dx)==1
        print('dy_dx:\n',dy_dx[0].eval(session=s))
    
    if __name__ == '__main__':
      run_eager()
      run_lazy()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-29
      • 1970-01-01
      • 2021-10-26
      • 1970-01-01
      • 2019-12-14
      相关资源
      最近更新 更多