Gradient of an image w.r.t. its classification error on a binary attribute

username: Federico_Romeo
cooked: <p>I have a pretrained GAN generator, that takes latent vectors from either Z space, shaped [1,512], or from W space shaped [1,18,512], and outputs face images of of dimension [3,1024,1024].</p>
<p>I also have a classifier that gives the prediction scores for 40 binary facial attributes.</p>
<p>My goal is to iteratively modify an input image in the direction of editing a single facial attribute. The edit wouldn’t be directly on the image, but un the latent vector which will be the input of the GAN generator that will create the image. For example i want to obtain the same face but with “Mustache”, or “Smiling”. The way i do it right now is the following:</p>
<pre><code class="lang-auto"># Generate an image and its correspondant latent vector
attribute = "Smiling"
z = torch.randn(1,18,512, requires_grad=True).cuda()
z.retain_grad()
image, w = generator(z, input_is_style=False, return_styles=True, randomize_noise=False)
w.retain_grad()

# Get the prediction score of 'attribute'
predictions = classifier(image)                         # shape 1,80
predictions.retain_grad() 
predictions = predictions.view(-1,40,2)                 # shape 1,40,2          
predictions = torch.softmax(predictions, dim=2)         # shape 1,40,2
predictions = predictions.squeeze(0)[:,1].unsqueeze(1)  # shape 1,40
prediction = predictions[attributes.index(attribute)]   # shape 1

i = 0
lr = 1e-2
target1 = torch.tensor([1.0], requires_grad=True).cuda()

while prediction.item() &lt; 0.8 and i&lt;100:

    print(f"\rIter: {i} - prediction: {prediction.item():.3f}, end='')
    predictions = classifier(image)                         # shape 1,80
    predictions.retain_grad()
    predictions = predictions.view(-1,40,2)                 # shape 1,40,2          
    predictions = torch.softmax(predictions, dim=2)         # shape 1,40,2
    predictions = predictions.squeeze(0)[:,1].unsqueeze(1)  # shape 1,40
    prediction = predictions[attributes.index(attribute)]   # shape 1

    # Compute the loss of my prediction with respect to a perfect prediciton of 1
    loss = torch.nn.functional.binary_cross_entropy(prediction, target1)
    loss.backward(retain_graph=True)
    
    # Update the vector in the gradient direction
    w -= lr * w.grad

    # Generate the new image from the modified latent vector w
    image, _= generator(w, randomize_noise=False, input_is_style=True, return_styles=True)
    i += 1

tensor2im(image.squeeze(0)).resize((256,256)).show()
</code></pre>
<p>This code snippet works, it doesn’t give any errors, but the modifications aren’t that evident nor clear.<br>
Do you think I’m doing something wrong gradient wise in particular? Thanks in advance.</p>

username: AbdulsalamBande
cooked: <p>To improve the quality of the generated image with the desired facial attribute, consider the following adjustments:</p>
<ol>
<li>Adjust the learning rate or use a learning rate scheduler to prevent overshooting the optimal latent vector.</li>
<li>Apply gradient clipping to stabilize updates and avoid large gradients.</li>
<li>Use regularization in the loss function to prevent the latent vector from moving too far from its original position, preserving the identity of the face.</li>
</ol>
<p>Experiment with different values for the learning rate, regularization weight, and momentum to find the best combination for your specific problem.</p>

username: Federico_Romeo
cooked: <p>Thanks for the reply.</p>
<p>With your suggestion the code will look like this:</p>
<pre><code class="lang-auto"># 1. Add a scheduler
optimizer = torch.optim.SGD(classifier.parameters(), lr=1e-1)
scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.9)

while [...] :
      
      prediction = ...

      # 3. How do i modify the loss with L2 reg?
      loss = torch.nn.functional.binary_cross_entropy(prediction, target1)
      loss.backward(retain_graph=True)

      lr = max(1e-3, scheduler.get_last_lr()[0])
      # 2. Clip grad value
      w -= lr * w.grad.clip(min_v,max_v)

      image = generate(w)
</code></pre>
<p>I do have some doubts.</p>
<ol>
<li>Any suggestions of which kind of <strong>scheduler</strong> and on its parameters? (starting lr, step…)</li>
<li>Is the <strong>optimizer</strong> right? I’m not sure about the parameters() and even on the SGD.</li>
<li>What about the <strong>loss regularitazion</strong>? I don’t get how can i modify it since i’m doing inference, i’m not updating weights.</li>
<li>
<strong>min_v</strong> and <strong>max_v</strong> of the clipping? -1,1?</li>
</ol>