Grad_fn is none in forward for grad_enabled tensor after differentiable operation

4158ndfkvBHJ1: <p>Hello everybody,</p>
<p>There is something I’m not understanding well about how autograd works. I am trying to implement a maximum likelihood model in pytorch. This model is maximizing a softmax distribution given some parameters, knowing some batch of data. My model class looks like this:</p>
<pre><code class="lang-python">class Model(nn.Module):

    def __init__(self, param_size, kernel_size, max_value):

        super(Model, self).__init__()

        self.kernel_size = kernel_size
        self.max_value = max_value
        self.params = torch.ones(param_size, requires_grad = True).float()

    def forward(self, data):

        pot_data, norm_data = data

        ### global potential computation 

        potential = torch.mul(pot_data, self.params)
        potential = torch.sum(potential, (1,2))

        ### global normalization computation  ###
        
        normalization = torch.mul(norm_data, self.params)     
        normalization = torch.sum(normalization, dim = 2)
        normalization = torch.logsumexp(-normalization, dim = 1)
        normalization = torch.sum(normalization, dim = 1)

        batch_likelihood = torch.sum(-potential - normalization)

        return batch_likelihood
</code></pre>
<p>I have a standard training look with some data pre-processing :</p>
<pre><code class="lang-python">for index, (img, label) in enumerate(dataloader):
        
        data = F.unfold(torch.unsqueeze(img, 1),
                    kernel_size = model.kernel_size,
                    stride = 1,
                    padding = 0)

        batch_size = data.size(0)
        nb_patchs = data.size(-1)
        nb_repeats = model.max_value

        pot_data = (data[:,:,:] != data[:,4,:].view((batch_size, 1, nb_patchs)))
        
        dup_data = torch.unsqueeze(data, 1).repeat(1, model.max_value, 1, 1)
        ranges = torch.unsqueeze(torch.unsqueeze(torch.arange(0, model.max_value, 1), 0), 2)
        ranges = ranges.repeat(batch_size, 1, nb_patchs)
        dup_data [:, :,4,:] = ranges 
        norm_data = (dup_data[:,:,:,:] != dup_data[:,:,4,:].view((batch_size, nb_repeats, 1, nb_patchs)))

        data = (pot_data, norm_data)

        optimizer.zero_grad()
        likelihood = model.forward(data)
        (-likelihood).backward()
        optimizer.step()
</code></pre>
<p>At every point in the <code>forward</code> method, <code>self.params.grad_fn</code> returns <code>None</code>, and I don’t understand why. Also, If I save my model parameters with <code>torch.save("model.pt")</code> and use something like <a href="https://netron.app/" rel="noopener nofollow ugc">https://netron.app/</a>, my computation graph doesn’t contain any operation.<br>
However, my likelihood does still get minimized, and my parameters get updated at every batch.</p>
<p>This is very confusing to me: I would expect <code>self.params.grad_fn</code> to be different from <code>None</code>, for example <code>MulBackward</code> after every <code>torch.mul</code> in the forward function, but this is not the case. Why is that ?</p>
<p>Also, given that <code>grad_fn</code> is <code>None</code>, how does the optimizer compute a gradient to update my parameters at every batch ?</p>
<p>I would be grateful for any help for better understanding how Autograd works in this case <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=9" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>

ptrblck: <p>It seems you are trying to initialize <code>self.params</code> as the trainable parameters in the model, which is not the case at the moment, since you are not registering the tensor and would need to use <code>nn.Parameter(torch.ones(...))</code> instead. This will make sure this parameter will be properly registered and e.g. passed to the optimizer via <code>model.parameters()</code>.</p>
<aside class="quote no-group" data-username="4158ndfkvBHJ1" data-post="1" data-topic="121953">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/4/d07c76/48.png" class="avatar"> 4158ndfkvBHJ1:</div>
<blockquote>
<p>I would expect <code>self.params.grad_fn</code> to be different from <code>None</code>, for example <code>MulBackward</code> after every <code>torch.mul</code> in the forward function, but this is not the case. Why is that ?</p>
</blockquote>
</aside>
<p>Assuming you would have properly registered <code>self.params</code> it wouldn’t show any <code>grad_fn</code>, since it’s a leaf tensor and was not created by an operation. In case you want to check the <code>grad_fn</code>s, you could check the forward activations e.g. <code>print(potential.grad_fn)</code>.</p>

4158ndfkvBHJ1: <p>Thanks a lot for your answer, it makes sense now. If I understand correctly, <code>grad_fn</code>s will only be evaluated with respect to tensors with <code>requires_grad=True</code>, and that’s how autograd will differentiate the expression wrt the parameters and not the data (for example) ?<br>
Also, this led to torchviz working well, so I could make sure that my model was computing precisely what I wanted.</p>