Backpropagate through one of the network modules

msuliman: <p>Hi everyone.</p>
<p>I have a model that is built on the top of multiple nn.Modules and my model have two loss functions. The error of one of these two loss functions will have to backpropagate through the whole network while the second error should only update one of the modules that build the model.</p>
<p>As an example, my modules are:</p>
<p>class first_module(nn.Module):<br>
<span class="hashtag">#some</span> code here#</p>
<p>class second_module(nn.Module):<br>
<span class="hashtag">#some</span> code here#</p>
<p>My final network is something like:</p>
<p>class model(nn.Module):<br>
def __ init __(self):<br>
self.first_part = first_module()<br>
self.second_part = second_module()</p>
<p>def forward(input):<br>
x = self.first_part (input)<br>
y = self.second_part(x)<br>
z = some_operations(y)</p>
<p>return z,y</p>
<p>Now on my main code i have:</p>
<p>network = model()<br>
output_1, output_2 = network(image)<br>
loss_1 = error_fn(output_1 , labels) # main loss<br>
loss_2 = error_fn(output_2, ref) # i want this error to only update layers on (class second_module)</p>
<p>In other words, second_module is updated jointly by loss_1 and loss_2, but other parts of the modules are only updated by loss_1.</p>
<p>Your help is highly appreciated.</p>

ptrblck: <p>You could use the <code>inputs</code> argument in <code>backward</code> to specify the gradient calculation for these parameters:</p>
<pre><code class="lang-python">class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.first_part = nn.Linear(10, 10)
        self.second_part = nn.Linear(10, 10)

    def forward(self, input):
        x = self.first_part(input)
        y = self.second_part(x)
        z = y * 2.
        return z, y

network = Model()
image = torch.randn(1, 10)
output_1, output_2 = network(image)
loss_1 = output_1.mean()
loss_2 = output_2.mean()

# make sure gradients are empty
for name, param in network.named_parameters():
    if param.grad is not None:
        print('{}.grad.abs().sum(): {}'.format(name, param.grad.abs().sum()))


# calculate gradients for `second_part` only
loss_2.backward(inputs=list(network.second_part.parameters()), retain_graph=True)

# check grads
for name, param in network.named_parameters():
    if param.grad is not None:
        print('{}.grad.abs().sum(): {}'.format(name, param.grad.abs().sum()))

# calculate all gradients
loss_1.backward()
for name, param in network.named_parameters():
    if param.grad is not None:
        print('{}.grad.abs().sum(): {}'.format(name, param.grad.abs().sum()))
</code></pre>

msuliman: <p>Many thanks for the reply. That is very helpful. It worked <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=10" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>
<p>One followup question that might be simple: given that I am having optimizer.zero_grad() at the start of each epoch iteration during my training, is there anything else that i need to do after loss_2.backward(…).<br>
I am just confused about why you are making sure that gradients are empty after every step above. What if they aren’t (which is indeed the case after the first backwards).</p>

ptrblck: <p>Checking for the empty (or zero) gradients was just used in this test to show that the behavior is indeed as I want it to be. I.e. the first</p>
<pre><code class="lang-python">loss_2.backward(inputs=list(network.second_part.parameters()), retain_graph=True)
</code></pre>
<p>call would populate the gradients of <code>second_part</code>'s parameters and leave the others untouched.</p>
<p>You should be fine using <code>optimizer.zero_grad()</code> at the beginning.</p>