AttributeError: 'GradSampleModule' object has no attribute for method

username: Leonmac
cooked: <p>Hello,<br>
I am using the flower as an FL framework and I am trying to put DP support by using Opacus.<br>
Here is the problem I meet:</p>
<ol>
<li>I am using a very common MNIST model and inherited to get a new class:</li>
</ol>
<pre><code class="lang-auto"># https://github.com/pytorch/examples/blob/main/mnist/main.py
class Net(nn.Module):
    def __init__(self) -&gt; None:
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.dropout1 = nn.Dropout(0.25)
        self.dropout2 = nn.Dropout(0.5)
        self.fc1 = nn.Linear(9216, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x) -&gt; torch.Tensor:
        x = self.conv1(x)
        x = F.relu(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x, 2)
        x = self.dropout1(x)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.dropout2(x)
        x = self.fc2(x)
        output = F.log_softmax(x, dim=1)
        return output


class MNISTNet(Net):
    """inheritance from Net (MNIST Model) """
    def get_weights(self): #-&gt; List[np.ndarray]:
        """Get model weights as a list of NumPy ndarrays."""
        return [val.cpu().numpy() for _, val in self.state_dict().items()]

    def set_weights(self, weights: List[np.ndarray]) -&gt; None:
        """Set model weights from a list of NumPy ndarrays.
        Parameters
        ----------
        weights: fl.common.Weights
            Weights received by the server and set to local model
        Returns
        -------
        """
        state_dict = OrderedDict(
            {
                k: torch.Tensor(v)
                for k, v in zip(self.state_dict().keys(), weights)
            }
        )
        self.load_state_dict(state_dict, strict=True)
</code></pre>
<p>Then I simply put the DP protection before training:</p>
<pre><code class="lang-auto">        model, optimz, train_loader = privacy_engine.make_private(
            module       = model,
            optimizer    = optimz,
            data_loader  = train_loader,
            noise_multiplier = 1.0,
            max_grad_norm = 1.0,
        )
        
        self.model.set_weights(parameters)

         &lt;... training ...&gt;

</code></pre>
<p>Then an error report is as below:</p>
<pre><code class="lang-auto">  File "/home/xeniro/prj/flower_framework/client_zone/dp_pytorch_client.py", line 194, in fit
    self.model.set_weights(parameters)
  File "/home/xeniro/miniconda3/envs/xflwr/lib/python3.10/site-packages/opacus/grad_sample/grad_sample_module.py", line 140, in __getattr__
    raise e
  File "/home/xeniro/miniconda3/envs/xflwr/lib/python3.10/site-packages/opacus/grad_sample/grad_sample_module.py", line 135, in __getattr__
    return super().__getattr__(item)
  File "/home/xeniro/miniconda3/envs/xflwr/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1185, in __getattr__
    raise AttributeError("'{}' object has no attribute '{}'".format(
AttributeError: 'GradSampleModule' object has no attribute 'set_weights'

</code></pre>
<p>Please let me know if there are some available solution.</p>

username: ptrblck
cooked: <p>I’m not familiar with the libs you are using and what <code>privacy_engine.make_private</code> does, but it seems the error is raised in <code>self.model.set_weights(parameters)</code> after manipulating the <code>model</code> in <code>make_private</code>.<br>
Would it be possible to call <code>set_weights</code> before or alternatively, is <code>model</code> now containing new attributes (e.g. was the original <code>model</code> moved to e.g. <code>model.module</code>)?</p>

username: Leonmac
cooked: <p><a class="mention" href="/u/ptrblck">@ptrblck</a> Thanks a lot for the quick response.<br>
Yes, if I move the “set_weights” before the “privacy_engine.make_private” then I can move forward (though some other issue, but not likely relevant to this).<br>
But why is that? set_weights only does a “parameters filling” job. I think this needs the Opacus team’s help.<br>
I find this <a href="https://discuss.pytorch.org/t/gradsamplemodule-object-has-no-attribute/151318">post</a> but I am not sure if that can explain my problem (“Opacus does not yet support advanced computation graph manipulations  (such as torch.autograd.grad()”)<br>
as I believe the above code does not involve computation graph manipulations? or does it?</p>
<p>BTW, I find after making “privacy_engine.make_private” some attributes would be removed, for exp,</p>
<ul>
<li>before that I can obtain the dataloader batch size by calling dataloader.batch_size.</li>
<li>after that dataloader.batch_size return None.</li>
</ul>
<p><a class="mention" href="/u/alexandresablayrolle">@alexandresablayrolle</a>  hope he can give a help.</p>

username: ptrblck
cooked: <p>I guess <code>model.set_weights()</code> is supposed to manipulate internal parameters and the <code>model</code> returned by <code>make_private</code> is indeed not a plain <code>nn.Module</code> anymore (with all your function definitions) but is a <code>GradSampleModule</code> object now providing <a href="https://github.com/pytorch/opacus/blob/e1db5ae54fea3f51d9fb9d483162646740ffe00a/opacus/grad_sample/grad_sample_module.py#L75-L419">these methods</a>. This would be the reason why your custom <code>set_weights</code> function is not available anymore.</p>

username: Leonmac
cooked: <p>Then that is a problem, even if I can pass the 1st iteration (by put my custom <code>set_weights</code> function ahead of the opacus function), as FL request a multiple round training, as soon as I get the aggregation parameter from the server, I still need to call <code>set_weights</code> and the fail will happen at the 2nd round training.</p>

username: Leonmac
cooked: <p>Some more thoughts:<br>
Why does Opacus privacy engine, need to manipulate the model? I find some older version example code, the privacy engine will only deal with the optimizer (i.e. dp-sgd).</p>

username: ptrblck
cooked: <p>I don’t know as I’m not familiar enough with Opacus and was only reading a bit through the code, so you would have to wait for an Opacus expert to chime in. <img src="https://discuss.pytorch.org/images/emoji/apple/wink.png?v=12" title=":wink:" class="emoji" alt=":wink:" loading="lazy" width="20" height="20"></p>

username: Peter_Romov
cooked: <p><a class="mention" href="/u/leonmac">@Leonmac</a> The problem here is that <code>privacy_engine.make_private</code> wraps your <code>model</code> object with <code>GradSampleModule(model)</code>. The latter is an instance of nn.Module which can do forward/backward passes. The difference from the original <code>model</code> is that 1) it computes per-sample gradients (this is key for dp-sgd) 2) it doesn’t inherit the custom methods you implemented in the original module.</p>
<p>The optimal way is to load weights before turning the model into private. If you set the weights before calling <code>make_private</code> it will work.</p>
<p>You can actually access your module via private field like <code>self.model._module.set_weights(..)</code>. This is a dangerous way as it may brake privacy accounting and DP-SGD itself, but it may help if you understand what you are doing.</p>
<p><a class="mention" href="/u/pstock">@pstock</a> maybe you know what’s the best way to implement FL with Opacus?</p>

username: Leonmac
cooked: <p><a class="mention" href="/u/peter_romov">@Peter_Romov</a>  Thanks a lot for the explain.<br>
I had changed my design that move the set_weight() out of the Model class and but wrapping the model outside of the training loop, that way the problem is solved.<br>
I still have some other questions regarding opacus:</p>
<ol>
<li>
<p>I find the dataloader output from “privacy_engine.make_private" will have the attribute like dataloader.batch_size out of work (return None)-- this is not a bid deal and a workaround is available but is this a correct behaviour?<br>
–does the make_private modify (add noise?) to Dataloader as well?</p>
</li>
<li>
<p>A FL training is different from a local Centralized Training (the training happens on client’s side, and a model aggregation happen on the server side), in such case, do I NEED to call the make_private each time when I get the updated aggregated model from server side?</p>
</li>
<li>
<p>In my test, I see such log many times:</p>
</li>
</ol>
<pre><code class="lang-auto">07/28/2022 14:41:49:INFO:Despite set_to_none is set to False, opacus will set p.grad_sample and p.summed_grad to None due to non-trivial gradient accumulation behaviour
</code></pre>
<p>I checked out this is relevant to DPOptimizer, zero_grad(<em>set_to_none=False</em> )<a href="https://opacus.ai/api/_modules/opacus/optimizers/optimizer.html#DPOptimizer.zero_grad" rel="noopener nofollow ugc">[source]</a><br>
I am not sure the meaning of these infor, doe that matter? and can I simply disable these INFO logging showing?</p>

username: karthikprasad
cooked: <p>Hi <a class="mention" href="/u/leonmac">@Leonmac</a>,<br>
Apologies for the delay in response. Since this question was marked as resolved, this escaped our radar.</p>
<blockquote>
<ol>
<li>I find the dataloader output from “privacy_engine.make_private" will have the attribute like dataloader.batch_size out of work (return None)-- this is not a bid deal and a workaround is available but is this a correct behaviour?<br>
–does the make_private modify (add noise?) to Dataloader as well?</li>
</ol>
</blockquote>
<p><code>make_private</code> does modify <code>DataLoader</code> and returns <code>DPDataLoader</code> (similar to modifying <code>Module</code> → <code>GradSampleModule</code>). The main change here is wrt batch sampler to enable Poisson sampling, which leads to variable batch_size. More on this can be found in the documentation: <a href="https://opacus.ai/api/data_loader.html#" class="inline-onebox" rel="noopener nofollow ugc">Opacus · Train PyTorch models with Differential Privacy</a></p>
<blockquote>
<p>do I NEED to call the make_private each time when I get the updated aggregated model from server side?</p>
</blockquote>
<p>I am not familiar with Flower, but I suspect you might need to make some tweaks. Opacus’s <code>make_private</code> creates a model that takes care of per-sample gradient computation and noise-addition during SGD. In the FL setting, you don’t need per-sample gradients to preserve user-level privacy (as discussed in <a href="https://arxiv.org/abs/1710.06963" class="inline-onebox" rel="noopener nofollow ugc">[1710.06963] Learning Differentially Private Recurrent Language Models</a>). Rather, you each client update itself is a gradient and you can simply aggregate them all, add noise, and update the central model.<br>
A sample implementation of this can be found at <a href="https://github.com/facebookresearch/FLSim/blob/main/flsim/servers/sync_dp_servers.py#L134" rel="noopener nofollow ugc">https://github.com/facebookresearch/FLSim/blob/main/flsim/servers/sync_dp_servers.py#L134</a>. Note that this doesn’t use opacus to add noise, or to wrap the model, but still achieves DP.</p>
<blockquote>
<p>In my test, I see such log many times</p>
</blockquote>
<p>As discussed in the previous answer, Opacus needs to compute per-sample gradients to clip them before adding noise. Internally this is done via an additional <code>.grad_sample</code> attribute, which is then used to update <code>.grad</code>. With regular Pytorch, <code>set_to_none=False</code> will not update <code>.grad</code> (this is true even in Opacus), BUT Opacus has to always clear out <code>.grad_sample</code>. Not doing it will mess with gradient accumulation.<br>
Unless your implementation depends on grad not being None, you can ignore this warning.</p>

username: Leonmac
cooked: <p><a class="mention" href="/u/karthikprasad">@karthikprasad</a> Greate thanks so much. I still have some other questions but maybe better put them into a different post.</p>