Opacus with large clipping norm performs differently from normal network

Jun_Wan: <p>Hi, I compared two tests:</p>
<ol>
<li>
<p>resnet20 on cifar10 with privacy-engine, the clipping norm is set to 10M. This should be equivalent to not doing clipping at all.</p>
</li>
<li>
<p>resnet20 on cifar10 without privacy-engine (noise-multiplier is set as 0), with exactly the same parameters as example 1.</p>
</li>
</ol>
<p>Test 2 soon reached 92% accuracy while test 1 struggled to reach 85%. I then wrote another test where we created two models for the two above tests and made them train on the same data (with the same trainloader) simultaneously. Model 1 has optimizer 1 which is attached to a privacy-engine, while model 2 has optimizer 2 which is just a normal SGD optimizer.</p>
<p>The code looks something like this:</p>
<p>loss1.backward()<br>
loss2.backward()</p>
<p>optimizer1.step()<br>
optimizer2.step()</p>
<p>Before we call the step() functions, the param.grad are exactly the same between the two models. However, after we called the step() functions, there are approximately a 3% difference between the param.grad of the two models.</p>
<p>Is this because pytorch’s default way of computing gradient is different from opacus even when the clipping value is 10 million? Or is it because of accuracy loss during opacus computations?</p>

ilyamironov: <p>Are you sure you use the same exact network in both cases? The “canonical” Resnet20 includes batch normalization, which is incompatible with DP-SGD.</p>

Jun_Wan: <p>Yes. We called the opacus’ convert_batchnorm_modules() function for both models. All the batchnorm layers are converted to groupnorm layers.</p>
<p>We also use model2.load_state_dict(copy.deepcopy(model1.state_dict())) at the beginning to make sure they start with the same network parameters.</p>

karthikprasad: <p>Hi <a class="mention" href="/u/jun_wan">@Jun_Wan</a>, is it possible your noise multiplier was non-zero in your first case? If yes, do you mind sharing a notebook with the above issue? This will help us debug further.</p>

ashkan_software: <p>Hi <a class="mention" href="/u/jun_wan">@Jun_Wan</a>. Has your issue been resolved? If not, do you mind sharing a notebook so we can look into this?</p>

Jun_Wan: <p>Hi, sorry. For some reasons, the previous notification emails went to the trash folder. I just noticed them today.</p>
<p>I posted our test code on <a href="https://github.com/junwan0224/test-code" class="inline-onebox" rel="noopener nofollow ugc">GitHub - junwan0224/test-code</a>. We used the compare.py file compare the gradients. One model has privacy engine while the other does not. We feed them the exact same data, but the resulted gradients are different.</p>
<p>For a previous question: yes, our noise multiplier is set as zero. Please let me know if there is any other question. Thank you for helping!</p>

ashkan_software: <p>Hi <a class="mention" href="/u/jun_wan">@Jun_Wan</a></p>
<p>Thanks for sharing your code and thanks again for using Opacus!</p>
<p>I looked at your code and I suspect the issue might be because of the learning rate. It seems that the learning rate for the 2 optimizers are different:</p>
<ul>
<li>on line 155 you define a LR scheduler for optimizer1 but not for optimizer2. So technically the two learnings are going through different training surfaces. You wrote</li>
</ul>
<aside class="quote no-group" data-username="Jun_Wan" data-post="1" data-topic="136817">
<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/jun_wan/48/34806_2.png" class="avatar"> Jun_Wan:</div>
<blockquote>
<p>Before we call the step() functions, the param.grad are exactly the same between the two models. However, after we called the step() functions, there are approximately a 3% difference between the param.grad of the two models.</p>
</blockquote>
</aside>
<p>and this kind of shows that, I think.</p>
<ul>
<li>
<p>I know the test is for resnet20, but in case you use other resnets, make sure to also include optimizer2 on line 169.</p>
</li>
<li>
<p>lines 224-228 seem to only be for optimizer1</p>
</li>
</ul>
<p>Last but not least, we have released Opacus 1.0 recently. So, when you have time, please migrate to Opacus 1.0 for more features: <a href="https://github.com/pytorch/opacus/releases/tag/v1.0.0" class="inline-onebox" rel="noopener nofollow ugc">Release Opacus v1.0.0 · pytorch/opacus · GitHub</a></p>
<p>Please let us know if that didn’t solve the issue and we can investigate further. <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=12" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>