Multi-gpu model with loss functions

jmontoyaz: <p>Hi everybody</p>
<p>I’m getting familiar with training multi-gpu models in Pytorch. I found this official tutorial on <a href="https://pytorch.org/tutorials/intermediate/model_parallel_tutorial.html" rel="noopener nofollow ugc">best practices for multi-gpu training</a>. I adapted the original code in order to return two predictions/outputs and use two losses afterwards. Predicted values are on separate GPUs, also note that the model uses 2x GPUs.<br>
The code looks as follows:</p>
<pre><code class="lang-auto">import torch
import torch.nn as nn
import torch.optim as optim


class ToyModel(nn.Module):
    def __init__(self):
        super(ToyModel, self).__init__()
        self.net0 = torch.nn.Linear(10, 10).to('cuda:0')
        self.relu = torch.nn.ReLU()
        self.net1 = torch.nn.Linear(10, 5).to('cuda:1')

    def forward(self, x):
        x_out0 = self.relu(self.net0(x.to('cuda:0')))
        x_out1 = self.net1(x_out0.to('cuda:1'))
        return x_out1, x_out0


if __name__ == '__main__':
    
    model = ToyModel()
    loss_fn = nn.MSELoss()
    optimizer = optim.SGD(model.parameters(), lr=0.001)

    optimizer.zero_grad()
    outputs1, outputs0 = model(torch.randn(20, 10))
    labels0 = torch.randn(20, 10).to('cuda:0')
    labels1 = torch.randn(20, 5).to('cuda:1')
    loss = loss_fn(outputs1, labels1) + loss_fn(outputs0, labels0) # DOES NOT WORK
    #loss = loss_fn(outputs0, labels0) # DOES WORK
    #loss = loss_fn(outputs1, labels1) # DOES WORK
    loss.backward()
    optimizer.step()
</code></pre>
<p>I’m getting the following error message in the backward pass of the loss function (<code>loss.backward()</code>):</p>
<blockquote>
<p>RuntimeError: Function AddBackward0 returned an invalid gradient at index 1 - expected device cuda:0 but got cuda:1</p>
</blockquote>
<p>Could somebody help me to clear out how to solve this issue? Note that <code>labels0</code> and <code>outputs0</code> are in GPU <code> 'cuda:0'</code>, whilst <code>labels1</code> and <code>outputs1</code> are in GPU <code> 'cuda:1'</code>. Is this the way at all to accomplish this task? or do there are better practices? I’ve a big model on 2+ GPUs and would like also to compute intermediate losses</p>

ptrblck: <p>You would have to push the calculated losses to the same device before summing them.<br>
This should work:</p>
<pre><code class="lang-python">loss = loss_fn(outputs1, labels1) + loss_fn(outputs0, labels0).to('cuda:1')
</code></pre>

jmontoyaz: <p>Great, thank you, it worked out! which GPU device I use for backprop does not affect the results/backprop itself, right? Especially, because the whole composed model is: <code>cnn1(cnn0(x))</code>, i.e. the outermost <code>cnn1</code> is in device <code>cuda:1</code> and the innermost <code>cnn0</code> is in device <code>cuda:0</code>. So, if I want a proper backward flow, should I put all the calculated losses in the device associated to the outermost cnn (<code>cuda:1</code>)? The following worked as well:</p>
<pre><code class="lang-auto">loss = loss_fn(outputs1, labels1).to('cuda:0') + loss_fn(outputs0, labels0)
</code></pre>

ptrblck: <p>The <code>to()</code> operation is differentiable and Autograd will backpropagate “through” this operation to the corresponding devices. I.e. the gradient calculation for the model on GPU0 will be performed on GPU0 and the same applies for GPU1.<br>
Your approach would thus also work and I wouldn’t expect any difference in the performance etc.</p>