视频地址:
①B站:https://www.bilibili.com/video/BV1JE411g7XF?p=73,https://www.bilibili.com/video/BV1JE411g7XF?p=74
②油管:https://www.youtube.com/watch?v=LpyL4nZSuqU,https://www.youtube.com/watch?v=-3LgL3NXLtI
之前的博客地址:
①生成对抗网络-基本概念|深度学习(李宏毅)(二十二)
②生成对抗网络-理论部分|深度学习(李宏毅)(二十三)
③生成对抗网络-改进方法|深度学习(李宏毅)(二十四)
一、Text-to-Image
- 概述
对于原来的GAN来说,只能够输入一个随机的向量,然后得到一个产生的对象,但是我们无法控制产生什么样的对象,而我们期待用GAN来做到这件事,也就是条件生成(Conditional Generation)。以Text-to-Image任务来说,我们希望给Generator输入一段文字,然后让它来产生对应的图片:
对于这样的一个任务,我们可以考虑用监督学习的方法来做,也就是给神经网络输入一段文字,来让其输出一张图片,并且要让图片与目标图片越接近越好,以此来达到条件生成的目的:
然而这样的做法很可能不太好,试想在训练资料中有多张火车的图片,包括正面的火车和侧面的火车,而按照监督学习的方式,模型会希望在输出“train”时输出的图片要和所有的火车的图片尽可能地相似,那么生成的结果可能就是多张图片的平均,效果看起来可能是很模糊的:
而使用Conditional GAN来完成这个任务就可以避免这个问题,对于Conditional GAN的Generator来说,要同时输入两个对象,一个是从先验分布中采样出来的样本,一个是当前的条件,也就是一段文字,然后Generator输出生成的对象:
而对于Discriminator来说,如果它只根据输入的图片是否是真实的来打分显然是不够的,这样会造成Generator在学习的过程中逐渐忽略条件,举例来说,如果Discriminator只关注图片是否真实那么Generator输入“train”时产生一张清晰的猫的图片也能从Discriminator处得到高分。因此,我们同样需要对Discriminator做一些改造,Conditional GAN中Discriminator要有两个输入,一个是条件,另一个是图片,仍然输出一个标量代表得分,不过现在这个得分评估两件事,一个是图片是否真实,另一个是和是否匹配:
这样的Discriminator的训练样本也要有所变化,对于正样本,必须是正确的文字描述和对应的真实的图片,而负样本分两种,分别是文字描述配上不真实的图片和真实的图片随机匹配错误的文字描述:
下图展示了用Conditional GAN做的二次元人物头像生成的效果:
- 算法
Initialize:
初始化的参数和的参数。
Step1 学习:
①从数据库中随机抽样个正样本;
②从一个分布(比如高斯分布或者均匀分布)中采样个噪声样本(noise sample);
③获得生成的数据,其中;
④从数据库中随机抽样个对象;
⑤目标函数记作,通过最大化(梯度上升)来更新,也就是,目标函数为:
Step2 学习:
①同样从一个分布(比如高斯分布或者均匀分布)中采样个噪声样本(noise sample);
②从数据库中采样个条件;
③目标函数同样记作,通过最小化(梯度下降)来更新,也就是,目标函数为:
上面算法中的Step1和Step2也是交替迭代进行的。对于Generator也可以使用梯度上上来极大化这个目标函数。
- Discriminator的两种架构
在Conditional GAN的中Discriminator的架构一般比较常见的是下图中的模式,这里的条件和对象都经过各自的一个network得到各自的embedding,然后将这两个embedding输入到另一个network中得到一个得分来衡量是否真实以及与是否匹配:
另一种不太常见的架构如下图所示,这里的首先通过一个network,这个network输出两个东西,一个是衡量是否真实的得分,另一个则是的embedding,然后这个的embedding以及条件要同时被输入到另一个network中,这个network输出一个得分来衡量与是否匹配:
这里的第二种架构的设计相对于第一种来说应该是更加合理的,对于第一种来说,如果Discriminator得出一个低分,那它无法知道为什么给一个低分,不知道是因为不够真实还是不够匹配,而第二种架构显然就可以明显地看出得分的分布。不过在文献中第一种架构是比较常见的,也没有实验来对比这两种架构设计,在实践中可以作为两种不同的方案以供选择。
- Stack GAN
在产生比较大的图片时,可能会需要Discriminator拥有更大的capacity,并且生成的效果也可能不会太好,Stack GAN是一种Conditional GAN,它的思路是先产生较小的图,然后再用小图去产生大图。它的架构图如下:
感兴趣的话可以参照它的原论文:https://arxiv.org/abs/1612.03242
二、Image-to-Image
还可以使用Conditional GAN来做Image-to-Image的生成,比如将轮廓图转换成真实图,黑白图转换成彩图,白天转换成黑夜等:
以将轮廓图转换成真实图片为例,如果采用监督学习的方法进行Image-to-Image的生成,做法也就是给神经网络输入一张图片,然后输出生成图片来让它和目标图片尽可能地接近:
这样的做法很可能产生和前面Text-to-Image中监督学习方法同样的问题,也就是生成的图片是训练资料的平均,看起来很模糊:
因此我们尝试用Conditional GAN的方法来完成这个任务,对于Generator来说会输入轮廓图和一个随机噪声,输出生成的真实的图片,然后Discriminator会关注Generator的输入和输出,也就是会给Discriminator输入轮廓图和生成图并对其真实度和匹配程度打分,该架构如下:
这样的做法其实也可能会遇到一些问题,比如在下图生成的图片中,虽然很清晰但是左上角多了个多余的烟囱,这可能是因为Discriminator会认为左上角加个烟囱好像也没什么不对:
我们可以尝试为生成的图片加一些约束来使得生成的图片更加地可控,具体的做法就是将监督学习的做法与Conditional GAN结合起来,也就是在训练Generator时不仅要让Discriminator的得分越高越好,也要让生成的图片与目标图片越接近越好:
这样的话就会产生比较好的效果:
在Image-to-Image的论文里它的Discriminator有经过特别的设计,也就是每次并不是将整张图片输入到Discriminator中,而是只输入图片的一部分:
Image-to-Image的原论文链接:https://arxiv.org/abs/1611.07004
三、Conditional GAN的其他应用
- Speech Enhancement
Conditional GAN也可以用来做语音的增强,也就是将有噪声的语音讯号转换成清晰的信号:
对于传统的监督学习方法,训练数据是清晰的和加噪的声音讯号的pair,做法就是向神经网络中输入加噪的声音讯号,然后让输出的声音讯号与清晰的讯号越接近越好,另外要说的是,声音讯号跟图片差不多也是个矩阵,因此可以套用一些图像处理的方法:
而使用Conditional GAN和监督学习相结合的方法就是除了要让Discriminator得分越高越好还让Generator的输出和清晰的讯号尽可能地接近,这是为了避免模型只学习到去噪却会使得讯号的信息改变。而Discriminator会分辨Generator的输入和输出是不是匹配的,也会看Generator的输出是不是clean的:
- Video Generation
使用Conditional GAN来做Video Generation的具体做法就是给Generator输入一段影片,然后让它产生接下来影片中要发生的事情,然后把原来的影片和产生的帧接起来输入到Discriminator中让它来区分最后一帧是不是真实的,同时也要让生成的帧与真实的下一帧越接近越好:
四、无监督条件生成
- 概述
前面介绍的Conditional GAN的方法全部都是有监督的方法,事实上也可以用GAN来做无监督的条件生成,也就是我们能够实现让一些数据从一个domain转换到另外一个domain,比如我们可以尝试让一张真实的图片转换成梵高画作的风格(风格转换),其实这样的任务也是无法收集到有标签的训练数据的:
另外这类无监督条件生成的例子也并非仅限于图像处理,在其他领域也可以应用得到,比如音频性别的转换、文本情感的转换等等:
- 方法的分类
能够做到domain的转换的方法大体上可以分为两类:
- Approach 1: Direct Transformation
这种方法的Generator的输入和输出没有办法差太多,只能够改变图片的颜色、质地、色调一类浅层的东西,对于将真实的图片转换成梵高画作的风格这样的风格转换的任务是比较适合的:
- Approach 2: Projection to Common Space
第二种方法适用于转换前后差距很大的任务,比如把真人的脸转换成动漫人物的脸,这样的任务不是改改颜色一类浅层的特征就可以办到的。这种方法会首先将输入通过一个Encoder变成一个特征向量,然后用这个特征向量解析得到一个转换后的图像:
- 第一种方法
以风格转换的任务为例,现在我们期待要将真实的图片转换成梵高画作的风格,我们有的数据是没有标签的两个domain的大量图片:
虽然没有标注数据,但是我们可以通过Discriminator在两个domain之间建立联系,具体的做法是训练一个Discriminator来分辨输入的图片是否是梵高的画作,而Generator的训练目标就是让它输出的图片被Discriminator认为是梵高的画作:
不过这么做可能会遇到一个问题,也就是由于Generator没有受到任何限制,所以可能会直接无视输入而产生一个与输入完全不同的图片来尝试骗过Discriminator
- 采用浅层网络
为了解决上述问题,第一种方法就是采用较渐层的Generator,只要采用比较浅的Generator就可以直接无视这个问题,因为对于比较简单的Generator来说,它的输出不会对输入改变太多。然而如果Generator是深层的就有可能使输入受到很大的改变从而产生上述问题。
- 采用两个Encoder
第二种解决方案就是让Generator的输入和输出都通过一个训练好的Encoder变成相应的code,在训练的时候要让这两个code尽可能地接近,这样就可以保证Generator不会让输入和输出相差太多:
- CycleGAN
CycleGAN的做法是让Generator(这个Generator记作)输出的图片再经过另一个Generator(这个Generator记作)还原成真实的图,然后让还原的这张图能够和原来的输入越接近越好,这个就叫做cycle consistency,在训练时另一个和Discriminator(这里的Discriminator记作)是固定住的:
那么是怎么训练的呢?事实上这个Generator的功能是输入一张梵高画作风格的图,输出真实风格的图,它的训练和的训练是一样的,对于的输出也要通过还原成梵高画作风格的图,同时要与原来的输入越接近越好,并且的Discriminator(这个Discriminator不妨记作)的作用是判定输入的图片是不是属于真实的风格。CycleGAN的整体架构如下图:
总结一下,也就是说CycleGAN需要训练两个Generator和两个Discriminator,他们之间相互支持训练能够保证Generator的输入和输出不会千差万别并且风格也能得到转换。
CycleGAN论文链接:https://arxiv.org/abs/1703.10593
另外研究表明CycleGAN有一个有趣的现象,比如在下图中,两个Generator会将原图转换成中间风格的图然后再转回来,然而中间的图的房顶上没有黑点,但是经过后又产生了黑点,这说明CycleGAN把原图中的信息隐藏起来了:
参考论文链接:https://arxiv.org/abs/1712.02950
- StarGAN
刚才CycleGAN讨论的是两个domain互相转换的情况,而如果我们期待能够在多个domain之间都能互相转换,那我们就需要在两两个domain之间训练各自的Generator:
而StarGAN这种方法可以只训练一个Generator就完成多个domain之间的互相转换。在StarGAN中,它的Discriminator会输入一张真实的图片或者生成的图片,然后会判定这张图片是real的还是fake的,并且判断这张图片属于哪一个domain:
它的Generator会输入来自某个domain的原始的图片和想要转换到的domain,然后输出转换后的图片,得到的这张图片既要输入给Discriminator来尝试骗过他,也要与原始图片的domain标签结合在一起重新输入给同一个Generator,然后Generator输出的重构的图片要和原始图片尽可能地接近,这个其实类似上面的cycle consistency:
StarGAN的domain的标签其实不是一个数字而是一个0-1向量,下图展示了对人脸特征的转换效果:
下面是对人脸表情的转换效果:
StarGAN论文链接:https://arxiv.org/abs/1711.09020
- 第二种方法
对于二次元人脸转换的问题,我们手里只有无监督的两个domain的数据:
第二种方法是期望能够通过Encoder来提取两个domain的输入图片的特征并且投影成一个隐层向量,然后Decoder能够通过这个隐层的向量来生成一张某个domain的图片。通过这种方式,我们期望能够做到将真实的人脸通过真实图片的Encoder编码成向量,然后使用这个向量和二次元人脸的Decoder来生成转换后的图片:
首先我们想到的做法是按照自编码器的方法分别训练两对Encoder和Decoder,同时也要为每个Decoder输出的图片训练一个Discriminator来鉴别是否属于这个domain:
事实上这种做法显然是有问题的,因为两个自编码器是单独训练的,他们的隐层向量的构建没有任何联系,很有可能隐层向量的每一维度对于两个domain来说表示完全不相同的特征,这可能造成真实人脸通过Encoder得到的隐层向量通过二次元人脸的Decoder后生成的图片跟原始图片完全不相干:
我们有许多不同的方法来解决上述问题:
- 共享参数
我们可以尝试将两个Encoder的最后几层以及Decoder的前几层的参数共享,通过这种方法我们就可以期待隐层向量的每一维度对于两个domain来说都表示相同的特征:
对于Encoder来说,这种共享参数最极端的状况就是Encoder的参数是完全共享的,也就是只使用一个Encoder,不过这个Encoder也要输入一个flag来表明输入的图片来自哪一个domain。
- Domain Discriminator
另一种方法就是加一个Domain Discriminator,这个Discriminator输入Encoder的隐层向量并且判断这个隐层向量来自哪一个domain,如果现在Encoder产生的隐层向量可以骗过Discriminator的话,那我们就可以期待隐层向量的每一维度对于两个domain来说都表示相同的特征:
- Cycle Consistency
这里也可以应用Cycle Consistency的方法,具体也就是将原始图片输入Encoder后得到隐层向量,然后用二次元人脸的Decoder把这个向量解析成图片,然后这个图片再通过二次元人脸的Encoder得到隐层向量,这个隐层向量再通过真实人脸的Decoder解析成图片,然后使得这个图片与原始图片越接近越好:
这个技术用在ComboGAN里,论文链接:https://arxiv.org/abs/1712.06909
- Semantic Consistency
Semantic Consistency的方法与Cycle Consistency不同的是,Cycle Consistency计算的是图片与图片像素级别的相似度,而Semantic Consistency计算的是两个Encoder获得的隐层向量之间的相似度:
这种方法的好处就是可以考虑Semantic上的相似度而不是Cycle Consistency只考虑表象上的相似度。
这个技术用在XGAN里,论文链接:https://arxiv.org/abs/1711.05139
- Voice Conversion
对于声音转换这个任务,在过去的方法中就是收集两个人的声音,让他们说一些一样的话然后按照监督学习的方法去实现:
而如果使用GAN的方法,就可以让两个人说不一样的话甚至不同的语言,然后用上述无监督的方法来完成这个任务:
这方面的内容就不再赘述。