【论文阅读】Generative Adversarial Nets
  CFu9A7vdykDj 2023年11月05日 28 0

Goodfellow I, Pouget-Abadie J, Mirza M, et al. Generative adversarial nets[J]. Advances in neural information processing systems, 2014, 27.

引用:61648

原作者代码:GitHub - goodfeli/adversarial: Code and hyperparameters for the paper "Generative Adversarial Networks"

摘要

我们提出了一种通过对抗过程估计生成模型的新框架,其中我们同时训练两个模型:捕获数据分布的生成式模型G,以及估计样本来自训练数据而不是G的概率的判别模型D。G的训练过程是最大化D犯错误的概率。这个框架对应于一个极大极小的双人游戏。在任意函数G和D的空间中,存在一个独特的解,其中G恢复训练数据分布,D在任何地方都等于1/2。在G和D由多层感知器定义的情况下,整个系统可以用反向传播算法进行训练。在训练或生成样本期间都不需要任何马尔可夫链或展开的近似推理网络。实验通过对生成样本的定性和定量评估证明了该框架的潜力。

1 简介

深度学习的前景是发现丰富的分层模型[2],这些模型代表了人工智能应用中遇到的各种数据的概率分布,例如自然图像、包含语音的音频波形和自然语言语料库中的符号。到目前为止,深度学习中最引人注目的成功涉及判别模型,通常是那些将高维、丰富的感官输入映射到类标签的模型[14,22]。这些引人注目的成功主要基于反向传播算法和Dropout算法,使用分段线性的单位[19,9,10],这些单位具有特别良好的梯度。深度生成模型的影响较小,因为很难近似最大似然估计和相关策略中出现的许多棘手的概率性的计算,也因为很难在生成上下文中利用分段线性的优势。我们提出了一种新的生成式模型估计过程来回避这些困难。

在所提出的对抗网络框架中,生成式模型与对手对抗:一个判别模型,它学习确定样本是来自模型分布还是数据分布。生成式模型可以被认为类似于一个伪造者团队,试图生产假币并在没有被发现的情况下使用它,而判别模型类似于警察,试图检测假币。这场比赛中的竞争驱使双方团队改进他们的方法,直到假币与真品无法区分。

该框架可以为多种模型和最优化算法产生特定的训练算法。在本文中,我们探讨了生成式模型通过多层感知器传递随机噪声生成样本的特殊情况,判别模型也是多层感知器。我们将这种特殊情况称为对抗网络。在这种情况下,我们可以仅使用非常成功的反向传播算法和Dropout算法[17]训练这两种模型,并仅使用前向传播/正向传播从生成式模型中采样。不需要近似推理或马尔可夫链。

2 相关工作

粗看

具有潜在变量的有向图模型的替代方案是具有潜在变量的无向图模型,例如受限玻尔兹曼机(RBM)[27,16],深度玻尔兹曼机(DBM)[26]及其众多变体。此类模型内的相互作用被表示为非归一化势函数的乘积,通过对随机变量所有状态的全局求和/积分归一化。这个量(配分函数)及其梯度对于除了最微不足道的实例之外的所有实例都是难以处理的,尽管它们可以通过马尔科夫链蒙特卡罗(MCMC)方法进行估计。混合对于依赖MCMC的学习算法提出了一个重大问题[3,5]。

深度信念网络(DBNs)[16]是包含单个向无层和几个有向层的混合模型。虽然存在快速近似逐层训练准则,但DBN会遇到与无向和有向模型相关的计算困难。

也有人提出了不近似或限制对数似然的替代标准,如分数匹配[18]和噪声对比估计(NCE)[13]。这两者都要求学习到的概率密度在分析上指定为规范化常数。请注意,在许多具有多层潜在变量(如DBN和DBM)的有趣生成模型中,甚至不可能推导出可处理的非归一化概率密度。一些模型,如去噪自动编码器[30]和收缩自动编码器,其学习规则与应用于RBM的分数匹配非常相似。在NCE中,正如在这项工作中一样,判别训练准则被用来拟合生成式模型。然而,生成式模型本身不是拟合单独的判别模型,而是用于将生成的数据与固定噪声分布的样本区分开来。因为NCE使用固定噪声分布,所以在模型在观测变量的一小部分子集上学习到甚至近似正确的分布后,学习速度会急剧减慢。

最后,一些技术不涉及明确定义概率分布,而是训练生成机器从期望的分布中抽取样本。这种方法的优点是,这种机器可以设计为通过反向传播进行训练。该领域最近的突出工作包括生成随机网络(GSN)框架[5],它扩展了广义去噪自动编码器[4]:两者都可以看作是定义了参数化马尔科夫链,即一个人学习执行生成马尔科夫链一步的机器的参数。与GSNs相比,对抗网络框架不需要马尔科夫链进行抽样。因为对抗网络在生成过程中不需要反馈循环,所以它们能够更好地利用分段线性的单元[19,9,10],这提高了反向传播算法的性能,但在反馈中使用时存在无界激活的问题。通过反向传播训练生成机器的最新例子包括最近关于自编码变分贝叶斯[20]和随机反向传播算法[24]的工作。

3 对抗网络

当模型都是多层感知器时,对抗性建模框架最容易应用。 为了学习生成器在数据$x$的分布$p_g$,我们定义了一个先验的输入噪声变量$p_{\boldsymbol{z}}(\boldsymbol{z})$,然后将到数据空间的映射表示为$G\left(\boldsymbol{z} ; \theta_g\right)$,其中$G$是由参数为$θ_g$的多层感知器表示的可微函数。我们还定义了第二个多层感知器 $D\left(\boldsymbol{x} ; \theta_d\right)$ ,它输出单个标量。$D(\boldsymbol{x})$表示$x$来自数据而不是$p_g$的概率。我们训练$D$,以最大化为训练示例和$G$中的样本分配正确标签的概率。我们同时训练$G$以最小化$\log (1-D(G(z)))$:

换言之,$D$和$G$用值函数$V(G,D)$进行了如下的两人极小极大博弈: image.png

在下一节中,我们提出了对抗网络的理论分析,本质上表明当G和D有足够的容量时,即在非参数极限下,训练准则允许人们恢复数据生成分布。图1给出了对该方法不太正式的、更多的教学解释。实际上,我们必须使用迭代和数值方法来执行游戏。在训练的内环中优化D到完成在计算上是禁止的,并且对于有限的数据集将导致过拟合。相反,我们在优化D的k步和优化G的1步之间交替进行,这导致D保持在最优解附近,只要G的变化足够缓慢。这种策略类似于SML/PCD[31,29]训练的方式,从一个学习步骤到下一个学习步骤,保持马尔可夫链中的样本,以避免马尔可夫链作为学习内部循环的一部分被烧毁。该过程在算法1中正式给出。

在实践中,方程1可能不能提供足够的梯度让G学习得更好。在学习早期,当G较差时,D可以拒绝高可信度的样本,因为这些样本与训练数据明显不同。此时,$log(1−D(G(z)))$饱和。与其训练G最小化$log(1−D(G(z)))$,不如训练G最大化$logD(G(z))$。这个目标函数使G和D的动态具有相同的固定点,但在学习早期提供了更强的梯度。

image-20231103151648896

图1:生成式对抗网络是通过同时更新判别分布 ( $D$, blue, dashed line) 来训练的,这样他就可以区分生成分布(黑色虚线)和生成分布(绿色实线)的样本。下面的水平线是z被采样的区域,在这种情况下均匀的。上面的水平线是x的域的一部分。向上的箭头显示了映射 $x=G(z)$ 是如何将非均匀分布 $p_g$ 强加到转换样本上的。G在$p_g$的高密度区域收缩,在$p_g$的低密度区域扩张。

(a)考虑一个接近收敛的对抗对: $p_g$ 类似于 $p_{\text {data }}$ , D是一个部分准确的分类器。

(b)在算法D的内部循环中,训练它从数据中区分样本,收敛到$D^*(\boldsymbol{x})=$ $\frac{p_{\text {and }}(x)}{p_{\text {dan }}(z)+p_g(x)}$。

(c) G更新后,D的梯度引导G(z)流向更可能被归类为数据的区域。

(d)经过几个步骤的训练,如果G和D有足够的能力,他们会达到一个点,两者都不能提高,因为 $p_g=p_{\text {data }}$。鉴别器无法区分两个分布,即$D(x)=\frac{1}{2}$。

4 理论结果

太难看了,看的下面这个视频

GAN | 生成对抗网络合集p1_哔哩哔哩_bilibili

$$ \min {G} \max {D} V(D, G)=E{x \sim P{\text {data }}(x)} \log D(x)+E_{z \sim P_{z}(z)}[\log (1-D(G(z)))] $$

假设$D$网络结构最后有一层$softmax$函数,则$D(\cdot) \in[0,1]$

image.png

为什么做下面的优化: $$ \min {G} \max {D} V(D, G)=E{x \sim P{\text {data }}(x)} \log D(x)+E_{z \sim P_{z}(z)}[\log (1-D(G(z)))] $$ 就可以得到$P_G(x)=P_{data}(x)$呢? $$ \begin{aligned} V(D, G) &=E_{x \sim P_{\text {data }}(x)} \log D(x)+E_{z \sim P_{z}(z)}[\log (1-D(G(z)))] \ &=\int_{x} p_{\text {data }}(x) \log D(x) d x+\int_{z} p_{Z}(z)[\log (1-D(G(z)))] d z \ &=\int_{x} p_{\text {data }}(x) \log D(x) d x+\int_{x} p_{G}(x) \log (1-D(x)) d x \ &=\int_{x} p_{\text {data }}(x) \log D(x)+p_{G}(x) \log (1-D(x)) d x \end{aligned} $$

$$ \begin{aligned} &V(D, G)=\int_{x} p_{\text {data }}(x) \log D(x)+p_{G}(x) \log (1-D(x)) d x \ &\max {D} V(D, G) \Leftrightarrow \max {D} p{\text {data }}(x) \log D(x)+p{G}(x) \log (1-D(x)) \ &\frac{d\left(p_{\text {data }}(x) \log D(x)+p_{G}(x) \log (1-D(x))\right)}{d D} \stackrel{\text { 令 }}{=} 0 \ &\Rightarrow \frac{p_{\text {data }}(x)}{D(x)}-\frac{p_{G}(x)}{1-D(x)}=0 \Rightarrow D(x)=\frac{p_{\text {data }}(x)}{p_{\text {data }}(x)+p_{G}(x)} \end{aligned} $$

将 $D(x)=\frac{p_{\text {data }}(x)}{p_{\text {data }}(x)+p_{G}(x)}$ 代入 $V(D, G)$ 有, $$ V=\int_{x} p_{\text {data }}(x) \log \frac{p_{\text {data }}(x)}{p_{\text {data }}(x)+p_{G}(x)}+p_{G}(x) \log \frac{p_{G}(x)}{p_{\text {data }}(x)+p_{G}(x)} d x\

=\int_{x} p_{\text {data }}(x) \log \frac{\frac{1}{2} p_{\text {data }}(x)}{\frac{p_{\text {data }}(x)+p_{G}(x)}{2}}+p_{G}(x) \log \frac{\frac{1}{2} p_{G}(x)}{\frac{p_{\text {data }}(x)+p_{G}(x)}{2}} d x $$

由 $K L(P | Q)=\sum_{x \in X} P(x) \log \frac{P(x)}{Q(x)}$ ,得上式为:

$$ V=-2 \log 2+K L\left(p_{\text {data }} | \frac{p_{\text {data }}(x)+p_{G}(x)}{2}\right)+K L\left(p_{G} | \frac{p_{\text {data }}(x)+p_{G}(x)}{2}\right) $$ 现在通过改变$G$,使得$V$最小 $$ \min {G}-2 \log 2+K L\left(p{\text {data }} | \frac{p_{\text {data }}(x)+p_{G}(x)}{2}\right)+K L\left(p_{G} | \frac{p_{\text {data }}(x)+p_{G}(x)}{2}\right) $$ 又由 $J S D(P | Q)=\frac{1}{2} K L(P | M)+\frac{1}{2} K L(Q | M)$, 其中 $M=\frac{1}{2}(P+Q)$, 得上式为: $$ \min {G}-2 \log 2+2 J S D\left(P{\text {data }} | P_{G}\right) $$ 故, 当 $P_{G}(x)=P_{\text {data }}(x)$ 时, 上式可得最小值。 所以只需要优化: $$ \min {G} \max {D} V(D, G)=E{x \sim P{\text {data }}(x)} \log D(x)+E_{z \sim P_{z}(z)}[\log (1-D(G(z)))] $$ 就可以得到 $P_{G}(\boldsymbol{x})=P_{\text {data }}(\boldsymbol{x})$.

5 实验

我们训练了一系列数据集的对抗网络,包括MNIST[23]、多伦多人脸数据库(TFD)[28]和CIFAR-10[21]。生成器网络使用整流器线性激活[19,9]和sigmoid激活的混合物,而鉴别器网络使用maxout[10]激活。Dropout[17]应用于训练鉴别器网络。虽然我们的理论框架允许在生成器的中间层使用dropout和其他噪声,但我们仅将噪声用作生成器网络最底层的输入。

image-20231103153846305

表1:基于Parzen窗口的对数似然估计。MNIST上报告的数字是测试集上样本的平均对数似然,平均的标准误差是跨示例计算的。在TFD上,我们计算了数据集折叠之间的标准误差,使用每个折叠的验证集选择不同的σ。在TFD上,σ在每个折叠上交叉验证,并计算每个折叠上的平均对数似然。对于MNIST,我们与数据集实值(而不是二进制)版本的其他模型进行了比较。

我们通过将高斯Parzen窗口拟合到用G生成的样本并报告该分布下的对数似然来估计$p_g$下测试集数据的概率。高斯的σ参数是通过在验证集上进行交叉验证获得的。该过程在Breuleux等人[8]中引入,并用于各种精确似然不可处理的生成模型[25,3,5]。结果报告在表1中。这种估计似然的方法具有较高的方差,在高维空间中表现不佳,但据我们所知,它是最好的方法。可以采样但不能直接估计似然的生成模型的进展推动了对如何评估此类模型的进一步研究。

在图2和图3中,我们展示了训练后从生成器网络中抽取的样本。虽然我们没有声称这些样本比现有方法生成的样本更好,但我们相信这些样本至少与文献中更好的生成模型竞争,并突出了对抗框架的潜力。

image.png

图2:来自模型的样本的可视化。最右边的列显示了相邻样本的最近训练示例,以便证明模型没有记住训练集。样本是公平的随机抽取,而不是精心挑选的。与深度生成模型的大多数其他可视化不同,这些图像显示了来自模型分布的实际样本,而不是条件意味着给定隐藏单元的样本。此外,这些样本是不相关的,因为抽样过程不依赖于马尔科夫链混合。a)MNIST b)TFD c)CIFAR-10(全连接模型) d)CIFAR-10(卷积判别器和“反卷积”生成器)

image.png 图3:通过在完整模型的z空间中的坐标之间进行线性插值获得的数字。

image-20231103154411044

表2:生成式建模的挑战:对涉及模型的每个主要操作的深度生成式建模的不同方法所遇到的困难的总结。

6 优点和缺点

这个新框架相对于以前的建模框架有优点也有缺点。缺点主要是没有明确表示$p_g(x)$,并且在训练期间D必须与G很好地同步(特别是,在不更新D的情况下,不得对G进行过多的训练,以避免“Helvetica场景”,即G将太多的z值折叠为相同的x值,从而具有足够的多样性来对$p_{data}$进行建模),就像玻尔兹曼机器的负链必须在学习步骤之间保持最新一样。优点是永远不需要马尔可夫链,只使用反向支撑来获得梯度,学习期间不需要推理,并且可以将各种各样的函数合并到模型中。表2总结了生成对抗网络与其他生成式建模方法的比较。

上述优势主要是计算上的。对抗模型也可能从生成器网络中获得一些统计优势,而不是直接用数据示例更新,而是只使用流经判别器的梯度。这意味着输入的组件不会直接复制到生成器的参数中。对抗网络的另一个优势是它们可以表示非常尖锐,甚至退化的分布,而基于马尔可夫链的方法要求分布有些模糊,以便链能够在模式之间混合。

7 结论及未来工作

这个框架允许许多简单的扩展:

  1. 条件生成模型$p(x|c)$可以通过将c作为输入加到G和D中得到。
  2. 学习近似推理可以通过训练辅助网络来预测给定x的z来执行。这类似于由唤醒-休眠算法[15]训练的推理网络,但优点是在生成器网络完成训练后,可以为固定生成器网络训练推理网络。
  3. 人们可以通过训练共享参数的一系列条件模型来近似建模所有条件$p(x_S|x _$)$,其中S是x的指数的子集。本质上,人们可以使用对抗网络来实现确定性MP-DBM的随机扩展[11]。
  4. 半监督学习:当可用的标注数据有限时,来自判别器或推理网络的特征可以提高分类器的性能。
  5. 效率改进:通过划分更好的方法来协调G和D或在训练期间确定更好的分布来采样z,可以大大加速训练。

本文证明了对抗建模框架的可行性,表明这些研究方向可能证明是有用的。

keras复现——Vanilla GAN实现生成mnist手写数字图片

from __future__ import print_function, division

from keras.datasets import mnist
from keras.layers import Input, Dense, Reshape, Flatten
from keras.layers import BatchNormalization
from keras.layers.advanced_activations import LeakyReLU
from keras.models import Sequential, Model
from keras.optimizer_v2.adam import Adam

import matplotlib.pyplot as plt
import numpy as np
import os


class GAN:
    def __init__(self):
        # --------------------------------- #
        #   行28,列28,也就是mnist的shape
        # --------------------------------- #
        self.img_rows = 28
        self.img_cols = 28
        self.channels = 1
        # 28,28,1
        self.img_shape = (self.img_rows, self.img_cols, self.channels)
        self.latent_dim = 100
        # adam优化器
        optimizer = Adam(0.0002, 0.5)

        self.discriminator = self.build_discriminator()
        self.discriminator.compile(loss='binary_crossentropy',
                                   optimizer=optimizer,
                                   metrics=['accuracy'])

        self.generator = self.build_generator()
        gan_input = Input(shape=(self.latent_dim,))
        img = self.generator(gan_input)
        # 在训练generate的时候不训练discriminator
        self.discriminator.trainable = False
        # 对生成的假图片进行预测
        validity = self.discriminator(img)
        self.combined = Model(gan_input, validity)
        self.combined.compile(loss='binary_crossentropy', optimizer=optimizer)

    def build_generator(self):
        # --------------------------------- #
        #   生成器,输入一串随机数字
        # --------------------------------- #
        model = Sequential()

        model.add(Dense(256, input_dim=self.latent_dim))
        model.add(LeakyReLU(alpha=0.2))
        model.add(BatchNormalization(momentum=0.8))

        model.add(Dense(512))
        model.add(LeakyReLU(alpha=0.2))
        model.add(BatchNormalization(momentum=0.8))

        model.add(Dense(1024))
        model.add(LeakyReLU(alpha=0.2))
        model.add(BatchNormalization(momentum=0.8))

        model.add(Dense(np.prod(self.img_shape), activation='tanh'))
        model.add(Reshape(self.img_shape))

        noise = Input(shape=(self.latent_dim,))
        img = model(noise)

        return Model(noise, img)

    def build_discriminator(self):
        # ----------------------------------- #
        #   评价器,对输入进来的图片进行评价
        # ----------------------------------- #
        model = Sequential()
        # 输入一张图片
        model.add(Flatten(input_shape=self.img_shape))
        model.add(Dense(512))
        model.add(LeakyReLU(alpha=0.2))
        model.add(Dense(256))
        model.add(LeakyReLU(alpha=0.2))
        # 判断真伪
        model.add(Dense(1, activation='sigmoid'))

        img = Input(shape=self.img_shape)
        validity = model(img)

        return Model(img, validity)

    def train(self, epochs, batch_size=128, sample_interval=50):
        # 获得数据
        (X_train, _), (_, _) = mnist.load_data()

        # 进行标准化
        X_train = X_train / 127.5 - 1.
        X_train = np.expand_dims(X_train, axis=3)

        # 创建标签
        valid = np.ones((batch_size, 1))
        fake = np.zeros((batch_size, 1))

        for epoch in range(epochs):

            # --------------------------- #
            #   随机选取batch_size个图片
            #   对discriminator进行训练
            # --------------------------- #
            idx = np.random.randint(0, X_train.shape[0], batch_size)
            imgs = X_train[idx]

            noise = np.random.normal(0, 1, (batch_size, self.latent_dim))

            gen_imgs = self.generator.predict(noise)

            d_loss_real = self.discriminator.train_on_batch(imgs, valid)
            d_loss_fake = self.discriminator.train_on_batch(gen_imgs, fake)
            d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)

            # --------------------------- #
            #  训练generator
            # --------------------------- #
            noise = np.random.normal(0, 1, (batch_size, self.latent_dim))
            g_loss = self.combined.train_on_batch(noise, valid)
            print("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (epoch, d_loss[0], 100 * d_loss[1], g_loss))

            if epoch % sample_interval == 0:
                self.sample_images(epoch)

    def sample_images(self, epoch):

        r, c = 5, 5
        noise = np.random.normal(0, 1, (r * c, self.latent_dim))
        gen_imgs = self.generator.predict(noise)

        gen_imgs = 0.5 * gen_imgs + 0.5

        fig, axs = plt.subplots(r, c)
        cnt = 0
        for i in range(r):
            for j in range(c):
                axs[i, j].imshow(gen_imgs[cnt, :, :, 0], cmap='gray')
                axs[i, j].axis('off')
                cnt += 1
        fig.savefig("images/%d.png" % epoch)
        plt.close()


if __name__ == '__main__':
    if not os.path.exists("./images"):
        os.makedirs("./images")
    gan = GAN()
    gan.train(epochs=30000, batch_size=256, sample_interval=200)

image.png

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
CFu9A7vdykDj