文献阅读笔记--GAN--Generative Adversarial NetworkGAN的原始论文-组会讲解

本文详细介绍了生成对抗网络(GAN)的起源、基本思想和工作原理,包括生成器和判别器的角色以及它们的优化目标。通过对目标函数的分析,展示了GAN如何通过最大化鉴别器的困惑度来最小化生成器与真实数据分布的差异。文中还探讨了GAN的全局最优解,以及训练过程中的收敛性,并提供了基于MNIST数据集的简单GAN实现代码示例,揭示了GAN在图像生成任务中的应用。
摘要由CSDN通过智能技术生成

Generative Adversarial Network

作者:Ian Goodfellow
时间:2014年
会议:NIPS 2014
论文地址Arxiv:https://arxiv.org/abs/1406.2661

Basic idea of GAN

生成对抗网络

GAN 主要包括了两个部分,即生成器 generator 与判别器 discriminator。

  • 生成器主要用来学习真实图像分布从而让自身生成的图像更加真实,以骗过判别器。
  • 判别器则需要对接收的图片进行真假判别。

在整个过程中,生成器努力地让生成的图像更加真实,而判别器则努力地去识别出图像的真假,这个过程相当于一个二人博弈。
随着时间的推移,生成器和判别器在不断地进行对抗,最终两个网络达到了一个动态均衡:生成器生成的图像接近于真实图像分布,而判别器识别不出真假图像,对于给定图像的预测为真的概率基本接近 0.5(相当于随机猜测类别)

公式和原理

目标函数和最大最小

目标函数

从他的framework 我们可以得到GAN的基本思路,一个生成器一个判别器,尝试从中找到最优解(平衡点)
然后,自然想到如何把这个思想转变成真正的模型。
论文中通过给出目标函数和训练过程来描述这个事情,文章中给出了目标函数:
在这里插入图片描述
思考如何得到目标函数:
首先我们有一些定义

  data→真实数据(groundtruth)
  pdata→真实数据的分布
  z→噪音(输入数据)
  pz→原始噪音的分布
  pg→经过生成器后的数据分布
  G()→生成映射函数
  D()→判别映射函数
G 是生成器,结构为一个多层感知机,参数为 θg,G(z;θg) 为生成映射函数,将噪音 z 映射到新的数据空间。
D 是判别器,也是一个多层感知机,参数为 θd,D(x;θd) 输出为一个标量,表示 x 来自真实数据data而不是生成数据的概率。

E ( . ) E(.) E(.) 表示 标签为正类时的期望函数的log 损失函数。
E ( p ∣ y ) = − 1 N ∑ i = 1 N ( y i ( log ⁡ p i ) + ( 1 − y i ) ( 1 − p i ) ) E(p \mid y)=\frac{-1}{N} \sum_{i=1}^{N}\left(y_{i}\left(\log p_{i}\right)+\left(1-y_{i}\right)\left(1-p_{i}\right)\right) E(py)=N1i=1N(yi(logpi)+(1yi)(1pi))

定义最优化问题的方法由两部分组成。首先我们需要定义一个判别器 D 以判别样本是不是从 P data  ( x ) P_{\text {data }}(x) Pdata (x) 分布中 取出来的, 因此有:
E x ∼ p data  ( x ) log ⁡ ( D ( x ) ) E_{x \sim p_{\text {data }}(x)} \log (D(x)) Expdata (x)log(D(x))
其中 E 指代取期望。这一项是根据「正类」 \quad (即辨别出 x \mathrm{x} x 属于真实数据 data ) ) ) 的对数损失函数而构建的。最 大化这一项相当于令判别器 D 在 x \mathrm{x} x 服从于 data 的概率密度时能准确地预测 D ( x ) = 1 \mathrm{D}(\mathrm{x})=1 D(x)=1, 即:
D ( x ) = 1  when  x ∼ p data  ( x ) D(x)=1 \text { when } x \sim p_{\text {data }}(x) D(x)=1 when xpdata (x)
另外一项是企图欺骗判别器的生成器 G。该项根据「负类」的对数损失函数而构建, 即:
E z ∼ p z ( z ) log ⁡ ( 1 − D ( G ( z ) ) ) E_{z \sim p_{z}(z)} \log (1-D(G(z))) Ezpz(z)log(1D(G(z)))
(上述概念中涉及一个模型认知问题:整个GAN看来是一个无监督的问题,分开来看,D部分其实是一个二分类有监督问题。)

分别 得到 鉴别器和生成器的最优表达式之后,我们可以定义目标函数为:
在这里插入图片描述
对于 D 而言要尽量使公式最大化 (识别能力强),而对于 G 又想使之最小 (生成的数据接近实际数据) 。整 个训练是一个迭代过程。其实极小极大化博亦可以分开理解,即在给定 G 的情况下先最大化 V ( D , G ) V(D, G) V(D,G) 而取 D,然后固定 D, 并最小化 V ( D , G ) V(D, G) V(D,G) 而得到 G。其中, 给定 G, 最大化 V ( D , G ) V(D, G) V(D,G) 评估了 P g P_{g} Pg P data  P_{\text {data }} Pdata  之间 的差异或距离。(同时也是训练算法的描述)
D G ∗ = arg ⁡ max ⁡ D V ( D , G ) D_{G}^{*}=\arg \max _{D} V(D, G) DG=argDmaxV(D,G)
最后,我们可以将最优化问题表达为:
G ∗ = argmin ⁡ G V ( G , D G ∗ ) G^{*}=\operatorname{argmin}_{G} V\left(G, D_{G}^{*}\right) G=argminGV(G,DG)

最大最小的博弈过程

在这里插入图片描述

图中,黑色曲线是真实样本的概率分布函数,绿色曲线是虚假样本的概率分布函数,蓝色曲线是判别器D的输出,它的值越大表示这个样本越有可能是真实样本。最下方的平行线是噪声z,它映射到了x。

我们可以看到,一开始, G(z) 和 x 是在同一个特征空间里的,它们分布的差异很大,这时,虽然鉴别真实样本和虚假样本的模型 D 性能也不强,但它很容易就能把两者区分开来,而随着训练的推进,虚假样本的分布逐渐与真实样本重合,D 虽然也在不断更新,但也已经力不从心了。

最后,黑线和绿线最后几乎重合,模型达到了最优状态,这时 D 的输出对于任意样本都是 0.5。

训练方法

在这里插入图片描述
在这里插入图片描述
模型按照如图所示的步骤训练。首先固定G,单独训练D,为了让D得到充分训练,有的时候要迭代多次。本论文中每一轮迭代D只训练一次。D训练完毕后,固定D,训练G,如此循环。训练的方式是反向传播算法。
可以注意到,在训练过程中,没有任何求出真实样本的分布或是生成样本的分布的过程。

在每一步的训练中:

  1. 取 m 个真实数据,使用 G 和 m 组随机数(一般使用服从正态分布的随机数)生成 m 个假数据
  2. 根据 max 部分的目标更新 D 的参数,提高 D 的分辨能力
  3. 根据 min 部分的目标更新 G 的参数,使 G 生成的数据更有迷惑性

下面, 我们需要证明:该最优化问题有唯一解 G ∗ , G^{*}, G, 并且该唯一解满足 P G = P data  P_{G}=P_{\text {data }} PG=Pdata 

理论部分-全局最优-命题一

命题一:原文如下图
在这里插入图片描述
在这里插入图片描述

命题一证明解析:
  1. 由目标函数得到$ V(D, G)$的表达式,并把期望改写为积分形式。
    (eg:这里是随机变量函数的期望公式,函数为 log ⁡ D ( x ) \log D(\boldsymbol{x}) logD(x),而 x x x 服从的分布为 p data  ( x ) p_{\text {data }}(\boldsymbol{x}) pdata (x) 所以改写为如图形式)
  2. 根据测度论中的 Radon-Nikodym 定理进行变换,得到第二个等号。
    (说明:见备注一,备注二,备注三)
  3. 在数据给定, G 给定的前提下, P data  ( x ) \quad P_{\text {data }}(x) Pdata (x) P G ( x ) P_{G}(x) PG(x) 都可以看作是常数,我们可以分别用 a , b a, b a,b 来表示他们,这样我们就可以得到如下的式子:
    在这里插入图片描述
  4. 在满足定理结论中的条件处,达到了最小,证明了第一个条件得到了,给定G的时候,鉴别器所满足的条件。

当然 GAN 过程的目标是令 P G = P data  。 P_{G}=P_{\text {data } 。} PG=Pdata  这对最优的 D 意味着什么呢? 我们可以将这一等式代入 D G ∗ D_{G *} DG 的表达式中:
D G ∗ = p data  p data  + p G = 1 2 D_{G}^{*}=\frac{p_{\text {data }}}{p_{\text {data }}+p_{G}}=\frac{1}{2} DG=pdata +pGpdata =21
这意味着判别器已经完全困惑了,它完全分辨不出 P data  P_{\text {data }} Pdata  P G P_{G} PG 的区别, 即判断样本来自 P data  P_{\text {data }} Pdata  P G P_{G} PG 的概率都为 1 2 \frac{1}{2} 21 。基于这一观点, GAN 作者证明了 G 就是极小极大博亦的解。该定理如下:

理论部分-全局最优-定理1

在这里插入图片描述

定理一证明解析:

由定理 if an only if:
可以看到这是一个充分必要条件,故需要进行双向证明。
首先我们先从反向逼近并证明 C ( G ) C(G) C(G) 的取值, 然后再利用由反向获得的新知识从正向证明。设 P G = P data  P_{G}=P_{\text {data }} PG=Pdata  (反向指预先知道最优 条件并做推导),我们可以反向推出
V ( G , D G ∗ ) = ∫ x p data  ( x ) log ⁡ 1 2 + p G ( x ) log ⁡ ( 1 − 1 2 ) d x V\left(G, D_{G}^{*}\right)=\int_{x} p_{\text {data }}(x) \log \frac{1}{2}+p_{G}(x) \log \left(1-\frac{1}{2}\right) \mathrm{d} x V(G,DG)=xpdata (x)log21+pG(x)log(121)dx
and
V ( G , D G ∗ ) = − log ⁡ 2 ∫ x p G ( x ) d x − log ⁡ 2 ∫ x p data  ( x ) d x = − 2 log ⁡ 2 = − log ⁡ 4 V\left(G, D_{G}^{*}\right)=-\log 2 \int_{x} p_{G}(x) \mathrm{d} x-\log 2 \int_{x} p_{\text {data }}(x) \mathrm{d} x=-2 \log 2=-\log 4 V(G,DG)=log2xpG(x)dxlog2xpdata (x)dx=2log2=log4
该值是全局最小值的候选, 因为它只有在 P G = P data  P_{G}=P_{\text {data }} PG=Pdata  的时候才出现。

然后,我们现在需要从正向证明这一个值常常为最小值,也就是同时满足「当」和「仅当」的条件。

现在放弃 P G = P data  P_{G}=P_{\text {data }} PG=Pdata  的假设, 对任意一
个 G,我们可以将上一步求出的最优判别器 D ∗ D^{*} D 代入到 C ( G ) = max ⁡ V ( G , D ) C(G)=\max V(G, D) C(G)=maxV(G,D) 中:
C ( G ) = ∫ x p data  ( x ) log ⁡ ( p data  ( x ) p G ( x ) + p data  ( x ) ) + p G ( x ) log ⁡ ( p G ( x ) p G ( x ) + p data  ( x ) ) d x C(G)=\int_{x} p_{\text {data }}(x) \log \left(\frac{p_{\text {data }}(x)}{p_{G}(x)+p_{\text {data }}(x)}\right)+p_{G}(x) \log \left(\frac{p_{G}(x)}{p_{G}(x)+p_{\text {data }}(x)}\right) \mathrm{d} x C(G)=xpdata (x)log(pG(x)+pdata (x)pdata (x))+pG(x)log(pG(x)+pdata (x)pG(x))dx

(技巧性的一步:因为已知 -log4 为全局最小候选值,所以我们希望构造某个值以使方程式中出现 log2。因此我们可以在每个积分中加上或减去 log2,并乘上概率密度。这是一个十分常见并且不会改变等式的数学证明技巧,因为本质上我们只是在方程加上了 0。)
C ( G ) = ∫ x ( log ⁡ 2 − log ⁡ 2 ) p data  ( x ) + p data  ( x ) log ⁡ ( p data  ( x ) p G ( x ) + p data  ( x ) ) + ( log ⁡ 2 − log ⁡ 2 ) p G ( x ) + p G ( x ) log ⁡ ( p G ( x ) p G ( x ) + p data  ( x ) ) d x \begin{aligned} C(G) &=\int_{x}(\log 2-\log 2) p_{\text {data }}(x)+p_{\text {data }}(x) \log \left(\frac{p_{\text {data }}(x)}{p_{G}(x)+p_{\text {data }}(x)}\right) \\ &+(\log 2-\log 2) p_{G}(x)+p_{G}(x) \log \left(\frac{p_{G}(x)}{p_{G}(x)+p_{\text {data }}(x)}\right) \mathrm{d} x \end{aligned} C(G)=x(log2log2)pdata (x)+pdata (x)log(pG(x)+pdata (x)pdata (x))+(log2log2)pG(x)+pG(x)log(pG(x)+pdata (x)pG(x))dx

采用该技巧主要是希望能够构建成含 log2 和 JS 散度的形式, 上式化简后可以得到以下表达式:
C ( G ) = − log ⁡ 2 ∫ x p G ( x ) + p data  ( x ) d x + ∫ x p data  ( x ) ( log ⁡ 2 + log ⁡ ( p data  ( x ) p G ( x ) + p data  ( x ) ) ) + p G ( x ) ( log ⁡ 2 + log ⁡ ( p G ( x ) p G ( x ) + p data  ( x ) ) ) d x \begin{array}{c} C(G)=-\log 2 \int_{x} p_{G}(x)+p_{\text {data }}(x) \mathrm{d} x \\ +\int_{x} p_{\text {data }}(x)\left(\log 2+\log \left(\frac{p_{\text {data }}(x)}{p_{G}(x)+p_{\text {data }}(x)}\right)\right) \\ +p_{G}(x)\left(\log 2+\log \left(\frac{p_{G}(x)}{p_{G}(x)+p_{\text {data }}(x)}\right)\right) \mathrm{d} x \end{array} C(G)=log2xpG(x)+pdata (x)dx+xpdata (x)(log2+log(pG(x)+pdata (x)pdata (x)))+pG(x)(log2+log(pG(x)+pdata (x)pG(x)))dx
因为概率密度的定义, P G P_{G} PG P data  P_{\text {data }} Pdata  在它们积分域上的积分等于 1 , 即:
− log ⁡ 2 ∫ x p G ( x ) + p data  ( x ) d x = − log ⁡ 2 ( 1 + 1 ) = − 2 log ⁡ 2 = − log ⁡ 4 -\log 2 \int_{x} p_{G}(x)+p_{\text {data }}(x) \mathrm{d} x=-\log 2(1+1)=-2 \log 2=-\log 4 log2xpG(x)+pdata (x)dx=log2(1+1)=2log2=log4

此外,根据对数的定义, 我们有:
log ⁡ 2 + log ⁡ ( p data  ( x ) p G ( x ) + p data  ( x ) ) = log ⁡ ( 2 p data  ( x ) p G ( x ) + p data  ( x ) ) = log ⁡ ( p data  ( x ) ( p G ( x ) + p data  ( x ) ) / 2 ) \begin{array}{c} \log 2+\log \left(\frac{p_{\text {data }}(x)}{p_{G}(x)+p_{\text {data }}(x)}\right)=\log \left(2 \frac{p_{\text {data }}(x)}{p_{G}(x)+p_{\text {data }}(x)}\right) \\ =\log \left(\frac{p_{\text {data }}(x)}{\left(p_{G}(x)+p_{\text {data }}(x)\right) / 2}\right) \end{array} log2+log(pG(x)+pdata (x)pdata (x))=log(2pG(x)+pdata (x)pdata (x))=log((pG(x)+pdata (x))/2pdata (x))
因此代入该等式, 我们可以写为:
C ( G ) = − log ⁡ 4 + ∫ x p data  ( x ) log ⁡ ( p data  ( x ) ( p G ( x ) + p data  ( x ) ) / 2 ) d x + ∫ x p G ( x ) log ⁡ ( p G ( x ) ( p G ( x ) + p data  ( x ) ) / 2 ) d x \begin{aligned} C(G)=&-\log 4+\int_{x} p_{\text {data }}(x) \log \left(\frac{p_{\text {data }}(x)}{\left(p_{G}(x)+p_{\text {data }}(x)\right) / 2}\right) \mathrm{d} x \\ &+\int_{x} p_{G}(x) \log \left(\frac{p_{G}(x)}{\left(p_{G}(x)+p_{\text {data }}(x)\right) / 2}\right) \mathrm{d} x \end{aligned} C(G)=log4+xpdata (x)log((pG(x)+pdata (x))/2pdata (x))dx+xpG(x)log((pG(x)+pdata (x))/2pG(x))dx
现在, 如果读者阅读了前文的 KL 散度, 那么我们就会发现每一个积分正好就是它。具体来说:
C ( G ) = − log ⁡ 4 + K L ( p data  ∣ p data  + p G 2 ) + K L ( p G ∣ p data  + p G 2 ) C(G)=-\log 4+K L\left(p_{\text {data }} \mid \frac{p_{\text {data }}+p_{G}}{2}\right)+K L\left(p_{G} \mid \frac{p_{\text {data }}+p_{G}}{2}\right) C(G)=log4+KL(pdata 2pdata +pG)+KL(pG2pdata +pG)

KL散度是非负的, 所以我们马上就能看出来 -log4 为 C ( G ) C(G) C(G) 的全局最小值。 如果我们进一步证明只有一个 G 能达到这一个值, 因为 P G = P data  P_{G}=P_{\text {data }} PG=Pdata  将会成为令 C ( G ) = − log ⁡ 4 C(G)=-\log 4 C(G)=log4

从前文可知 KL 散度是非对称的, 所以 C ( G ) C(G) C(G) 中的 K L ( P data  ∥ ( P data  + P G ) / 2 ) K L\left(P_{\text {data }} \|\left(P_{\text {data }}+P_{G}\right) / 2\right) KL(Pdata (Pdata +PG)/2) 左右两项是不能交换 的, 但如果同时加上另一项 K L ( P data  ∥ ( P data  + P G ) / 2 ) K L\left(P_{\text {data }} \|\left(P_{\text {data }}+P_{G}\right) / 2\right) KL(Pdata (Pdata +PG)/2), 它们的和就能变成对称项。这两项 KL 散度 的和即可以表示为 JS 散度(Jenson-Shannon divergence ) :
JSD ⁡ ( P ∥ Q ) = 1 2 D ( P ∥ M ) + 1 2 D ( Q ∥ M ) M = 1 2 ( P + Q ) \begin{aligned} \operatorname{JSD}(P \| Q)=\frac{1}{2} D(P \| M) &+\frac{1}{2} D(Q \| M) \\ M &=\frac{1}{2}(P+Q) \end{aligned} JSD(PQ)=21D(PM)M+21D(QM)=21(P+Q)
假设存在两个分布 P 和 Q, 且这两个分布的平均分布 M = ( P + Q ) / 2 , M=(P+Q) / 2, M=(P+Q)/2, 那么这两个分布之间的 JS 散 度为 P 与 M 之间的 KL散度加上 Q 与 M 之间的 KL 散度再除以 2。 JS 散度的取值为 0 到 log2。若两个分布完全没有交集,那么 JS 散度取最大值 log2; 若两个分布完全一 样, 那么 JS 散度取最小值 0。 因此 C ( G ) C(G) C(G) 可以根据 JS 散度的定义改写为:
C ( G ) = − log ⁡ 4 + 2 ⋅ J S D ( p data  ∣ p G ) C(G)=-\log 4+2 \cdot J S D\left(p_{\text {data }} \mid p_{G}\right) C(G)=log4+2JSD(pdata pG)
这一散度其实就是 Jenson-Shannon 距离度量的平方。根据它的属性: 当 P G = P data  P_{G}=P_{\text {data }} PG=Pdata  时, J S D ( P data  ( x ) ∥ P G ( x ) ) J S D\left(P_{\text {data }}(x) \| P_{G}(x)\right) JSD(Pdata (x)PG(x)) 为 0 。综上所述, 生成分布当且仅当等于真实数据分布式时, 我们可以取得最 优生成器。
(在这里插入图片描述
)

理论部分-算法收敛-命题二

给定足够的训练数据和正确的环境,训练过程将收敛到最优 G

命题2。如果G和D有足够的能力,在算法1的每一步, 让判别器达到给定的最优G,并更新pg以改进标准。。
E x ∼ p data  [ log ⁡ D G ∗ ( x ) ] + E x ∼ p g [ log ⁡ ( 1 − D G ∗ ( x ) ) ] \mathbb{E}_{\boldsymbol{x} \sim p_{\text {data }}}\left[\log D_{G}^{*}(\boldsymbol{x})\right]+\mathbb{E}_{\boldsymbol{x} \sim p_{g}}\left[\log \left(1-D_{G}^{*}(\boldsymbol{x})\right)\right] Expdata [logDG(x)]+Expg[log(1DG(x))]
然后pg收敘到pdata
证明。将V ( G , D ) = U ( p g , D ) (\mathrm{G}, \mathrm{D})=\mathrm{U}(\mathrm{pg}, \mathrm{D}) (G,D)=U(pg,D) 视为上述准则中pg的函数。 \quad 注意U U ( p g , \mathrm{U}(\mathrm{pg}, U(pg, D)在pg中是凸的, 凸函数的上项的子导数 包括函数在最大点处的导数。换句话说, 如果 f ( x ) = sup ⁡ α ∈ A f α ( x ) f(x)=\sup _{\alpha \in A} f_{\alpha}(x) f(x)=supαAfα(x) 并且 f α ( x ) f_{\alpha}(x) fα(x) 对每个 α \alpha α 中的x都是凸的, 如果
G.sup D ⋃ ( P g , D ) 在pg中凸且有唯一全局最优  _{D} \bigcup\left(P_{g}, D\right)_{\text {在pg中凸且有唯一全局最优 }} D(Pg,D)pg中凸且有唯一全局最优  β = argsup ⁡ α ∈ A f α ( x ) \beta=\operatorname{argsup}_{\alpha \in A} f_{\alpha}(x) β=argsupαAfα(x) α f β ( x ) ∈ α f 。 \alpha f_{\beta}(x) \in \alpha f_{\text {。}} αfβ(x)αf这相当于在给定对应
值的情况下计算pg在最优D处的梯度下降更新, 已在thm1中证明, 因此在pg更新足够小的情况下,pg收敘于 p x , \mathrm{px}, px, 得出证明。
在实践中, 对抗网络通过函数G ( z ; θ g ) (z ; \theta g) (z;θg) 表示有限的pg分布族;而我们优化的是\thetag而不是pg本身。用多层感知器 定义G引入了参数空间中的多个临界点。然而, 多层感知器在实际应用中的优异性能表明, 尽管其缺乏理论保 障,但仍是一种合理的模型。
参考资料

知识补充

极大似然估计

在这里的关键是,引出,生成器生成分布和真实分布差距衡量的思路,但无法计算,所以,GAN解决了这个问题
https://zhuanlan.zhihu.com/p/266677860
我们根本无法算出 生成器的分布
最后包含,生成器的分布是包含一个 G(z)= x的示性函数,而,GAN 正是由这个思路提出了鉴别器的思路,因为鉴别器在鉴别的就是 生成的时候是x(真实的)

https://blog.csdn.net/stalbo/article/details/79283399

KL散度(KL divergence)

这是统计中的一个概念,是衡量两种概率分布的相似程度,其越小,表示两种概率分布越接近。
离散的:
在这里插入图片描述
连续的:
在这里插入图片描述
GAN中的应用:
我们想要将一个随机高斯噪声z通过一个生成网络G得到一个和真的数据分布 P data  ( x ) P_{\text {data }}(x) Pdata (x) 差不多的生成 分布 P G ( x ; θ ) , P_{G}(x ; \theta), PG(x;θ), 其中的参数 θ \theta θ 是网络的参数决定的,我们希望找到 θ \theta θ 使得 P G ( x ; θ ) P_{G}(x ; \theta) PG(x;θ) P data  ( x ) P_{\text {data }}(x) Pdata (x) 尽可能接近。

注意:KL散度不是距离。因此KL散度不具有交换性,所以不能理解为距离的概念,衡量的并不是两分布在空间中的远近,更准确的理解应该是衡量一个分布比另一个分布的信息损失。

JS散度

JS散度用于衡量两种分布之间的差异,它用在生成对抗网络的数学推到上,克服了KL散度不是距离、不对称的缺点

它的定义如下

JSD ⁡ ( p ∥ q ) = 1 2 D ( p ∥ m ) + 1 2 D ( q ∥ m ) , m = 1 2 ( p + q ) \operatorname{JSD}(\mathrm{p} \| \mathrm{q})=\frac{1}{2} \mathrm{D}(\mathrm{p} \| \mathrm{m})+\frac{1}{2} \mathrm{D}(\mathrm{q} \| \mathrm{m}), \mathrm{m}=\frac{1}{2}(p+q) JSD(pq)=21D(pm)+21D(qm),m=21(p+q)

总结

关于GAN

本质都是,尝试取衡量 真实分布和生成结果的距离或者相似程度,拟合程度。
其实在GAN之前,就已经有Auto-Encoder,VAE这样的方法来使用神经网络做生成式任务了。

GAN的最大的创新就是在于非常精妙地引入了判别器,从样本的维度解决了衡量两个分布差异的问题。

这种生成器和判别器对抗学习的模式,也必将在各种生成式任务中发挥其巨大的威力。

备注

备注一:

在 GAN 原论文中,有一个思想和其它很多方法都不同,即生成器 G 不需要满足可逆条件。Scott Rome 认为这一点非常重要,因为实践中 G 就是不可逆的。而很多证明笔记都忽略了这一点,他们在证明时错误地使用了积分换元公式,而积分换元却又恰好基于 G 的可逆条件。Scott 认为证明只能基于以下等式的成立性:
E z ∼ p z ( z ) log ⁡ ( 1 − D ( G ( z ) ) ) = E x ∼ p G ( x ) log ⁡ ( 1 − D ( x ) ) E_{z \sim p_{z}(z)} \log (1-D(G(z)))=E_{x \sim p_{G(x)}} \log (1-D(x)) Ezpz(z)log(1D(G(z)))=ExpG(x)log(1D(x))
该等式来源于测度论中的 Radon-Nikodym 定理。。 有一些证明过程使用了积分换元公式, 但进行积分换元就必须计算 G ( − 1 ) G^{(-1)} G(1), 而 G 的逆却并没有假定为存 在。并且在神经网络的实践中,它也并不存在。可能这个方法在机器学习和统计学文献中太常见了, 因 此我们忽略了它。
关于,生成器可逆的问题,思考在备注二。
关于R-N定理的通俗理解,见备注三。

备注二:

根据以下几个观点构成备注二:

  1. 普通积分还原公式,要求变换的函数 在 [ a , b ] [a, b] [a,b]闭区间上可导等条件。
  2. 神经网络中,通常不是普通的一元、多元函数,而是矩阵函数。
  3. 个人认为,矩阵函数想要进行积分还原公式,需要要求系数矩阵可逆。
  4. 结合备注一的结论,G实际中基本上都是不可逆的。举个例子来说明这个问题,GAN的最简单形式中,生成器使用的只是一个简单的多层感知机(见代码)。
  5. 同时,多层感知机是一个线性层+激活函数
  6. 理论上,线性层+单射的激活函数,就可逆。
  7. 但实际情况中,神经网络对应的矩阵(本质就是一个矩阵相乘)不是方阵,所以,实际中,这个过程不可逆。
  8. 是奇异阵或者是方阵的时候,无解或者多解。这都是不可逆的。
  9. 举一个简单的例子帮助理解:一个行向量和一个列向量相乘得到一个值。我们怎么可能在知道这个值和网络参数的时候得到 输入值呢?
  10. 故其实,大多数生成器,包括普通的多层感知机都是不可逆。因为这实在是一个太基本的东西,反而很少见人讨论。
    (ack-天天)

备注三:

学习DL中,我们无需懂得在测度论中如何证明R-N定理(多种证明方法)
可以从直觉上明白一个事情。

R-N定理是一个广义的积分换元定理,无视了可逆条件
顺便可以加深一个概念,GAN积分等式中的,
p G ( x ) p_{G}(x) pG(x) p ( z ) p(z) p(z) 包括 x,z 就是一种测度。
该定理支持了在满足一定条件下,测度之间变换。
在这里插入图片描述
在这里插入图片描述

理论部分参考资料:

  1. GAN - Intuitive Approach to Mathematics
  2. https://blog.csdn.net/stalbo/article/details/79283399

代码

import argparse
import os
import numpy as np
import math

import torchvision.transforms as transforms
from torchvision.utils import save_image

from torch.utils.data import DataLoader
from torchvision import datasets
from torch.autograd import Variable

import torch.nn as nn
import torch.nn.functional as F
import torch

os.makedirs("images", exist_ok=True)

parser = argparse.ArgumentParser()
parser.add_argument("--n_epochs", type=int, default=200, help="number of epochs of training")
parser.add_argument("--batch_size", type=int, default=64, help="size of the batches")
parser.add_argument("--lr", type=float, default=0.0002, help="adam: learning rate")
parser.add_argument("--b1", type=float, default=0.5, help="adam: decay of first order momentum of gradient")
parser.add_argument("--b2", type=float, default=0.999, help="adam: decay of first order momentum of gradient")
parser.add_argument("--n_cpu", type=int, default=8, help="number of cpu threads to use during batch generation")
parser.add_argument("--latent_dim", type=int, default=100, help="dimensionality of the latent space")
parser.add_argument("--img_size", type=int, default=28, help="size of each image dimension")
parser.add_argument("--channels", type=int, default=1, help="number of image channels")
parser.add_argument("--sample_interval", type=int, default=400, help="interval betwen image samples")
opt = parser.parse_args()
print(opt)

img_shape = (opt.channels, opt.img_size, opt.img_size)

cuda = True if torch.cuda.is_available() else False


class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()

        def block(in_feat, out_feat, normalize=True):
            layers = [nn.Linear(in_feat, out_feat)]
            if normalize:
                layers.append(nn.BatchNorm1d(out_feat, 0.8))
            layers.append(nn.LeakyReLU(0.2, inplace=True))
            return layers

        self.model = nn.Sequential(
            *block(opt.latent_dim, 128, normalize=False),
            *block(128, 256),
            *block(256, 512),
            *block(512, 1024),
            nn.Linear(1024, int(np.prod(img_shape))),
            nn.Tanh()
        )

    def forward(self, z):
        img = self.model(z)
        img = img.view(img.size(0), *img_shape)
        return img


class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()

        self.model = nn.Sequential(
            nn.Linear(int(np.prod(img_shape)), 512),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(512, 256),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(256, 1),
            nn.Sigmoid(),
        )

    def forward(self, img):
        img_flat = img.view(img.size(0), -1)
        validity = self.model(img_flat)

        return validity


# Loss function
adversarial_loss = torch.nn.BCELoss()

# Initialize generator and discriminator
generator = Generator()
discriminator = Discriminator()

if cuda:
    generator.cuda()
    discriminator.cuda()
    adversarial_loss.cuda()

# Configure data loader
os.makedirs("../../data/mnist", exist_ok=True)
dataloader = torch.utils.data.DataLoader(
    datasets.MNIST(
        "../../data/mnist",
        train=True,
        download=True,
        transform=transforms.Compose(
            [transforms.Resize(opt.img_size), transforms.ToTensor(), transforms.Normalize([0.5], [0.5])]
        ),
    ),
    batch_size=opt.batch_size,
    shuffle=True,
)

# Optimizers
optimizer_G = torch.optim.Adam(generator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))

Tensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor

# ----------
#  Training
# ----------

for epoch in range(opt.n_epochs):
    for i, (imgs, _) in enumerate(dataloader):

        # Adversarial ground truths
        valid = Variable(Tensor(imgs.size(0), 1).fill_(1.0), requires_grad=False)
        fake = Variable(Tensor(imgs.size(0), 1).fill_(0.0), requires_grad=False)

        # Configure input
        real_imgs = Variable(imgs.type(Tensor))

        # -----------------
        #  Train Generator
        # -----------------

        optimizer_G.zero_grad()

        # Sample noise as generator input
        z = Variable(Tensor(np.random.normal(0, 1, (imgs.shape[0], opt.latent_dim))))

        # Generate a batch of images
        gen_imgs = generator(z)

        # Loss measures generator's ability to fool the discriminator
        g_loss = adversarial_loss(discriminator(gen_imgs), valid)

        g_loss.backward()
        optimizer_G.step()

        # ---------------------
        #  Train Discriminator
        # ---------------------

        optimizer_D.zero_grad()

        # Measure discriminator's ability to classify real from generated samples
        real_loss = adversarial_loss(discriminator(real_imgs), valid)
        fake_loss = adversarial_loss(discriminator(gen_imgs.detach()), fake)
        d_loss = (real_loss + fake_loss) / 2

        d_loss.backward()
        optimizer_D.step()

        print(
            "[Epoch %d/%d] [Batch %d/%d] [D loss: %f] [G loss: %f]"
            % (epoch, opt.n_epochs, i, len(dataloader), d_loss.item(), g_loss.item())
        )

        batches_done = epoch * len(dataloader) + i
        if batches_done % opt.sample_interval == 0:
            save_image(gen_imgs.data[:25], "images/%d.png" % batches_done, nrow=5, normalize=True)

基于MNIST生成的图片
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这个实现中需要注意的一点是,原论文中 G 的训练是希望减小 log(1-D(G(z)),而代码中是使用二值交叉熵BCE(G(z), 1),即希望提高-log(D(G(x))),虽然都是希望让 D(G(x)) 趋近于1 ,但数值上还是有细微的不同。

代码部分参考资料

代码参考资料

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值