Generative Adversarial Networks简称为GAN,包含三个重要成分:生成器Generator,判别器Discriminator和对抗Adversarial。
基本概念:
生成器(Generator):生成器的作用是根据一个随机产生的向量(vector)产生一张图片,也就是说Generator model的输入是一个向量,输出是一张图片。
判别器(Discriminator):判别器的作用是判断一张图片是来自生成器还是来自真正的图片,也就是说Disriminator的输入是一张图片,输出是一个标量:1表示该图片是真实的图片,0表示该图片是生成的。
对抗(Adversarial):生成器想要生成令判别器无法分辨的假图片,也就是假图片需要接近真正的图片;判别器需要给生成(假)图片低分,给真正的图片高分,具有抵抗生成器的分辨能力。生成器的模仿能力和判别器的分辨能力是在对抗的过程中逐渐加强的,最后达到生成器产生令判别器无法区分的图片,此时判别器的输出应该是0.5(也就是说生成器产生的图片和真正的图片无差别,因此判别器无法判断该图片到底是真是假,来自生成器和真正图片的概率是相同的,均为0.5)。这个对抗的过程是通过迭代实现的,该过程也可以视为判别器对生成器的指导过程,两者是互相依赖的关系。生成器最初的输入是随机的一个向量(可以服从任一个分布),因此产生的图片肯定是很假的,判别器很容易判断,返回一个低分,这个低分就作为一个指导,令生成器向一定的方向改进,生成更真实的图片,再用来训练判别器,使判别器加强辨别能力,继续对更真实的生成结果指导,如此往复,令生成器和判别器都不断改进。然后放上一个问题:是李宏毅教授的课程中提出的:既然判别器相当于生成器的老师,为什么不能让判别器(discriminator)直接生成图片?
算法:
z:初始的随机噪声向量,p(z)是先验概率,因为该变量服从的分布可视为已知的(有一个问题:z是一个向量,向量的先验概率是什么意思?)。
:是生成器根据输入向量z生成的一张图片(其实还是向量:若图片尺寸为H*W且是灰度图像,就是一个长度为H*W的向量,这个程度上生成器可以看作是一种编码过程)。由于生成器是一个多层感知机构成的网络,参数
就是网络中的参数(各层感知机之间连接的权重)。
x:真正图片的向量。
:是判别器对真正图片的向量的打分(真正图片的得分一定是比较高的),其中的参数
也是多层感知机网络中的参数。函数
表示的就是x来自真正图片的概率,因此输出应该是介于0到1之间。
因此一个好的生成器应该是能令判别器得到一个高分(最大化
),好的判别器是对
得到低分(最小化
),对
得到高分(最大化
)。因此将一个生成对抗网络的质量量化的话,就是一个minimax问题,可以用如下公式来表示:
对判别器进行优化是使得函数V最大化,对生成器进行优化是使得函数V最小化。由于生成器和判别器都是多层感知机构成的网络(其实就是神经网络),可以用反向传播法训练(也会涉及到梯度上升和下降法)。在对生成器的训练中,刚开始生成器产生的图片肯定在判别器中得分很低,导致的值很接近1,梯度中涉及的
很小,这使得反向传播中没有足够的梯度导致参数更新效果不好(梯度消失),因此取代最小化
,直接通过最大化
来训练生成器。算法的具体操作如下:
通过上述的算法可知:
1. 先训练判别器:最初的生成器输入是噪声(随机的),表示的应该是一个随机向量(服从先验分布
),将生成器根据
产生的向量
和真正的图片向量
分别送入判别器,分别得到
和
,这是采样一个向量得到的结果,一共采样m次然后取均值。因为优化判别器时要max函数V,因此使用梯度上升法(ascending),更新判别器的参数
,得到新的判别器(就这样在生成器固定的情况下,一共迭代k次完成对判别器的更新)。
2. 训练生成器:当判别器通过k次迭代更新后,固定判别器,重新从先验分布中采样m次,对函数V进行梯度下降法更新参数
(因为函数V中的第一项与生成器G无关,因此只对函数V的后半部分进行梯度下降就可以)。这就是训练生成器的过程,也就是训练k次判别器后对生成器进行一次训练。
写到这里又想起来,生成模型是无监督学习(unsupervised learning),但其实是不是相当于:利用判别器给生成器产生的数据打了标签,然后进行了监督学习(supervised learning),不过判别器打标签的过程中只利用了数据库中无标签的图像和生成器产生的假图像,是利用两者间的差别产生标签的。
在实际code的实现中,整个过程是由生成器和判别器结合起来实现的。生成器和判别器都是神经网络,将两个网络连接起来构成整体的模型。这个模型的前半部分是生成器,后半部分是判别器,所以输入是生成器的输入(噪声),输出是判别器的输出(一个介于0到1的标量也就是该噪声生成图像的得分),模型中间的输出也就是生成器的输出和判别器的输入(生成器产生的假图像)。训练过程就是先固定后面的layers,梯度上升训练前面的layer使输出越大越好,然后再固定前面的layers,梯度下降训练后面的layer使得输出越小越好。
原理:
结构化学习:
参考李宏毅老师的课程,他提到了结构化学习(structured learning)的概念:一般的机器学习其实可以理解为一个函数,从输入映射到输出。如果是回归问题,输出是一个标量;如果是一个分类问题,输出是类别(one-hot编码的形式)。如果输出不再是这样简单的结果,而是矩阵,句子等等具有结构的结果,就称为structured learning。如果输出是图像,其实形式就是一个矩阵,矩阵中的每个元素就是一个像素值(像素之间的关系就是结构)。
structured learning:
1. zero-shot/one-shot learning:这两种算法都是分类算法,zero-shot learning是指训练集中没有某些类别的样本,但仍能学习到该类别的映射(和非监督学习有点关系?);one-shot learning是指训练集中某些类别只有一个或少数几个样本也仍能学习到映射。GAN中隐藏层(hidden layer)产生的假图像都是数据集中没有的图像,但是判别器要给出判别结果,相当于zero/one-shot learning。
2. consider globally:由于结构化学习的输出具有结构,其中最重要的是具有大局观,把握component之间的关系。结构化学习有两种途径:自下而上(bottom up)和自上而下(top down)方法。bottom up是从component的程度上产生整个object,可以理解为先产生一个个组件,然后把它们组合起来。这种方法产生组件时没有考虑整体结构,会失去大局观。top down是先产生一个完整的object,再找最好的一个结果。Generator可以视为bottom up的方法,Discriminator可以视为top down的方法。
Generator能否直接独立产生图像?
Generator独立产生图像时,其实就是输入一个编码(code),输出一个图像(matrix)。如果数据集中每个图像都有其对应的低维的编码,就和普通的图像分类器的训练方法相同,相当于监督学习。但是实际上并没有图像的code,这是个非监督学习。这里可以联想到自编码器(auto-encoder):auto-encoder根据图像生成一个低维的编码,然后再用auto-decoder将这个编码解码为一个图像,使其与输入的图像尽可能相像。那么解码得到的图像和原图像间的差异就是标签,可以进行监督学习。这整个模型中的auto-decoder其实就是Generator:根据code生成图像。因此通过自编码器就可以直接得到Generator,不需要Discriminator。但这样产生的Generator和GAN中的相比,需要更多层的网络得到像素间的关系(更深的网络才能较好地表示像素间的非线性关系),以产生具有大局观的图像(原因可能是相当于标签的解码得到的图像和原图像间的差异是像素级的,只能反映单个像素间的差异,生成时也是像素级的,不能完全反映整张图像间的差异;但是Discriminator给出的标签却是基于整张图像的,具有大局观,也就是之前提到的top down方法,因为Discriminator的输入是生成器产生的整张图像)。
联想到关键点检测和姿态估计的问题:在关键点检测和多人的姿态估计中涉及的应该也是structured learning。关键点检测中,一般需要较大的感受野,这样会得到不同关键点间的关联,对彼此的检测有所帮助,也是需要大局观。多人的姿态估计中更是需要structure:自下而上的方法就是检测出所有关键点,再依此分配给不同的object;自上而下的方法就是先定位完整的object,再该范围内再检测关键点。
Discriminator能否直接独立产生图像?
Discriminator的输入就是一张完整的图像,可以基于图像的level上判别,就会考虑像素间的关系。比如Discriminator是一个卷积神经网络,通过卷积核的设置就可以实现不同的判别需求,如下图所示:
该判别器需要给真正手写数字图像高分,手写数字的图像不会出现孤立的像素点,因此判别的标准是如果出现孤立点,要给低分,这就是考虑了像素间的关系,这种功能只要通过右侧的卷积核就可以实现了。
想用判别器生成图像,可以用这样的方法:,遍历所有x,找到能使得D最大的x,就是生成的图像。但这种生成方法是不太可行的,argmax函数求解并不是很容易。即使假设可以利用判别器生成图像,但判别器的训练仍然存在问题。因为数据集中的图像都是真正的图像,输入到判别器中都只会得到高分,判别器训练后只会打高分。因此必须还要给判别器输入一些假图像让其学会打低分。如果产生的negative example非常假,和数据集中的图像分别非常明显,则相当于了二分类问题:真图像和非常假的图像,这样即使比较差的图像可能也会得到高分(明显区别于非常假的图像)。所以假图像(negative example)的生成也是非常重要的,要想产生质量合适的图像,就要通过迭代(iterative)的方式实现。首先有positive example和随机产生的negative example,在每一次迭代中,discriminator根据这些example学习,得到一个好的discriminator,用它做图像的生成:
,得到较好的negative example,用来替代下一次迭代中的negative example。由于下一次迭代中discriminator会变得更好,用其产生的negative example也会更好。
图示discriminator的训练过程:
上图中,假设是一维的情况,x是positive example也就是真图像,z是negative model,最开始可以看到图(a)中,z来自于x的右半部分,因此图中绿色的实线在x的右半部分具有峰值,其表示生成器产生的分布。黑色的虚线表示真实数据集的分布(但是没搞懂为什么有两条)。蓝色的虚线就是判别器的输出分布。图(a)可以视作初始情况,图(b)则是固定住G,对判别器进行迭代更新的过程。可以看到蓝色的虚线在绿色实线低的位置都很高(偏左的部分是因为黑色虚线和绿色实线都很低,都很少会产生这附近的点,因此判别器可能会给一个比较高的值(其实也有可能给一个低的值吧),偏右的部分是由于真实数据集的峰值在此处,而生成器产生的分布也就是绿色实线在此处很低,判别器可以轻松判断此处的点基本都是真实点)。图(b)右侧蓝色虚线很低,因为黑色虚线很低,而绿色实线很高,说明此处的点几乎都是生成器产生的,所以应该给低分。图(c)是判别器经过k次迭代改进后固定住,然后更新生成器。生成器就会向判别器给高分的方向靠拢,模拟判别器觉得好的分布。到图(d)时,生成器几乎完美靠近真实分布,判别器的值为一条横线,z的点都在x分布的峰值处(说明生成器产生的z和真实数据集的分布几乎一样了)。
公式推导:
生成器Generator:
假设GAN是要生成图像,数据集中真实的图像可以表示为一个高维的向量(尺寸为H*W的图像表示为长度为H*W的向量)。在这个H*W维度的空间中,这些真实的图像只是空间中极小一部分(绝大部分该维度的向量都不是这些真实图像),因此存在着一个真实图像的分布,只有在高的地方才表示该处的向量x很容易产生图像,如下图所示:
蓝色的区域为Image Space,这里的向量x才具有高,也就是高概率能产生真实图像,而白色的区域可以看到会产生假图像(negative example)或者甚至根本不是图像。但事实上这个真实图像的分布是未知的,我们只有服从该分布的一些样本(就是数据集中的那些真实图像),那么该如何模仿这个未知的分布呢?需要借助最大似然估计(maximum likelihood estimation)。
是要生成的分布,
是分布中未知的参数,找到
以产生越来越接近
的
,其实就是参数估计,这里就要利用最大似然估计,最大化似然函数以找到
。
求解的过程中有一步转换:上图中黄色标注的部分。上一步中
中的
是数据集中的真实图片,也就是分布
的采样点。采样点是具有随机性的,因为不一定每次都采样同样的点,因此相对于这些采样点的函数求和后再max,其实直接使用
分布的均值是近似相同的作用。上图的最后引入了KL散度,因为之前讲到过,就不写了。最大似然就相当于求两者间最小KL散度,但如何定义
使两者有最小KL散度呢。因为
如果比较复杂,是无法计算它的likelihood function
的(比如
是由neural network定义的),那该如何找
呢,就要通过生成器。因为
这样一个高维空间中的分布想要直接去模拟得到
是很难的(
难以计算),生成器的作用就是从一个简单的分布产生一个复杂的分布
,如下图所示:
生成器是一个神经网络,相当于一个非常复杂的函数,它的输入是服从正态分布的样本点z,然后产生高维的x,相当于把一个简单的分布映射到了一个复杂的分布,就得到了。接下来就是计算两者的最小散度,因为
和
的具体形式都是未知的,是无法直接求散度的。尽管形式未知,但我们能从
和
中采样:从
中采样就是直接从数据集的真实图像中采样,从
中采样就是先从高斯分布中采样z然后输入到生成器中得到
。那如何根据一堆sample出来的样本来计算两个分布间的散度呢,就需要判别器了。
判别器Discriminator:
判别器输入就是服从两个分布的样本,给真实图片的高分,给生成的低分。训练判别器的目标函数如下:
这与普通网络的训练几乎相同。而上述的函数V(D,G)其实和JS散度有很密切的关系,甚至可以等价于JS散度(散度很小表示两者很接近,则一定较难区分,函数V(D,G)的值就会比较大)。所以上部分想要最小化两个分布间散度就可以通过判别器实现了,以最大化函数V(D,G)为目标函数对判别器训练,就会得到能够最小化两个分布间散度的判别器。
那么为什么V(D,G)和JS散度间有这样的关系呢:
目标函数为,在生成器固定的情况下找到判别器D使得该函数达到最大值,寻找该函数的最大值过程如下:
假设D(x)可以是任何的函数(虽然不可能,因为只有无数多的神经元才能构成所有函数),那么对于输入x,D(x)可以得到任意值(没有限制)。因此上面公式想要max,就相当于max该式子:,丢掉了对x的积分。因为无论x取什么值,D(x)都能产生任意值,因此x的积分可以省略掉。接下来就要找
对应的判别器,过程如下:
因此找到的最优判别器为:,把最优判别器带入目标函数进一步转化:
可以看到最后转化为了和JS散度相关的形式,即最大的V(D,G)约等于两个分布的散度。借下图回顾一下,最优生成器是要令两者间散度最小,而该散度可以通过求V(D,G)的max值得到。
因此寻找生成器的过程可以转化为如下的minimax问题:
假设只有三种D(x),那么这个minimax问题可以用下图表示:
三个图分别对应三种判别器D(x),也对应三种生成器。纵轴表示固定住的生成器,横轴表示变量x,可以看到蓝线也就是判别器随x的变化取不同的值。对每种判别器先找到其最大值,就是红色点,这点也就是两个分布间的散度。然后将三个红色点进行比较,找出其中最小值,是第三个判别器。因此第三个判别器所对应的生成器就是我们要找的生成器。但事实上,肯定不会只有三种判别器D,那如何接这个minimax的问题呢,就是GAN的算法了:
这其实就是前面Algorithm1的内容。
上图中把式子写作
,因为是先固定G,然后找到符合式子
的D,也就是找到D后它就相当于常数了,代入进去函数V就只包含变量G了。找到最优生成器G*就是要找到最小化函数
的那个G,很明显可以利用梯度下降法。但存在一个问题:
是一个包含max的函数,能直接求微分吗。事实上是可以的,如上图所示:一个含有max的函数f(x),求其微分时是分段的,当
最大时,
的微分就是
的微分;当
最大时,
的微分就是
的微分,以此类推。通过对
求梯度利用梯度下降法更新生成器的参数,得到一个优化的生成器。那么如何得到函数
呢,也就是
,可以利用梯度上升法。首先存在初始化的生成器
,此时函数
就仅仅是D的函数(相当于上述max函数f(x)中的一段:
),在这段上找到令
取到最大值的
,将
带入函数V,得到
,这个值就代表了当前生成器产生的分布
和
之间的散度,我们也就得到了函数
,就可以更新生成器得到
了。然后再根据
重新求
,如此迭代,通过min函数L(G)更新生成器。但有一个问题,这个迭代过程是不是真的一直在最小化
也就是
和
这两个分布间的散度?难道梯度下降存在什么问题吗?问题出在函数
上。该函数求微分是不同于一般的函数的,存在分段的问题,上面已经提到过。
中由于包含了max,在不同的自变量取值下会对应不同的子函数(名字随便取的,其实就是相当于f(x)中的
,
等),求微分也会产生不同的函数式。然而此时判别器D是还未更新的,保持着原来的值。那么对当前的L(G)函数求微分时,由于函数式变了,但D的值又没变(这里D其实是自变量,因为G是已经固定住了),得到的值就不再是微分值了,如下图所示:
未变,然后由于生成器从
变成了
,导致函数V(G,D)从
变成了
,右图在
处得到的红点显然并不是真正的
/散度,右边的更高的红点才是此时V函数的最大值,因此进行梯度下降时并不一定是在最小化两个分布间的散度。因此更新G时要小心,尽量小步伐更新,这样G不会从一个子区间跳到另一个子区间,函数V也不会跳到另一段。但D的更新却是要迭代多次的尽可能达到最大值,只有这样V才能代表散度(不然违背了原理),所以Algorithm1里面D是要迭代k次后G才会迭代一次。
以上是原理部分,实际操作中,函数V不会如此取均值:,因为分布未知。实际操作是采样:
采样m个点然后取均值,近似于对整个分布求均值。这个式子其实就相当于在训练一个二分类器,判别器D其实就是二分类器,其中来自数据集的真实图片是正样本(positive example),生成器产生的是负样本(negative example)。
总结下来,判别器就是用来找到两个分布间的散度的,而生成器就是用来minimize散度的。