Tensor Hook triggered despite Handle.remove() when Replacing Hook with new one

moutan: <p>For my purposes, I want only a specific set of weights in Conv2D kernels to update for a batch. Lets say for kernel w/ dimensions <em>(40,C_In,k,k)</em> I want batch 1 to cause the update of only kernel weights[0: 20] and batch 2 to cause the update of kernel weights[20:]. For this , I define the following hooks:</p>
<pre><code class="lang-auto">def hook_batch_1 (grad):
    grad_clone = grad.clone()
    grad_clone[20:] = 0
    print("Hook for Batch 1 Working")
    return grad_clone  

def hook_batch_2 (grad):
    grad_clone = grad.clone()
    grad_clone[:20] = 0
    print("Hook for Batch 2 Working")    
    return grad_clone  
</code></pre>
<p>I have a list where the corresponding handles are stored:</p>
<pre><code class="lang-auto">tensor_hook_handles = [ ]
tensor_hook_handles.append(layer.weight.register_hook(hook_batch_1))
</code></pre>
<p>and once batch1 is processed, I then call the .Remove() method on the handles and proceed to set the new hooks for batch2</p>
<pre><code class="lang-auto">for handle in tensor_hook_handles:
    handle.remove()
tensor_hook_handles.clear() #empty list
tensor_hook_handles.append(layer.weight.register_hook(hook_batch_2))
</code></pre>
<p>However,when the new hook is set, the weights that get updated are still the ones that hook_batch_1 defined. Interestingly, the print I get is “Hook for Batch 2 Working” which indicates that the hooks were set but they are having no impact on the gradients given that the previous hook still prevails somehow.</p>
<p>Any ideas on how to fix this or on what I may be missing here? I am under the impression that Tensor hooks are modifiable as mentioned here: <a href="https://github.com/pytorch/pytorch/issues/25723" class="inline-onebox" rel="noopener nofollow ugc">backward_hook triggered despite RemovableHandle.remove() · Issue #25723 · pytorch/pytorch · GitHub</a></p>
<p>Best</p>

albanD: <p>Hi,</p>
<p>Your two hooks are doing the exact same thing except the printing. So it is expected that the only difference you would see is the print no?</p>

moutan: <p>Hello,</p>
<p>The hooks are targeting distinct non overlapping partitions of the kernels in the output channel dimensions so that’s not the issue. Notice the difference in the indexing <code>grad_clone[:20]</code> vs <code>grad_clone[20:]</code>. If I begin with <code>hook_batch_2</code> and then try to set <code>hook_batch_1</code> a different set of weights (the ones that correspond to kernel_weight[:20]) get updated so it’s a matter of which hook gets set first.<br>
After the first hook is set, independently of which one it is, when I set the following one, the behavior of the first one remains despite the print indicating that the new one has been set.</p>

albanD: <p>Thanks for the clarification.</p>
<blockquote>
<p>However,when the new hook is set, the weights that get updated are still the ones that hook_batch_1 defined</p>
</blockquote>
<p>one thing that can cause this is that some optimizer (adam, sgd with momentum, etc) actually still update the weights when the gradients are full of 0s. Because of all the momentum terms.<br>
So masking out the gradient won’t be enough to prevent the weights from changing when you do your optimizer step.</p>

moutan: <p>I can confirm it was the choice of optimizer which caused the behavior, both adam and sgd with momentum would behave as I previously described. Sgd with momentum=0 does what I want it to do.</p>
<p>Thank you very much for the help!</p>