视频地址:
①B站:https://www.bilibili.com/video/BV1JE411g7XF?p=72
②油管:https://www.youtube.com/watch?v=DQNNMiAP5lw
一、基本概念
- Generation
生成(Generation)是指通过让机器学习的模型输入一个随机的向量,来让它产生图片或者文字等,而生成对抗网络(Generative Adversarial Network,GAN)就是用来完成生成任务的一种常用的技术:
仅仅输入一个随机的向量来产生图片或者文字没有太大的应用前景,而如果能够控制模型的输出的话就会有更多可以应用的场景,这种方法叫做条件生成(Conditional Generation),会在后面的课程中进行介绍。
- Generator和Discriminator
GAN有两个部分组成,一个是Generator,另一个是Discriminator,这两个部分通过迭代的方式交替训练最终能够让Generator产生一些比较真实的图片或者句子等。在本文的介绍中大多以图像生成为例。
对于Generator,它的输入是一个向量,输出是一张图片(可以用高维向量来表示),Generator本身可能是一个神经网络或者函数:
举例来说,GAN可以用来生成二次元人物的头像,下图展示了Generator生成头像的效果,下图中Generator输入的向量的每一维度可能代表了生成图像的一些特征,比如调整某些维度可以调整人物头发的长短、头发的颜色和嘴巴的大小等:
Discriminator也可以是一个神经网络或者函数,它的输入是一张图片,输出一个标量,这个标量代表了输入的图片有多么的真实(realistic),因此可以看做一个得分,通常限制这个标量的值在0到1之间。Discriminator用来鉴别输入的图片是真实的图片(训练资料)还是Generator生成的图片,对于真实的图片会有一个较高的得分,不真实的图片得分就会较低:
- Generator和Discriminator的关系
这一部分感性地介绍Generator和Discriminator之间的关系。
Generator和Discriminator的关系可以比喻成捕食者和猎物的关系,以鸟捕食蝴蝶为例,蝴蝶就是Generator,鸟就是Discriminator,蝴蝶会因为鸟的捕食而逐步进化成棕色,最后进化成枯叶蝶,而鸟也会根据蝴蝶的进行而初步判断自己捕食的标准,然后就可以从波波进化成比比鸟,最终进化成更强的捕食者:
在生成对抗网络中,Generator和Discriminator以一种对抗的方式学习,以二次元头像生成为例,Generator生成的图像会尝试生成更真实的图片来骗过Discriminator,Discriminator会根据Generator生成的图像来一步步提高自己判定真实性的标准:
另外,除了对抗的比喻,也可以有更加和平的比喻,比如可以把Generator和Discriminator比作学生和老师的关系,Generator是学生,Discriminator是老师。Generator的每一个版本都会比之前的版本生成的图像更真实,而Discriminator作为老师,每一个版本都会更加地严格:
在后面的内容中会回答以下两个问题:
①Generator作为学生为什么不自己学呢?也就是说,为什么必须要有一个Discriminator才能促使Generator学习到如何进行生成。
②Discriminator作为老师为什么不自己做呢?也就是说,Discriminator为什么不能自己进行生成,而只能通过Generator进行生成。
- 算法
Generator和Discriminator是迭代交替训练的,首先Generator和Discriminator作为两个神经网络要进行初始化,接下来在每次迭代中都要先训练Discriminator再训练Generator,也就是完成以下两步:
Step1:固定住Generator,从一个高斯分布或者均匀分布(或者其他分布)随机抽样几个向量然后输入Generator中获取对应的生成的图片,同时也要从训练资料的数据库中随机抽样一部分真实的图片,然后让Discriminator学习如何鉴别真实的图片和生成的图片,具体的就是尽可能让Discriminator给真实的图片打高分,给生成的图片打低分,训练过程就当做一个分类或者回归任务来更新Discriminator的参数就好:
Step2:固定Discriminator,调整Generator的参数,目标就是让Generator生成的图片输入到Discriminator后能够“骗”过Discriminator(尽可能地得到高分)。具体的过程就是将Generator和Discriminator看做一个巨大的神经网络,在训练时这个神经网络的Discriminator的参数是不变的,只会更新Generator的参数,这样的话Generator生成的图片就会越来越接近真实的图片:
以代表Discriminator,代表Generator,更加形式化的算法流程为:
Initialize:
初始化的参数和的参数。
Step1 学习:
①从数据库中随机抽样个样本;
②从一个分布(比如高斯分布或者均匀分布)中采样个噪声样本(noise sample);
③获得生成的数据,其中;
④目标函数记作,通过最大化(梯度上升)来更新,也就是,目标函数为:
Step2 学习:
①同样从一个分布(比如高斯分布或者均匀分布)中采样个噪声样本(noise sample);
②目标函数同样记作,通过最小化(梯度下降)来更新,也就是,目标函数为:
要注意上述算法中Step1 ④中的的输出层加了sigmoid函数来限制输出的值只能在0到1之间,但事实上添加sigmoid函数的方法不是效果最好的方法,但是使用这种方式完全可以实现一个GAN,后续会介绍更加优化的方法。
上面的Step1和Step2是交替进行的。
- 效果
下面的图展示了按照上述算法训练GAN迭代多次的效果:
GAN也可以用来进行三次元人脸的生成,并且通过调节输入向量(以二维为例)也可以看到一些渐变的变化:
二、作为结构化学习的生成对抗网络
- 什么是结构化学习
机器学习的主要过程就是寻找一个函数:
对于不同的机器学习模型来说,其输出可能有所不同,比如:
Regression:输出一个标量
Classification:输出类别(one-hot向量)
Structured Learning/Prediction:输出一个矩阵、句子、图、树等结构化的对象。这些输出的特点就是由一些component组成,并且component之间是有依赖关系的。
- 结构化学习的一些例子
下面的图中展示了一些结构化学习的输入和输出:
对于让机器输出一个矩阵的结构化学习,这里也有一些有趣的例子,比如输入略图后输出一个真实的房子的图片,输入黑白图片后输出彩色图片,输入一段文字后输出对应的图片,这些例子可以通过GAN来实现:
- 为什么结构化学习是具有挑战性的
One-shot/Zero-shot Learning是指在某些类别不具备或者具备很少的训练样本的情况下期待仍然能够对这些类别做预测。我们可以把结构化学习看做One-shot/Zero-shot Learning的一种,具体的可以把结构化学习的输出看做类别,以机器翻译为例,也就是把输出的每一个句子都看作一个类别,而在训练集中每个句子几乎全部只出现一次,而在测试集中每个句子在训练资料中都没有出现过,因此神经网络就需要学会创造这件事情,比起通常的分类任务来说,结构化学习也就需要更高级的“智能”。
对于结构化学习来说,机器的输出是由多个component组成的,比如图片由像素组成,句子由单词组成,因此机器在生成这些component时,需要具备“大局观”,需要“globally”地进行生成。
举例来说,在生成图片时,如果生成一个基本的component(比如一个点)并不能代表结果的好坏,而要看这个点在最终生成的图片中的全局的影响:
另外一个例子是纪晓岚去给翰林的老婆祝寿,写了一首诗,第一句是“这个婆娘不是人”,如果单看这一句本身容易造成误解,而下一句是“九天仙女下凡尘”,因此要从全局的角度来看待机器生成的效果,也因此结构化学习就要求学习输出的component之间的依赖关系:
GAN就可以看做一种结构化学习的方法。结构化学习的两种方式是Bottom Up和Top Down。Bottom Up是指在产生一个完整的对象时,机器是一个一个component地去产生,这样的缺点就是容易失去大局观,而Top Down是指产生完一个对象以后在从整体来看产生的效果好不好,坏处就是用这个方法很难做生成。事实上,GAN的Generator就相当于Bottom Up的方法,Discriminator就相当于Top Down的方法。这里的解释有些抽象,后面会具体介绍这里的这个观点:
三、问题1:Generator能不能不依赖Discriminator自己学习?
- Generator自己学习的方法
面对Discriminator的存在,我们会考虑一个问题,就是Generator能不能不依赖Discriminator自己学习?为了让Generator独立地学习到如何进行生成,我们会考虑到一种朴素的实现的方法,就是训练一个Generator,输入一个向量,输出一张图片(这张图片可以表示为一个很长的向量):
这个Generator的训练方式同样很朴素,那就是让生成的图片与训练资料越接近越好:
上面的方式是让两张图片尽可能地接近,事实上这也就是让两个长长的向量越接近越好,这种训练的方式类似于传统的分类任务的训练方法,即让输出和标签越接近越好,因此这种朴素的生成方法是很容易实现的:
- 使用自编码器来改进Generator的输入
不过对于生成任务来说,这里存在一个问题,就是我们需要为每个训练样本安排一个向量(code),这个code应该具备一定的合理性,否则可能会让训练出现一些问题。举例来说,如果进行随机取样给下图中两个数字1安排到两个相差很远的code,那么Generator能否学到输入两个截然不同的code来产生十分相似的图片呢?显然是很困难的:
上面的问题也就表明训练资料对应的code不能是任意安排的,而应该期待code具备一定的表示性,最好能够让code的每一个维度能控制生成图片的一些特征,事实上这个问题也是有办法解决的,就是利用自编码器(Auto-Encoder)来进行图片的encode:
有关自编码器的讲解可以参考以下链接,这里只简略介绍一下在生成任务中的应用:
①无监督学习-自编码器|深度学习(李宏毅)(十九)
②无监督学习-自编码器-补充|深度学习(李宏毅)(二十)
在这里自编码器的作用是通过其Encoder提供code,这样的code就具备一定的表示性,而其Decoder也就充当了Generator的角色(输入向量产生图片):
以二维的code为例,利用自编码器得到的code的每一维度代表特定的含义,输入不同的code就会让Decoder生成不一样的图片:
而在一个区域内等距取样然后输入给Decoder让其产生图片的话就会看到一些渐变的效果,也就是说这里的code的每一个维度都会控制生成图片的一些特征(比如有没有圆圈或者倾斜的方向等等):
使用自编码器以后,由于训练资料是有限的,因此也可能会遇到一些问题。举例来说,如果将向量输入到Decoder会产生左斜的数字1,将向量输入到Decoder会产生右斜的数字1,那么将输入到Decoder中未必会产生一张正立的1,这是因为Decoder是非线性的,因此未必会产生想象中的平均的效果:
可以使用变分自编码器(Variational Auto-encoder,VAE)来尝试解决这个问题,VAE的做法是让自编码器输出code的同时也输出code的每一个维度的方差,然后从高斯分布中采样,将采样结果与方差结合再与code加起来以后再输入到Decoder中。这样的做法相当于对code进行加噪,这样就会让自编码器学习到的表示更加地稳定,从而让它知道除了输入、要产生数字1,输入加噪的、也要产生数字1:
- 存在的问题
自编码器训练的目标是让生成的图片与原来的图片之间的每一个像素的欧氏距离越小越好,事实上如果自编码器并不能完全重构原来的图像,而是必然地会产生一些损失,机器如何规划这些损失,如何进行取舍就变得至关重要:
举例来说,在下面生成的四张图片中,相对于原来的训练资料而言,由于训练的目标是让像素之间的欧氏距离越小越好,那么模型更倾向于选择像素差异较小的上面两张图片,而下面两张图片由于像素差异较大而会造成较大的loss,但是事实上下面两张图片更加地真实,从整体上来看更加像是一个数字,这也就是自编码器进行生成缺乏大局观的体现:
如下图,对于一个自编码器来说,假设L层是其最后一层,在输出时最后一层的每一个神经元就相当于一个像素,而这些神经元之间缺乏必要的联系,在输出时它们之间是互不影响的:
就比如如果图片相邻像素之间想要产生一样的颜色,对于缺少相关性的两个相邻的输出神经元来说是很困难的:
可以通过增加自编码器的层数来增强这种相关性,因此对于GAN和自编码器来说,如果要产生同样的生成效果,自编码器就会比GAN要庞大地多。
下面的实验证明了这一观点,这里用变分自编码器训练了一个Generator,图中绿色的点是拟合的目标,蓝色是生成的结果(给Generator输入一个高斯分布),发现在拟合目标的不同的簇之间还散布着一些蓝色的点,这就是由于Generator的输出的不同维度之间缺乏相关性导致的:
四、问题2:Discriminator能不能自己生成?
- Discriminator相较于Generator的优势
对于Discriminator来说,它在不同的文献中还有很多其他的别名,比如Evaluation function、Potential Function、Energy Function等,但是无论它叫什么名字,其功能也只是输入一个对象(比如图片),然后输出一个得分:
鉴于上述Discriminator的特点,Discriminator相较于Generator的优势就在于它能够从整体上去评价一个对象的好坏,而Generator的输出的不同维度之间缺乏相关性,因此Generator很难有一个整体的、全局的度量标准,而Discriminator就克服了这个问题。举例来说,对于下面两个不同的2来说,Discriminator中可能就存在一个图中这样的卷积核,因此也就能使得Discriminator看到左边的2就打低分,看到右边的2就打高分:
- 使用Discriminator来进行生成
既然一个Discriminator能够整体地评价一个对象好还是不好,那么能不能用Discriminator来做生成呢?我们最直观地想到利用下面这个式子来生成能让Discriminator打高分的对象:
以图像生成为例,我们解上面的式子可以通过穷举所有的图片(也就是所有像素的所有颜色的组合)来找出对应的,这听起来是非常地困难的。对于要让Discriminator来完成生成任务这件事来说,主要的问题就在这里。不过现在我们就假设有一种合理的算法能够解上面这个式子,然后来看一下还会遇到些什么样的问题。
Discriminator的训练需要的训练资料包括positive的样本和negative的样本(必须要有negative的样本,否则Discriminator就会学习到给所有样本都打高分),而通常我们只有positive的样本,如何获取negative的样本就成为一个问题,如果我们直接使用一些噪声或者给图片加噪作为negative的样本,这样就会让机器学习到看到噪声就打低分就好,显然获得一些好的negative的样本是很重要的。
我们的解决方案就是让Discriminator自己产生negative的样本,训练的方式是迭代地训练Discriminator,每次迭代使用的positive的样本就是真实的图片,而negative的样本就是利用式子产生的能得高分的生成的图片,然后利用这些训练资料来训练Discriminator:
接下来以一种图示化的方式来解释这个迭代的过程,以一维的空间为例,如下图,我们期待Discriminator应该给一些真实图片分布的区域打高分,给其他区域打低分:
然而由于数据实际上是分布在高维空间中的,想让Discriminator给所有除真实图片分布以外的区域打低分这件事是比较困难的,而不断的迭代就是为了实现这一期待的结果。在下面所示的过程中,蓝色的代表生成的图片(也就是通过上面式子解出来的negative的样本),绿色的相当于真实的样本。首先,初始的生成的与真实的图片分布如下:
在经过调整一次参数后,会使得蓝色区域得低分,绿色区域得高分,但是无法保证其他区域也会得低分,因此也有可能出现图中所示得高分的其他区域:
接下来重新生成negative的样本后就会变成这样:
接下来再更新一下参数,将蓝色区域得分压低:
经过不断地迭代,我们希望最终能达到下图中的效果,这时训练的过程就可以停下了:
这样训练一个Discriminator是很合理的,然而这一切都建立在存在一个算法可以求解这一假设上,然而对于一个非线性的Discriminator(比如Discriminator是各种类型的神经网络的时候)来说,直接求解这一式子是相当困难的(几乎不可能完成)。然而使用Generator+Discriminator(GAN)的方式就完美地解决了这个问题,在前面介绍过的GAN中Generator的训练方式就是让Generator学习如何产生让Discriminator打高分的对象,也就是这一学习的过程就相当于求解了这个式子,因此GAN是一种合理地利用Generator和Discriminator双方优点的解决方案:
- 概率图模型与Discriminator
事实上结构化学习中的一些概率图模型也就可以看做一种仅仅利用Discriminator的生成方式。各种概率图模型比如CRF、RBM等可以看做一个Discriminator,而其建模的联合概率或者条件概率就相当于Discriminator的打分,而对于其中的一些推断问题,比如Decoding问题,需要求解使概率最大的序列或者其他对象时,就可以看做是在做生成:
不过这些概率图模型之所以能够求解,主要是因为做了一些假设,比喻线性的假设或者一些随机变量相互独立的假设,而这些假设往往会降低模型的能力,但是如果没有这些假设,就又会面临无法求解的问题,因此GAN所取得的成就是突破性的。
五、总结
- Generator和Discriminator各自的优缺点
- Generator的优缺点
Generator的优点是它很容易进行生成,因为它是一个前馈网络或者其他的网络,是很容易生成一张图片或者其他对象的,而它的缺点就是它只是在模仿,由于它输出的component之间缺乏相关性,导致它只能学习到训练资料的表象而学习不到其大局观的精神。
- Discriminator的优缺点
Discriminator有点在于它具备大局观,能够整体地、全局地度量一个对象的好坏,但它的缺点就是它很难做生成,尤其是Discriminator的层数很深的时候,并且也很难得到negative的样本。
- GAN的优势
- 从Discriminator的角度来看
从Discriminator的角度来看,Discriminator可以利用Generator来生成negative的样本:
- 从Generator的角度来看
Generator仍然是一个一个component地进行生成,但是度量生成效果是通过Discriminator的全局的标准来衡量的。
- 实验
之前使用VAE进行的实验现在使用GAN所产生的效果如下图所示,而且VAE和GAN中使用的Generator的架构是一样的,图中红点是生成的样本,蓝点是真实的样本:
可以看到不同的簇之间几乎没有生成的样本,效果要比VAE要好得多。
另外也使用VAE和GAN进行了三次元人脸的生成,得到效果如下:
可以看到GAN相当于VAE可以产生更加清晰的效果。
下面的图来自Google的一篇论文,其中对比了不同的GAN以及VAE在MINST和CIFAR10数据集上的效果,图中使用的FID Score越小,说明生成的效果越真实。之所以每种GAN都有一个很大的range,是因为尝试了不同的参数,GAN对不同的参数是很敏感的,因此效果会很有一个很大的range。而VAE对于不同的参数就会比较稳定,但是VAE所能达到的最好的效果总是不如GAN所能产生的最好的效果: