如何在 Keras 中找到自定义模型的导数?

How do I find the derivative of a custom model in Keras?

我有一个自定义模型,它采用任意 "hidden model" 作为输入并将其包装在另一个张量中,该张量将隐藏模型的输出视为 return 并通过添加来计算隐含输出1 乘以原始数据:

class Model(tf.keras.Model):
    def __init__(self, hidden_model):
        super(Model, self).__init__(name='')
        self.hidden_model = hidden_model

    def build(
        self,
        reference_price_shape,
        hidden_inputs_shape):

        super(Model, self).build([reference_price_shape, hidden_inputs_shape])

    def call(self, inputs):
        reference_prices = inputs[0]
        hidden_layers_input = inputs[1]
        hidden_output = self.hidden_model(hidden_layers_input)
        return (hidden_output + 1) * reference_prices

    def compute_output_shape(self, input_shape):
        return (input_shape[0][0], 1)

但是,我现在想知道模型对每个输入的变化有多敏感。为此,我想我可以使用 keras.backend.gradients:

rows = 10
cols = 2

hidden_model = tf.keras.Sequential()

hidden_model.add(
    tf.keras.layers.Dense(
        1,
        name='output',
        use_bias=True,
        kernel_initializer=tf.constant_initializer(0.1),
        bias_initializer=tf.constant_initializer(0)))

model = Model(hidden_model)
model.build(
    reference_price_shape=(rows,),
    hidden_inputs_shape=(rows, cols))

from tensorflow.keras import backend as K
grads = K.gradients(model.output, model.input)

但是,这 return 是一个错误:

--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) in 1 from tensorflow import keras 2 from tensorflow.keras import backend as K ----> 3 K.gradients(hidden_model.output, hidden_model.input)

/usr/lib64/python3.6/site-packages/tensorflow_core/python/keras/backend.py in gradients(loss, variables) 3795 """ 3796 return gradients_module.gradients( -> 3797 loss, variables, colocate_gradients_with_ops=True) 3798 3799

/usr/lib64/python3.6/site-packages/tensorflow_core/python/ops/gradients_impl.py in gradients(ys, xs, grad_ys, name, colocate_gradients_with_ops, gate_gradients, aggregation_method, stop_gradients, unconnected_gradients) 156 ys, xs, grad_ys, name, colocate_gradients_with_ops, 157 gate_gradients, aggregation_method, stop_gradients, --> 158 unconnected_gradients) 159 # pylint: enable=protected-access 160

/usr/lib64/python3.6/site-packages/tensorflow_core/python/ops/gradients_util.py in _GradientsHelper(ys, xs, grad_ys, name, colocate_gradients_with_ops, gate_gradients, aggregation_method, stop_gradients, unconnected_gradients, src_graph) 503 """Implementation of gradients().""" 504 if context.executing_eagerly(): --> 505 raise RuntimeError("tf.gradients is not supported when eager execution " 506 "is enabled. Use tf.GradientTape instead.") 507 if src_graph is None:

RuntimeError: tf.gradients is not supported when eager execution is enabled. Use tf.GradientTape instead.

我查看了 tf.GradientTape 的指南,在此基础上我尝试将以下内容添加到我的代码中:

with tf.GradientTape() as g:
  g.watch(x)

但是我把它放在哪里x 是张量,我没有输入张量。我只有 inputs,这是一个 numpy 数组数组。

只是为了增加混乱,有一个 github post here 似乎暗示这是 tensorflow 2.0 错误,添加 tf.compat.v1.disable_eager_execution() 将为我解决问题。它没有(尽管它确实得到了上述错误以更改为 Layer model_1 has no inbound nodes. - 不确定这是向前还是向后迈出的一步)。

抱歉,我意识到这个问题几乎站不住脚,但在这一点上我真的很困惑,这可能是我能做的最好的事情,将其定义为可以回答的问题。

作为测试,我尝试用 运行ning K.gradients 代替 hidden_model,哪种方法有效:

但我不知道该怎么办,因为我通常 运行 我的模型使用 model.predict(input_data) - 我应该如何使用该张量获得局部导数?

所以我认为我有两个问题:

  1. 对于 whole 模型,我如何计算我的输出相对于我的输入的导数 - 它一直都是张量,所以 Keras/tensorflow 即使使用我的自定义 call() function/model.
  2. 也确实应该能够应用链式法则
  3. 一旦我有了导数张量,我可以用它做什么?

我最初认为我应该尝试将这些问题分开,但单独提出其中任何一个问题都可能是 XY 问题,所以我想我应该一起提问,以便为回答者提供一些背景信息。

这是可能的,但需要一些工作(显然)。希望看到更优雅的解决方案。但这对我来说是最好的了。

import tensorflow as tf
from tensorflow.keras import backend as K
import numpy as np

rows = 10
cols = 2

with tf.Graph().as_default():


  hidden_model = tf.keras.Sequential()

  hidden_model.add(
      tf.keras.layers.Dense(
          1,
          name='output',
          use_bias=True,
          kernel_initializer=tf.constant_initializer(0.1),
          bias_initializer=tf.constant_initializer(0)))

  model = Model(hidden_model)
  model.build(
      reference_price_shape=(rows,),
      hidden_inputs_shape=(rows, cols))

请注意,模型构建需要发生在您尝试在其中获取梯度的同一张图中。可能不需要是默认图,而是相同的图。

然后在图形的相同上下文中,创建一个渐变带上下文。另请注意,x 需要是 tf.Variable() 才能注册为渐变的输入。

  with tf.GradientTape() as tape:
    x = tf.Variable(np.random.normal(size=(10, rows, cols)), dtype=tf.float32)
    out = model(x)

这样你就可以得到渐变。

  grads = tape.gradient(out, x)

  sess = tf.compat.v1.keras.backend.get_session()
  sess.run(tf.compat.v1.global_variables_initializer())
  g = sess.run(grads)
  print(g)