CycleGAN 无依赖风格转换 - 生成模型 GAN 网络 | 莫烦Python

CycleGAN 无依赖风格转换

作者: 莫烦 编辑: 莫烦 2021-04-15

学习资料:

怎么了

CycleGAN 是一种奇妙的 GAN,它与前面的 Pix2PixCCGAN 都不一样。我非常喜欢 CycleGAN 的模式,也为它能够解决的问题感到震撼。 前阵子我在抖音的时候,发现了它这种视频,那时候我还不懂GAN,这种视频效果让我百思不得其解。 用真人的图像,变成卡通图像。

tiktok

如果你用监督学习来做吧,你哪来那么多带着卡通图像的真人图像,让一个画家画断手也弄不来一一对应的人脸卡通画呀。那这种人脸卡通化的技术又是怎么搞的呢? 后来我深入了GAN,了解到了 CycleGAN 这种技术后,这个问题才迎刃而解。即使抖音中的卡通化不是按照 CycleGAN 来搞的, 那么肯定还是借鉴了 CycleGAN 的思路来做的。

所以一句话来描述 CycleGAN 做的事情:训练风格转换时,不要求具备一一对应的风格转换数据,也能学习到风格转换的规律。 我详细展开一下,通常在训练风格转换时,都是拿着原图 X 和 原图对应的转换后的 Y 来作为一对监督数据,把 X 转换成 Y,但是很多时候, 我们并不能方便地获取到转换后的风格图像(毕竟我们不愿看到很多画家画断手)。CycleGAN 就提供了另一种思路,没有一一对应的 XY 是吗?没问题, 你多给我点 X 和 多给我点 Y,他们对不对应没关系,我照样可以训练风格迁移。

paper res paper res2 paper res2

上图就是论文中经典的生成结果,比如普通马变斑马,简笔画填充,图片虚化等。 更多有关于GAN做图片生成图片的技术,我在这个短视频中做了具体介绍了。 这次教学中,为了减少训练时间,统一横向对比其他教学中的模型, 我还是用 mnist 数据做了这样一个实验,让 6 和 9 两个数字进行风格迁移,就类似马和斑马的风格迁移一样。

results

怎么训练

为了实现无监督形式的风格迁移,我们第一个反应应该是下面这种方式。构建两套GAN的 pic2pic 系统,分别训练一个 猫狗生成器狗猫生成器。 用这两种生成器进行猫狗两者的互相风格迁移。

my struct trail

CycleGAN 用了另一种非常优雅的方式来结合这两种生成器,在不浪费计算资源的前提下,复用两种生成器, 在训练猫狗生成器的时候,也同时能训练一把狗猫生成器。一石二鸟,一举两得。这就是 CycleGAN 的魔法。

paper structure

上图是paper中的图示,看起来是有那么一点复杂,我们化简一下,看看化简之后的结构图和我画的上一张 猫狗生成器狗猫生成器 有啥差别。

my structure

和我画的上一张互相独立的两个生成器对比,这张图片中展示的则是相互依赖的生成器。为什么要做成依赖的?因为与其单独训练两个生成器, 不如利用这两者之间的互相转换关系,多利用一层有价值的关系信息,将他们捆绑起来,促进生成器的效用。所以不管是在训练猫生狗还是在训练狗生猫时, 都用到了 猫狗生成器狗猫生成器。 而且是以一种 AutoEncoder 的方式训练这两种生成器, 让他们具备抽取有用信息的能力。

CycleGAN AutoEncoder
压缩/Encode/迁移 生成风格化的原图 生成对原图的理解
解压/Decode/重构 从风格图重构原图 从理解中重构原图

CycleGAN 模型要想办法抽离原图的关键信息,构造由关键信息组成的风格图片;再尝试从风格化的图片中找到原图的信息,并重构回原图片。 所以至始至终,在这条转变的线路中流淌着的就是被模型提取出来的原图关键信息。 对比 原图 和 重构图 之间的差别,我们就实现了 AutoEncoder 的计算误差的基础功能。但问题来了,我们怎么保证风格化的图就是我们想要的风格呢? 在 AutoEncoder 中,不用管压缩后的理解是什么,因为那不重要,我们不会使用到它。但是 CycleGAN 中,风格化图片是有用的, 我们怎么确保它能是一张风格化图片?这就是 CycleGAN 和 AutoEncoder 最大的差别了。 我们可以借鉴 GAN 的 Discriminator 来控制中间这个生成的风格化图片,让它趋近真实图片。

所以整个 CycleGAN 的训练中,有两种误差:

  1. Discriminator 的真假判断误差,确保迁移后/风格化后的图长得像一张真实的图片;
  2. 和 AutoEncoder 类似的重构误差 (cycle-consistency loss),保证 GF 两个模型理解原图信息,能找到原图关键信息。

对应的误差计算公式分别是:

adversarial loss cycle consistency loss

将他们两个误差整合起来就变成了这样:

full loss

接下来我们来撸一撸代码。

秀代码

如果想直接看全部代码, 请点击这里去往我的Github. 我在这里说一下代码当中的重点部分。

在训练 CycleGAN 的时候,我们已经提到,为了训练效率,它会同时利用两种不同风格的数据,在一个 cycle 中训练两种数据。所以他的 train() 方法稍微有点不同。 在我用 mnist 做 demo 的这个例子中,我会让 CycleGAN 转换 6 和 9,让第一个生成器 G 从 6 生成 9,让第二个生成器 F 从 9 生成 6。 所以在每次 gan.step() 的时候,我都把 6 和 9 同时传给它。

在 CycleGAN 内部,如上面画的结构图,它其实同时包含了两种风格变换器,也就是两个生成器,我在代码中用:

  • self.g12:从风格1生成风格2
  • self.g21:从风格2生成风格1

同时 CycleGAN 也会有两个判别器:

  • self.d12:检验 self.g12 生成效果
  • self.d21:检验 self.g21 生成效果

而这里面的 self._get_generatorself._get_discriminator 基本上都是复用了 CCGAN 定义的结构。 Generator 是一个 pic2pic 的结构,Discriminator 是一个典型二分类的结构。

CycleGAN 代码最最重点的部分是在它如何训练的部分,前面这些都是结构性的铺垫。在训练 Discriminator 的时候还好说,和普通的 GAN 没太多差别, 只是需要同时计算两个 Discriminator 的 loss 而已。

而 Generator 的训练就相对比较复杂一点,中间包含了计算:

  1. Cycle consistency loss
  2. Discriminator 的 Adversarial loss
  3. Identity loss

前面两个loss我们都介绍过,那这个 Identity loss 又是个什么鬼?在原论文中,它觉得在真实照片和画作互转的时候,是和现实生活中 马与斑马 互转这种情况不同的。 画作中,背景色等很多环境信息也要进行转移,如果不加这个 Identity Loss,这些信息的转移会存在偏差。所以这是一个选着性添加的loss。

这里面我单独将 cycle()identity() 拿出来写,这样逻辑会稍微清晰点,在计算 cycle() 的时候,简化起来是这样一个逻辑: g21(g12(img1))g12(g21(img2)),将原图变出去,再变回来。在计算一下变回来后还像不像原图。 而 identity() 就是在检测风格化后的画作和原画作差距多少?它需要尽量减少迁移后的差距,保留原画作主色调。

最后train出来,效果就如下图啦,可以看出,6具有的一些属性,比如高矮胖瘦都成功的传递到9身上了。反之9也传递到6身上了。这就是 CycleGAN 来回迁移的独特魅力, 同时也意味着,g12g21 保持着内在的联系,他们是镜像的同属性风格变换器。

res

问题

其实 CycleGAN 并不完美,后作还有很多提升它效果的论文,我用这套框架在 CelebA 数据集上试了试,实现男女性别转换。虽然转换成功了,但是普遍还是有点糊。 特别是男转女的时候。我猜可能是男生头发本来就少,还要生成变化莫测的女生头发,男转女的生成器负担太大了。反之,女转男稍稍好一点。哈哈哈,是不是女汉子要比娘炮更好当?? 这个实验的代码在这CelebA CycleGAN

celebA

除了训练难度,对数据分布的识别可能也不太好,比如论文中一张经典图片。要做马转斑马,但是马上的人也被当成马的一部分,被转成斑马了, 所以常听到的人马兽人马兽是真的吗?

human zebra

总结

CycleGAN 是我见识到的 GAN 应用比较有趣的其中一个,不同于经典 GAN 的训练,它利用了 Cycle 循环的训练方法,同时训练 来回变身 大法, 让基佬,Gay老不用花钱整容也可以翻身了。


降低知识传递的门槛

莫烦很常从互联网上学习知识,开源分享的人是我学习的榜样。 他们的行为也改变了我对教育的态度: 降低知识传递的门槛免费 奉献我的所学正是受这种态度的影响。 通过 【赞助莫烦】 能让我感到认同,我也更有理由坚持下去。