视频链接:https://www.bilibili.com/video/BV1tE411Z78A
在李宏毅GAN教程(1) 的基础上增添修改内容
文章目录
Basic idea of GAN
GAN是想让机器自己生成东西。随便给它一个输入,比方说高斯分布的vector,就会产生一个图片或者一句话等,丢不同的vertor产生不同的图片或sentence。GAN的目标就是训练出这样的NN Genertor.
其实这样也没啥意义,最有意义的是condition的GAN(以后再说)。
最终的目标是:我们输入一个已知的东西,让机器产生对应的东西。比方说输入一个句子输出一张对应的图片,输入一张图片生成另外一张图片。(有条件生成)
生成器Generator
在GAN里面我们想要训练的东西是Generator,他是一个神经网络,它的输入是一个vector,它的输出是一个图片,或者说是一个更高维的vector;对于输入的vector,它的每一个维度可能代表图片某种特征,当我们改变这个维度时,这个特征就会发生改变。比如第一个维度代表的是头发,当该维度值改变后,头发颜色就会变化。
假设这个 input vector 的倒数第二个 dimension 对应到头发是不是蓝色的,那值越大代表头发越蓝。把这个值从 2.4 调到5.4,产生出来的角色就会变成蓝头发的角色。那这样的角色看起来非常像,因为你只改了倒数第二维而已,其他维度的值是固定不变的,只改变了头发的颜色。其他的特征仍然会是很相似的。
判别器Discriminator
但是在GAN中,同时会训练出一个判别器。他也是一个神经网络,输入是一个高维向量,如一张图片,输出是一个数值(scalar),用于判断这张图片的质量。当这个输出值越高,越像真实图片。
两者关系
GAN 主要包括了两个部分,即生成器 generator 与判别器 discriminator。生成器主要用来学习真实图像分布从而让自身生成的图像更加真实,以骗过判别器。判别器则需要对接收的图片进行真假判别。在整个过程中,生成器努力地让生成的图像更加真实,而判别器则努力地去识别出图像的真假,这个过程相当于一个二人博弈,随着时间的推移,生成器和判别器在不断地进行对抗,最终两个网络达到了一个动态均衡:生成器生成的图像接近于真实图像分布,而判别器识别不出真假图像,对于给定图像的预测为真的概率基本接近 0.5(相当于随机猜测类别)。
generator和discriminator之间的关系就好像是猎食者跟他的猎物之间的关系。
GAN的概念,有点像生物的演化。 Generator演化(蝴蝶), Discriminator也跟着演化(天敌)。
一个实际的过程
假设让机器做二次元头像的生成,首先你要准备一个database,里面有很多真实的二次元人物的头像。
一开始你的生成器网络的参数是随机的,它自己不知道如何产生二次元人物头像。就先随便生成一些奇怪的东西。
然后去learn第一代的discriminator,方法是根据real image跟generator产生的image去调整里面的参数。即生成器产生一堆假的image,标记为0,;然后还有真正的image,都标记为1.然后用这些数据去learn一个神经网络判别分类器。
调生成器的参数得到新一代的生成器,让它生成的图片能够骗过前代的判别器。 如何调? 将V1生成器和V1判别器组合在一起组合成一个大网络,在固定v1判别器部分网络参数的同时,梯度下降调整生成器模型参数,使输出为1.就得到了V2生成器。
一个形象又和平的比喻
就产生了两个疑问:(之后再介绍)
1、为什么生成器不自己学?
2、为什么判别器不自己做?
算法过程
每个 iteration 里面,你要做两件事,有两个步骤。第一个步骤,你把 generator 的 fix 住,把 generator 的参数固定住,你只去调 discriminator 参数,你只去 train discriminator 把 generator 固定住好怎么做呢?你有一个固定住的 generator 然后你今天把一大堆的 random vector 丢到这个 generator 里面,那这个 generator 就会产生很多的图片。那因为一开始这个 generator 它的参数是随机的,所以它产生出来的图片并不会特别好,可能是非常糟的。
那接下来你有一个 data base 我们刚才讲过说,如果你只要让机器产生某种东西,你要收集那种东西的范例,你要让它产生二次元物的头像,你要收集及很多二次元物的头像当做范例,你会从这个二次元人物的头像 database 里面 sample 出一些 example。所以你现在有两组图片,一组图片是从 data 里面 sample 出来的,另外一组图片是 generator 所生成的。那接下来你就要去训练你的,要去调整你的 discriminator 的参数。
如果是从 database 选出来的就是高分,从 generator 产生出来的 image 就是低分。可以当做classification或是regression
- 首先随机初始化判别器和生成器两个神经网络的参数;
- 进行下面的迭代过程:
- 1固定生成器的参数,去调节判别器的参数
- 2固定discriminator的参数,调节generator参数
实际操作这一步时会把generator和discriminator当做一个巨大的network
但是在这个network里面的其中的一个 layer 它很宽,而它的 output 就是一张 image。假设图片是 64 乘 64 的,那也就会有其中一个他的 output 会是 64 乘 64 的数值。你把那个 output 拿出来,就可以把它看作是一张 image。而你在训练这个 network的时候,固定最后几个 hidden layer 只调前面几个 hidden layer 让它的 output 的值越大越好
具体算法:
theta:参数
m相当于batch-size
Noise sample可以尝试从Gaussian分布, { z . . . } \{z...\} {z...}相当于初始化参数
D()使用过sigmoid
和gradient descent相反
下面训练generator时的{z…}可以和之前不一样
G ( z i ) G(z^i) G(zi)是generator生成的一张图片, D ( G ( z i ) ) D(G(z^i)) D(G(zi))是discriminator输出的一个scalar(数值)
式子里面,我们只写说把这个 set update 一次参数,但至于说你要 update 几次参数,其实这是另外一个 hyperparameter 是你要调的。你可以说在这边我执行这一项三次,我执行这一项五次都可以,这是一个参数,你要去调它的。
首先初始化判别器和生成器
1、然后从database中抽取m个图片(like batch size); 从一个分布中抽取m个vector,它的维度时超参数,自己设定; 使用m个vector产生m个image。 之后去调整判别器:首先把m张真实图片都拿出来,经过判别器得到分数,然后经过log再统统平均起来(当然希望这个越大越好,因为希望真实的图片得分高);对于生成器生成的m张图片当然希望值越小越好,因此用1-值,其越大越好。因此使用梯度上升的方法,调节判别器参数。(实际训练过程是给真实图片赋值为1,生成图片赋值为0;训练二分类器;等同于上述过程)
2、重新生成m张图片,G(Z)就是一张图片,再把它丢到判别器中D(G(Z));再对所有的生成的求平均,在D不改变的情况下,希望这个值越大越好
GAN AS Structure Learning
one/zero shot:假设有些类别你根本没有任何的范例或者是只有非常非常少的范例
假设我们把每一种机器可能的 output视为一个 class 的话,那其实 structured learning 是一个极端的 one 或者是 zero shot learning 的 problem 所以因为这些 class 它都只会出现一次。所以在这种的 task 里面如何学到一般化,如何学着去输出从来没有看过的东西,就变成一个很重要的问题。那也可以想象说要让机器做到这件事情,要让机器可以输出它在training的时候从来没有看过的东西。那这个机器是需要有一定程度的智慧的,或者是讲得更拟人化一点,机器必须要学会创造,它才能够解 structured learning 的问题。因为它在 testing 的时候,它需要输出的正确答案,很有可能是 training 的时候还一次也没有看过的。那这个东西我们就可以把它视为是一种创造。那如果今天机器要解决 structure learning 的问题,它必须要有规划的概念,它必须要有大局观。因为今天机器要产生一个很复杂的物件,而这个复杂的物件是由很多比较简单的 component 所组成的。
那机器在产生这张图片的时候,他一定要在心里分清楚说我画,我点 pixel 上去代表是眼睛,点这个 pixel 上去代表是嘴巴,才不会变成说画三只眼睛,画两张嘴巴等等。所以今天在这个 structure learning 里面,真正重要的不是你产生了什么 component 而是 component 和 component 之间的关系。机器要产生的是一个完整的句子,甚至是一篇完整的文章,单碳生成的一部分,你没有办法判断它是好的还是不好的。
GAN其实是一个Structured Learning的solution
在传统的 structured learning 的文献里面有两套方法,一套方法是 bottom up 的,一套方法是 top down 的方法。
所以bottom up的方法是说我们今天要产生一个完整的物件,机器是一个一个 component 分开去产生这个物件,那这样的缺点就是很容易失去大局观。top down是说产生完一个完整的物件以后再去从整体来看说这个产生的物件好不好,这样讲一点抽象等下会具体告诉大家说这件事实际上是怎么做的。但是这边的坏处就是用这个方法你很难做generation
生成器可以自己学习吗?(生成器就是Autoencoder中的decoder)可以的话,单独learn会有什么问题?(输出像素之间独立)
生成器就是给一个vector得到一个image;输入不同的vector就得到不同的图片;
如何训练这个生成器呢?在传统的监督学习中,给NN一个输入和输出的pairs,就这样一直train下去就好。比如有一系列的图片,随便给他一个code,然后训练即可,使输出和label越接近越好。但是会有很大的问题,当输出类似的时候,输入由于是随机的,有很大差别,这样很难训练出一个网络;
因为这两个 1 是很像的,但如果他们 input 非常不一样的话,那可能可能很难认出
我们希望有共同特征的input,对应的vector有某些相似之处
如何得到与实际数据有关的输入vector呢?你可以训练一个encoder,给encorder一个图片,它将输出一个向量。
encoder 本身不能够自己train,一定要再加一个 decoder 才有办法
decoder 吃个 code 他要把这个 code 变成一张图片,在 training 的时候给一张 image 这张变成一个 vector 然后decoder 把这个 vector 解回原来的 image
在autoencoder中,其实对于decoder,其实就是一个生成器。
这个 decoder 根本就是我们要学的 generator 所以事实上你train好一个 auto encoder 以后,你把 decoder 拿出来,那个就是我们的 generator
但是存在一个问题:你的training data里面的image是有限的,当生成器在看到a的情况下生成偏左的1,在看到b的情况下也生成偏右1,当a和b 的平均,会产生什么样的图片呢?事实是由于网络是非线性的,得到的不一定是正的1,也可能是噪声。
为了解决上面的问题,因此我们有了VAE;VAE不仅产生一个code(m1,m2,m3)还会产生每一个维度的方差;然后将方差和正态分布中抽取的噪声进行相乘,之后加上code上去,相当于加上noise的code;之后输入的decoder中就得到图片;这样情况下,decoder不只是看到a或b产生一些数字,当看到a或b加上一些noise也要产生数字;这样使decoder更加鲁邦。
encoder 这边这个 m 代表它产生出来的 code 它不只产生一个 code vector 它还会产生每一个 dimension 的 various ,接下来从 normal distribution 里面去 sample 一个 noise 出来,把这些 noise 跟这个 varience 相乘,把这个 noise 加到你的 code 上面去,代表有加 noise code 丢到 decoder 里面。然后 decoder 要根据有 noise code 还原出原来的图片。
但是这种VAE和decoder的生成器少了什么东西?
生成器在train的时候,我们希望的是最终生成器生成的图片和我们给定的图片在像素级别上越像越好。可以把图片表示成vector然后计算equidistant?,但是肯定会产生一些mistake。
对计算机来说,上面两个图片误差小,下面的误差大;但是对于人来说,下面的好,因为只是尾巴长一点而已。
原因是在decoder网络中,每一个输出就是image中每一个像素的值,因此输出是独立的,无法相互影响,就会产生下面的问题:一个点多出来不好,如果能补全的话会好,但是做不到。因为无法相互配合。
我们觉得这张图片是不行的,并不是因为这边放了一个 pixel 有什么不好,在这边放一个 pixel 这件事情本身是没有错的。因为你如果说这边放个 pixel 但是你把这边补满它仍然是一个像是自然手写的数字。这并不是说你把一个 pixel 放在这边涂黑是有错的,而是这边涂黑以后,在相邻的地方你没有把它跟着涂黑,所以在一个 structured learning 里面, component 和 component 之间的关系是非常重要的。train一个 generator 去生成图片的时候,会发现一个 network 的架构其实没有那么容易让我们把 component 和 component 之间的关系放进去。
每个输出是independent的,没有办法互相影响一起去产生一个好的结果。
解决办法是使用更深层的网络,比如添加更多的hidden layer去捕捉这种关联;如果你单纯的想用cutoencoder这种技术去做生成器的话,相比于GAN,你需要更深的network才能产生跟GAN接近的结果。
对 generator 来说,要考虑dimension之间的 correlation是有点困难的。
判别器为什么不自己学习生成图片?(可以生成但是很卡(很慢))
判别器就是给定一张图片,输出一个值。
discriminator:
对于单纯的生成器,想要去判别不同像素之间的关联是很困难的;对discriminator来说,产生component之间的correlation比较容易
对判别器来说,判断一张图片好不好是很容易的,因为你是把输出好的图片丢给判别器的,然后去评价它好不好。比方说有一个滤波器,他会去检索有没有孤立的像素点,有的话就是低分。
产生完一张完整的图片以后,要检查这张图片里面的 component 和 component 对不对,是比较容易的。在生成的时候,因为是一个 component 独立生成,不容易考虑他们之间的关系。但是等整张图片已经生成完以后,要在检查这个关系对不对是比较容易的。好这个就是 discriminator 所占到的优势。
判别器如何产生图片:
假如说已经有了一个判别器,它能够鉴别一个图片是好的还是不好的,我们就可以用这个判别器去生成图片。穷举所有的输入x,看看判别器给它的分数,找到最大的就是判别器生成的(如何找,别管他),结束。所以很麻烦。
另一方面,我们怎么去训练这个判别器呢?因为我们手上只有positive的example,没有负面的。discriminator只擅长批评,但只用正例训练会导致看到什么东西他都会觉得是正面的例子,都给他高分。
需要给一些负样本
一个办法是直接用noise,让它成为负样本。但是会出现一张稍微有点像图片的picutre就会得到很高的分数。但是我们想要的判别器是,如果两只眼睛不一样,就能判断出这不是一张人脸的判别器。但是怎样产生这些好的负样本呢?
我们需要好的负样本才能训练一个判别器,我们需要一个好的判别器才能找出好的负样本
好的负样本:需要一个迭代的算法
- 开始的时候你又一堆正样本和负样本:正样本是真实图片,负样本随机产生的噪声
- 开始迭代
- 你的判别器需要做的就是去给正样本高的分数,负样本低的分数
- 当你学出一个判别器后,再用判别器去做生成(就是一个argmax的过程),生成一堆它觉得好的的图片,之后作为负样本重复1过程
从概率分布角度看上述过程:
X的分布不同的话,判别模型会给它不同的分数。他当然会给real example一个很高的分数,给其他区域低的分数。
這個紅色的線代表的是discriminator的value, input一個object, 這個object如果是image的話(讲图片从高维简化到低维)
随着它分布的位置不同, discriminator 会给它不同的分数。好,那你假设是落在这个地方, discriminator 当然要给落在这个区域的东西高的分数,不在这个高分的区域低的分数。但是因为实际上这整个而且可以分布的 space 是非常巨大的。
事实上去学D(x)的过程是一个迭代的过程:
- 加入蓝色的是判别器生成图片的分布,绿色的是真实图片分布,我们需要做的是让D(x)给绿色的点高分,蓝色的点低分;(如果只训练一次的话,可能有的地方的分数比real分数还高)
- 之后去寻找找出判别器的弱点(得分最大的地方让它变成负样本)
- 如何寻找。之后让判别器去生成图片(也就是会给高分的图片,其实就是生成器生的),然后让判别器让这些地方变成低分。
- 反复迭代。最终正样本和负样本就会重合在一起。
最终生成器只会生成真实图片分布中的picture,然后判别器最终会判断不出好坏…
如果图片过少的话可能overfit,即生成的图片就是原数据集中的图片
- 判别器只学着给绿色区域高分,给蓝色区域低分,但可能没有学到给其他(右边)区域多少分数,所以可能这个区域的分数比 real data 分数还要高。
- 接下来继续生成negative example:通过搜索找到评分最高的区域,在那个区域生成(右边高分区域)
- 不断重复后:分布中又出现real data的地方分数才最高,negative example跟positive example他們的distribution就會重合在一起,此时training的process就會停下來
因此只用判别器也可以做生成
事实上,下面都是用判别器做生成;其实这个garph就是一个判别器;1、迭代 2、更新
剛才講的那個train discriminator的process其實就是general的Graphical Model的training
Graphical Model中graph其实就是你的 discriminator ,输入你的 observation 然后那 graph 告诉你说这组 data 产生出来的几率有多少,那个几率就是 discriminator 算出来的分数。
总结
生成器和判别器的对比:
- 单纯的生成器:很容易生成图片,因为就是一个前向输出过程。但是它不考虑像素之间的联系(只学到表象,没学到精神或全局信息)
- 单纯的判别器:可以学习考虑大局,但是很难去生成图片(要去解一个argmax的问题,要做一些线性假设,线性意味着限制了它的能力,但如果是非线性就无法解)
经过GAN,都可以解决
generator取代了这个argmax的过程;现在我们用generator去得到好的负样本来取代argmax ;因为生成器在train的时候就是去学一些image,是可以躲过判别器的(给高分的)。
GAN有什么好处?
- 从判别器角度:过去不知道怎么解argmax的过程,由generator来取代;
- 从生成器角度:虽然在生成图片过程中的像素之间依然没有联系,但是它的图片好坏是由有大局观的discriminator来判断的。从而能够学到有大局观的生成器。
generator在學怎麼解arg max這個problem
得到的結論是所有不同的GAN其實performance都差不多:
FID Score越小代表產生出來的圖片越像真實的圖片