Insert data processing between network and loss

Fabrice_Auzanneau: <p>Hi</p>
<p>I tried to find a similar topic, but did not find any. My problem is the following: try to approximate the inverse of a set of filters using a neural network. I’m using pytorch and I have 1D data.</p>
<p>The approach is as follows: The network is defined and I do the usual forward / backward steps. The forward pass is done as:</p>
<pre><code class="lang-auto"># Zero the gradients
optimizer.zero_grad()
# Perform forward pass
outputs = mlp(inputs)
</code></pre>
<p>Then I insert my filters:</p>
<pre><code class="lang-auto">outputs = myFilters(outputs)
</code></pre>
<p>And compute the loss, trying to fit the output with the input:</p>
<pre><code class="lang-auto"># Compute loss
loss = loss_function(outputs, inputs)
loss.backward()
optimizer.step()
</code></pre>
<p>But I get an error in the <code>loss.backward()</code> step:</p>
<blockquote>
<p>RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn</p>
</blockquote>
<p>As I understand it, it says that Pytorch cannot backpropagate the error through the filter before passing through the network. How can I do that ?</p>
<p>Thanks for your help.</p>

ptrblck: <p>Could you post the definition of <code>myFilters</code> as it seems this method detaches the tensor from the computation graph and is thus raising the error.</p>

Fabrice_Auzanneau: <p>Thanks for your answer<br>
currently, the filter is done as follows. First, some parameters are defined in advance (before the training phase):</p>
<pre><code class="lang-auto">    # Sigmoid parameter
    sig = 5
    
    # Filter parameters
    cutoff = 500
    ftype = 'cheby2'
    btype = 'low'
    order = 5
    att = 30 # dB
    nyq = 22050
    b, a = signal.iirfilter(order, cutoff / nyq, rs=att,
                                    btype=btype, ftype=ftype)
</code></pre>
<p>and during training:</p>
<pre><code class="lang-auto">            # Perform forward pass
            outputs = mlp(inputs)
            
            # Filter
            tmp = 1 + np.exp(-outputs.detach().numpy() * sig)
            dat = 2 / tmp - 1
            outputData = signal.lfilter(b, a, dat)
            outputs = torch.from_numpy(outputData)
            
            # Compute loss
            loss = loss_function(outputs, inputs)
            loss.backward()
</code></pre>

ptrblck: <p>You are explicitly detaching the model output ans use numpy which Autograd won’t be able to track. You could either use pure PyTorch methods or create a custom <code>autograd.Function</code> including the <code>backward</code> method.</p>

Fabrice_Auzanneau: <p>When you say ’ You could either use pure PyTorch methods’, do you mean for the <code>#Filter</code> block, instead of using numpy?<br>
I guess I can do that for the 2 first lines, but how can I do that for the <code>signal.lfilter</code> function? Is there any equivalent in pytorch?</p>
<p><strong>Edit:</strong><br>
I replaced the lines:</p>
<pre><code class="lang-auto">            tmp = 1 + np.exp(-outputs.detach().numpy() * sig)
            dat = 2 / tmp - 1
</code></pre>
<p>by</p>
<pre><code class="lang-auto">            tmp = 1 + torch.exp(torch.mul(outputs, -sig))
            dat = torch.div(2, tmp) - 1
</code></pre>
<p>So I guess that now, the autograd will automatically take care of them. I still need to find how to compute the low pass filter.</p>
<p>I see <a href="https://pytorch.org/audio/stable/generated/torchaudio.functional.lfilter.html#torchaudio.functional.lfilter" rel="noopener nofollow ugc">here</a> that there exists a <code>torchaudio.functional.lfilter</code> function:</p>
<pre><code class="lang-auto">torchaudio.functional.lfilter(waveform: Tensor, 
a_coeffs: Tensor, 
b_coeffs: Tensor, 
clamp: bool = True, 
batching: bool = True) → Tensor
</code></pre>
<p>If I use it, will it be automatically managed by the autograd?</p>
<p><strong>Edit2:</strong><br>
So I changed the filter block with:</p>
<pre><code class="lang-auto">            # Filter
            tmp = 1 + torch.exp(torch.mul(outputs, -sig))
            dat = torch.div(2, tmp) - 1
            outputs = functional.lfilter(dat, a_tensor, b_tensor)
</code></pre>
<p>where:</p>
<pre><code class="lang-auto">    b, a = signal.iirfilter(order, cutoff / nyq, rs=att,
                                    btype=btype, ftype=ftype)
    a_tensor = torch.from_numpy(a).to(torch.float32)
    b_tensor = torch.from_numpy(b).to(torch.float32)
</code></pre>
<p>Now I get a new error message:</p>
<blockquote>
<pre><code>outputs = functional.lfilter(dat, a_tensor, b_tensor)
</code></pre>
<p>File “C:\Users\me\AppData\Local\Programs\Spyder\pkgs\torchaudio\functional.py”, line 594, in lfilter<br>
o0.addmv_(windowed_output_signal, a_coeffs_flipped, alpha=-1)</p>
<p>RuntimeError: Output 0 of UnbindBackward is a view and is being modified inplace. This view is the output of a function that returns multiple views. Such functions do not allow the output views to be modified inplace. You should replace the inplace operation by an out-of-place one.</p>
</blockquote>
<p>What does this mean?</p>
<p><strong>Edit3:</strong><br>
As I want this processing block to remain unchanged during the training process, I tried to add a no_grad() instruction:</p>
<pre><code class="lang-auto">            # Filter
            with torch.no_grad():
                tmp = 1 + torch.exp(torch.mul(outputs, -sig))
                dat = torch.div(2, tmp) - 1
                outputs = functional.lfilter(dat, a_tensor, b_tensor)
</code></pre>
<p>but this leads to the following error:</p>
<pre><code class="lang-auto">    loss.backward()

  File "C:\Users\me\AppData\Local\Programs\Spyder\pkgs\torch\tensor.py", line 221, in backward
    torch.autograd.backward(self, gradient, retain_graph, create_graph)

  File "C:\Users\me\AppData\Local\Programs\Spyder\pkgs\torch\autograd\__init__.py", line 132, in backward
    allow_unreachable=True)  # allow_unreachable flag

RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
</code></pre>
<p>I’m back to the beginning…</p>

ptrblck: <p>Your approach looks correct. Could you try to <code>.clone()</code> the input to the function to check if this would get rid of the inplace error?</p>

Fabrice_Auzanneau: <p>Thanks<br>
When I do this:</p>
<pre><code class="lang-auto">            inp = torch.clone(outputs)
            tmp = 1 + torch.exp(torch.mul(inp, -sig))
            dat = torch.div(2, tmp) - 1
            outputs = functional.lfilter(dat, a_tensor, b_tensor)
</code></pre>
<p>I still get the inplace error message:</p>
<blockquote>
<pre><code>outputs = functional.lfilter(dat, a_tensor, b_tensor)
</code></pre>
<p>File “C:\Users\me\AppData\Local\Programs\Spyder\pkgs\torchaudio\functional.py”, line 594, in lfilter<br>
o0.addmv_(windowed_output_signal, a_coeffs_flipped, alpha=-1)</p>
<p>RuntimeError: Output 0 of UnbindBackward is a view and is being modified inplace. This view is the output of a function that returns multiple views. Such functions do not allow the output views to be modified inplace. You should replace the inplace operation by an out-of-place one.</p>
</blockquote>
<p>and if I try to clone the input data of the filter (<code>dat</code>), it doesn’t change.</p>
<p>But adding <code>no_grad</code> before the filters, like this:</p>
<pre><code class="lang-auto">            # Filter
            with torch.no_grad():
                inp = torch.clone(outputs)
                tmp = 1 + torch.exp(torch.mul(inp, -sig))
                dat = torch.div(2, tmp) - 1
                outputs = functional.lfilter(dat, a_tensor, b_tensor)
</code></pre>
<p>leads to the following error:</p>
<blockquote>
<pre><code>loss.backward()
</code></pre>
<p>File “C:\Users\me\AppData\Local\Programs\Spyder\pkgs\torch\tensor.py”, line 221, in backward<br>
torch.autograd.backward(self, gradient, retain_graph, create_graph)</p>
<p>File “C:\Users\me\AppData\Local\Programs\Spyder\pkgs\torch\autograd_<em>init</em>_.py”, line 132, in backward<br>
allow_unreachable=True)  # allow_unreachable flag</p>
<p>RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn</p>
</blockquote>

ptrblck: <p>The inplace operation should have been removed in <a href="https://github.com/pytorch/audio/pull/1244">this PR</a> two years ago so you might need to update <code>torchaudio</code> to a newer version.</p>

Fabrice_Auzanneau: <p>I upgraded torchaudio, as well as torch (my python setup is a mess, so it took some time), and removed the <code>with torch.no_grad():</code> and now it works.</p>
<blockquote>
<h3>Training…</h3>
<p>Loss after epoch    0: train  0.012097, valid  0.047847 ** Saving **<br>
Loss after epoch    1: train  0.009809, valid  0.046365 ** Saving **<br>
Loss after epoch    2: train  0.009157, valid  0.046639 ** Saving ** .<br>
Loss after epoch    3: train  0.009253, valid  0.048142<br>
Loss after epoch    4: train  0.009138, valid  0.048630 ** Saving **<br>
Loss after epoch    5: train  0.008838, valid  0.048512 ** Saving ** .<br>
Loss after epoch    6: train  0.008995, valid  0.047558<br>
Loss after epoch    7: train  0.008856, valid  0.048354<br>
Loss after epoch    8: train  0.008831, valid  0.050807 ** Saving ** .<br>
Loss after epoch    9: train  0.008876, valid  0.046235<br>
Loss after epoch   10: train  0.008922, valid  0.048804</p>
</blockquote>
<p>Thanks a lot for your help!<br>
Have a good day</p>