StyleGAN 混合多风格
学习资料:
- 我制作的GAN简介短片
- 我制作的让GAN生成你想要的
- 论文 A Style-Based Generator Architecture for Generative Adversarial Networks
- 本节代码
- 代码有我自己定义的依赖utils.py, visual.py, mnist_ds.py
怎么了¶
StyleGAN 最大的不同是它的生成器有着完全不同的结构,作者对于 GAN 有了另一种定义。 以前的 GAN 我们都认为是将一个随机想法
,变成一张图片。而 StyleGAN 认为另一种模式可能更棒, 那就是我有一个随机想法
,这个想法不会突然变成一张图片,而是通过这个想法
其改动原本的一张图片,将图片中的鼻子改大一点,眼镜改小一点。 这里的想法
控制的是原照片当中的风格
Style,所以它也被称为 StyleGAN。
用一句话来概述 StyleGAN:我要做一个黏土花瓶,黏土已经有了,我的想法只负责把黏土捏成不同的样子,而不是创造黏土。 以前的算法都是去创造黏土花瓶,而 StyleGAN 不创造黏土,它只想着怎么改黏土的样子。
所以如果沿着这个思路,StyleGAN 就是一种控制风格的 GAN,而不是单纯的生成模型。所以我们可以拿着它做下面这种风格混合实验, 这也是我们今天要用 mnist 做的练习了。风格可以分层,底层/中层/高层等。下面的例子中顶上黑色的数字控制着一层风格,右边黑色的控制这另一层风格。 将这两层糅合起来,就变成了中间白色的数字了。
在数字的例子中,我也说不上来这到底是怎么个糅合的,但是如果你看看论文中的图,它用人脸来糅合,这效果就更明显了。 左边不同层级的风格控制,生成出不一样的融合脸。
想看看它如何动态变化的?没问题,给你看。
怎么训练¶
这个训练过程真的像上面的那个黏土捏瓶子图。如果说这个瓶子是一个竖着放的CNN,GAN的随机噪点控制的就是你捏瓶子的手,哪里力道要轻点,哪里力道要重点。 随机噪点只管控制这种风格
力道,最后这瓶子也就被捏成了特别的样子。天哪,我觉得我的比喻太生动,太形象了。
在把paper中的网络结构图简化一下,先给你看看我简化的样子,然后再给你放一张paper里的样子,你做一下对比,应该能很容易理解具体的结构属性。 在下面的图中,我们有一个 Mapping Net
用来将随机想法
转变成 CNN 不同层的力道
,如果没有 Mapping Net
提供的力道
,那么这个 CNN 每次都会生成一样的图片。 只要控制不同的力道,做一下整形手术,图片中的内容风格就不一样了。StyleGAN 把手伸进了 CNN 的结构中,和 CNN 做着偷鸡摸狗
的勾当。
在论文里,它的示意图是下面这样。Mapping net
将随机噪点 z
转换成一个 w
,由 w
经过 A
加工,通过 AdaIN
的结构来控制 g
CNN 输出特征图的风格。 论文里还加上了一些额外的 Noise
,从 B
的口子灌入到 g
(Synthesis net
),负责给CNN再提供一点随机性。
StyleGAN 目前有两篇论文,第二版在结构上进行一些修改与简化,使之更有效率,不过在这个教学中,我们专注在第一版中,我觉得第一版的代码好写一点。
- 第一版 A Style-Based Generator Architecture for Generative Adversarial Networks
- 第二版 Analyzing and Improving the Image Quality of StyleGAN
在来看看论文里的动图吧,实验结果还是挺有趣的。如果你操纵 CNN 中不同 level 的 A
,它是如何影响生成效果的?
如果你操控不同 B
的随机噪声,它又是如何影响生成的?
接下来我们来看看,为什么要加入 Mapping net
, 他的作用是什么?如果用白话描述就是:我的世界和你的世界不一样,我们需要一个桥梁来联通两个世界。 具体再解释一下的话,在我的世界里我还没发现有胡子的女人,所以我给他留白了一份探索空间(类似下图a)。而你的世界里,你压根就没想过女人会不会有胡子的问题, 所以你并没有留白任何探索空间(图b)。那么 Mapping net
构建了一个桥梁,将你的世界转化成了我的世界的模式(图b到图c)。这样,我们两个世界就能对接啦。
为什么图b是一个圆形的呢?因为我们通常用正态分布来 sample 随机噪声,所以一般来说,都需要从正态分布的空间,转移到其他不规则空间分布。唯有这样, 噪声空间的数据分布才能比较好的映射的真实数据的空间分布。
最后,论文图里面的那个 AdaIN 是啥?简单来说,这是一种 Mapping net 把手伸进 CNN 特征图的过程,操作特征图的过程。 里面的 y
是通过 Mapping net 的 w
结果变换而来,里面的 x
就是特征图,AdaIN 用一种 re-normalization 的方式, 将特征图重新放大,平移
了,变成了新的特征图,或者你可以认为,特征图的风格被改变了。
正式对每一张特征图的风格变换,才导致了最后生成的图片变化。注:在 StyleGAN2, 它不操控特征图了,转而操控生成特征图的卷积核,真是一种聪明的办法。
秀代码¶
如果想直接看全部代码, 请点击这里去往我的Github. 因为代码太长,我在这里说一下代码当中的重点部分。我的代码采用的架构是 StyleGAN2 中的这张图的(c)结构, 因为这张图比 StyleGAN 原版的图要清楚,代码比较好组织。
A
操控特征图,调整后的特征图在进入卷积,卷积出来的新特征图过一遍 norm,在加一些 B
噪声,一个 block 就这样完成了。所以下面我会先创建这个 block, 作为 StyleGAN 的核心模块。
在这其中有三个我定义的小插件,分别是 AdaNorm
, AdaMod
, AddNoise
, 这三者都是 Synthesis net
的组件。用来控制风格的。 AdaNorm
就是图c当中的 norm 部分,用来重新将特征图拉回原来的正态分布。
而 AdaMod
则是把 Mapping net
出来的 w
加工成两个 y
用来控制特征图 x
的缩放平移变化。 所有在这里我们需需要将 w
通过举证运算,转化成 y
使之可以匹配特征图的格式。
而 AddNoise
则是添加随机噪点的方式,这里其实还可以有其他方式去添加, 我为了简化在这里传入的 inputs 的 noise,将它设定成每一层 CNN 特征图都可以用的 noise,所以每层CNN传递的noise都一样。 但是我会对这个 noise 选取不同的长宽大小,来匹配上当前特征图的长宽。
在组合这些插件的 StyleGAN
中,我重点说说 Generator 的写法,因为 Discriminator 和以前的网络没什么差别。 在 Generator 中,定义一个 const
作为基础图像,由接下来的 Style 层不断给它转换风格。最后变成不一样的图片。
其中的 Mapping net
就比较简单了,就是几层全连接层。但是有意思的是,如果我在 Mapping net
的全连接层加上激活函数,做一些非线性的话, 生成结果就要差很多。我不明白其中的道理,因为安理来说,非线性是 mapping 的核心概念,就是要转换空间分布的,可是为什么这个试验中,反而有坏处呢? 有想法的同学可以在下面讨论一下。
我的训练结果展示的是风格融合的结果, 具体代码见我的Github. 在 styleGAN 中,你可以将两种不同的 mapping w
嵌入到不同的 Synthesis net
层中,这里我嵌入了2中不同的 z
噪声。 而且这两种不同的 z
噪声在训练时每次都被插入到不同的 CNN 层中。方式如下。
训练出来的效果如下,下图展示的是最后一个训练 epoch 的结果。从结果看出,还是有些融合的效果,但是用数字做例子总还不是那么合适,总感觉怪怪的。 不过我在下面还做了些其他实验,你可以看看人脸和卡通脸的效果,这就稍微好点了。
延伸¶
很多学GAN的人心里面都有一个小小二次元愿望,比如用GAN来生成老婆
。我其实还好,只是恰好有这些二次元的数据,我就拿着试了试。上面用 mnist 手写数字的 StyleGAN 总感觉哪里怪怪的,但是当我们用二次元人脸数据时,情况就好很多,而且也能看出来明显的风格混合了。 这个项目的详情,请看我的 Github anime-StyleGAN
明星脸我也尝试了一下,效果还可以~ 具体项目 Github celebA-styleGAN
总结¶
哈哈,终于开始有点意思了,作者在 StyleGAN 中提出了一个非常新颖的方法来做 Generator,就是从特征图的改变做起。 其实 StyleGAN 的表现还是挺好的,但是我总觉得操控特征图有点效率不高,这可能也是为什么在 StyleGAN2 的版本中, 作者换成了操控卷积核了。