Derivative of model outputs w.r.t input features

username: jarvico
cooked: <p>Hi all,</p>
<p>Assume that we have a pertained NN model like LeNet-5 which successfully predicts handwritten digits. In this case number of features is 784 (assuming 28x28 input images) and number of outputs is 10. Sum of the output values(probs) adds up to 1 and each output shows the probability of that class for the given input image.</p>
<p>Now, assuming that I already have this model. And I can predict the class of any input image using feedforward model prediction.</p>
<p>My question is: For any test input image, I would like to calculate derivative of any model output w.r.t model input features. This way I will be calculating a 784x10 Jacobian matrix J.</p>
<p>For example J[0,0] is the derivative of output 0 w.r.t input feature 0 and J[2,3] is derivative of output 3 w.r.t input feature 2 and so on…</p>
<p>Actually maybe I can calculate each element of this Jacobian via a messy code block but I wonder if there is any easy and elegant way of doing so.</p>
<p>Any comment on how to calculate the J matrix?</p>

username: KFrank
cooked: <p>Hello Ömer!</p>
<aside class="quote no-group" data-username="jarvico" data-post="1" data-topic="95814" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/jarvico/48/28509_2.png" class="avatar"> jarvico:</div>
<blockquote>
<p>My question is: For any test input image, I would like to calculate derivative of any model output w.r.t model input features. This way I will be calculating a 784x10 Jacobian matrix J.</p>
</blockquote>
</aside>
<p>Just as you use pytorch’s autograd to calculate the derivatives (gradient)<br>
of your loss function with respect to your model’s parameters (and then<br>
use those to update your model with gradient descent), you can use<br>
autograd to calculate the derivatives of a prediction for a single class<br>
with respect to the input to your model.  This will be a single column of<br>
your Jacobian matrix.</p>
<p>You can then loop over predictions / columns to build the full Jacobian.</p>
<p>Models work on batches, even if you want to process a single image.<br>
So, for a single image, you need a batch with batch size of one.</p>
<p>Let’s say you have an <code>input</code> tensor, with shape <code>[1, 784]</code>.  (It could be<br>
<code>[1, 28, 28]</code>, if that is what your model expects.)  You say your model<br>
has ten classes, so you will have:</p>
<pre data-code-wrap="python"><code class="lang-python">preds = model (input)
</code></pre>
<p>where <code>preds</code> has shape <code>[nBatch, nClass] = [1, 10]</code>.</p>
<p>Tell pytorch to track gradients with respect to your <code>input</code>, apply your<br>
model to input, and call <code>.backward()</code> on your <code>preds[i]</code>, looping over<br>
<code>i</code>.  The <code>.grad</code> property of of your <code>input</code> tensor will be the <code>i</code>th column<br>
of the Jacobian.</p>
<pre data-code-wrap="python"><code class="lang-python">J = torch.zeros ((1, 784, 10))   # loop will fill in Jacobian
input.requires_grad = True
preds = model (input)
for  i in range (10):
    grd = torch.zeros ((1, 10))   # same shape as preds
    grd[0, i] = 1    # column of Jacobian to compute
    preds.backward (gradient = grd, retain_graph = True)
    J[:,:,i] = input.grad   # fill in one column of Jacobian
    input.grad.zero_()   # .backward() accumulates gradients, so reset to zero

</code></pre>
<p>You could also try pytorch’s <strong>experimental</strong> <a href="https://pytorch.org/docs/stable/autograd.html#torch.autograd.functional.jacobian" rel="noopener nofollow ugc">jacobian()</a> function, which<br>
I <em>think</em> basically wraps the loop I outlined above, but with more bells<br>
and whistles (but I’ve never used it).</p>
<p>Good luck.</p>
<p>K. Frank</p>

username: jarvico
cooked: <p>Hi Frank,</p>
<p>I just did a very minor change and it seems to work. Thanks for your help! Appreciated…</p>
<p>my_input=test_data[num][0].view(1,1,28,28)</p>
<p>J = torch.zeros((784, 10))   # loop will fill in Jacobian<br>
J = J.float()</p>
<p>my_input.requires_grad_()</p>
<p>preds = model(my_input)<br>
print("preds shape is: ",preds.shape)</p>
<p>for i in range (10):<br>
grd = torch.zeros ((1, 10))   # same shape as preds<br>
grd[0, i] = 1    # column of Jacobian to compute<br>
preds.backward(gradient = grd, retain_graph = True)<br>
J[:,i] = my_input.grad.view(784).float()   # fill in one column of Jacobian<br>
my_input.grad.zero_()   # .backward() accumulates gradients, so reset to zero</p>
<p>print(J.shape)<br>
print(J)</p>

username: nicole_tein
cooked: <p>Thanks! Really helpful.</p>