Perplexity about Generator training in conditional GAN

username: Federico_Ottomano
cooked: <p>When training the generator of my conditional GAN what I do is:</p>
<pre><code class="lang-auto">    output = disc(fake, real[1]).view(-1)
    lossG = criterion(output, torch.ones_like(output))
    gen.zero_grad()
    lossG.backward()
    opt_gen.step()

    if batch_idx == 0:
        print(
            f"Epoch [{epoch}/{num_epochs}] Batch {batch_idx}/{len(loader)} \
                  Loss D: {lossD:.4f}, loss G: {lossG:.4f}"
        )

        step += 1

loss_D.append(lossD.detach().numpy())
loss_G.append(lossG.detach().numpy())
</code></pre>
<p>Since we are minimizing, should I declare instead:</p>
<pre><code class="lang-auto">output = -disc(fake, real[1]).view(-1)
</code></pre>
<p>?</p>

username: KFrank
cooked: <p>Hi Frederico!</p>
<aside class="quote no-group" data-username="Federico_Ottomano" data-post="1" data-topic="147169" 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/federico_ottomano/48/43102_2.png" class="avatar"> Federico_Ottomano:</div>
<blockquote>
<pre><code class="lang-auto">    output = disc(fake, real[1]).view(-1)
    lossG = criterion(output, torch.ones_like(output))
</code></pre>
</blockquote>
</aside>
<p>You don’t tell us what <code>criterion</code> is, so we have to make some<br>
(hopefully reasonable) assumptions …</p>
<aside class="quote no-group">
<blockquote>
<pre><code class="lang-auto">    gen.zero_grad()
    lossG.backward()
    opt_gen.step()
</code></pre>
</blockquote>
</aside>
<p>If <code>lossG</code> becomes <em>smaller</em> when <code>disc</code> does a <em>worse</em> job of<br>
distinguishing <code>fake</code> from <code>real</code> (and you are only using<br>
<code>lossG.backward()</code> to modify the weights of <code>gen</code> and not of<br>
<code>disc</code>), then this should work as written.  Backpropagation<br>
of <code>lossG</code> will train <code>gen</code> to do a better job of fooling <code>disc</code>.</p>
<p>But if <code>criterion</code> returns a typical loss for the performance of<br>
<code>disc</code> that becomes smaller when <code>disc</code> does a <em>better</em> job of<br>
distinguishing <code>fake</code> from <code>real</code>, then …</p>
<aside class="quote no-group">
<blockquote>
<p>Since we are minimizing, should I declare instead:</p>
<pre><code class="lang-auto">output = -disc(fake, real[1]).view(-1)
</code></pre>
<p>?</p>
</blockquote>
</aside>
<p>No, not quite.  This flips the sign of whatever it is that <code>disc</code> returns<br>
and that might not be what you want.  (It would depend on the details<br>
of <code>criterion</code>.)</p>
<p>It is true – assuming that <code>criterion</code> becomes smaller when <code>disc</code><br>
performs better – that you will have to flip a sign somewhere so that<br>
you train <code>gen</code> to do a better job of fooling <code>disc</code>.</p>
<p>Assuming again that you are only using <code>lossG.backward()</code> to update<br>
<code>gen</code> (and not <code>disc</code>) the most straightforward thing to do is just flip the<br>
sign of <code>lossG</code>:</p>
<pre data-code-wrap="python"><code class="lang-python">lossG = -criterion(output, torch.ones_like(output))
lossG.backward()
</code></pre>
<p>Now <code>lossG</code> becomes smaller when <code>disc</code> does worse, which means<br>
that <code>gen</code> did a better job of fooling <code>disc</code>, which is what you want to<br>
train <code>gen</code> to do.</p>
<p>Best.</p>
<p>K. Frank</p>

username: Federico_Ottomano
cooked: <p>Hi Frank and thanks for the kind answer.</p>
<p>Long story short, I’ve been implementing both (Conditional) GAN and <em>Wasserstein</em> GAN with gradient penalty, and looking at tutorials of people implementing those I was pretty confused, but now I think that I’ve cleared out my perplexities. The code I’ve posted above is generator training of my standard GAN and we don’t use the minus sign as we are trying to optimize the following objective function</p>
<p><div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/9/d/9d17a2138418d3f1dc25aa04f085765f9c7a5235.png" data-download-href="https://discuss.pytorch.org/uploads/default/9d17a2138418d3f1dc25aa04f085765f9c7a5235" title="CondGAN"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/9/d/9d17a2138418d3f1dc25aa04f085765f9c7a5235_2_690x42.png" alt="CondGAN" data-base62-sha1="mpHAZckolBCFkBfAcG7nKzT2yvH" width="690" height="42" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/9/d/9d17a2138418d3f1dc25aa04f085765f9c7a5235_2_690x42.png, https://discuss.pytorch.org/uploads/default/optimized/3X/9/d/9d17a2138418d3f1dc25aa04f085765f9c7a5235_2_1035x63.png 1.5x, https://discuss.pytorch.org/uploads/default/original/3X/9/d/9d17a2138418d3f1dc25aa04f085765f9c7a5235.png 2x" data-dominant-color="F1F1F1"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">CondGAN</span><span class="informations">1077×67 9.77 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>
<p>If we consider the GP-WGAN then the objective function for the generator becomes</p>
<p><div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/3/f/3fb0fadbf86b845cb6fe0e80ffda06e6711bd565.png" data-download-href="https://discuss.pytorch.org/uploads/default/3fb0fadbf86b845cb6fe0e80ffda06e6711bd565" title="WGAN"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/3/f/3fb0fadbf86b845cb6fe0e80ffda06e6711bd565_2_690x53.png" alt="WGAN" data-base62-sha1="95rg19QexkeiL8xYZcjxIVJ0CDH" width="690" height="53" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/3/f/3fb0fadbf86b845cb6fe0e80ffda06e6711bd565_2_690x53.png, https://discuss.pytorch.org/uploads/default/optimized/3X/3/f/3fb0fadbf86b845cb6fe0e80ffda06e6711bd565_2_1035x79.png 1.5x, https://discuss.pytorch.org/uploads/default/original/3X/3/f/3fb0fadbf86b845cb6fe0e80ffda06e6711bd565.png 2x" data-dominant-color="F3F3F3"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">WGAN</span><span class="informations">1064×83 13.8 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>
<p>where the first term is ignored asd we’re minimizing over $\theta$. I think that’s where my confusion arose, while looking at code tutorials people have provided where they were putting minus sign for the critic predictions of fake samples.</p>
<p>Tell me your thoughts about this, if you think that’s correct.</p>
<p>Also, in WGAN case, how do we interpret the losses for generator and discriminator? I guess we still can say that if the critic loss is low then we’re separating ‘in a good way’ p_data and p_G while if the generator loss is low then we are succeeding in getting p_G close to p_data (In a more Euclidean sense this time by considering Wasserstein metric).</p>