DataParallel not splitting the data between the GPUs

nbansal90: <p>Hi Everyone,</p>
<p>I am using 4 GPUs for training a model, which was earlier being trained on single gpu, for leveraging the data parallelism and speeding up the training process. For my code, I have set the batch size as 8, and was expecting that while training on 4 GPUs the data would evenly distribute among the 4gpus as individual batch size of 2. But I find that all the <code>inputs</code> are always placed on GPU 0.</p>
<p><code>Code for Data Parallelism</code></p>
<pre><code class="lang-auto">print ("GPU Count ", torch.cuda.device_count())
    models["encoder"] = ResnetEncoder(
        num_layers=options.resnet_num_layers, pretrained=True)
    models["depth"] = DepthDecoder(
        models["encoder"].num_ch_enc, scales=range(args.numScales), use_bn=args.use_bn)

    if torch.cuda.device_count() &gt; 1:
        models["encoder"] = torch.nn.DataParallel(models["encoder"])
        models["decoder"] = torch.nn.DataParallel(models["depth"])
    models["encoder"].cuda()
    models["depth"].cuda()
</code></pre>
<p><code> Data Loading Phase</code></p>
<pre><code class="lang-auto">for epoch in range(options.numEpochs):
        data_iterator = tqdm(dataloader)

        optimizer.zero_grad()
        for sampleIndex, inputs in enumerate(data_iterator):
            for k in inputs:
                print (inputs[k].size())
            # predict depth maps, now only apply on the first frame
            outputs = {}
            for seq_index in range(options.numFrames):
                # images are supposed to be normalized to [0, 1] in dataloader
                inputs['image', seq_index] = inputs['image', seq_index].cuda()
                inputs['extrinsic', seq_index].cuda()
                print (inputs['image', seq_index].device)
</code></pre>
<p>The <code>inputs</code> is a dictionary which has tensors of following size:</p>
<pre><code class="lang-auto">torch.Size([8, 3, 480, 640])
torch.Size([8, 3, 480, 640])
torch.Size([8, 480, 640])
torch.Size([8, 4, 4])
torch.Size([8, 3, 480, 640])
torch.Size([8, 3, 480, 640])
torch.Size([8, 480, 640])
torch.Size([8, 4, 4])
torch.Size([8, 3, 480, 640])
torch.Size([8, 3, 480, 640])
torch.Size([8, 480, 640])
torch.Size([8, 4, 4])
torch.Size([8, 6])
</code></pre>
<p>The following print statement <code> print (inputs['image', seq_index].device)</code> always print <code>Cuda: 0</code>. I was expecting it should have printed <code>1, 2 or 3 </code> too but looks like the data is not getting splitted between all the GPUs. Am I missing something here ?</p>

ptrblck: <p>The data will be split in the <code>forward</code> method of the model (actually before calling it), so you might want to check the shape and device of the data inside the model not in the <code>DataLoader</code> loop.</p>

nbansal90: <p><a class="mention" href="/u/ptrblck">@ptrblck</a>  Thank you! That works absolutely as expected now . I had an added query to my previous question. In my network model as printout out in the below code snippet:</p>
<pre><code class="lang-auto">      for seq_index in range(options.numFrames):
                # images are supposed to be normalized to [0, 1] in dataloader
                inputs['image', seq_index] = inputs['image', seq_index].cuda()
                inputs['extrinsic', seq_index].cuda()
                if seq_index == 0:
                    # use color augmented image for training (not to use for loss computing)
                    features = models["encoder"](inputs['image_aug', seq_index].cuda())
                    print ("Type of feature ", len(features))
                    multi_scale_depth_pred = models["depth"](features)
</code></pre>
<p>Line <code>features = models["encoder"](inputs['image_aug', seq_index].cuda())</code>  returns a list of size <code>5</code> where each of the element of the list itself is a 4-dimensional tensor.<code>(Batch Size x Channel x H xW)</code> Now this <code>features</code> is fed to another model which is <code>model["depth"]</code> Now at this point I had two question:</p>
<ol>
<li>
<p>Since I again wish to split this feature in between my multiple GPUs, Do I need to perform any specific operation on <code>features</code> since <code>features.cuda()</code> is an invalid command. (since <code>features</code> is a list)</p>
</li>
<li>
<p>The second part is: <code>features</code> which I am receiving from <code>models["encoder"]</code> would come back from multiple GPUs , how do I make sure the <code>features</code> coming from a given gpu goes to same gpu in step <code>multi_scale_depth_pred = models["depth"](features)</code></p>
</li>
</ol>
<p>Thank you,<br>
Nitin</p>

ptrblck: <ol>
<li>
<p>I’m not sure, if <code>nn.DataParallel</code> works with lists as inputs and I would recommend to check it with a very simple model.</p>
</li>
<li>
<p><code>nn.DataParallel</code> will split the input batch in dim0 and send each chunk to the corresponding device. The result will be gathered on the default device again. Passing this tensor into the next <code>nn.DataParallel</code> module will perform the same splitting. The general workflow is explained <a href="https://medium.com/huggingface/training-larger-batches-practical-tips-on-1-gpu-multi-gpu-distributed-setups-ec88c3e51255">here</a> in more detail. If you need more control how the data is split etc. you could try to use a manual approach by cloning some logic from the <code>nn.DataParallel</code> implementation.</p>
</li>
</ol>

nbansal90: <p>Thanks <a class="mention" href="/u/ptrblck">@ptrblck</a>  for the prompt reply. I actually printed the size of the features in the <code>model["decoder"]</code> <code>forward</code> function but I found that <code>batch_size</code> was not reduced (implying the data not being distributed among multiple GPUs. To be specific <code>features</code> is a list of size 5,and each of its element is a 4 dimensional tensor(Batch Size x Channel x h x w). Point I am getting confused is, each element of the list (which is a tensor) has to shared across multiple gpu(in Dataparallel case), Which<br>
I am not sure , how shall I go about this, since <code>features.cuda()</code> is an obvious invalid statement ( <code> AttributeError: 'list' object has no attribute 'cuda'</code>)</p>

ptrblck: <p>I don’t quite understand the “each element of the list (which is a tensor) has to shared across multiple gpu(in Dataparallel case)”.<br>
<code>nn.DataParallel</code> will split the input tensor in <code>dim0</code> and will send each chunk to a GPU. The elements won’t be duplicated, which implies the “sharing”.<br>
Since the <code>list</code> input is apparently not working, you could create a tensor of this list and make sure that the split dimension is in <code>dim0</code>.</p>