C++ autograd for torch::jit::script::Module

FulgurIgor: <p>Hello, everybody!</p>
<p>I’m trying to evaluate the gradients of the model in C++ code using torch/script.</p>
<p>The original model was constructed in Python side with</p>
<pre><code class="lang-auto">#!/usr/bin/python3

import sys
import torch
import torch.nn as nn

class MLP(nn.Sequential):
  def __init__(self, layers=(7, 1)):
    modules = []
    for idx in range(len(layers) - 1):
      modules.append(nn.Linear(layers[idx], layers[idx + 1]))
      modules.append(nn.ELU())
    super().__init__(*modules[:-1])
</code></pre>
<p>Then I created a model and saved it to file:</p>
<pre><code class="lang-auto">model = MLP()
a = torch.ones((7), requires_grad=True)
traced_model_gen = torch.jit.trace(model, a)
traced_model_gen.save("traced_jit_model.pt")
</code></pre>
<p>Then, I loaded the model and evaluated its gradients:</p>
<pre><code class="lang-auto">jit_model = torch.jit.load("traced_jit_model.pt")
res1 = jit_model.forward(a)
print(res1)
grad1 = torch.autograd.grad(res1, a, create_graph=True, only_inputs=True)
print(grad1)
</code></pre>
<p>And it works fine.</p>
<p>Then I tried to load this model to C++ code and also evaluate the derivatives:</p>
<pre><code class="lang-auto">#include &lt;iostream&gt;
#include &lt;torch/script.h&gt;
#include &lt;torch/csrc/autograd/autograd.h&gt;
int main()
{
  torch::jit::script::Module module = torch::jit::load("traced_jit_model.pt");
  static float point[] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
  at::Tensor input = torch::from_blob(point, {7}, torch::TensorOptions().dtype(torch::kFloat));
  std::vector&lt;torch::jit::IValue&gt; inputs;
  inputs.push_back(input);
  auto outputs = module.forward(inputs);
  // the next line produces the same value like res1 in Python code
  torch::Tensor res1 = outputs.toTensor();
  std::cout &lt;&lt; res1[0].item&lt;float&gt;(); &lt;&lt; std::endl;
  auto grad1 = torch::autograd::grad(outputs, inputs, true);
}
</code></pre>
<p>I’m trying to evaluate gradients in the last line, but, unfortunately, compilation says about type inconsistency:</p>
<pre><code class="lang-auto">basic_torch.cpp:76:37: error: invalid initialization of reference of type ‘const variable_list&amp;’ {aka ‘const std::vector&lt;at::Tensor&gt;&amp;’} from expression of type ‘c10::IValue’
   76 |   auto grad = torch::autograd::grad(outputs, inputs, true);
      |                                     ^~~~~~~
In file included frombasic_torch.cpp:3:
libtorch/include/torch/csrc/autograd/autograd.h:74:26: note: in passing argument 1 of ‘torch::autograd::variable_list torch::autograd::grad(const variable_list&amp;, const variable_list&amp;, const variable_list&amp;, c10::optional&lt;bool&gt;, bool, bool)’
   74 |     const variable_list&amp; outputs,
      |     ~~~~~~~~~~~~~~~~~~~~~^~~~~~~
^Cmake[2]: *** [CMakeFiles/basic_torch.dir/build.make:63: CMakeFiles/basic_torch.dir/basic_torch.cpp.o] Interrupt
</code></pre>
<p>I looked at <a href="https://pytorch.org/tutorials/advanced/cpp_autograd.html" class="inline-onebox" rel="noopener nofollow ugc">Autograd in C++ Frontend — PyTorch Tutorials 1.10.0+cu102 documentation</a> for understanding how to evaluate the gradients of the model, but I did not find any information.</p>
<p>Are there any manuals on how to evaluate model gradients with autograd?</p>
<p>UPD:<br>
libtorch version: <code>1.11.0.dev20211028+cpu</code></p>

tom: <p>The C++ API doesn’t have all the “pass a tensor and it’ll be treated as a 1-element list of tensors” functions that PyTorch has in Python.<br>
In other words, use <code>torch::autograd::grad({outputs}, {inputs}, /* grad_outptus=*/ {}, /* create_graph=*/ true);</code> or so, as seen in the <a href="https://pytorch.org/tutorials/advanced/cpp_autograd.html#computing-higher-order-gradients-in-c" rel="noopener nofollow ugc">higher order gradient example</a> for C++.</p>
<p>Best regards</p>
<p>Thomas</p>

FulgurIgor: <p>Thank you, Thomas!</p>
<p>I successfully translated code from Python to C++!</p>
<p>There is the answer:</p>
<pre><code class="lang-auto">torch::jit::script::Module module = torch::jit::load("traced_jit_model.pt");
auto input1 = torch::ones({7}).requires_grad_(true);
std::vector&lt;torch::jit::IValue&gt; inputs;
inputs.push_back(input1);
auto output1 = module(inputs);
auto grad_output1 = torch::ones_like(output1.toTensor());
auto gradient1 = torch::autograd::grad({output1.toTensor()}, {input1}, /*grad_outputs=*/{grad_output1}, /*create_graph=*/true);
std::cout &lt;&lt; gradient1 &lt;&lt; std::endl;
</code></pre>
<p>It was quite simplier than I thought!</p>
<p>Best regards,<br>
Igor</p>