No gradient problem when modifying model's weights

Aobo_Chen: <p>When I was trying to add a perturbation to a model and optimize the perturbation itself but not the model parameters. I did like the following, which is very simple:</p>
<pre><code class="lang-auto">import torch
import torch.nn as nn
import torch.optim as optim

# Define a simple model
class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.linear = nn.Linear(1, 1)

    def forward(self, x, perturbation, clean):
        self.linear.weight.data = perturbation + clean
        return self.linear(x) 

# Generate clean dataset
X = torch.tensor([[1.0], [2.0], [3.0], [4.0]], dtype=torch.float32)
Y = 2 * X

# Create the model
model = SimpleModel()

# Define perturbation as a separate parameter
perturbation = torch.tensor(0.1, requires_grad=True)
clean = model.linear.weight.data
# Train the model and optimize the perturbation
criterion = nn.MSELoss()
optimizer_perturbation = optim.SGD([perturbation], lr=0.01)  # Optimize the perturbation

for epoch in range(100):
    optimizer_perturbation.zero_grad()

    outputs = model(X,clean,perturbation)
    loss = criterion(outputs, Y)
    loss.backward()
    # Update the perturbation
    optimizer_perturbation.step()
</code></pre>
<p>However, the gradient of perturbation was still None after executing loss.backward(). I cannot figure out  why. What caused this problem and what should I do to realize the result I needed?<br>
<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/1/f/1fa31b97829b6f208f8a4183a1d41fe1aec69db5.png" data-download-href="https://discuss.pytorch.org/uploads/default/1fa31b97829b6f208f8a4183a1d41fe1aec69db5" title="perturbation"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/1/f/1fa31b97829b6f208f8a4183a1d41fe1aec69db5_2_690x268.png" alt="perturbation" data-base62-sha1="4vSg1g61OOFCxxWCOD2M3uJLj4p" width="690" height="268" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/1/f/1fa31b97829b6f208f8a4183a1d41fe1aec69db5_2_690x268.png, https://discuss.pytorch.org/uploads/default/original/3X/1/f/1fa31b97829b6f208f8a4183a1d41fe1aec69db5.png 1.5x, https://discuss.pytorch.org/uploads/default/original/3X/1/f/1fa31b97829b6f208f8a4183a1d41fe1aec69db5.png 2x" data-dominant-color="282926"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">perturbation</span><span class="informations">888×346 194 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>

KFrank: <p>Hi Aobo!</p>
<aside class="quote no-group quote-modified" data-username="Aobo_Chen" data-post="1" data-topic="191245" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/aobo_chen/48/61281_2.png" class="avatar"> Aobo_Chen:</div>
<blockquote>
<pre><code class="lang-auto">        self.linear.weight.data = perturbation + clean
</code></pre>
<p>However, the gradient of perturbation was still None after executing loss.backward().</p>
</blockquote>
</aside>
<p>The short answer is that the (public-facing) use of <code>.data</code> is deprecated and<br>
can lead to errors.</p>
<p>More generally, pytorch really doesn’t like to let you modify <code>Parameter</code>s<br>
(such as <code>self.linear.weight</code>) while they are being tracked by autograd.</p>
<p>To fix this issue for the use case you posted, use the functional version of<br>
<code>linear()</code>:</p>
<pre data-code-wrap="python"><code class="lang-python">class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.weight = torch.randn (1, 1)   # not a Parameter
        self.bias = torch.randn (1)        # not a Parameter

    def forward(self, x, perturbation, clean):
        return  torch.nn.functional.linear (x, self.weight + perturbation + clean, self.bias)
</code></pre>
<p>If the <code>perturbation</code> you pass into <code>SimpleModel</code> has <code>requires_grad = True</code>,<br>
then you will be able to backpropagate through the call to <code>SimpleModel</code> and<br>
obtain gradients for <code>perturbation</code>.</p>
<p>As an aside, please don’t post screenshots of textual information as  doing so<br>
breaks accessibility, searchability, and copy-paste.</p>
<p>Best.</p>
<p>K. Frank</p>

Aobo_Chen: <p>Oh I’m sorry for posting text pics and won’t do that again.</p>
<p>Thank you for your explain. So you mean I should reconstruct all the layers’ <code>forward()</code> function if I want to do that in a more complex model, right?</p>
<p>Doing this can meet my need but it just looks a bit complicated.</p>

Aobo_Chen: <p>Oh I found it not that complicated at all, at least for DNN and CNN. Thank you again. I appreciate your help.</p>