附课程提到的各式各样的GAN:https://github.com/hindupuravinash/the-gan-zoo
想要让机器做到的是生成东西。->训练出来一个generator。
假设要做图像生成,要做的是随便给一个输入(random sample一个vector,比如从gaussian distribution sample一个vector),generator产生一个image。丢不同的vector,就应该产生不同的image。
对于文字来说也是一样,丢一个vector,就输出一个句子,丢另一个vector就输出另一个句子。
(生成图像背景下)GAN的input就是一个vector,输出就是一个高维的vector,这个vector的每一个dimension就对应图像中一个pixel的颜色,把这个vector排成照片的尺寸->得到image。
(在图片背景下)在GAN中,同时还会训练一个discriminator。与generator相同,这个discriminator也是一个neural network。一个discriminator是吃一张image当做input,输出的是一个scalar(一个数值),代表产生的图片的quality,这个数值越大,就代表产生出来的图片的quality越高,代表产生的图片看起来越像真实的图片。
generator和discriminator的关系就像是捕食者和被捕食者之间的关系。比如枯叶蝶在生存的压力下(不会被捕食者吃),从正常颜色的蝴蝶变成褐色的,然后捕食者也会进化,现在不看颜色,要看纹路,所以枯叶蝶就会继续变(变成现在这个样子)
->共同进化
比如现在我们要做一个二刺螈头像的生成,首先要准备一个database,里面是真实的二刺螈人物的头像。一开始generator的参数是随机的,generator也不知道怎么产生二刺螈人物的头像。discriminator做的事情就是给一张图片,判断这张图片是generator生成的还是真实的图片。
接下来generator要做的事情就是想办法骗过第一代的discriminator。比如产生了有色彩的图片,第一代discriminator会被第二代的generator骗过,但是第二代的discriminator会学着去分辨第二代generator产生的图片和真实的图片。
generator接下来继续进化到第三代,第三代generator产生的图片可以骗过第二代的discriminator,然后第二代discriminator会进化成第三代discriminator。
->generator产生的图片就会越来越真实。
一开始我们有一个generator一个discriminator,两个的参数都是random initialized。
接下来要iterative的去train generator和discriminator。要跑很多的iteration。在每一个iteration里面,要做两件事:
1. 把generator的参数固定住,只去调discriminator的参数(只train discriminator)
? ? ? ? 方法:把一大堆random vector丢到generator里面,generator就会产生很多的图片。因为一开始generator的参数是随机的,所以产生的图片并不会特别好。我们有一个database,里面都是真实的二刺螈人物头像图片,从这个database里面sample出一些例子->现在有两组图片,一组是从database里面sample出来的,一组是generator生成的。接下来要去训练discriminator的参数。那么怎么调整discriminator的参数呢?
????????目标是假如image是realistic的,就给比较高的分数,如果是generator产生的,就给比较低的分数。
2. 固定discriminator,只调generator(只train generator)
? ? ? ? 方法:先把一个vector丢到generator里面,generator会产生一个image,接下来把这张图片丢到discriminator里面,discriminator会给这张图片一个分数。generator训练的目标是骗过discriminator,也就是我们希望generator产生出来的图片,discriminator可以给比较高的分数。
在实际执行上,会把generator和discriminator看做是一个大型的网络。比如generator有5层,discriminator也有5层,我们会把两个接在一起,得到一个10层的网络。这个10层的network的input是一个vector,output是一个数值。在这个10层的network中的其中一个hidden layer会很宽,这个hidden layer的output就是一张image。比如一张image是64×64,那么这个hidden layer的输出就是64×64的宽度,这个输出就可以看做是图像。
在train这个network的时候就是固定最后几个hidden layer,只调前面几个hidden layer,让输出越大越好。这个时候做的就不是gradient descend了,而是gradient ascend(不过没差啦)。
这里因为要让输出越大越好,所以一定要固定最后几个hidden layer,否则,只要调最后一个layer的weight,让最后一层的weight越大越好,就能让output的值爆表。
在每次迭代中:
从database里面sample出来m张image(这里的m实际上是batch size),同时从某个distribution里面sample出m个vector(老师提到这里采用什么distribution可能影响不大,这里vector的dimension可以自己指定,后期是需要调的。注:这里的vector的dimension指的是vector的长度)
根据sample出来的m个vector,产生m张image。
调整参数。让logD(x^i)越大越好->给真实image的值越大越好。
这里使用的是gradient ascend,在gradient descend里面是减,这里换成加即可。
要update几次参数呢?这也是一个需要调的hyperparameter
-----------------------↑训练discriminator-----------------------------
接下来训练generator。
同样也sample出来m个vector。丢到generator里面。
update参数,让generator产生的图片的分手越高越好。
继续gradient ascend。
(老师点评这已经比很多作画崩坏的动画公司画的要好,很好,别说了,破防了)
也可以生成真实的人脸。
GAN其实可以被视为structured learning。什么是structured learning?machine learning就是去找一个函数,input一个东西,output另一个东西。如果是regression task,就output一个数值,如果是classification task,就是output一个class。如果我们遇到的问题是一个更复杂的问题,不是output一个数值/class,可能是一个sequence、matrix、graph、tree。这个task就是structured learning。
为什么structured learning被视为有挑战性的任务?
structured learning可以被视为one-shot/zero-shot learning(假设现在有一个分类的task,每一个类别都需要给机器一些例子,比如苹果和橘子,要给机器苹果和橘子的照片。但是有些类别可能没有例子,或者只有有限的例子->one-shot/zero-shot)。structured learning可以被视为是一个极端的one-shot/zero-shot。比如生成句子,在training data里面,没有句子是重复的,包括test data也没有。如果把每一个不同的输出看成是一个class,那么training data每一个class可能只出现一次,test data里面的句子压根就没在training data中出现过。->输出从来没有见过的东西。
如果机器要解决structured learning的问题,要求机器必须有规划的概念,有大局观。比如机器在画人脸的时候要知道这个pixel是眼睛,这个pixel是嘴巴这样子。
在传统的structured learning中有两套方法:bottom up和top down。
bottom up的方法是指,我们现在要产生一个完整的东西,机器是一个一个component分开去产生->这样的缺点是很容易失去大局观。
top-down就是先产生一个完整的东西之后,再在整体的角度看好不好。->坏处是很难做generator
generator就是一个bottom up的方法,discriminator则属于top down。
generator的学习就是input一个vector,output一个图片。比如手写数字,input不同的vector,output不同的数字的图片。要训练这样的一个generator,也可以收集数字-图片的数据,给每个数字assign一个vector就好啦。完全就类似classifier。?
但是现在的问题是,怎么assign vector给数字呢?随机产生?随机产生可能会造成不好train。我们还是希望相似的数字有相似的vector,比如这里数字1都是0.1,2都是0.2,3都是0.3,然后另一个值可能就代表了数字倾斜的方向。
怎么得到这样的vector呢?其实也是可以学的,比如encoder。给一张图片,让encoder用一个向量表示这个图片。
?怎么train这样的encoder呢?auto-encoder。
decoder实际上就是一个generator。generator实际上在做的就是给一个code,让产生某一个对应的图片,现在有一个encoder,工作是产生一个图片对应的code,decoder要做的就是看到这个code,产生这个code对应的图片。
用auto-encoder会遇到什么问题?因为training data里面的image是有限的,generator可能learn到的是看到是看到vector a产生某张图片,看到vector b产生另一张图片,但是看到vector a和vector b的平均,应该产生什么样的图片?
解决方案:variation auto-encoder
encoder不仅产生一个code,还会产生每一个dimension的variance。接下来从normal distribution里面sample一个noise出来,把noise和variance相乘,把noise加到code里面,再把加了noise的code丢到decoder里面去,让decoder还原出input。
->让generator知道看到vector a加noise/vector b加noise/a/b……都要输出数字->让decoder更加稳定
(学生提问,这个code的dimension怎么决定,老师的回答总结一下就是,我们希望这个code是一个low dimension的vector,如果train不起来,让dimension变大可能可以train起来,但是未必嫩得到我们要的东西,因为假设我们把code设置的和input一样大,那么其实只要一直copy input就好了,但是这并没有达到我们使用auto-encoder的目的->我们希望的模型的作用是用低维代替高维信息)
generator train的目标是希望和某张图片越像越好。如图,就是希望generator的output和这个2越像越好,怎么评判像不像呢?通常计算的就是两张图pixel by pixel的差距。比如把这两张图片表示成一个vector,然后计算这两个vector的距离。
但是真正train的时候generator是会犯一些错的,没有办法和target一模一样,可能会在一些地方坐妥协。这个时候在什么地方做妥协就很重要了。
比如现在有四个不同的generator产生了四个不同的图片。那么按照之前的要求,那么就应该选1 pixel error。但是以人的观点来看,却并非如此。
比如在某个pixel点了颜色,这并不代表不对,不对的是没有把周围的pixel补满。->没有考虑pixel和pixel之间的correlation。
discriminator的作用是,输入一个object,输出一个分数,分数代表输入的东西有多好。在别的领域可能有别的名字。
可以用discriminator做generator嘛?可以。
对于generator来说,要考虑component和component之间的correlation是比较困难的(因为每一个component都是独立生成的)。但是对于discriminator来说,就比较容易了。因为对于discriminator,是产生完整的image之后,再把这张image丢给discriminator,让discriminator给评价,所以discriminator就可以轻易的告诉我们这张图片是好/不好的(高分/低分)。怎么做呢?可能这里discriminator采用CNN,比如有一个CNN的filter是去判断有没有独立的点(各个方向都孤立),如果有,就给低分。
怎么使用discriminator产生东西呢?穷举所有可能的x,把所有的x都丢进去,看哪个x得到最高的分数,discriminator给高分的x就是生成的结果。
但是这个生成是很痛苦的,因为discriminator只擅长批评,没办法给一些有建设性的意见,可以说xxx是不好的,但是自己又想不出来什么样是好的。
假设上面的问题都解决了。现在我们要训练discriminator。方法就是给很多好的例子,给这些例子是高分的,再给一大堆不好的例子,告诉这些例子是低分。但是实际上我们只有好的例子,discriminator学到的可能就是无论什么图片,一律给高分(什么清汤大老爷?)。
->我们需要给machine一些不好的例子,但是从哪里找这些例子呢?
如果我们告诉机器,real的例子就是好的,随机生成的噪声就是差的,机器当然可以轻松分辨两个。但是之后如果我们给一个画的不好的(左下),机器也会给高分,毕竟比noise好很多。
->真的产生很好的negative example,这样discriminator才能真的学会鉴别好的例子和差的例子。但是怎么产生这些非常好的negative example呢?
解决方案:iterative方法。
假设我现在有一堆positive example和negative example,在一开始positive example是人画的image,negative example是random sample出来的一对noise。这时,discriminator学会的是,给这些positive example高的分数,给negative example低的分数。当我们学会这个discriminator以后,可以用discriminator做generation。
拿到generation之后,把这些作为下一轮的negative example
实际上,我们面对的是一个高维空间,很难去把没有出现example的地方的分数都压低。
实际上的做法。
比如图中real的左侧的分数我们并不知道高还是低,我们只是给real的区域高分,给generated的低分。接下来就在这个区域内(real的区域)产生新的generated(找到discriminator的弱点)
在一些没有real example,且凸起来的区域sample一些example,在下一个iteration把分数压下去。
(同学提问:是否会overfitting?很难知道有没有overfitting)
用generator解决argmax问题。
这里和前面的VAE是类似的,但是可以看到在空白的地方,就没有很多点了(对比之前的绿色点,这里采用的是红色)
之前(指课程中的之前)学到的network都是一个network,给一个x就输出y。我们学了各式各样的架构来处理不同的x(image、sequence)和不同的y(数值、类别、sequence)。
现在要进入一个新的主题,这里要把network当做generator来用。现在network的输入会加上一个random的variable(Z),这个Z是从某一个distribution里sample出来的。所以现在network不是只看一个固定的x得到输出,而是同时看x和Z得到输出。怎么同时看呢?比方说,可以x是个向量,Z也是个向量,给两个向量直接接起来得到一个比较长的向量,当做input。或者假设x和Z的长度刚好一模一样,相加以后得到network的input。
这里Z特别的地方是,Z是不固定的,每一次我们使用这个network的时候,都会随机生成一个Z(是从一个distribution里面sample出来的,这个distribution我们必须知道其表达式,比如gaussian distribution、uniform distribution)。sample的Z不同,得到不同的y。因此现在的network的输出不再是单一的、固定的东西,而是一个复杂的distribution。
这种可以输出复杂的distribution的network就叫做generator。
为什么需要输出是一个分布呢?输出x得到固定的y的问题在哪里呢?
eg:
video prediction。给机器一段视频,让机器预测接下来会发生什么事情。这里的例子是预测小精灵游戏的画面。
怎么预测呢?给network过去的游戏画面,输出新的游戏画面(下一个时间点的游戏画面)。得到数据集的方法也很简答,只要录制玩小精灵的画面就好了。比如这里就是给定三个frame,预测下一个frame。
怎么输出一张图片?一张图片就是很长的一个向量,所以只要让network输出很长的向量就好啦。
这样做(指上面说的,固定的输入对应输出)的问题是什么呢?
输出的结果可能会出现一些问题:
比如画面可能比较模糊,小精灵走着走着分裂了,还有的小精灵走着走着就消失了。为什么会有小精灵裂开呢?因为对于network来说,在数据集中,同样的输入,有时在同样的转角,有时小精灵往左转,有时小精灵往右转,这两种可能性同时存在,所以network就学会了端水,两边都讨好。
怎么处理这个问题呢?让输出是有概率的,即:不是单一的输出,而是输出一个概率分布。
当我们给这个network加上一个Z的时候,输出就变成了一个distribution->输出就不再是固定的了
我们希望训练一个network,包含了左转/右转的可能性。比如我们现在使用的是一个binary的random variable,network就可能学到Z sample到1的时候向左转,Z sample到0的时候向右转
什么时候我们会需要这样的generator呢?当我们的任务需要一些“创造力”的时候。->同样的输入有多个不同可能的输出,但是每一个输出都是对的。
比如画图、对话。
啊??(建议老师去写同人好啦)
各种各样的GAN。
等下的例子属于unconditional generation(还是前面的生成二刺螈人物脸)。
什么是unconditional generation?把x拿掉。输出就是Z,输出就是y。Z这边都采用normal distribution sample出来的向量这样子。这个向量通常是一个low-dimention的向量,维度是自己指定的。丢到generator里面,生成一个图片(一个高维的向量->一个长长的向量)
希望无论sample到什么Z,都得到二刺螈人物的人脸。
在GAN里面,除了generator,我们还需要训练一个discriminator(也是一个neural network)。discriminator会拿一张图片作为输入,输出是一个数值(分数,越高代表越真实)
?
?
?
补充之前的课程没有提到的,用StyleGAN做的结果。
Progressive GAN,可以产生非常高清的人脸
为什么generator和discriminator的互动可以产生上述的这些图片?
在训练的时候我们是要定一个loss function,定完之后用gradient descent去调参数,去minimize loss function就好啦。在generation的时候到底要对谁操作呢?
训练的目标:?
我们有一个generator,我们给它一大堆的vector(比如:从normal distribution中sample出来的东西),丢进generator之后会产生一个比较复杂的distribution。这个复杂的distribution称为P_G。我们还有一堆data(这个是真实的data->真正的图片,收集来的训练集),这些真正的data也形成了一个另外的distribution,称为P_data。我们希望P_G和P_data越接近越好。
divergence可以理解为两个distribution之间的距离,divergence越大代表两个distribution越不像,divergence越小久代表两个distribution越接近。
∴希望divergence越小越好
但是这边我们又遇到一个问题:
这个divergence很难算。如果divergence都很难算出来,肯定没办法去找到一个generator来minimize这个divergence了。
GAN很厉害的地方就死可以突破不知道怎么计算divergence的限制。
只要知道怎么从P_G和P_data中sample东西出来,就有办法计算divergence->不需要知道P_G和P_data实际上的formulation长什么样子,只要能够sample就能算divergence
OK,那么P_G和P_data可以sample嘛?当然可以。
把训练集拿出来,从里面随机拿一些图片出来就得到P_data了。同理,generator这边吃一些random的vector,吐出来一些图片,就是P_G。
根据real data(P_data sample出来的结果)和generative data(P_G sample出来的结果),训练一个discriminator。训练的目标是看到real data给高分,看到generative data给低分。
(弹幕:相当于用神经网络训练了一个divergency的计算方式)
log(D(y)),有一堆y是从P_data里sample出来的,是real image,把这些真的image丢到D中,得到分数,再取log。
log(1-D(y)),有一堆y是从P_G里sample出来的,把这些生成的图片丢到D中,得到分数,再1-分数,再取log
(弹幕:意思是用y~Pdata的分布对logD(y)做加权平均)
我们希望这个objective function越大越好。->希望y~Pdata的D(y)越大越好(真实图片的分数越高越好)->让V的值越大越好
(弹幕:js dvergence是两个KL divergence的平均,目的为了对称
JS 散度, 信息熵的延申
JS散度就是特殊的KL散度)
(弹幕:分类用的是交叉墒(分类效果越好,值越低)函数D是交叉墒乘负号,所以值越大,分类效果越好。如果分布接近,那就是不管怎么训练,分类效果都不好,也就是值永远都train 不大
div大的时候log(Dy)和log(1-Dy)的值在实际上在maxV(D,G)中偏大
所以我们为了找到div小的情况,需要将问题转化为找到maxV(D,G)的最小值)
本来我们的目标是要找一个generator去minimize P_G和P_data的?divergence。
现在我们知道,我们只要训练一个discriminator,训练完以后这个objective function的最大值,就和这个divergence有关->直接做替换
(弹幕:就是想求G,被div卡住,然后发现div就是D这个分类器的负loss的最大值。多看两遍就懂了
不是要求差异,前面推导出div跟obj func相关,由于不能直接求出div,所以先求obj func,obj func的计算是用max,但最终目标是div越小越好,所以再求min
MSE是不能用的 因为根据问题需求来说 MSE不适合 然后这里意思应该是 generator中是尽量产生相似的 所以是mini 而discrimination是尽量分开 所以是max
生成器不断优化使得判别器准确率降低)
(弹幕:简单来说DIV越小 maxV也越小 两个是相关的,生成器就是要得到最小的DIV也就是最小的MaxV)
为什么不用真的JS Divergence,为什么不用别的divergence?->可以,改那个objective function就可以了(详见论文F-GAN)
GAN不好train(救命这也谐音梗)
即使可以minimize JS divergence,GAN也不好train
在大部分例子中P_G和P_data重叠的部分往往非常少。WHY?
1. data本身的特性(P_G和P_data都是呀产生图片,图片其实是高维空间里面的一个低维的manifold)
怎么知道图片是高维空间里面的一个低维manifold?
在高维空间里随便sample一个点,通常没办法构成一个二刺螈人物的头像,所以二刺螈人物的头像的分布在高维空间中其实是非常狭窄的。所以二刺螈人物头像图片的分布,如果按二维空间举例的话,就像是二维空间里的一条线。∴高维空间里随便sample一个点,都不是图片,只有非常少的范围sample出来是图片。
∴P_G和P_data都是low-dimention的manifold->以二维空间为例,就是P_G和P_data都是二维空间里的两条线->除非刚好重合,其他情况下,两条线相交的范围几乎是可以忽略的
2. 我们并不知道P_G和P_data长什么样子,我们对P_G和P_data分布的理解其实来自于sample。->也许P_G和P_data有非常大的overlap的范围,但是我们实际上在了解P_G和P_data,计算他们的divergence的时候,是从P_G里面sample一些点出来,从P_data里面sample一些点出来,如果sample的点不够多、不够密,就算这两个distribution实际上有充电,但是由于sample的点不够多,对discriminator而言,也是没有重叠的。
如图,如果sample出来的蓝色和橙色的点不多,可以轻松把两者分开。
影响是,如果两个distribution是没有重叠的,那么JS divergence算出的结果永远都是log2。
假设蓝色线是P_G,红色线是P_data,两个都是一条直线,中间有很长的距离,去计算P_G和P_data的JS divergence就是log2。如果P_G和P_data蛮接近的,去计算P_G和P_data的JS divergence,算出来的结果也是log2。除非P_G和P_data有重合,算出来JS divergence才是0(不是log2)
但是明明中间的case比左边的好->中间的generator就比左边的generator好,但是从JS divergence上面看不出来->没有办法把左边的generator变成中间的generator。
(弹幕:说白了就是对于分类器而言,总是能把两类图片完全分开,根本混不到一起
是不是可以理解为,你再怎么生成图片,discriminator都觉得你百分百不像,所以generator都没有改进的方向了)
既然是JS divergence的问题,那么换一个衡量方式(换一个divergence)能否解决问题呢?
->wasserstein distance
假设有两个distribution,一个称为P,一个称为Q。wasserstein distance计算的方法是,想象我们在开一台推土机,把P想成是一堆土,把Q想成是我们想把土堆的目的地,推土机把P这边的土移动到Q的平均距离就是wasserstein distance。
如果是更复杂的distribution要计算wasserstein distance就比较困难了。
假设我想要这时的P重新塑造一下形状,让两者比较接近一点,有很多可能的moving plans,可以就近搬迁,也可以舍近求远。
不同的方法有不同的距离,所以这里给的定义就是->最小的距离
先不讨论上述问题怎么计算。先假设我们可以计算wasserstein distance了,有了wasserstein distance我们能做什么?
由上图,中间就比左边的数值要好。
WGAN(用wasserstein distance取代JS divergence的GAN)
那么现在的问题就是怎么计算wasserstein distance。
更正:图中的D(x)改成D(y)
D必须是一个one-lipschitz function(足够平滑的function)
Lipschitz Functions - Department of Mathematics at UTSA
Optimal No-Regret Learning for One-Sided Lipschitz Functions (openreview.net)
Lipschitz Function -- from Wolfram MathWorld
为什么要求足够平滑呢?假设real image和generated image的分布如上图中右下图所示,如果没有这个限制,只看大括号里面的值的话,是要真正的值越大越好,让generated的值越小越好,如果没有任何限制,那么会给real无限大的正值,给generated无限大的负值->这个training根本没办法收敛。
1-lipschitz是要求discriminator不可以变化太剧烈,没办法让real的值非常大,generated的值非常小(因为这样就不平滑了)
如果real和generated的距离很远->wasserstein distance就会很大
如果real和generated的距离很近,即使没有重合,因为有了1-lipschitz的限制,real和generated的值就没办法差很多->算出来的wasserstein distance就会比较小。
(弹幕:平滑曲线保证打分不取得正负无穷,差距不会过大)
怎么确保discriminator一定符合1-lipschitz的限制?
最早的做法是确保network的参数在c和-c之间,超过c就是c,超过-c就是-c->这一方法并不一定能让discriminator变成1-lipschitz function,但是也许就可以让discriminator比较平滑。但是并没有真的去解这个optimization问题。
->gradient penalty?
即使如此,GAN依旧很难train。
GAN有一个本质上困难的地方。generator和discriminator是互相砥砺,共同训练,如果其中一个变差,那么另一个当然也会受到影响。
假设在train discriminator的时候,没有train好,discriminator没办法分辨真的和产生出来的图片的差异,那么generator就失去了可以进步的目标->generator就没办法再进步了。->discriminator也会跟着停下来。->只要其中一个没办法再进步,另一个自然也会停下来
train GAN最难的其实是拿GAN生成文字。WHY?如果要生成文字,需要一个seq2seq的model(有一个decoder,这个decoder会产生一段文字)。这个seq2seq的model就是generator。在transformer里面,是decoder,但是在GAN里面就是generator。
与image的生成有什么不同吗?high-level来看(从算法角度),可能没有太大的不同,因为接下来还是训练一个discriminator,discriminator把这段文字读进去,判断这段文字是真正的文字,还是机器产生出来的文字。而decoder(generator)就是想办法去骗过discriminator->调整generator的参数,想办法让discriminator觉得generator产生出来的东西是真的。
难点在于,如果要用gradient descent去traindecoder,让discriminator output的分数越大越好,发现做不到。
为什么做不到?在计算微分的时候,就是某个参数有变化的时候对目标造成了多大的影响。现在假设我们改变了decoder的参数,对discriminator的输出有什么影响?如果decoder的参数有小小的变化,那么decoder输出的distribution就也有小小的变化,这并不一定会影响最大的token(很大概率不会影响概率排列中最大的那个token是哪个),所以对于discriminator来说,它输出的分数就没有改变(输入都没变当然输出也不会变了)->根本没办法做gradient descend
(弹幕:就是 虽然decoder参数确实变了 但是产生出来的Token跟上次一样 就没啥意义)
老师留的思考题,为什么这里不行,但是CNN里面的maxpooling就可以?
argmax与max的区别_maxargmax-CSDN博客
(弹幕:因为cnn里是求max()这里是求argmax(),一个可导一个不可以)
不过就算不能做gradient descend,也不用担心,可以使用reinforcement learning(什么硬train一发?)
用reinforcement learning(RL)会造成什么问题?RL是以难train而闻名,而GAN也是以难train而闻名(什么双重debuff),所以过于炸裂了。
因此再很长的一段时间,没有人能把generator训练起来去产生文字。通常要先做pre-train。直到ScrachGAN->不用pre-train
(弹幕:注意这里输入到D的是确切的文字,就算这个decoder输出的max概率的数值变化了,对应的文本内容可能不变)
能不能用supervise的方法呢?假设我们有一堆图片,每一张图片都去配一个vector(比如从gaussian distribution中sample出来的vector),接下来当做supervised learning来做。可以(甚至真的有这样的模型)。
但是,如果真的放随机的向量,train的结果会很差,甚至可能train不起来->需要一些特殊的方法(详见论文)
怎么评价现在产生的generator好还是不好。
要评估一个generator的好坏,并没有那么容易。直觉的做法可能还是找人来看……
但是这显然就不够客观(甚至可能论文后面都没有什么accuracy,就放几张图片,表达我这个看起来比之前的都要好这个样子)
所以,有办法设计一个比较客观&自动的方法来衡量一个generator的好坏呢?针对一些特定的任务,是可以设计的~比如:生成二刺螈人物的头像,评估的标准是跑一个动画人物人脸检测的系统,看提供的图片里面,抓到几个动画人物的人脸。比如提供1000张图片里面抓到900个人脸的generator,就显然比抓大300个人脸的generator要好。
如果是更一般的case呢?
有一个方法是跑一个图像分类系统,把GAN产生出来的图片丢到一个图像分类系统里面,看产生什么样的结果。图像分类系统输入是一张图片y,输出是一个概率分布P(c|y)。如果概率分布越集中,就代表现在产生的图片可能越好。
但是光用这个方法是不够的,如果光用这个评估的方法,会被一个叫做mode collapse的问题骗过去。mode collapse是说在train GAN的时候,有的时候train着train着发现generator输出的图片来来回回就那几张。相当于是抓到discriminator的漏洞,就在漏洞周围生成了,反应在图片上就是,生成的某个人脸越来越多。解决方法是在train到mode collapse的时候就把train停下来,把之前train的model拿出来用。
另一个问题比mode collapse更难监测到->mode dropping
图中蓝色的星星都是真实的数据,但是generated的数据可能只占据其中的一部分。乍一看生成的数据的多样性也很OK,但是实际上我们看不到的是真实的数据的分布是更广泛的,不仅仅局限于生成数据所在的位置。
比如图中生成真实人脸的例子,第t个iteration的结果看起来很正常,有男有女有向左看有向右看,但是一旦我们去第t+1个iteration,就很容易看出来,这两个显然是有问题的,前一个肤色偏白,后一个肤色偏黄,这显然已经是政治正确的问题了(×)
如何衡量generator产生的图片的多样性够不够?
过去的某个做法是借助image classify。让generator产生一堆图片(e.g. 1000张图片),把图片都丢到image classify里面,看被判断成哪一个class。每一张图片都会给我们一个distribution。把所有的distribution平均起来,看看平均的distribution长什么样子,如果平均的distribution非常集中,就代表现在的多样性不够。
如果另一个case,不同的图片丢进image classifier产生出来的输出的分布都非常的不同,平均完以后发现,平均完之后的结果非常平坦->也许多样性是足够的。
发现diversity和quality可能是互斥的,之前在说quality的时候,说的是越集中代表quality越好,但是在diversity的时候就是越平坦代表diversity越好(什么?又来trade-off?)
应该注意diversity和quality针对的范围是不同的。对于quality来说,是只看一张图片,把一张图片丢到classifier里面,看分布有没有非常的集中,而diversity看的是一堆图片,看一堆图片image classifier输出的平均。
(弹幕:一个是对单个输出,一个是对一批输出
quality是看每张图片的输出结果是不是很确定的,diversity要求他们确定到不同的类别上)
在二刺螈人物图片生成的时候可能就不适合用inception score了,虽然二刺螈的人脸有不同的瞳色、发色etc,但是说到底都还是人脸,所以做出来的diversity并不会很好。
FID,先把产生出来的二刺螈人物丢到inception network里面,如果一路走到最后,会得到的结果是,人脸。所以我们不拿这个类别。我们使用进入softmax之前的hidden layer的输出,用这个向量代表着张图片。虽然都是人脸,但是因为肤色、发型等等的区别,这个向量还是会有不一样的。
更正,左下角不是FIT是FID
前面的方法并没有完全解决GAN的评价问题。假设训练的generator产生的图片和真实的图片一样,但是着并不是我们想要的(想要数据集里的数据直接sample还快点),我们是希望generator产生新的图片。
第一反应是直接比较生成的数据和真实数据的相似度。但是加入generator学会的是把真实的图片左右翻转呢?这个时候比相似度又比不出来。
到目前为止,我们讲的generator的输入都是一个随机的分布,这个不一定有用。现在想要更进一步,我们想要操控generator的输出,给generator一个condition x,让generator根据x和z来产生y。这样的conditional generation有什么应用呢?比如可以做文字->图片的生成。
文字生成图片实际上是一个supervised learning的问题,所以需要一些labeled的data,所以需要一些图片,并且这些图片要有一些文字的描述(题外话:看来那年辉夜大小姐实火,甚至开始猜测今年老师会用哪部动画)
在text-to-image的任务里面x就是一段文字,那么怎么把一段文字输入给generator呢?怎么做都可以,以前可能会用RNN读过去,得到一个向量,然后丢给generator。现在可能可以丢到transformer的encoder里面去,把encoder的output的向量都拼起来,反正怎么做都行,只要把文字变成向量就行。
比如我们这里输入了一个red eyes,那么最后就会画出来一个红眼睛的角色,每次画出来的都不一样,画出来什么角色取决于sample到什么样的z。sample到不一样的z,画出来的角色就不一样,但是这些角色都是红眼睛的。
现在我们的generator有两个输入,一个是从normal distribution sample出来的z,另一个是x,也就是一段文字,generator会产生一张图片y。我们还需要一个discriminator,如果按照我们过去学过的东西,discriminator就是吃进来一张图片y当做输入,输出一个数值,这个数值代表输入的图片多像真实的图片(是真实的还是生成的)。怎么训练discriminator呢?如果看到真实的图片,就输出1,如果看到生成的图片,就输出0。
但是这样的办法没办法解conditional GAN的问题。因为如果我们这么做,这个discriminator只会看y当做输入的话,generator会产生可以骗过discriminator的非常清晰的图片,但是可能和输入完全没有任何关系。因为对generator来说,只要产生清晰的图片,就可以骗过discriminator了,就没必要管input的文字是什么,反正discriminator又不看文字叙述。
所以在conditional GAN里面要做有点不一样的设计,让discriminator不仅只吃图片y,还要吃condition x,然后产生一个数值,这个数值不仅是看图片好不好,还要看图片和文字的匹配程度。
->discriminator需要成对的数据,看到成对的数据就给高分,否则给低分
不仅可以看一段文字产生图片,也可以看一张图片产生图片
与文字产生图片没有很大的区别。只是把文字的部分用图片替代掉而已。输入一张图片产生一张图片,可以采用supervise的方法。但是从文献上来说,如果只是用supervised learning的方法得不到非常好的结果(非常模糊),这可能是因为同样的输入对应到不一样的输出(类似前面小精灵的例子),所以得到的结果是一个平均的结果(一个模糊的结果)。
如果单纯用GAN的话,很真实,但是想象力有点过于丰富,会产生一些输入没有的东西。比如输出是一个房子,左上角没有其他的东西,但是在输出的地方在屋顶上加了一个。如果要得到更好的结果往往是GAN+supervised learning,同时使用。
其他应用,比如给GAN听一段声音,让GAN产生一张图片。比如听一段狗叫的声音,看能不能输出一个修勾的图片。需要的成对的图片就是声音和图片,这个很简单,可以直接用影像资料。
把GAN用在unsupervised learning上。
训练一个network,输入是x输出是y,需要成对的数据才可以训练。但是我们可能还会遇到一种情况,我们有一堆x有一堆y,但x和y是不成对的(unlabeled),在这种状况下能不能拿这样的数据来训练network呢?
(弹幕:不成对应该是可以的,成对应该是可以加快训练速度,但不一定能提高robust)
怎么使用这些没有标注的数据呢?在HW3和HW5中都有这样的例子(HW里面属于semi-supervised learning),但是多多少少还是需要一些成对的数据。比如在HW3里面,需要先训练出来一个模型,这个模型会帮助提供pseudo labeling,如果开始根本没有多少数据,模型很差,那么就根本没办法产生比较好的pseudo label。包括HW5做back translation,有一个back translation的model才可以做。
但是假设我们一点成对的数据都没有呢?
(什么地狱笑话,说可以找本科生标注orz)
什么情况下没有成对的数据呢?比如image style transfer。加入我们现在要训练一个deep network,要把x domain的图(真人的头像)转成y domain的图(二刺螈人物的头像)。在这个例子里面就没有成对的数据。
现在想要做到的是:输入是x domain图片的分布,输出是y domain图片的分布(之前输入的x都是gaussian distribution里面sample出来的vector)
好像听起来也没有很复杂,可以套用之前GAN的做法。在原来的GAN里面,我们在gaussian sample一个向量,丢到generator里面(其实不一定要从gaussian里面sample,只要使用的distribution是有办法被sample的就OK了)。现在如果输入的是x domain的distribution,只要改成从x domain sample就好了(很简单,只要从真实的人脸照片里面随便挑一张)。把这个照片丢到generator里面,让它产生另一个distribution里面的图片。怎么让它变成y domain的distribution呢?就需要一个discriminator,给这个discriminator看很多y domain的图,这样就可以分辨是不是y domain的图,看到y domain的图就给高分,看到不是y domain的图就给低分。
但是仔细想想,光是套用原来的GAN 训练generator和discriminator好像是不够的。因为现在discriminator要做的是让generator输出一张y domain的图,generator可能真的能学到输出y domain的图,但是它输出的y domain的图不一定要和输入有关系(并没有限制要求generator要做这件事)。可能generator完全就不care输入的是什么,反正只要输出一个二刺螈头像就好了。
->虽然也是二刺螈人物的头像,但是和真实的照片没什么关系,这显然不是我们想要的
?
怎么解决这个问题呢?怎么强化输入和输出的关系呢?回忆一下,我们在conditional GAN的时候也遇到了类似的问题。但是我们这边要用unpaired data学习,也没办法直接套用conditional GAN的思路(因为conditional GAN里面是有成对的数据的,可以使用成对的数据来训练discriminator)。
但是现在我们没有成对的数据来告诉discriminator,什么样的x和y的组合才是对的。
这里的idea是cycle GAN,在cycle GAN里面会训练两个generator。第一个generator的工作是把x domain的图变成y domain的图,第二个generator的工作是看到一张y domain的图,就把它还原回x domain。在训练的时候,增加一个额外的目标,输入一张图片,我们除了要把x domain转成y domain,还要把图片从y domain转回原来的x domain,并且要求还原的图片和原来的图片越接近越好(什么auto-encoder?)。因为这里从x到y,再从y到x,是一个cycle,所以叫做cycle GAN。
但是只知道有关系,怎么知道这个关系是不是我们要的呢?
(实际上是没办法保证的,但是anyway就是很玄学,机器似乎就能够按照这种方式转换,即使不使用cycle GAN,也能得到比较好的转换)
cycle GAN是双向的。
在做cycle GAN的时候可以同时做另一个方向的训练,把y domain的图片拿来生成x domain的图片,再把x domain的图片还原回y domain,同样也要让输入和输出越接近越好。这里搭配的discriminator是要去看中间的像不像真实的人脸的图片。
其他风格转换的GAN
不能说各不相同,只能说一模一样……
更进阶的版本。cycle GAN只能做两种风格间的转换,starGAN可以在多种风格间做转换(什么变色龙?)
同样的技术不仅仅可以用在图像上,也可以用在文字上,也可以做文字风格的转换(但是这个看起来好阴阳怪气啊)。当然这里要用到的就是seq2seq的模型了。
假设这里要做的是把负面的句子转成正面的句子,首先要收集一堆负面的句子和一堆正面的句子。
接下来套用cycle GAN的方法。因为这里要转回去,牵涉到一个问题,怎么算两个句子的相似度。图片的话还比较好理解,图片就是两个向量,两个向量的距离就是相似度。(老师的意思是自己去探索,不过我记得在讲NLP的地方有提到哎,反正文字也是向量啦)
其他应用。
——————————————————————
后面还有一些标注的是选修(我大概看了一眼PPT,似乎和之前说的还是有区别的,有机会补上好了,写到这里已经太长了,我发现写很长的话CSDN的这个编辑器不太好用,会有延迟)
(选修)To Learn More - GAN Basic Theory_哔哩哔哩_bilibili
后面补上的话会把链接放在这里: