Update a model's parameter will get an error

YuanSnowing: <p>this is my error code:</p>
<pre><code class="lang-auto">import torch
import torch.nn as nn

pos = [0.1, 0.2, 0.3]
pos_tens = torch.Tensor(pos)
pos_pm = nn.Parameter(pos_tens, requires_grad=True)

class Transformation(nn.Module):
    def __init__(self, trans=None):
        super(Transformation, self).__init__()
        self.trans = nn.Parameter(trans, requires_grad=True)
    def update(self, xyz):
        trans = self.trans.repeat(10, 1)
        new = xyz + trans
        return new
class Onemodel(nn.Module):
    def __init__(self, xyz=None):
        super(Onemodel, self).__init__()
        self.xyz = nn.Parameter(xyz, requires_grad=True)

mode = Onemodel(pos_tens.repeat(10, 1))
tsf = Transformation(pos_tens)
mode.xyz = tsf.update(mode.xyz)
y = mode.xyz.sum()
y.backward()
</code></pre>
<p>when I just run it, I will get this error:</p>
<pre><code class="lang-auto">Traceback (most recent call last):
  File "/irip/yuanxuening_2022/workspace/gsgen-multi/test.py", line 22, in &lt;module&gt;
    mode.xyz = tsf.update(mode.xyz)
  File "/irip/yuanxuening_2022/anaconda3/envs/gsgen/lib/python3.9/site-packages/torch/nn/modules/module.py", line 1635, in __setattr__
    raise TypeError("cannot assign '{}' as parameter '{}' "
TypeError: cannot assign 'torch.FloatTensor' as parameter 'xyz' (torch.nn.Parameter or None expected)
</code></pre>
<p>I don’t understand why this error occur, also have no idea about how to correct it.</p>

J_Johnson: <aside class="quote no-group" data-username="YuanSnowing" data-post="1" data-topic="196639">
<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/yuansnowing/48/67303_2.png" class="avatar"> YuanSnowing:</div>
<blockquote>
<pre><code class="lang-auto">def update(self, xyz):
        trans = self.trans.repeat(10, 1)
        new = xyz + trans
        return new
</code></pre>
</blockquote>
</aside>
<p>Have you tried wrapping the update function with <code>with torch.no_grad():</code>?</p>
<pre><code class="lang-auto">def update(self, xyz):
    with torch.no_grad():
        trans = self.trans.repeat(10, 1)
        new = xyz + trans
    return new

...
with torch.no_grad():
    mode.xyz = tsf.update(mode.xyz)
</code></pre>
<p>Anytime you’re performing changes to trainable parameters, it’s a good idea to do so outside of autograd tracking.</p>

YuanSnowing: <p>Thank you for your suggestion! I have tried the relevant methods, but they have not been effective. I just tried again and there will still be the same error.</p>
<pre><code class="lang-auto">import torch
import torch.nn as nn

pos = [0.1, 0.2, 0.3]
pos_tens = torch.Tensor(pos)
pos_pm = nn.Parameter(pos_tens, requires_grad=True)

class Transformation(nn.Module):
    def __init__(self, trans=None):
        super(Transformation, self).__init__()
        self.trans = nn.Parameter(trans, requires_grad=True)
    def update(self, xyz):
        with torch.no_grad():
            trans = self.trans.repeat(10, 1)
            new = xyz + trans
        return new
class Onemodel(nn.Module):
    def __init__(self, xyz=None):
        super(Onemodel, self).__init__()
        self.xyz = nn.Parameter(xyz, requires_grad=True)

mode = Onemodel(pos_tens.repeat(10, 1))
tsf = Transformation(pos_tens)
with torch.no_grad():
    mode.xyz = tsf.update(mode.xyz)
y = mode.xyz.sum()
y.backward()
</code></pre>
<p>Also, I want to keep the gradient of the variable tsf.trans, will this affect it?</p>

J_Johnson: <aside class="quote no-group" data-username="YuanSnowing" data-post="3" data-topic="196639">
<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/yuansnowing/48/67303_2.png" class="avatar"> YuanSnowing:</div>
<blockquote>
<p>Thank you for your suggestion! I have tried the relevant methods, but they have not been effective. I just tried again and there will still be the same error.</p>
</blockquote>
</aside>
<p>Okay, try this:</p>
<pre><code class="lang-auto">with torch.no_grad():
    mode.xyz.data = tsf.update(mode.xyz)  # &lt;&lt;&lt; add .data
</code></pre>

YuanSnowing: <aside class="quote no-group" data-username="J_Johnson" data-post="4" data-topic="196639">
<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/j_johnson/48/55494_2.png" class="avatar"> J_Johnson:</div>
<blockquote>
<pre><code class="lang-auto">with torch.no_grad():
    mode.xyz.data = tsf.update(mode.xyz)  # &lt;&lt;&lt; add .data
</code></pre>
</blockquote>
</aside>
<p>Thank you for your help! This can indeed avoid that error, but I hope to keep the gradient of the parameter <code>tsf.trans</code>, which cannot be done.</p>
<pre><code class="lang-auto">print(tsf.trans.grad) # None, hope it not none
</code></pre>

J_Johnson: <p>It’s really strange to try and update trainable parameters while performing autograd. The whole point of autograd is to update those parameters via gradient descent. I.e. nudge them closer in a direction to get the outputs to match a specific target. But it looks like you’re updating them manually and then tracking those updates for autograd.</p>
<p>Additionally, the specific actions you’re taking <em>in this example</em> won’t result in any gradients.</p>
<p>Anyways, to your question, just remove the <code>with torch.no_grad():</code> if you still want those tracked in autograd. Although, I can’t guarantee it won’t cause other errors.</p>

YuanSnowing: <aside class="quote no-group quote-modified" data-username="J_Johnson" data-post="6" data-topic="196639" 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/j_johnson/48/55494_2.png" class="avatar"> J_Johnson:</div>
<blockquote>
<p>It’s really strange to … errors.</p>
</blockquote>
</aside>
<p>Oh, I understand what you mean. In fact, this may be a problem with my code organization. My two models correspond to the position <code>A</code> of the object and the offset <code>B</code> of the object, respectively. I hope to optimize both parts <code>A</code> and <code>B</code>. However, when rendering the entire scene, it is necessary to first <code>A=A+B</code>, and then perform the loss (then <code>A=A-B</code>).</p>
<p>So in fact, I should use a new variable to represent the position of the object, rather than making modifications directly on <code>A</code>. (But if modifications can be made directly on <code>A</code>, code writing will become much easier, so I hope to be able to make direct modifications.)</p>
<p>So, returning to the previous question, I tried to comment the<code>with torch.no_grad()</code> out, but it still didn’t work.</p>
<pre><code class="lang-auto">import torch
import torch.nn as nn

pos = [0.1, 0.2, 0.3]
pos_tens = torch.Tensor(pos)
pos_pm = nn.Parameter(pos_tens, requires_grad=True)

class Transformation(nn.Module):
    def __init__(self, trans=None):
        super(Transformation, self).__init__()
        self.trans = nn.Parameter(trans, requires_grad=True)
    def update(self, xyz):
        # with torch.no_grad():
        trans = self.trans.repeat(10, 1)
        new = xyz + trans
        return new
class Onemodel(nn.Module):
    def __init__(self, xyz=None):
        super(Onemodel, self).__init__()
        self.xyz = nn.Parameter(xyz, requires_grad=True)

mode = Onemodel(pos_tens.repeat(10, 1))
tsf = Transformation(pos_tens)
# with torch.no_grad():
mode.xyz.data = tsf.update(mode.xyz)
y = mode.xyz.sum()
y.backward()

print(tsf.trans.grad) # None, hope it not none
</code></pre>

J_Johnson: <aside class="quote no-group" data-username="YuanSnowing" data-post="7" data-topic="196639">
<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/yuansnowing/48/67303_2.png" class="avatar"> YuanSnowing:</div>
<blockquote>
<p>So, returning to the previous question, I tried to comment the<code>with torch.no_grad()</code> out, but it still didn’t work.</p>
</blockquote>
</aside>
<p>There are two reasons why.</p>
<p>The first, you need to set an optimizer in order to track gradients. After defining the model, you can call something like this:</p>
<pre><code class="lang-auto">optimizer = torch.optim.Adam(mode.parameters(), lr = 0.001)
optimizer2 = torch.optim.Adam(tsf.parameters(), lr = 0.001)
</code></pre>
<p>Second, what you’re doing to the model is not going to result in any gradients. Manually changing the trainable weights is not a method which gets tracked in autograd.</p>
<p>I will modify your example to something that does result in gradients.</p>
<pre><code class="lang-auto">import torch
import torch.nn as nn

pos = [0.1, 0.2, 0.3]
pos_tens = torch.Tensor(pos)
pos_pm = nn.Parameter(pos_tens, requires_grad=True)

class Transformation(nn.Module):
    def __init__(self, trans=None):
        super(Transformation, self).__init__()
        self.trans = nn.Parameter(trans, requires_grad=True)

    def update(self, xyz):
        trans = self.trans.repeat(10, 1)
        new = xyz + trans
        return new

    def forward(self, x): #define a forward pass
        x = x*self.trans+self.trans
        return x

class Onemodel(nn.Module):
    def __init__(self, xyz=None):
        super(Onemodel, self).__init__()
        self.xyz = nn.Parameter(xyz, requires_grad=True)

    def forward(self, x): #define a forward pass
        x = x@self.xyz.T
        return x

mode = Onemodel(pos_tens.repeat(10, 1))
tsf = Transformation(pos_tens)

#define the optimizer(s)
optimizer = torch.optim.Adam(mode.parameters(), lr = 0.001)
optimizer2 = torch.optim.Adam(tsf.parameters(), lr = 0.001)

mode.xyz.data = tsf.update(mode.xyz)

#create some data to run through the models
dummy_data = torch.rand((32, 3))
x = tsf(dummy_data)
x = mode(x)

#define targets to compare with the outputs
targets = torch.randint(0,1,(32,10)).float()

#define a loss function
loss_function = nn.L1Loss()

#calculate the loss
loss = loss_function(x, targets)

loss.backward()

print(tsf.trans.grad)
</code></pre>

YuanSnowing: <p>Thank you very much for your help. Comparing your code, I have found some issues with my own code (such as the Transformation class not having a forward function). At the same time, I feel that I need to learn more about the basics of pytorch, rather than just starting to modify the code. Thank you very, very, very much!</p>

J_Johnson: <p>Glad it helped. I suggest taking about an hour to run through this set of tutorials to get started:</p>
<p><a href="https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html" class="onebox" target="_blank" rel="noopener nofollow ugc">https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html</a></p>
<p>From there, you can find more mid-level and advanced tutorials here:</p>
<p><a href="https://pytorch.org/tutorials/" class="onebox" target="_blank" rel="noopener nofollow ugc">https://pytorch.org/tutorials/</a></p>
<p>Also, if you want to understand more about the inner workings of the math involved under the hood, there is a good YouTube series titled “Neural Networks from Scratch” by sentdex.</p>