4.2.8. 损失函数与优化器#

在正式开始训练前,需定义损失函数。DCGAN的损失函数LGAN表示为以下形式:

\[ \min_{G} \max_{D} \ L_{GAN}(D,G) = E_{x \backsim P_{data}^{(x)}}[log \ D(x)] + E_{z \backsim p_z^{(z)}}[log (1-D(G(z)))] \tag {4-4} \]

其中,D表示判别器,G表示生成器,x表示训练数据,z表示随机噪声,表示数学期望。上式等号左边包含了两个部分,即min G和max D,因此我们可以将损失计算分为两步进行。

首先,对于判别器来说,有:

\[ \max_D \ L_{GAN}(D)=E_{x \backsim p_{data}^{(x)}}[log \ D(x)] + E_{z \backsim p_z^{(z)}}[log(1-D(G(z)))] \tag {4-5} \]

我们希望判别器能够正确分辨生成图片和真实图片,相当于要使D(x)接近1、D(G(z))接近0,即1-D(G(z))也接近1。由于log是一个单调递增的函数,因此,我们希望D(x)接近1、1-D(G(z))接近1,可等价为希望判别器计算出的函数值最大化。

对于生成器来说,由于只与损失函数中的第二项有关,因此只需考虑损失函数中的第二项,即:

\[ \min_{G} L_{GAN} (G) = E_{z \backsim p_z^{(z)} } [log(1-D(G(z)))] \tag {4-6} \]

我们要通过训练,使生成器能够生成以假乱真的图片,这时,判别器对于生成样本G(z) 的判别值D(G(z))应接近1。又因为D(G(z))的值接近1的过程,会让log(1- D(G(z)))的值逐渐减小,因此,我们的期望即等价于希望生成器计算出的函数值最小化。

下面我们详细说明如何使用二进制交叉熵损失BCELoss表示DCGAN的损失函数。

在第二章中,我们给出了BCELoss的表达式如下所示,其中yi是真实标签,p(yi)是模型对该标签预测值:

\[ BCELoss=- \frac {1} {n} \sum_{i}^{n}(y_i \cdot log\ p(y_i) + (1-y_i) \cdot log(1-p(y_i)) ) \tag {4-7} \]

DCGAN先训练判别器,再训练生成器。训练判别器时,首先输入一批真实图像x,判别器的输出为D(x),又因为真实图像的标签都为1,那么此时的损失BCELoss1即为:

\[BCELoss_1=- \frac {1}{n} \sum_{i}^{n}log \ y_i=-E_{x \backsim p_{data}^{(x)}}[log \ D(x)] \tag {4-8}\]

然后输入一批生成的图像G(z),判别器的输出为D(G(z)),生成图像的标签都为0,那么此时的损失BCELoss2可表示为:

\[ BCELoss_2=- \frac {1}{n} \sum_{i}^{n} log(1-y_i)=-E_{x \backsim p_z^{(z)}}[log(1-D(G(z)))] \tag {4-9} \]

把上面两个损失值加起来,我们可以表示出判别器的二元交叉熵损失:

\[ BCELoss_D=-E_{x \backsim p_{data}^{x}}[log \ D(x)] -E_{x \backsim p_z^{(z)}}[log(1-D(G(z)))]=-L_{GAN}(D) \tag {4-10} \]

容易发现,它和判别器损失函数的值恰好相反。这样,我们就可以通过最小化判别器的BCELoss实现判别器损失函数值最大化的目标了! 现在,让我们尝试将生成器的损失函数也转换为用BCELoss表示。先前提到过,我们希望生成器最终生成的图片能够以假乱真,即判别器对生成图片G(z)的判别结果D(G(z))趋于1,使得log(1- D(G(z)))最小化,这可以等价为对-log(D(G(z)))的最小化:

\[ \min_{G} L_{GAN}(G)=E_{z \backsim p_z^{(z)}}[log(1-D(G(z)))] \Leftrightarrow -E_{z \backsim p_z^{(z)}}[log \ D(G(z))] \tag {4-11} \]

不难看出,如果我们将上式补全,即可用BCELoss表示生成器的损失,这样我们便可以同样地通过计算BCELoss来得到生成器损失值了。补全后的损失函数可表示如下:

\[\begin{split} \begin{align} \begin{aligned} L_{GAN}(G) &= -E_{z \backsim p_z^{(z)}}[log \ D(G(z))] \\ &= - \frac {1} {n} \sum_i^{n} (1 \cdot log \ D(G(z))-(1-1) \cdot log(1-D(G(z)))) = BCELoss_G \\ \end{aligned} \ \tag {4-12} \end{align} \end{split}\]

我们已经成功用BCELoss表示出了DCGAN生成器和判别器的损失函数!在程序中,我们可以通过定义BCELoss函数在训练时计算生成器和判别器的损失值。除此之外,我们还需要定义优化器。我们把生成器和判别器的优化器都设置为Adam,如下示代码:

chapter_4_2_8_01.py#
 1import torch
 2from torch import nn
 3
 4from chapter_4_2_3_01 import lr, beta
 5from chapter_4_2_7_01 import netG, netD
 6
 7# 损失函数与优化器
 8criterion = nn.BCELoss()
 9optimizerD = torch.optim.Adam(netD.parameters(), lr=lr, betas=(beta, 0.999))
10optimizerG = torch.optim.Adam(netG.parameters(), lr=lr, betas=(beta, 0.999))