Initializing a member tensor after creation with DataParallel (repost)

pytorcher: <p>I have a member tensor that is created/saved during a backward hook. Hook looks like:</p>
<pre><code class="lang-auto">def saveGrad(self, grad_input, grad_output):
        self.currentGrad = grad_output[0].detach()
</code></pre>
<p>This works fine on one GPU but when running on multiple it tells me 'AttributeError: (module) object has no attribute ‘currentGrad’ '. I imagine I could do a .copy_() or something, but I won’t actually know the shape until the network has been run on an image.</p>

albanD: <p>Hi,</p>
<p>Where do you get this error? Keep in mind that when running on multiple GPUs, a different clone of the Module runs on each device. So they don’t share <code>self</code>.</p>

pytorcher: <p>Right, that is my understanding. But I don’t know what to do to ensure something is accessible in both clones.  I am getting the error in a member function</p>
<pre><code class="lang-auto">    def memberFunction(self):
            self.otherFunction(self.currentGrad)
</code></pre>
<p>From <a href="https://discuss.pytorch.org/t/multi-gpu-backwards-hook-on-wrong-device/92942/2" class="inline-onebox">Multi GPU backwards hook on wrong device</a> the register_buffer fixed this error for all the places where I do know the tensor size ahead of time.  But the question is essentially, how do i register_buffer so it works on multiple GPUs if the size is dependent on the input.</p>

albanD: <p>I am not sure to understand why not knowing the size is an issue? Can you just resize the Tensor before using it?</p>

pytorcher: <p>Possibly, how would I do that?  I tried:<br>
<code>self.register_buffer('currentGrad', torch.zeros(1).to(gf.device).double())</code><br>
in init() and then in the backward hook I do:</p>
<pre><code class="lang-auto">def saveGrad(self, grad_input, grad_output):
    if(len(self.currentGrad) == 1):
        self.currentGrad = self.currentGrad.repeat(grad_output[0].shape)
    self.currentGrad = grad_output[0].detach()
</code></pre>
<p>But that gives me an error that implies it did not save the modification:</p>
<pre><code class="lang-auto">ValueError: not enough values to unpack (expected 4, got 1)
Uncaught exception. Entering post mortem debugging

</code></pre>

pytorcher: <p>Been a few days, any thoughts?</p>

albanD: <p>The error seems to point to another place in the code no? You need to make sure not to unpack too many values.</p>
<p>Also for <code>torch.zeros(1).to(gf.device).double()</code>, you can replace it with <code>torch.zeros(1, device=gf.device, dtype=torch.double)</code>.</p>

pytorcher: <p>Ah, good to know on the second part.</p>
<p>But no, your suggestion above was to initialize it as a buffer so it gets picked up by dataparallel and then resize it later before using it, correct?  When I try to do that it looks like the resize is not getting picked up by the buffer that was registered and when it gets called one (or both) of the copies still have the original size.</p>
<p>Just to put all the code in the same place:</p>
<pre><code class="lang-auto">def init(self):
    ...
    self.register_buffer('currentGrad', torch.zeros(1).to(gf.device).double())

def memberFunction(self):
    self.otherFunction(self.currentGrad)

def saveGrad(self, grad_input, grad_output):
    if(len(self.currentGrad) == 1):
        self.currentGrad = self.currentGrad.repeat(grad_output[0].shape)
    self.currentGrad = grad_output[0].detach()
</code></pre>
<p>What I had noticed with the earlier thread is it seems like setting things with <code>self.currentGrad=...</code> would cause the problem where it is not found but <code>self.currentGrad += ...</code> for in place modifications did work.  Is there a way to do an in place resize maybe?</p>

albanD: <p>Sorry this thread is quite long and I don’t have it in my head.</p>
<p>But each “self” that you get on different devices are different. So any change you make to one of them will not be reflected on the others, or the original module.<br>
But for a given Module, while running on its GPU, if you set a value in the forward, it will still be there in the forward.</p>
<p>Also as mentioned in the doc, <code>register_backward_hook()</code> is not working properly at the moment and should not be used.</p>

pytorcher: <p>I feel like that is not what <a class="mention" href="/u/ptrblck">@ptrblck</a> was saying here: <a href="https://discuss.pytorch.org/t/multi-gpu-backwards-hook-on-wrong-device/92942/5" class="inline-onebox">Multi GPU backwards hook on wrong device</a>.  It sounded like doing register_buffer allows you to define “self” variables that ensure that they work properly on multiple GPUs.  Did I misread his post about that?</p>
<p>I feel like I need to use register_backward_hook.  It is also working entirely correct for me on one GPU.  Is there a page or thread you can point me to for what to do instead?</p>

albanD: <aside class="quote no-group" data-username="pytorcher" data-post="10" data-topic="93967">
<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/p/7ea924/48.png" class="avatar"> pytorcher:</div>
<blockquote>
<p>It sounded like doing register_buffer allows you to define “self” variables that ensure that they work properly on multiple GPUs. Did I misread his post about that?</p>
</blockquote>
</aside>
<p>It does, but depends what you want to do.<br>
Having it being a buffer means that the replication code will properly replicate it and move it to the right GPU for this particular replica. So you will be able to use it properly with the inputs from this Module.<br>
But that does not mean that it will be shared between the replicas!</p>

pytorcher: <p>Ok, so I think that is essentially the core of my question.  Is it possible to resize a buffer after it has been declared?  And additionally is it possible to add a buffer after DataParallel has been called?  Or if I want to do these things, will I need to essentially do:</p>
<pre><code class="lang-auto">net = net.module#get rid of data parallel
net.registerNewBuffers()
net = DataParallel(net)#redeclare as data parallel
</code></pre>
<p>And As an extra question, if I have 10 buffers do I need to have them all have a personal name or can I make an array of buffers?</p>

albanD: <blockquote>
<p>Is it possible to resize a buffer after it has been declared?</p>
</blockquote>
<p>Yes, you can do <code>.resize_()</code>. Note that the content of the Tensor will be un-initialized though. So you need to make sure that you either zero it or write some value before using it.</p>
<blockquote>
<p>And additionally is it possible to add a buffer after DataParallel has been called?</p>
</blockquote>
<p>Maybe but I would not recommend that. It sounds quite fragile.</p>
<blockquote>
<p>if I have 10 buffers do I need to have them all have a personal name or can I make an array of buffers?</p>
</blockquote>
<p>No you cannot do an array of buffers at the moment. You will have to register them one by one.</p>

pytorcher: <p>I think the resize worked!  But replacing register_backwards_hook() did not fix my original problem.  I’ll post a new thread for that for where I am now.  Thanks!</p>
<aside class="quote quote-modified" data-post="1" data-topic="96046">
  <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/p/7ea924/48.png" class="avatar">
    <a href="https://discuss.pytorch.org/t/multi-gpu-hook-not-correctly-filling-buffer/96046">Multi GPU Hook not correctly filling buffer</a> 
  </div>
  <blockquote>
    Coming from <a href="https://discuss.pytorch.org/t/multi-gpu-backwards-hook-on-wrong-device/92942" class="inline-onebox">Multi GPU backwards hook on wrong device</a>, that fixed it so it didnt error anymore, but it is not actually returning the same value with 1 GPU and 2.  I was informed in <a href="https://discuss.pytorch.org/t/initializing-a-member-tensor-after-creation-with-dataparallel-repost/93967/8" class="inline-onebox">Initializing a member tensor after creation with DataParallel (repost)</a> that I shouldn’t be using register_backwards_hooks at all, so now I am doing it with a custom function.  I think this is all the relevant code: 
My values tracker looks like 
class valueTracker(nn.Module):
    def __init__(self, out_channels):
     …
  </blockquote>
</aside>