GAN系列:李宏毅老师GAN课程P2+P3——Conditional GAN

看这个课程的时候,有弹幕说P1后直接看P4,然后再看P2和P3,这个顺序确实更好一点。这部分主题是条件GAN(Conditional GAN),所谓的条件就是我们对要生成的图片的要求和限制。之前生成器的输入是一个随机噪声向量,生成器根据向量产生任意的图像。在实际应用中,产生的图像一般是有特定限制的,比如要产生火车的图像,人脸的图像等。因此Conditional GAN更有实用价值。Conditional GAN中,整个GAN结构有所改变。生成器的输入不再只有一个低维向量,增加了条件(condition),这个条件的形式有多种,可以是文本(Text-to-Image),可以是图像(Image-to-Image)。这样生成器在条件的限制下产生特定的图像(限制就指导了图像生成的方向,之前的GAN只能通过判别器来指导图像生成的真假)。产生的图像若是直接输入到判别器中,判别器无任何改变,仍然只会判断真假,无法判断是否符合条件。因此这个限制条件也要和图像一起输入到判别器中,这样生成器的输入和输出均被关联到判别器中(判别器中会不会隐藏着生成器的结构呢),此时判别器就拥有了判断真假和判断条件两种作用。

在这里要明确一下,普通的GAN中,训练集的信息就只有真实图像的矩阵,图像没有任何标签,这样直接生成图像就是无监督学习(unsupervised learning);但Conditional GAN训练集的图像是有标签的,就是所谓的条件condition,那就相当于监督学习(supervised learning)了。

举例1. Text-to-Image:

输入文本信息,产生相应的图像和一般的图像分类问题刚好反过来,如果也用传统的监督学习方法来训练网络(如下图所示:训练目标就是生成的图片和数据集中图片尽可能接近——让网络学习到狗这个图像的结构,然后生成各种各样的狗)。这时会存在的问题是,普通的监督学习会产生各种狗的图像叠加后的平均图像(可以理解为网络学习到的是一个平均结果,而图像一平均就会产生非常模糊的结果)。GAN不同的是,判别器会区分真假图像,这些模糊的结果其实就是假图像,所以判别器的存在会导致这些图像被排除掉,指导生成器产生真正的图像,而不是单纯地对各种各样的图像取平均。

如下图所示:因为有各种火车的图像,都对应同样的train文本,导致网络学习时学到了各种火车图像的平均结果(输入是一样的,都是train,然而对应的标签却不同,是各种各样的火车图片,所以学习的结果就是一个模糊的标签。就相当于图片分类的网络,输入同一张图片,然而一会标签为1,一会标签为0,网络肯定学习到的是混乱的结果),根本就不能算作一张火车的图片。

和普通的GAN相比,conditional GAN的判别器输入加上了条件,那么输出所代表的意义也改变了:图片正确(真实)且和文字匹配才给高分,图片很假以及图片真实但不和文字(条件)匹配也是低分,如下图所示:

判别器所输出的标量包含了两个判别,其优化算法如下图所示:和普通的GAN相比,首先从数据集中采样得到的不仅是图像向量,而是图像向量和其文本标签组成的pairs(c, x),c就是condition也就是文本,x和之前相同;上面采样得到的标签还要和随机噪声向量组成pairs(c, z),输入到生成器中产生图像;产生的图像和其对应的标签(就是用来产生这个图像的标签)组成pairs(c, x~)输入到判别器中;由于判别器的目的有所改变(通过判别真假和条件与图像是否匹配),用来优化判别器的目标函数V增加了一项:log(1-D(c^{i},\hat{x}^{i})),注意这里的\hat{x}^{i}是从数据集中重新采样的真实图像向量(这是因为函数V里增加的那项而增加的步骤,普通GAN中没有此步),这一项里的条件c和图像向量x都是正确的,但是是不匹配的(因为条件标签是第一步时采样的,图像向量是此步之前刚刚采样的,采样的随机性保证了它们的不匹配),所以这对pair进入判别器的得分是越低越好,log函数就是越高越好,和前面那一项形式一样(其实只要是被视为错误的pairs都会是判别器给分越低越好,所以它们在目标函数中的形式就是一样的。如果又给判别器增加一项任务,a情况下判别器要给高分,那么a这种pairs和现在的既是真实图像又是条件匹配的那组pairs都是得分越高越好,所以在目标函数中的形式也是一样的:logD(...))。

判别器通过k次迭代训练后,开始训练生成器,和之前不同的是:重新从数据集中采样条件标签,和随机噪声向量组成pairs用来训练。下图里最后一步的参数更新估计写错了,因为是maximize所以应该是加上梯度吧。

在上述的Conditional GAN中,若是判别器给了一组pair低分,有两种可能性:图像是假的;图像和条件不匹配。这种打分方式作为指标用来指导(优化)这个对抗网络可能会有一定的模糊性:生成了假图像和生成了不匹配的pairs这两种情况的界限并不明显。因此在此基础上可进行一定的改进:判别器输出两个结果,图片正确与否和文字匹配与否分别打分。这导致判别器的结构有所改变,如下图所示:

上面的结构是只输出一个标量的判别器结构,和上一篇代码阅读里的判别器网络结构似乎不太一样,因为那个GAN中判别器的输入就是图像向量,而Conditional GAN的输入有图像(object)有文字(condition),是两种不同形式的信息,首先要各自通过一个network统一到同一种形式(这里的network应该就是编码器吧)。有共同的编码形式后,组成pairs输入判别器主体的神经网络中。

下面的结构是分别输出图像真假得分和条件匹配得分的判别器结构。图像通过普通的GAN判别器网络后输出真假的得分,然后结合条件c一起输入到一个network中得到标签匹配得分。注意图像object接的网络是绿色的,说明和上面那种结构中的判别器主体网络接近(卷积神经网络,而且是降维的,实现分类功能),而条件condition接的网络是蓝色的,说明和上面那种结构中紧挨着两个输入的网络是近似的,那就应该是个编码器类似的,把condition编码到一定维度,刚好图像object从绿色神经网络到这个蓝色编码器的箭头表示的应该也是一定维度的向量(不知道这个箭头表示的就是最后那个判别器得分还是判别器中间某一层的结果,如果是全连接层的结果,也是向量的形式),这两个向量都经过编码到同样维度后判断匹配度(如果是编码为只含0和1的向量,计算匹配度很简单,向量间进行异或就可以吧)。

另一种改进的结构:stack-GAN,看到stack就知道肯定是叠加的结构了。stack-GAN整体上就相当于两个GAN堆叠,如下图所示,若想生成256*256大小的图片,由于维度太高可能不好训练,可以先第一个GAN生成64*64大小的图片,第二个GAN再生成256*256的(是不是相当于网络深度增加了,但感觉这种结构块的叠加比直接增加网络深度效果好,比如VGG,resnet什么的都是结构块叠加吧,又开始瞎想了)。这种stack-GAN应该会生成质量更高的图片,因为中间也有判别器啊,相当于中级监督吧。

这个结构还挺复杂的,因为只是在课程里听了一点点,没有看论文,结构就没仔细研究,不知道上面的猜想都对不对。

举例2. Image-to-Image:

无知限制了我的想象力,在看到下图前我都想不出来几个Image-to-Image的例子。

和前面Text-to-Image一样,普通的supervised learning会产生模糊的图片,因此通常使用Conditional GAN的方法。Conditional GAN的训练集中每个样本对应着两个图像,一个作为输入,一个作为标签。Conditional GAN的目标是能够把符合输入形式的图像转变成标签形式的图像。如下图所示,传统监督学习以图像相近为目标学到的结果是模糊的,单纯的GAN(指的是非监督的)虽然避免了假图像(模糊)的结果,但也产生了奇怪的不应存在的部分。对应于前面的Text-to-Image,那里的标签(条件)是文字,所以判别器除了判断真假以外还判断文字和图片的匹配(都转化为了向量);在Image-to-Image中,标签(条件)也是图像,和输入的形式是一样的,判别器要判断图像和图像间的匹配很简单(都不需要靠编码器转化形式了),常见的卷积网络提取特征应该就可以(其实就是产生模糊图像的传统监督学习方法)。判别器原本的判别真假功能,再加上对图像间是否close的判别,就可以实现下图中最后的那个效果,没有奇奇怪怪的部分啦。所以一方面GAN有创造性(是个生成模型嘛),一方面又有不可控性(无监督学习),而Conditional GAN其实就是把GAN和普通监督学习结合起来了(所谓的结合其实就体现在目标函数的改变上吧),那么Conditional GAN算是半监督学习?一个Conditional GAN其实完成的是两个任务:判断真假和生成匹配的图片,判断真假的部分是没有标签的无监督学习,生成匹配的图片是有标签的监督学习。

一种改进的GAN结构:patch-GAN,把一整张图像切分成多个patch,对每个patch进行生成和判别,就相当于把图片变小了。不过patch_size的选择应该仔细,每个patch太小的话,损失太多context,无论是生成还是判别都会结果变差吧;如果patch很大,也是高维向量,网络依然不好训练。

在Text-to-Image中提到过stack-GAN,感觉patch-GAN的目的和它有点相近又有点相反:把图片切成patch,相当于把一张大图变成了多个patch串联,而stack-GAN是两个GAN串联。就好像水流通过水管,stack是把水管变细但变长了(变细为了省材料/减少参数,变长为了扩大容量/加强网络的表示能力),patch是直接把水流变细了/图片变成patch了。一个优化工具的结构,一个优化资源的结构。我为什么有这么多乱七八糟的想法。

那么Conditional GAN能否做到unsupervised learning呢,这就是P3中的Unsupervised Conditional GAN。

举例:把真实的风景图片转换到梵高的作图风格,可以分别收集到真实的风景图片和梵高的作品图片,但是两者间并没有联系,图片内容完全不同(所以就不能直接用做标签吧)。Unsupervised Conditional GAN到底如何实现呢,它怎么利用现有的梵高的作品图片,然后让生成器知道要生成梵高这种风格的图片呢?

unsupervised conditional generation:

主要有两大类方法:Direct Transformation和Projection to Common Space,应用场景不同。

1. Direct Transformation:

这种方法就适用前面的转换图片风格的情景,保留图片语义内容,仅仅转换其展示出来的风格,对生成图片来讲是比较小比较容易的变动。Direct Transformation是直接从数据集图片(Domain X)生成目标图片(Domain Y),能实现的映射关系肯定相对简单,因此适用于图片小改的情景,比如转换颜色,风格等。

生成器如何训练呢:目前我们有Domain X和Domain Y的图片,但是由于无法根据图片内容建立两个Domain间图片的一对一关联(也就是无法组成Conditional GAN中的pairs),那生成器怎么知道要生成Domain Y的图片呢?——借助一个Domain Y的判别器。

Domain Y的判别器D_{Y}的输入是Domain Y的图片,因此训练后可以判别一张图片是否属于Domain Y。D_{Y}相当于一个图片分类器,只不过一般的分类器的标签是更具体和局部的,比如图片中包含猫,房子等,这些语义内容的component比较复杂,要通过比较深的卷积网络提取高级的特征才能分类;但是判别器D_{Y}的标签是是否属于梵高风格(Domain Y),是一个大标签(针对于图像全局),风格这种特征一般就是包含线条,纹理等比较简单的特征,一个简单的卷积网络应该就能实现。

那么生成器G_{X\rightarrow Y}的目标就是产生能骗过该判别器D_{Y}的图片,也就是Domain Y风格的图片,但会产生一个问题:因为Domain X和Domain Y的图片语义内容不一样,生成器为了骗过判别器可能学到的是Domain Y的图片内容和风格,如下图所示,产生的图片内容和Domain X无关系。

因此,生成器G_{X\rightarrow Y}不仅要学会Domain Y的风格,还要保证产生的图片内容不变。一种途径是可以直接忽视这个问题,使用一个比较简单的生成器网络(因为shallow的network structure会一定程度上保证生成器的输入和输出不会变化太多:毕竟不同的图像语义内容像素值上真的差距太大);但如果Generator network很深的话,输入输出真的会差距很大,就需要添加其他的限制了。一种做法如下:

用一个预训练好的网络比如VGG用作Encoder Network(这种pre-trained network应该都是取到VGG的一部分吧,应该是全卷积网络吧),G_{X\rightarrow Y}的输入输出都通过该网络得到一个embedded的结果。接下来G_{X\rightarrow Y}在训练中,一方面要生成Domain Y风格的图片骗过D_{Y},一方面又要保证Encoder Network的两个embedded输出间的差距要尽可能小。因为G_{X\rightarrow Y}的输入和输出都经过同一个Encoder Network得到embedded结果,只要保证两者的embedded结果接近,就能保证它们本身也接近,生成的图片就不会跑偏了。G_{X\rightarrow Y}的这两个功能应该一起体现在目标函数中吧:在优化Generator Network时,目标函数中和判别器D_{Y}相关的那一项应该是D_{Y}越大越好(越大越接近Domain Y的风格),和Encoder Network相关的那一项应该是两个embedded结果间的差异/范数越小越好(越小越能保证图像的语义内容不变)。

另一种方法就是cycle GAN:除了要训练一个Domain X到Domain Y的生成器G_{X\rightarrow Y},还要训练一个Domain Y到Domain X的生成器G_{Y\rightarrow X}G_{Y\rightarrow X}的目的是把生成器G_{X\rightarrow Y}产生的Domain Y的图再转回原图(就是Domain X里的样子),训练方法就是让G_{Y\rightarrow X}的输出和原图越接近越好。既然G_{Y\rightarrow X}要从中间的输出中恢复出原图,中间的输出就不能再一昧地为了骗过判别器D_{Y}而产生Domain Y语义的图片了,这个中间结果必须较好地保留原图(Domain X)的语义信息(刚好较深的卷积网络才能提取出较好的语义信息,cycle GAN就是针对生成器G_{X\rightarrow Y}比较深的情况的),如下图所示:

仅根据判别器D_{Y}的输出进行训练的生成器G_{X\rightarrow Y},产生了和输入不匹配的梵高自画像(语义和风格都属于Domain Y),缺少生成器G_{Y\rightarrow X}重建图像所需要的语义信息。结合生成器G_{Y\rightarrow X}的输出和判别器D_{Y}的输出进行训练的生成器G_{X\rightarrow Y}可以产生我们期望的图像(cycle consistency)。

cycle GAN的进阶版是双向的cycle GAN:将Domain Y的图片输入生成器G_{Y\rightarrow X},转成Domain X的风格,转化效果由一个Domain X的判别器D_{X}判定,将该转换后图像输入生成器G_{X\rightarrow Y}重建回Domain Y,和上面的操作一模一样,就是新增了一个domain相反的路径。此时共有两个生成器G_{X\rightarrow Y}G_{Y\rightarrow X};两个判别器D_{X}D_{Y},共同训练,如下图所示。这种双向结构充分利用了两个domain的数据,效果应该加强了,老师的讲义里有一个转换头发颜色的实例,打算看看那个代码,因为训练那部分比较复杂。

cycle GAN目前存在一个缺陷:the information is hidden。在下图的例子中,想要把一张航拍照片转化成中间风格的图片,发现红色框内的黑点在转换后不见了,但是在重建中又出现了,说明这些黑点的信息其实没有在转换后消失,只是被隐藏了而已(像素值差异小到人眼不可见,相当于缩水了),说明此时生成器G_{X\rightarrow Y}会在训练中学会把不符合Domain Y的部分隐藏起来的方法企图骗过判别器D_{Y},重建生成器G_{Y\rightarrow X}也会在训练中学会从这些缩水的隐藏部分中重建信息,而不是从生成的Domain Y图像中重建信息(大概是因为隐藏的部分是小的值,小的值更容易训练,所以网络偏向这个方向学习?就像ResNet效果更好一样)。这些缩水的部分就相当于“小抄”/噪声,只要有了“小抄”,就能重建图像。因此cycle consistency就失去了意义,通过它并没有真正地改善生成器G_{X\rightarrow Y}的生成能力(光学会看小抄了)。只要能携带“小抄”,生成器G_{X\rightarrow Y}无论生成怎样的图片,G_{Y\rightarrow X}都能通过“小抄”重建原图,那就无法通过G_{Y\rightarrow X}来控制生成器G_{X\rightarrow Y}产生的图片内容了,又可能会产生一张梵高自画像。虽然这次的自画像和之前那种情况下产生的略有差异(加上了“小抄”),但人眼看不出来。

以上的GAN都是实现两个Domain间的转换,而starGAN是用来在多个Domain间互相转换图像的。理论上若用上面的结构,4个Domain就需要12个生成器,而starGAN只需要一个Generator,对比如下图:

starGAN完整结构如下图:

 结合两个实例理解一下,它们的背景:

共有5*5个Domain:图中黄色和绿色的label中都有5个元素/特征,这10个特征任意组合有25种,每一种组合就是一个Domain。CelebA和RaFD是把类似的元素/特征整理到了一个向量中而已,CelebA里都是外貌特征,RaFD里都是表情。每张图片的标签都是one-hot编码形式,一共有12位,黄色的5位表示CelebA中的5个特征的值,绿色的5位就是RaFD中的5个特征的值,最后两位表示图片是否包含Celeb和RaFD中的特征。如果没有CelebA和RaFD这样的归类,一张图片的标签长度就会是25位,表示25个Domain。

实例1:

图(a):用CelebA这个Domain的数据先来训练判别器:输入Real image,判别器会判断为真,然后再判断Domain,所以会有两个输出;输入Fake image,判别器判断为假后就结束了,只有一个输出。估计在优化判别器的目标函数中,判断为真时,会根据Domain的标签计算判别器误差(看判别器判定的标签中有几位0-1编码和正确标签的编码是不一样的,不一样的位数越少loss也越少);判断为假时,可能loss直接设为一个比较高的值(没看论文瞎猜的)。

图(b),(c),(d):训练生成器,输入图片,目标Domain(紫色框的Mask vector)和该Domain中想要的特性(上图中是想要黑发年轻男性)。生成的Fake image同时输入判别器和生成器:判别器给评分,生成器重建。根据判别器的输出和重建结果和原图的差异优化生成器:产生的图片既要真实而且具有我们想要的特性,还必须和原图语义相同。

实例2:

以上都是从一个Domain直接转换到另一个Domain,也就是direct transformation;当要生成的Domain和原图差太多,就要用另一种方法了。

2. Pojection to Common Space:

两个Domain间差距很大的例子:从真实人脸生成动画脸,这种情况下图像浅层的语义已经变了。这种方法的整体思路是通过训练找到两个Domain间的common space,应该就是一种低维的编码。编码器和解码器其实就是生成器和判别器,生成器不再直接生成图片(从图片直接生成一个浅层语义完全不同的图片太难了),而是生成Common Space中的编码,判别器也不是给图片打分,而是给编码打分。真实的人脸和动画脸的common space其实可以理解为是共同点——脸部特征,因此这个编码就相当于低维的特征向量,如下图所示:

该方法的完整结构如下图所示:有两个Encoder:EN_{X}EN_{Y};两个Decoder:DE_{X}DE_{Y};Domain X的图像输入时,用EN_{X}对其编码,Domain Y的图像输入时,用EN_{Y}对其编码。这个编码称为latent vector,将它输入到DE_{X}中,将得到Domain X的图片,输入DE_{Y}中将得到Domain Y的图片。

我们期待能实现:Domain X的图片输入EN_{X}中得到latent representation(latent vector),这个vector中的每一个元素都代表着面部的某个特征,然后将vector输入DE_{Y}中就能解码出Domain Y的图片。

困难的一点是:Domain X和Domain Y的图片是分开的,没有建立关联。因此训练它们仍需要借助重建的过程:

这时DE_{X}DE_{Y}的输出会略微模糊(因为reconstruction error是两幅图像像素间差值的和,隐含着平均的概念)。继续改进的话,可以在输出图像的地方再加判别器,能够让DE_{X}产生的image更像真实的Domain X图片,如下图。

上面的训练方法是在单独训练两个支路:EN_{X}DE_{X}D_{X}是一条,EN_{Y}DE_{Y}D_{Y}是另一条。尽管EN_{X}EN_{Y}都把图像编码到了同样的维度,DE_{X}DE_{Y}也都能从该维度的向量中还原图像。但两个支路中latent vector中每个元素所代表的意义都是不一样的(X支路中可能第一个元素表示眼睛大小,Y支路中第一个元素可能表示的是嘴巴大小),这种不匹配导致Domain X的图像通过EN_{X}DE_{Y}后并不能生成好的Domain Y的图像,其实就是中间的vector没有实现common。

解决方法是:把EN_{X}EN_{Y}DE_{X}DE_{Y}tie到一起。每个Encoder和Decoder其实都是网络,网络功能由参数控制,保证参数一致则网络实现的功能就一样了。两个编码器和Common Space的联系主要在它们的最后几层(靠近输出部分),因此令两个编码器的最后几层共享参数,前面几层的参数可以不一样。Decoder是前面的几层与Common Space联系密切,因此这几层共享参数,后面的几层则不共用。最极端的情况下,EN_{X}EN_{Y}可以是完全一样的,输入图片是只要再搭配着一个Domain的标签就可以。

接下来可以针对中间的latent vector继续改进:增加一个Domain Discriminator判别latent vector是来自于哪一个Domain。那么在训练中,EN_{X}EN_{Y}就会努力骗过该判别器,让它无法从vector中判断出Domain,就会使得EN_{X}EN_{Y}产生的编码越来越像,也就是vector中同一个位置的元素表示相同的意义。

这部分好长好难啊。回学校一星期了,学习状态还蛮好的,就是太久不学习现在感觉好累。

猜你喜欢

转载自blog.csdn.net/lynlindasy/article/details/87691675
GAN
今日推荐