12

I just asked a question on the same topic but for custom models (How do I find the derivative of a custom model in Keras?) but realised quickly that this was trying to run before I could walk so that question has been marked as a duplicate of this one.

I've tried to simplify my scenario and now have a (not custom) keras model consisting of 2 Dense layers:

inputs = tf.keras.Input((cols,), name='input')

layer_1 = tf.keras.layers.Dense(
        10,
        name='layer_1',
        input_dim=cols,
        use_bias=True,
        kernel_initializer=tf.constant_initializer(0.5),
        bias_initializer=tf.constant_initializer(0.1))(inputs)

outputs = tf.keras.layers.Dense(
        1,
        name='alpha',
        use_bias=True,
        kernel_initializer=tf.constant_initializer(0.1),
        bias_initializer=tf.constant_initializer(0))(layer_1)

model = tf.keras.Model(inputs=inputs, outputs=outputs)

prediction = model.predict(input_data)
# gradients = ...

Now I would like to know the derivative of outputs with respect to inputs for inputs = input_data.

What I've tried so far:

This answer to a different question suggests running grads = K.gradients(model.output, model.input). However, if I run that I get this error;

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

I can only assume this is something to do with eager execution now being the default.

Another approach was in the answer to my question on custom keras models, which involved adding this:

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

What I don't understand about this approach is how I'm supposed to load the data. It requires x to be a variable, but my x is a tf.keras.Input object. I also don't understand what that with statement is doing, some kind of magic but I don't understand it.

There's a very similar-sounding question to this one here: Get Gradients with Keras Tensorflow 2.0 although the application and scenario are sufficiently different for me to have difficulty applying the answer to this scenario. It did lead me to add the following to my code:

with tf.GradientTape() as t:
    t.watch(outputs)

That does work, but now what? I run model.predict(...), but then how do I get my gradients? The answer says I should run t.gradient(outputs, x_tensor).numpy(), but what do I put in for x_tensor? I don't have an input variable. I tried running t.gradient(outputs, model.inputs) after running predict, but that resulted in this:

enter image description here

quant
  • 21,507
  • 32
  • 115
  • 211
  • How is this different from your previous question? In Keras all models are custom. – Dr. Snoopy Jan 04 '20 at 12:46
  • @MatiasValdenegro it's different as I'm no longer creating a custom model class that inherits from tf.keras.Model. See this for more info: https://www.tensorflow.org/guide/keras/custom_layers_and_models – quant Jan 04 '20 at 12:48
  • That does not change how gradients are computed, so the solution is the same, hence the same question. – Dr. Snoopy Jan 04 '20 at 12:49
  • @MatiasValdenegro please feel free to answer either of my questions then. The answer still eludes me! – quant Jan 04 '20 at 12:49
  • That's not how it works, you should ask the question once, choose a question and delete the other. I can also mark as duplicates. And read about this at the help center: https://stackoverflow.com/help/duplicates – Dr. Snoopy Jan 04 '20 at 12:53
  • @MatiasValdenegro I've voted to close my previous question as it's less useful. Feel free to do the same but please leave this one open as I'd very much like to know how to do this. – quant Jan 04 '20 at 12:56
  • I assume by gradient you mean the weights of internal layers of the network. You can do: `model.get_layer('alpha').get_weights()[0]` – YOLO Jan 04 '20 at 13:03
  • 2
    @YOLO Weights are not gradients. – Dr. Snoopy Jan 04 '20 at 13:05

1 Answers1

11

I ended up getting this to work with a variant of the answer to this question: Get Gradients with Keras Tensorflow 2.0

x_tensor = tf.convert_to_tensor(input_data, dtype=tf.float32)
with tf.GradientTape() as t:
    t.watch(x_tensor)
    output = model(x_tensor)

result = output
gradients = t.gradient(output, x_tensor)

This allows me to obtain both the output and the gradient without redundant computation.

quant
  • 21,507
  • 32
  • 115
  • 211
  • 1
    Is it really required to convert the input numpy array to a tensor? It probably works without that. – Dr. Snoopy Jan 04 '20 at 13:54
  • I don't think you need `watch` there. I've been training a lot of models in eager mode using `GradientTape`. I never used `watch`. – Daniel Möller Jan 04 '20 at 14:55
  • @MatiasValdenegro, I'm not sure, but maybe, because they are trying to get the "gradients" with respect to the data, there is a possibility that the data needs to be a tensor in this case? – Daniel Möller Jan 04 '20 at 14:56
  • @MatiasValdenegro the `.numpy()` is not necessary. I'll remove it. – quant Jan 05 '20 at 00:35
  • 1
    @DanielMöller I tried removing `watch` but `t.gradient` returns `NoneType` if I do that. I think this has to do with whether or not the tensors are "trainable". I'm using `scipy` for training so `keras` may not recognise the weights as trainable. – quant Jan 05 '20 at 00:35