# 特征匹配损失

训练DCGAN时，判别器输出一个0到1的值表示输入样本是真实样本的概率，生成器的目标是使该判别值最大。可见，我们只是依据判别值指导生成器更新参数，对于生成器来说可参考的信息太少。为了让生成器参考更多的信息，我们尝试使用特征匹配损失。

特征匹配损失可使生成样本与真实样本在判别器中间层输出的特征互相匹配，来改善对抗训练的不稳定性。从判别器的结构来看，整个网络由特征提取层和分类层组成。特征提取层提取输入样本的抽象特征，分类层则将抽象特征映射为一个判别值，完成对输入样本的分类。前面的训练方法我们只是参考了分类层的输出，而使用特征匹配损失可参考特征提取层的输出。分别截取生成样本与真实样本在判别器某一特征提取层的输出特征，并度量这两个抽象特征之间的差距，通过这个差距来指导生成器学习更多信息，使得生成的样本更加真实可靠。

特征匹配损失表示为生成样本在判别器中间层上特征的期望值与真实样本在判别器中间层上特征的期望值的均方误差。具体实现方面，因为特征匹配损失是为了更新生成器的参数，所以仅用于生成器，判别器仍使用原来的二分类交叉熵损失。

特征匹配损失的表达式如下：

$$
||E_{x \backsim p_{data}} f(x) - E_{z \backsim p_{z}^{(z)}}f(G(z))||_2^2 \tag {4-15}
$$

其中f ( )表示判别器中间层的映射，表示求取期望。

基于PyTorch实现的代码如下：

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

我们使用单独的层来表示判别器的网络结构，方便取出每一层的输出，进行特征匹配损失的计算。因为我们更改了判别器的输出，在对判别器进行训练时，只需要out5，所以我们使用NULLD表示判别器用不到的输出。对生成器进行训练时，先使用netD(real_cpu.detach())获得真实样本的中间特征，再使用netD(fake)获得生成样本的中间特征。这里我们分别取真实样本与生成样本在判别器self.layer4层的输出特征out4。然后对这两个特征求取对应的期望，并计算获得特征匹配损失。最后利用特征匹配损失更新模型。

基于PyTorch实现的代码如下，完整代码可扫描二维码下载。

:::{todo}
待适配
:::


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

采用特征匹配损失的训练时间约为189分钟，训练结果为图4-11右图，图4-11左图为未使用改进方法的训练结果。使用特征匹配损失后，部分生成样本有所改进，但一部分样本存在模式奔溃的现象。

:::::{grid} 2 2 2 2

::::{grid-item}
:::{figure} ../../_static/4/4.3/4-11-a.png
:::
::::
::::{grid-item}
:::{figure} ../../_static/4/4.3/4-11-b.png
:::
::::
:::::
<div class="show-mid">图4-11 改进方法前后的建筑立面效果对比</div>
<br>
<br>
