如何只定义 Tensorflow 子图的梯度?

How Can I Define Only the Gradient for a Tensorflow Subgraph?

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

我从 c​​ifar10 教程代码开始,现在我正在结合使用卷积和特征值分解来打破符号微分。 IE。图形被构建,然后在调用 train() 时脚本以 "No gradient defined for operation [...] (op type: SelfAdjointEig)" 停止。不足为奇。

有问题的子图的输入仍然只是输入特征图和正在使用的过滤器,我手边有梯度的公式,在给定子图的输入的情况下,它们应该可以直接实现以及相对于其输出的梯度。

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

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

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

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

这是 Sergey Ioffe 的一个技巧:

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

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

所以在你的情况下,你的 g(x) 可能是一个身份操作,使用 gradient_override_map

的自定义渐变

用乘法和除法代替加减 t 怎么样?

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

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

这是适用于 TensorFlow 2.0 的方法。请注意,在 2.0 中,我们很高兴有 2 种不同的 autodiff 算法:GradientTape 用于 eager 模式,tf.gradient 用于非 eager 模式(这里称为 "lazy")。我们证明 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()