视频地址:
①B站:https://www.bilibili.com/video/BV15W411i7uP
②油管:https://www.youtube.com/watch?v=0CKeqXl5IY0
之前的博客地址:生成对抗网络-基本概念|深度学习(李宏毅)(二十二)
一、极大似然估计
- 极大似然估计
在GAN中,对于真实的训练样本的分布,记作,这个分布也就是GAN试图去拟合、逼近的分布。另外有一个由参数
控制的分布记作
,其实也就是GAN或者说Generator生成的对象的分布。简单来说我们的目标就是让
和
越接近越好。
举例来说,可以是一个高斯混合模型(Gaussian Maxture Model,GMM),那么此时参数
就是GMM中高斯分布的均值和方差或者其他参数,我们的目的也就是求解最优的参数
来让
尽可能地接近
。从
中采样
,对于
我们可以计算
,然后就可以计算样本的似然:
寻找最优化的参数的方法也就是极大似然估计的方法,即寻找一个参数
来最大化
。我们可以对这个过程做以下变换:
上面的式子中,由于是从
采样得到的样本,因此似然就近似于上式中关于
的期望值,这个期望值写成积分的形式后可以减去一个与
无关的项,最终得到
与
的KL散度。也就是说,求解参数
的过程,也就是最小化分布
与
的KL散度的过程,使得这两个分布能够不断地逼近。KL散度能够用来衡量两个分布的接近程度(越小越接近),其公式如下:
- 存在的问题
使用GMM的话会限制模型的拟合能力,而对于要拟合的分布来说,其往往是图片、文字一类复杂的结构,因此我们期待使用一个神经网络,也就是一个比较复杂的、拟合能力较强的、一般化的
。现在我们尝试使用一个神经网络来替代GMM,现在的
也就相当于神经网络的参数,具体地,我们有一个Generator,它的输入是一个随机变量
,
服从高斯分布或者均匀分布
,而它的输出就是
,可以理解为生成的图片。也就是说现在我们的目标就是学习一个最优化的Generator的参数
,来让
通过Generator产生的
的分布
与
越接近越好:

由于是一个服从高斯分布或者均匀分布的随机变量,因此通过Generator以后得到的
也会服从一个分布,虽然
服从一个简单的分布,但是由于神经网络可以是很庞大的,因此
的分布可以是很复杂的。计算
出现的概率可以使用以下公式:
对于上面的概率公式,这个概率是很难计算的,显然不能用它来做极大似然估计,因此问题也就出在这里。类比GMM,在GMM中生成的过程是先从几个高斯分布中按照一定概率来抽取一个高斯分布,然后从这个高斯分布中抽取一个,而在Generator中生成的过程是从
抽取一个
,然后
通过
得到
,不一样的是对于一个给定的
,GMM可以轻易地计算
出现的概率,而Generator的对于一个给定的
很难计算其概率,并且概率公式中还包含指示函数,使得进行极大似然估计时没办法做微分,也就出现了问题,而GAN的优势就在于它解决了这个问题,这就是GAN最大的贡献。
二、GAN的原理
- GAN的基本理念
在GAN中Generator是一个函数,输入是
,输出是
,给定一个
的分布
,函数
也就定义了分布
,然而单纯利用Generator无法使用极大似然估计。另外有一个Discriminator记作函数
,它的输入是
,输出是一个标量,它能够衡量
与
之间的差异,不过它计算的不是KL散度,而是另一种散度。如何利用GAN来求解最优化的Generator呢?只需要求解下面这个式子:
直观地来看,如下图,对于上面的式子,如果对于特定的,比如
、
、
,令
最大的
就是红点对应的
,然后求解使
最小的
,就能解得最优化的
:

也就是目标函数,可以使用这个式子:
对于一个给定的,
的值就相当于
与
之间差异的程度,其实这个值就等于
与
的某种散度,然后再求解一个能使
最小的
,就能找到一个能使
与
最接近的
了。
- GAN的原理
接下来就解释一下为什么按照上面的方式求解就能得到最优化的结果。首先对于一个给定的,寻找需要寻找一个
来使
最大,我们先来将
的式子展开:
对于上面的式子,我们想让最大,自然希望对于每一个
,积分号里面的式子都能最大,因此我们只看积分号里面的部分:
上面的式子中,作为客观存在的分布,因此看做常数,而
由于是给定的,因此也看做常数,而
作为变量,也就是:
那这个问题就成了求解来使得下面这个式子最大的问题:
直接微分然后令其等于即可:
也就是说:
最优的显然位于
到
之间,因此在实际实现GAN时可以给Discriminator最后设置一个sigmoid函数。
在下图中,不同的的图像的最高点对应的
就对应着由上面的式子解出来的
,而将
代入
中得到的
就表示了在给定的当前
的情况下
与
的某种散度,在图中也就是红点到横轴的距离:

接下来说明为什么能够代表
与
之间的差异程度。将
代入
中并做一些变换:
这里我们定义另一种衡量分布差异程度的散度,叫做JS散度(Jensen–Shannon Divergence,JSD)。不同于KL散度的是,JS散度是对称的,也就是。它的定义如下:
因此将也就相当于在衡量
与
的JS散度,JS散度的值介于
到
之间,如果两个分布完全一致那么其JS散度就是
,如果两个分布完全没有交集,那么其JS散度就是
。当然也可以识别别的
来让它衡量别的散度。
总结一下:
①首先我们有一个Generator
和一个Discriminator
;
②我们需要通过下面这一个式子寻找一个:
③对于给定的,有:
④然后需要求解最优的,满足
时才会得到最小的
。
- 算法
接下来要做的就是求解求解,我们把
记作
,那么求解
的方法按照梯度下降的方法就好:
那么对于这样这种带
的式子要怎么做微分呢?我们可以把带
的问题以下面这个问题来看,对于函数
,其公式为:
现在要求解,那么假设
的图像如下:

那么在求解时所在的位置对应的哪一个
最大,那么对
的微分也就是
:

在求解这个问题时就按照对当前对应的最大的
微分的方式进行梯度下降即可:

上面的问题类比到求解上就是
相当于
,
相当于
,
也就相当于不同的
,只不过
是有限个,
有无限多个,不同的
也就对应了不同的能使
最大的
。因此我们的求解方式是按照梯度下降的方法来求解
,每次更新
以后要计算当前
对应的
,然后再一次地更新
。该流程如下:
- 初始化
![]()
- 求解
来最大化
,
就是
与
的JS散度
- 更新
来获得
:
![]()
- 求解
来最大化
,
就是
与
的JS散度
- 更新
来获得
:
![]()
- ……
这里有个小问题就是在使用梯度下降更新过以后可能会使得
与
的JS散度不减反增,比如下图这个例子,横轴表示
,在更新过
的参数后有可能
会比原来更大,对于这样的问题我们就只能假设
,在更新参数
时一次不能更新太多:

在实际操作的时候因为无法积分所以我们并不能真正地计算中的两个期望,因此采用采样的方法。对于给定的
,求解使
最大化的
时,从
中采样
,从Generator
中采样
,然后最大化:
上面这个式子非常类似与一个二分类器的损失函数,也就是二分类的交叉熵,在二分类中,如果是个positive的样本,我们要尽可能地极小化
,如果
是个negative的样本,我们要尽可能地极小化
。因此极大化
也就相当于二分类问题中极小化交叉熵损失函数,也就是说,我们真正在求解
时只需要当做一个二分类问题来做就好了,具体地:
这一点直观上也是可以理解的,如果这个二分类器的loss很小,就代表它可以很容易地分辨真实的样本和生成的样本,与
的JS散度就很大,而如果这个二分类器的loss很大,就代表它分辨不出真实的样本和生成的样本,
与
的JS散度就很小。
现在我们就可以更加清晰地来理解上一篇文章中的训练的算法:
Initialize:
初始化的参数
和
的参数
。
Step1 学习
:
①从数据库中随机抽样个样本
;
②从一个分布(比如高斯分布或者均匀分布)中采样个噪声样本(noise sample)
;
③获得生成的数据
,其中
;
④目标函数记作,通过最大化(梯度上升)
来更新
,也就是
,目标函数
为:
Step2 学习
:
①同样从一个分布(比如高斯分布或者均匀分布)中采样个噪声样本(noise sample)
;
②目标函数同样记作,通过最小化(梯度下降)
来更新
,也就是
,目标函数
为:
上面的算法中Step1和Step2是交替进行的,但是在每一次迭代中应该将学习的步骤重复多次,不过即使这样也不能学习到
的全局最优点,学习到的也只是
的lower bound,而对于学习
的步骤只需要进行一次,这是因为之前说过的原因,即
不能一次更新太多。
三、实践中的一些issue
- 实作中Generator目标函数的问题
在训练Generator时,我们实际上在极小化这个式子:
以为横轴,画出
和
的图像如图所示:

在训练一开始,接近
,但是
的梯度比较小,在趋近于
的地方
的梯度反而比较大,这与我们的期待是不一致的,我们期望模型在训练初始时梯度应该大一些,在接近收敛时梯度应该小一些。因此,在实际操作中,我们真正优化的式子是:
这个式子的梯度就符合我们的期望,不过这样就不是在极小化JS散度,而是在极小化另外一个奇怪的散度。
- 评估JS散度
在训练Discriminator时,理论上Discriminator的loss就代表JS散度的大小,但是在实际操作时Discriminator的loss几乎趋近于,也就是说Discriminator总是有办法把生成的图片与真实的图片分开。举例来说,在下面的实验中,Generator采用了训练1、10、25个epoch的三种,其中训练越多epoch的Generator产生的图片越接近真实,但是从图中看到无论哪一种Generator它们的Discriminator的loss总是能够趋近于
,并且Discriminator也总能训练到100%的准确率,Discriminator的loss并不能反映JS散度的大小:

另外一个例子如下,使用一个较强和一个较弱的Generator,可以看到强的Generator生成的图片已经很真实了,但是它们的Discriminator的loss缺失差不多的,这表明Discriminator的loss并没有反映JS散度:

Discriminator的loss接近于,表明JS散度最大,也就是
,
与
完全没有交集。原因有以下两点:
- Reason1:通过采样的方式来训练
由于我们始终没有办法直接计算损失函数中的期望,因此只能通过采样的方法来进行训练,那么有可能如下图所示,对于采样出的样本,由于Discriminator过于powerful,那么它总有办法寻找一个边界来分开样本,类似过拟合:

解决这种问题我们考虑让Discriminator变得弱一点,要么迭代次数少一点要么加dropout,不过要将Discriminator变弱到什么程度,这又是很难把握的,而且这与我们最初的设想又出现了矛盾,Discriminator能够衡量JS散度的一个前提就是Discriminator要足够地powerful,因此这里就出现了一些矛盾。
- Reason2:数据的本质
GAN要拟合的数据和Generator生成的数据实际上是高维空间中的流形(manifold)。拿二维空间中的一维流形来说,可能与
很少有交集,或者交集很少,像图中这样的数据的JS散度就会很小:

我们之前有说过GAN的训练和生物进化很类似,比如下面图中生物进化出眼睛的过程,只要从左到右的进化对生物的繁衍是有利的,这个进化的过程才能持续下去:

GAN的训练也类似,比如下图中与
越来越接近,最终数据分布趋于一致,我们期待模型能够以这样的过程逐步迭代达到最佳效果,但是可以看出在达到最佳效果(JS散度为
)之前,每一步的JS散度都是
,也就是说目前的GAN没有动力一致演化下去:

解决这个问题的方法是可以给Discriminator的输入添加一些噪声或者给标签添加一些噪声(随机标记一些正样本为负样本,负样本为正样本),这样会使数据产生下图中的效果,因而重叠的部分就有可能变大:

不过要将加入的噪声随着训练而减弱,否则会影响机器对真实的数据分布的判断。
另一种方式是使用别的度量差异度的方式,比如WGAN这方法,这一类方法下一篇中再具体介绍。
- Mode Collapse
GAN还容易产生Mode Collapse的问题,以高斯分布为例,如果有两个高斯分布,而
只产生了一个:

举例来说,在下面的二次元人物头像生成的图片中就有许多图片是重复的,这就是Mode Collapse的问题:

再举一个例子来说,比如要拟合的数据如下图:

我们期待GAN能够按照下面的方式来逐步学习到数据的真实分布:

而实际的结果可能只会像下面这样,这就是Mode Collapse的问题:

出现Mode Collapse的原因可能如下图所示。在有两个高斯分布而
只能产生一个高斯分布的情况下,对于KL散度,通过它的式子可以看出,在
没有值,而
有值的地方就会产生无穷大的值,因此为了让KL散度尽可能地小,
就会尽可能地覆盖所有
有值的地方,即使有些
没有值的地方被覆盖到也在所不惜。而对于Reverse KL散度来说正好相反,在
没有值,而
有值的地方就会产生无穷大的值,因此为了让Reverse KL散度小,
就不会冒险去覆盖
没有值的地方,因此就可能会固守在一个高斯分布上:

但是因为是可以由我们自己来设计的,因此我们可以设计目标函数
来让GAN最小化KL散度,然而Mode Collapse的问题还是存在。具体的有关Mode Collapse的问题之后再介绍,这里就不再赘述。
最后列一个Ian Goodfellow的有关GAN的Tutorial:https://arxiv.org/abs/1701.00160