Two backward hooks cannot access the same dict-object

EigilB: <p>When registering two nn.Sigmoid() backward hooks that print saved activations from a forward pass, saved in a <code>dict</code>, I encounter no problems. However, as soon as I make one of the backward hooks <code>return</code> a new <code>grad_input</code>, the other backward hook gives an error when trying to access the activation dict. From what does this error stem? And how should I access forward activations, if I cannot use a <code>dict</code>?</p>
<p><strong>Network</strong></p>
<pre><code class="lang-auto">class HookNet(nn.Module):
    def __init__(self):
        super(HookNet, self).__init__()
        self.fc1 = nn.Linear(2,2)
        self.s1 = nn.Sigmoid()
        self.fc2 = nn.Linear(2,1)
        self.s2 = nn.Sigmoid()
        self.fc1.weight = torch.nn.Parameter(torch.Tensor([[1, 2],[-1, 2]]))
        self.fc1.bias = torch.nn.Parameter(torch.Tensor([0]))
        self.fc2.weight = torch.nn.Parameter(torch.Tensor([[1, 2]]))
        self.fc2.bias = torch.nn.Parameter(torch.Tensor([0]))
        
    def forward(self, x):
        x= self.fc1(x)
        x = self.s1(x)
        x= self.fc2(x)
        x = self.s2(x)
        return x
hooknet = HookNet()
</code></pre>
<p><strong>Saving forward activations and printing them in the backward pass</strong></p>
<pre><code class="lang-auto">saved_activations = {}
def forward_save_act(name, module, input, output):
    saved_activations[name] = (input[0].data, output.data)

def backward_use_act(name, module, grad_input, grad_output):
    print('___Backward pass for '+str(name)+'___')
    input, output = saved_activations[name]
    print('Saved Input: '+str(input))
    print('Saved Output: '+str(output))
    print('Grad_input needed to be overwritten: '+str(grad_input))
    new_grad_input = output/input
    grad_tuple = (new_grad_input.data*grad_output[0],)
    print('New grad input: '+str(grad_tuple))

from functools import partial
for name, m in hooknet.named_modules():
    if type(m) == nn.Sigmoid:
        m.register_forward_hook(partial(forward_save_act, name))
        m.register_backward_hook(partial(backward_use_act, name))
</code></pre>
<p>This yields good results, and during the backward pass, the hooks are not troubled accessing <code>saved_activations</code> and printing them. An example:</p>
<pre><code class="lang-auto">inp = torch.Tensor([1, 1])
inp.requires_grad=True
out = hooknet(inp)
out.backward()
</code></pre>
<blockquote>
<p>Output: [0.91794074]<br>
<em><strong>Backward pass for s2</strong></em><br>
Saved Input: tensor([2.4147])<br>
Saved Output: tensor([0.9179])<br>
Grad_input needed to be overwritten: (tensor([0.0753]),)<br>
New grad input: (tensor([0.3801]),)<br>
<em><strong>Backward pass for s1</strong></em><br>
Saved Input: tensor([3., 1.])<br>
Saved Output: tensor([0.9526, 0.7311])<br>
Grad_input needed to be overwritten: (tensor([0.0034, 0.0296]),)<br>
New grad input: (tensor([0.0239, 0.1101]),)</p>
</blockquote>
<p>However, if I return the grad_input calculated, I encounter problems for the backward pass for s1. The interesting thing is, that the error is affiliated with the loading from the <code>dict</code>.</p>
<pre><code class="lang-auto">def backward_use_act(name, module, grad_input, grad_output):
    print('___Backward pass for '+str(name)+'___')
    input, output = saved_activations[name]
    print('Saved Input: '+str(input))
    print('Saved Output: '+str(output))
    print('Grad_input needed to be overwritten: '+str(grad_input))
    new_grad_input = output/input
    grad_tuple = (new_grad_input.data*grad_output[0],)
    print('New grad input: '+str(grad_tuple))
    return(grad_tuple)

from functools import partial
for name, m in hooknet.named_modules():
    if type(m) == nn.Sigmoid:
        m.register_forward_hook(partial(forward_save_act, name))
        m.register_backward_hook(partial(backward_use_act, name))
        
inp = torch.Tensor([1, 1])
inp.requires_grad=True
out = hooknet(inp)
out.backward()
</code></pre>
<blockquote>
<p><em><strong>Backward pass for s2</strong></em><br>
Saved Input: tensor([2.4147])<br>
Saved Output: tensor([0.9179])<br>
Grad_input needed to be overwritten: (tensor([0.0753]),)<br>
New grad input: (tensor([0.3801]),)<br>
Traceback (most recent call last):</p>
<p>File “”, line 49, in <br>
out.backward()</p>
<p>File “/Users/Eigil/opt/anaconda3/lib/python3.7/site-packages/torch/tensor.py”, line 195, in backward<br>
torch.autograd.backward(self, gradient, retain_graph, create_graph)</p>
<p>File “/Users/Eigil/opt/anaconda3/lib/python3.7/site-&gt;packages/torch/autograd/<strong>init</strong>.py”, line 99, in backward<br>
allow_unreachable=True)  # allow_unreachable flag</p>
<p>File “”, line 28, in backward_use_act<br>
print(‘<em><strong>Backward pass for ‘+str(name)+’</strong></em>’)</p>
<p>SystemError: &lt;class ‘str’&gt; returned a result with an error set</p>
</blockquote>
<p>If I hardcode the<code> grad_tuple</code>s rather than loading them from the <code>dict</code> (or pass them as an additional argument through <code>partial</code>) I encounter no problems.</p>

albanD: <p>Hi,</p>
<p>Is it really related to the dict? If you just return a constant it behaves differently?</p>

EigilB: <p>Whoops, I guess not. That’s a little embarrasing, should I change the title of the post?<br>
If I use the alternative backward hook function</p>
<pre><code class="lang-auto">def backward_use_act(name, module, grad_input, grad_output):
    print('___Backward pass for '+str(name)+'___')
    print('Grad_input needed to be overwritten: '+str(grad_input))
    grad_tuple = (grad_input[0],)
    print('New grad input: '+str(grad_tuple))
    return(grad_tuple)
</code></pre>
<p>I also get an error, when the backprop reaches s1</p>
<blockquote>
<p><em><strong>Backward pass for s2</strong></em><br>
Grad_input needed to be overwritten: (tensor([0.0753]),)<br>
New grad input: (tensor([0.0753]),)<br>
Traceback (most recent call last):</p>
<p>File “”, line 46, in <br>
out.backward()</p>
<p>File “/Users/Eigil/opt/anaconda3/lib/python3.7/site-packages/torch/tensor.py”, line 195, &gt;in backward<br>
torch.autograd.backward(self, gradient, retain_graph, create_graph)</p>
<p>File “/Users/Eigil/opt/anaconda3/lib/python3.7/site-&gt;packages/torch/autograd/<strong>init</strong>.py”, line 99, in backward<br>
allow_unreachable=True)  # allow_unreachable flag</p>
<p>File “”, line 28, in backward_use_act<br>
print(‘<em><strong>Backward pass for ‘+str(name)+’</strong></em>’)</p>
<p>SystemError: &lt;class ‘str’&gt; returned a result with an error set</p>
</blockquote>
<p>However, I get no error, if I don’t include the <code>return</code>-call:</p>
<pre><code class="lang-auto">def backward_use_act(name, module, grad_input, grad_output):
    print('___Backward pass for '+str(name)+'___')
    print('Grad_input needed to be overwritten: '+str(grad_input))
    grad_tuple = (grad_input[0],)
    print('New grad input: '+str(grad_tuple))
</code></pre>
<blockquote>
<p><em><strong>Backward pass for s2</strong></em><br>
Grad_input needed to be overwritten: (tensor([0.0753]),)<br>
New grad input: (tensor([0.0753]),)<br>
<em><strong>Backward pass for s1</strong></em><br>
Grad_input needed to be overwritten: (tensor([0.0034, 0.0296]),)<br>
New grad input: (tensor([0.0034, 0.0296]),)</p>
</blockquote>
<p>I do not understand this behavior. In this instance, the <code>return</code>-objects are identical to the computed <code>grad_input</code>, but errors are still raised?</p>

albanD: <p>I am not sure why this happens…<br>
Maybe due to the partial that you use to give the name?</p>
<p>Also note that we currently discourage the use of the backward_hook() on the nn.Module (or at least be aware that they can return incorrect results) see the doc for more details.</p>

EigilB: <p>It might be due to partial, however it seems like the name is passed without trouble (It outputs the right name, when prompted and appears to load the right activations).</p>
<p>I am aware of the discouragement, however for non-linear modules such as nn.Sigmoid, nn.Tanh and nn.ReLU, I hypothesized that due to a very simple input-output relationship (with only one node in the computation graph), the ambiguity of grad_input and grad_output disappeared, unlike for nn.Linear modules, where the gradients could be with respect to both weights, biases or earlier layers. It seems like I might have been mistaken, though.</p>
<p>If backward hooks are broken, are there any other options for changing the calculated gradients of the non-linearities?</p>

albanD: <p>Hi,</p>
<p>You can use the <code>Tensor</code> hooks directly that will work fine.<br>
In particular, if you add a hook to the input of the sigmoid, you will be able to change the gradient that flow back from there.</p>
<p>I was mentionning the name because the error seems to point to it:</p>
<pre><code class="lang-python">print(‘Backward pass for '+str(name)+'’)

SystemError: &lt;class ‘str’&gt; returned a result with an error set
</code></pre>

EigilB: <p>Thank you for your help, Alban.</p>
<p>I encounter errors even without the print statements, so I don’t think the name is inflicting the error:</p>
<pre><code class="lang-auto">def backward_use_act(name, module, grad_input, grad_output):
    grad_tuple = (grad_input[0],)
    return(grad_tuple)
</code></pre>
<blockquote>
<p>Traceback (most recent call last):</p>
<p>File “”, line 42, in <br>
out.backward()</p>
<p>File “/Users/Eigil/opt/anaconda3/lib/python3.7/site-packages/torch/tensor.py”, line 195, &gt;in backward<br>
torch.autograd.backward(self, gradient, retain_graph, create_graph)</p>
<p>File “/Users/Eigil/opt/anaconda3/lib/python3.7/site-&gt;packages/torch/autograd/<strong>init</strong>.py”, line 99, in backward<br>
allow_unreachable=True)  # allow_unreachable flag</p>
<p>SystemError: PyEval_EvalFrameEx returned a result with an error set</p>
</blockquote>
<p>For using the <code>tensor</code> hook (<code>register_hook</code>), how would I access the tensor, that gets inputted to the Sigmoid? This tensor gets created by the weights, biases and activations in a <code>nn.Linear</code> module, how do I access the resulting Tensor in my example?</p>

albanD: <p>The current workaround would be to use a <code>register_forward_hook()</code> on the nn.Module as you were doing. And in this forward hook, add a Tensor <code>register_hook()</code> on the inputs (or outputs) that are of interest.</p>
<p>This looks very surprising. Can you try to get the smallest possible example that reproduces this so that we can open a bug report on github?</p>

harini_narasimhan: <p>Did anyone found the solution? I got the same error while using .backward() to get the gradient</p>

albanD: <p>Which error? Can you share the full error stack/message and if possible a code sample to reproduce it?</p>