Ddp: evaluation, gather output, loss, and stuff. how to?

sbelharbi: <p>— sorry for possible redundancy with other threads but i didnt find an answer.<br>
hi,</p>
<p>trying to do evaluation in ddp.<br>
forward in each gpu works fine.<br>
but how can i gather all the outputs to a single gpu (master for example), to measure metrics onces an over ENTIRE minibatch because each process forward only a chunk of the minibatch.<br>
or we can compute the metric over each gpu, but average over single one to reduce the communication overhead, then broadcast to others if necessary also to reduce communication.</p>
<p>how to that? it isa  reduce-all op over outputs.<br>
ddp seems to focus only on synch grads…,.<br>
what if you want to synch outputs, losses, other stuff…<br>
for example the computed loss is not the full loss over ENTIRE minibatch, but only a chunk.</p>
<p>so, is there a way to gather things on demand?<br>
still reading the doc.<br>
i may have missed something.<br>
but, i dont think i saw any doc about this, probably ddp does not do this but only synch grads.</p>
<p>pytorch <a href="https://github.com/PyTorchLightning/pytorch-lightning/discussions/3225" rel="noopener nofollow ugc">lightning</a> and also <a href="https://github.com/PyTorchLightning/pytorch-lightning/issues/702" rel="noopener nofollow ugc">here</a> seems to have started working on this…<br>
still reading their thread.<br>
please feel free to leave a message or a snippet of code on how to do it.</p>
<p>the idea is to be able to evaluate using distributed computation, then gather the output or some intermediate metrics to single machine to do the average.</p>
<p>also, cant evaluate properly because every process have access only to a chunk of the minibatch. so, you cant loop over entire dataset unless you create a NEW dataloader, and use ddp.module to do the forward not ddp. because if you try to use ddp forward while only the master is doing the forward, it will get stuck waiting for other processes to do their forward… also, we are using distributedsampler… so it wil prevent a process from seeing the entire data.</p>
<p>again, i may have missed something.</p>
<p>thanks</p>

sbelharbi: <p>first of all, pytorch lightning has done it!!! that’s cool.<br>
<a href="https://torchmetrics.readthedocs.io/en/latest/" rel="noopener nofollow ugc">metrics</a> over distributed models, an entire package just for this.</p>
<p>based on this threads <a href="https://github.com/PyTorchLightning/pytorch-lightning/issues/3159" rel="noopener nofollow ugc">one</a> and <a href="https://github.com/PyTorchLightning/pytorch-lightning/discussions/3225" rel="noopener nofollow ugc">two</a> here are some solutions.</p>
<ol>
<li>
<p>drop distrib.comput. meaning you loose the the distributed comp power. evaluate only over the master for example. to do this, you need to drop the distributed sampler over the validation. use it only for trainsent. the master now can see the entire dataset. you can run and get the performance over the master. either you allow  the other processes to do the same thing as the master. so it is a waste of energy. but they will have the correct value of performance. but useless because you have it at the master. the one that you will use to log the perf. or you can block the other processes and let only the master do the evaluation. in this case, you need add some blocking barriers. free only the master. so this solution does not exploit distributed computation for evaluation.</p>
</li>
<li>
<p>use distributed comp. in this case, each gpu wil work on a chunk of minbatch in parallel. for this you need to manually synch the OUTPUT of your model either between all gpus using all_gather or just gather at the master then compute the metric. one may think to compute an unormalized metric over each gpu, then just send it in order to avoid sending an entire OUTPUT of a network. some metrics may not like that. alos, you may loose the chunk size, so you need to synched it as well. now, that you synched all the gpus, you can compute the metric.</p>
</li>
</ol>
<p>downside of solution 2:<br>
a. you need to deal with each type of your network output.<br>
b. the synch may create an overhead that will slow things. does anyone know how much to cost to do <code>torch.distributed.all_gather</code>? compared to <code>torch.distributed.gather</code>? the last one is expected to be cheap. but you need to keep track in your code that only master has the right metric…<br>
c. your code will be filled with synch calls…<br>
d. there are metrics that do not like this at all because they need to process each sample alone. and if the metric is class that update the metric after seeing each sample, there will be a problem… you need to store all the predictions, then synch them, then do the update by looping over each sample… metrics that can simply average over the entire predictions are fine.</p>
<p>based on code from pytorch lightning, here is a code to synch, and pseudo-code to use it</p>
<pre><code class="lang-python">import torch
import torch.distributed as dist

def sync_tensor_across_gpus(t: Union[torch.Tensor, None]
                            ) -&gt; Union[torch.Tensor, None]:
    # t needs to have dim 0 for troch.cat below. 
    # if not, you need to prepare it.
    if t is None:
        return None
    group = dist.group.WORLD
    group_size = torch.distributed.get_world_size(group)
    gather_t_tensor = [torch.zeros_like(t) for _ in
                       range(group_size)]
    dist.all_gather(gather_t_tensor, t)  # this works with nccl backend when tensors need to be on gpu. 
   # for gloo and mpi backends, tensors need to be on cpu. also this works single machine with 
   # multiple   gpus. for multiple nodes, you should use dist.all_gather_multigpu. both have the 
   # same definition... see [here](https://pytorch.org/docs/stable/distributed.html). 
   #  somewhere in the same page, it was mentioned that dist.all_gather_multigpu is more for 
   # multi-nodes. still dont see the benefit of all_gather_multigpu. the provided working case in 
   # the doc is  vague... 
    return torch.cat(gather_t_tensor, dim=0)

</code></pre>
<p>usage,</p>
<pre><code class="lang-python"># size images: (32, 3, 224, 224)
cl_logits = self.model(images)  #
# size cl_logits: (32, 100)  # one process.
cl_logits = sync_tensor_across_gpus(cl_logits)
# size now: (64, 100)  
# gather the 2 processes where each has a chunk of size 32 samples.
# using torch,distributed.all_gather, now every gpu has this full minibatch.
</code></pre>
<p>again, please let me know what is the optimum cost for synch, all_gather, or just gather at the master? is the cost of all_gather is huge when the tensors are large?</p>
<p>also, in some metrics, you can simply sync the unnormalized value to avoid sync the model output. you need to synch the total number of samples for normalization.</p>
<p>thanks</p>

gcramer23: <aside class="quote no-group" data-username="sbelharbi" data-post="1" data-topic="130593">
<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/s/f475e1/48.png" class="avatar"> sbelharbi:</div>
<blockquote>
<p>how to that? it isa reduce-all op over outputs.<br>
ddp seems to focus only on synch grads…,.<br>
what if you want to synch outputs, losses, other stuff…<br>
for example the computed loss is not the full loss over ENTIRE minibatch, but only a chunk.</p>
<p>so, is there a way to gather things on demand?<br>
still reading the doc.<br>
i may have missed something.<br>
but, i dont think i saw any doc about this, probably ddp does not do this but only synch grads.</p>
</blockquote>
</aside>
<p>DDP provides gradient synchronization across processes. If you require data be shared between processes you need to communicate between the processes <a href="https://pytorch.org/docs/stable/distributed.html" rel="noopener nofollow ugc">Distributed communication package - torch.distributed — PyTorch 1.9.0 documentation </a>.</p>