Clamping leaf tensor without using .data

ElToto: <p>Hello I want to create a noise which if added to normal images create adversarial examples.</p>
<p>I am getting a lot of images from my dataloader and I want to add the noise to each image.<br>
The model is pretrained so it correctly classifies the original images without the noise.<br>
I want to train this noise so that the prediction of image + noise is wrong.</p>
<p>So I create a new Tensor (adv_noise) and an Adam optimizer which optimizes the noise.</p>
<p>But after every step I need to make sure that the maximum and minimum values of<br>
adv_noise are below a given epsilon.</p>
<pre><code>class AE():
  def __init__():
    # create random noise with same size as one image
    # I want to backprop this self.adv_noise so it increases the loss
    init_noise = torch.nn.init.xavier_uniform_(torch.empty(3, 100,100), gain=2)
    self.adv_noise = init_noise.cuda().requires_grad_(True)
    self.optimizer = Adam([self.adv_noise], lr=lr)
    self.eps = 10/255 # max and min values of adv noise arent allowed to be bigger/smaller than epsilon
   """i am also creating the model, criterion, and dataloader here """ 
  
  def train(self):
     self.model.requires_grad_(False)
     for data in dataloader():
       self.optimizer.zero_grad()
       image, label = data
       model_input = image.cuda()+self.adv_noise
       pred = model(model_input)
       loss = self.criterion(pred, label)
       loss.backward()
       self.optimizer.step()
       # up to this point this should be pretty standard
       # But now I need to make certain that self.adv_noise doesnt extend a certain range epsilon
       self.adv_noise.data = self.adv_noise.data.clamp(-self.eps, self.eps)
</code></pre>
<p>The last line here works. But I don’t really know why. I’m reading that one shouldn’t use  .data anymore<br>
so I’d like to change this last line so it conforms to modern pytorch standards.</p>
<p>How I tried to change the last line:</p>
<pre><code>self.adv_noise.clamp_(-self.eps, self.eps)
</code></pre>
<blockquote>
<p>Error: a leaf Variable that requires grad is being used in an in-place operation.</p>
</blockquote>
<p>2nd try:</p>
<pre><code>self.adv_noise = self.adv_noise.clamp(-self.eps, self.eps)
</code></pre>
<blockquote>
<p>RuntimeError: Trying to backward through the graph a second time, but the saved intermediate results have already been freed. Specify retain_graph=True when calling backward the first time.</p>
</blockquote>
<p>3d try:</p>
<pre><code>self.adv_noise = self.adv_noise.detach().clamp(-self.eps, self.eps)
</code></pre>
<blockquote>
<p>RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn</p>
</blockquote>
<p>Like I said the upper implementation works but I’d like to not use .data</p>
<p>(as a side note)</p>
<pre><code>self.adv_noise.data = self.adv_noise.data.clamp(-eps, eps) # this works
</code></pre>
<p>But</p>
<pre><code>self.adv_noise = self.adv_noise.data.clamp(-eps, eps) # this doesn't work
</code></pre>
<blockquote>
<p>RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn</p>
</blockquote>

dejanbatanjac: <p><code>t.data</code> is the same as <code>t.detach()</code> so you may use the later.</p>

ElToto: <p>I thought similary but as you can see I tried this in my 3rd. try and this throws runtime exceptions. I think this happens because If i create a tensor with .detach() the gradients are no longer tracked. Although I need this in the next for loop.</p>

dejanbatanjac: <p>I would cite <a class="mention" href="/u/alband">@albanD</a> in here since this is still relevant for PyTorch 4,0 and later.</p>
<aside class="quote" data-post="21" data-topic="16861">
  <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/alband/48/215_2.png" class="avatar">
    <a href="https://discuss.pytorch.org/t/clone-and-detach-in-v0-4-0/16861/21">Clone and detach in v0.4.0</a> 
  </div>
  <blockquote>
    Hi, 
Yes, .detach() gives a new Tensor that is a view of the original one. So any inplace modification of one will affect the other. 
You should use .clone() if you want a Tensor with the same content backed with new memory. 
And .detach().clone() if you want a new Tensor backward with new memory and that does not share the autograd history of the original one.
  </blockquote>
</aside>


ElToto: <p>Tried it out:</p>
<pre><code>self.adv_noise = self.adv_noise.detach().clone().clamp(-eps, eps)
</code></pre>
<p>But still same error as before</p>
<blockquote>
<p>RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn</p>
</blockquote>
<p>Also the same error if I change the order:</p>
<pre><code>self.adv_noise = self.adv_noise.clamp(-eps,eps).detach().clone()
</code></pre>

albanD: <p>Hi,</p>
<p>You should use <code>with torch.no_grad():</code> around the code that should not track gradients. That will solve the issue for clamp_</p>

ElToto: <p>Yup this did the trick, thanks</p>
<pre><code>with torch.no_grad():
  self.adv_noise.clamp_(-eps, eps)</code></pre>