# 损失函数与优化器

在正式开始训练前，需定义损失函数。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{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}
$$

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


:::{literalinclude} ../codes/chapter_4_2_8_01.py
:caption: chapter_4_2_8_01.py
:language: python
:linenos:
:::
