二、Generative adversarial network (GAN)
在一篇关于 “What are some recent and potentially upcoming breakthroughs in unsupervised learning?” 的帖子上,Yann LeCun 回答说 “Adversarial training is the coolest thing since sliced bread. GAN and its the variations that are now being proposed is the most interesting ides in the last 10 years in ML, in my opinion.” 他给 GAN 非常高的评价。
下面先说 GAN 的基本思想。在 GAN 中,我们想要让机器生成东西,比如图像,写文章。在图像生成中,你需要做的事情就是训练一个 Generator(生成器)。你随便给它一个输入(或者从高斯分布中采样一个 vector),然后丢到 Generator 里面,就会产生一张 image。如果输入不同的 vector,就会产生不同的 image。文字生成器类似。
我们想要用 GAN 达成目标就需要训练这样一个 Generator。但是你或许有困惑说,输入一个随机的 vector,让它输出一张图片或一段文字有什么用?其实比较有用的 Condition generation,你可以输入一些条件,让它输出对应的图像或语句。Generator 其实是一个神经网络或者一个函数。在图像生成中,vector 的每一个元素都会对应到图像的某种特征。只要改变了某一个数值,生成的图片就会不同。
在 GAN 中,除了要训练 Generator,还要同时训练一个 Discriminator(判别器)。那么两者之间有什么样的关系呢?
Discriminator 其实也是一个神经网络。在图像生成中,它的输入就是一张图像,它的输出是一个 scalar(标量)。这个数值代表产生的这张图片的质量,数值越大,表示产生的这张图片越真实。
在下图中,假设 1.0 就是你设置的最高值,那么说明机器这张图画得很好。
总的来说,Generator 和 Discriminator 的关系就像是猎食者和它的猎物。如下图所示,右上角为一只枯叶蝶,之所以和枯叶非常相似,是因为有天敌的压力。其实它的祖先也是彩色的,因为麻雀会吃枯叶蝶,而麻雀吃枯叶蝶的标准就是如果它是彩色的,就会被吃掉。所以枯叶蝶的祖先被迫变成棕色的蝴蝶。但是枯叶蝶的天敌它也是会演化的,波波进化变成了比比鸟。而比比鸟判别是否是食物的标准是不是看颜色,而是看有没有蝴蝶的纹路。随后枯叶蝶的祖先迫于压力又变成了看起来像是叶脉的纹理,以此骗过比比鸟。比比鸟也会再进化成比雕,然后看是否有别的标准来判断食物是否可吃。以此类推,猎食者和食物就不断的迭代中变得越来越强。那么,枯叶蝶就是 Generator,比比鸟就是 Discriminator。
假设你想让机器生成二次元的图像。首先,你要准备一个 database,它有很多真实的二次元的图像。一开始的 Generator 的参数是随机的,所以它只能产生类似于噪声的图像,那么 Discriminator 做的就是给它一张图片,判断是 Generator 生成的,还是像是真实的图片。接下来 Generator 要做的事情就是想办法骗过第一代的 Discriminator。第一代的 Discriminator 可以分辨第一代的 Generator 的 output 和真实图片的差异(比如判断图片有没有颜色)。所以第二代的 Generator 就会产生有颜色的图片,但是 Discriminator 也会跟着进化(判断生成的图片有没有嘴巴)。进一步,Generator 和 Discriminator 不断进化,那么 Generator 产生的图片就会越来越真实。
总而言之,Generator 和 Discriminator 有一张对抗(博弈)的关系,所以用 “adversarial” 命名这个技术。那么有人可能会有疑问?为什么会让两个网络互相对抗呢,为什么不能彼此合作呢?世界不是充满爱与和平吗?其实这个是一个拟人化的手法,原始的 GAN 的 Paper 里面举出的例子是做假钞和警察的故事,警察就是 Discriminator,他判断是真钞还是假钞。这个例子中 Generator 和 Discriminator 是非常明显的对抗关系。其实在我们这个例子中,它们也可以看作是合作的关系,Generator 是一个学生,Discriminator 是一个老师,学生正在学习怎么画二次元头像。所以它们是写作敌人,念做朋友。
那么你有没有想过下面这个问题。为什么 Generator 没有办法自己学,为什么一定要有 Discriminator 介入?
下面展示了 GAN 的操作过程。首先随机初始化 Generator 和 Discriminator 两个网络的权重。然后进入不断地迭代,在每个迭代过程中,第一个步骤就是先固定住 Generator 的参数,只调整 Discriminator 的参数。我们首先使用一堆从高斯分布中采样的 vector 作为 Generator 的输入,然后产生一堆图片。因为刚开始 Generator 的参数是随机的,所以产生的图片可能是非常糟糕的。接下来,我们从 Database 中随机采样出与输入相同个数的一组图片,然后使用这组图片与生成的图片来调整 Discriminator 的参数。
下一步就是要固定住 Discriminator 的参数,只去调整 Generator 的参数。首先,先使用 Generator 生成一个 vector 对应的图片,然后将这张图片输入到 Discriminator 中得到一个分数。Generator 训练的目标就是要去骗过 Discriminator ,其实要做的就是让它产生的图片得到比较高的分数。由于上一步 Discriminator 已经训练过了,它能够判别是否为真实的图片,所以调整 Generator 之后就会产生更真实的图片。
完整的算法如下所示:
下面是用 GAN 生成二次元头像不同迭代次数的结果展示。
我们都知道机器学习是寻找一个函数: f : X → Y f: X \to Y f:X→Y。如果是一个回归任务,那么输出就是一个 scalar。如果是分类任务,输出就是一个 class(one-hot vector)。如果你的任务输出要求更复杂,比如一个 sequence,一个 martix等,这种任务就叫做 Structured Learning。机器翻译,语音识别,聊天机器人,图像转换,文字转图像等都是 Structured Learning。
之前我们留了一个问题?问题是 Generator 是都可以自己学习?事实上它是可以自己学习的。现在我们以手写数字作为例子,它的输入就是 vector,输出就是一张数字的图片。
那么怎么训练这样一个 Generator 呢?在传统的机器学习里面,只需要给出训练数据对进行监督训练即可。那么这里我们只需要收集 vector-图片 这样的样本对即可。假设你有一个手写数字的 database,然后你给每个数字定义一个 vector 就 ok 了,然后使得生成的 image 与 真实的图片越接近越好。这跟传统的监督学习是一模一样的。
那么这里的问题就是我们怎么产生这个 vector 呢?如果随机产生的话,Generator 的训练可能会非常的困难,因为上图中的两个 1 是非常相似的。如果两个 vector 差别很大的话,是很难生成两个相似的东西。上图中我们定义 vector 时,可以如下定义:0.1 表示 1 这个数,负数表示向左倾斜,正数表示向右倾斜。但是这个非常麻烦,需要手动定义,是不现实的。
那么有什么好的办法吗?那么自编码器(Auto-encoder)就可以实现。如下图所示,给出一张图片,经过编码器将其转化为一个 vector。由于 Encoder 不能自己训练,所以还需要给它加入一个 Decoder ,将其变成一张图片,然后使得输入和输出越接近越好。其中 Decoder 就是 Generator。
但是这里有一个问题,如下图所示:vector a 会产生一个数字 1 的图片,vector b 也会产生一个数字 1 的图片,那么 0.5 * vector a + 0.5 * vector b 会产生什么呢?
你有可能会猜测也会产生 1。但是不然,因为 Generator 是一个网络,是一个非线性的,并不定产生的就是图片,很有可能是噪声。那么怎么解决这个问题呢?
Variational Auto-encoder(VAE,变分自编码器) 就可以解决这个问题。它的输入经过 Encoder 不仅产生一个 code,还会产生 code 每一个维度的偏差。然后我们从高斯分布中采样一些 noise,然后与偏差相乘,最终加到 code 中得到有噪声的 code,然后 Decoder 根据有噪声的 code 还原出原始图片。这样就实现了即使 code 有噪声,它也能产生出图片而不是噪声,这样的 Decoder 就会更加稳定。
接下来,我们继续探讨 Auto-encoder 的 training。下左图是 Generator 生成的图像,我们希望生成的 2 的图像与真实的 2 的图像越接近越好。最常用的就是计算两张图片 Pixel by Pixel 的差值。理想情况就是 Generator 可以完全 copy target 图像。但是真正训练时 Generator 会犯错,不可能生成与 Target 完全一样的图像。它会选择性的在一些像素点妥协,从而没有办法与目标一模一样。那么选择在什么地方妥协就会变得非常重要。
例如,下图由不同的 Generator 产生了四张数字 2 的图片。如果我们的损失是计算 pixel 之间的差值的话,左边两张图都只差了一个 pixel,后边两张图片差了 6 个 pixel。所以对于这样的 Generator,它更倾向于生成左图这样的图片。但是从人的观点来看,左图两张图片并不准确,不像是人手写的数字。但是右图两张图片只是将笔画写长了而已。所以对于人来说,反而右图是可以接受的。
所以我们设置目标时,不能单纯让 output 和 target 越像越好。如今做 Structured learning 的时候,它是比较复杂的,它里面包含很多的成分,其中成分之间的关系是非常重要的。如下图所示,在第一张数字 2 的图片中,数字 2 的尾巴有一个点,如果我们将其补充完整变成第二张数字 2 的图片,它仍然像是人手写的数字。这里我们可以将每一像素看成一个成分,其实它们之间的关系是很难用一个神经网络表示的。
如上右图所示,假设 layer L 就是网络的最后一层,每一个神经元表示一个像素。假设 Layer L-1 层的输出已经给定,那么第 L 层的每个神经元的输出也随之确定,且是独立的。如果我们希望第 L 层的第一个神经元和第二个神经元输出的一样的颜色,这个我们是没有办法做到,两个神经元之间没有办法互相影响。这就是单纯训练一个 Generator 困难的地方。但是上面的情况是只看了一个 Layer,如果我们多添加几个 hidden layers(深度网络),就可以将这个联系添加进去。
总的来说,如果你不想用 Discriminator,单纯用 Auto-encoder 的技术生成图像。根据经验,如果是相同的网络,一个用 GAN 训练,一个用 Auto-encoder 训练。结果就是 GAN 可以生成,但是 Auto-encoder 需要一个更深的网络才能够产生与 GAN 接近的结果。