不能写一手好代码的工程师不是好数据科学家!本文作者 Nolan Kent 曾经是一名恶意软件分析师,具有很强的工程能力。在本文中,他编写了一个可视化工具用于观察 StyleGAN 模型中的特征图,对理解该模型起到了巨大作用。
图 1:修改单一位置的空间特征图以生成动漫人物
「写在前面」
这篇技术博客介绍了一个使用生成式对抗网络完成的项目。由于这是一个个人项目,我采用了一个在专业领域中通常不会使用的动漫人物数据集「DANBOORU2018」。
下面给出了这个数据集的链接以及使用该数据集模型的详细介绍:
https://www.gwern.net/Danbooru2018?source=post_page-----4cf764578e----------------------
作者表示,所做的大部分工作纯粹是出于学习的目的,但最后得到了更为有趣的结果:矩形肖像的嘴部动画。
图 2:快速/平滑的嘴部动画。由于缺乏数据,男性肖像的质量通常更低
在这个项目中,有一个部分是使得和生成对抗网络交互并且对其进行学习更容易的工具,但是现在这个工具对用户而言并不是十分友好。
如果继续该项目,一个很重要的目标是发布一个新版本的工具,可以使任何人能立即上手来制作一个如图 2 所示的动漫人物。但是现在,它主要还是一个用于研究的工具:
图 3:该工具部分界面的截图。用户界面确实需要改进,但目前这仍然只是一个原型,我将经常在其基础上添加和删除一些功能。
我发现,比起使用 jupyter notebook,将实验代码融合到这样的工具中,可以更容易地在不同的设定下重复实验。有些思想只有通过重复的实验才能越来越明朗,所以如果没有这个工具,我觉得我可能会遗漏博客中提到的一些见解。如果你只是对示例的动漫图像感兴趣而并不关注技术细节,你可以直接跳到本文的「实验结果:动漫人物」这一章。
许多个人项目存在的主要问题之一是:它们只包含单一的视角。我写此博客的目的是为了获取他人对该课题的看法,详细描述我在这个项目中的工作经验,并接受建设性的批评和指正。
引言和成果小结
这些年,我养成了定期复现有关生成式模型论文的习惯,所以我大约在 StyleGAN 论文发表的时候,就陆陆续续的开始了这个项目,我的工作主要包含三个部分:
1. 复现了 StyleGAN,并作出了一些调整
2. 基于我的实现训练了该模型
3. 实现了一个用于可视化模型并与模型交互的工具
首先,我抱着学习练习的心态复现了 StyleGAN,这也是因为当时官方代码(https://github.com/NVlabs/stylegan)还没有开源。StyleGAN 的结果比我接触过的其它模型要好得多,所以我想要进一步深入研究一番。
一个令我很兴奋的生成式模型的应用是:它能够为电子游戏自动生成资源。对于我来说,StyleGAN 是我复现的第一个在电子游戏上生成的结果可接受的模型,所以我的初步尝试就是让「Unity」等游戏引擎加载该模型。
为此,我制作了一个「.NET」动态链接库(DLL),它可以与模型进行交互,从理论上来讲也能够被导入到「Unity」中。为了测试这个动态链接库,我创建了一个与之交互的工具。最终,我为这个工具添加了越来越多的功能,直到这个工具成为了本项目最大的组成部分之一。
以下是项目的整体架构:
图 4:从 TensorFlow python 实现到使用工具生成图像的工作流程
我喜欢使用工具来可视化「不透明」的数字对象(比如恶意软件和深度学习模型)并与之交互,所以我增加的一个功能就是对特征图的可视化及修改(见图 5,https://towardsdatascience.com/applied-deep-learning-part-4-convolutional-neural-networks-584bc134c1e2)。
观察不同图像上各层特征图最活跃的部分有助于我了解模型在做什么,并且会使对一些面部特征的自动定位变得简单。当对图片进行修改时,我注意到,对特征图特定区域的值进行加或减会产生一些有意义的变化,比如张开和合上嘴巴(见图 2)。与自动面部特征检测相结合,这可被用来在不需要标签的情况下,对所有生成的图像进行一致且有意义的修改。(见图 9、10)。
图 5:特征图可被用来识别有意义的区域(上面一行图像:眼睛,下面一行图像:人脸)。红色方块表示负激活值,绿色方块表示正激活值,白色方形表示激活值接近 0。
小结:以上是使用特征图来修改面部特征的功能,以下是本博客剩余部分内容的组织结构。
生成式模型的应用
使用工具生成动漫人物
关于代码的简要讨论
复现的细节
对数据的讨论
对训练步骤的讨论
应用
总的来说,我对生成式模型的以下几个方面很感兴趣:
更好地通过程序化的方式生成游戏资源
让艺术创作变得更容易更快
无监督学习的基本潜能与生成式因素解耦(disentaglement)相结合
更好地通过程序化的方式生成游戏资源
生成式模型有望将程序化生成推向一个新的高度,这令我十分激动。据我所知,现今的基于规则的程序化生成技术不能根据高度复杂的分布随机创造样本。例如,程序化生成的关卡的一个小节多半可以与该关卡的其余部分独立开来,并且仍然被玩家所接受。
随机生成人物肖像类的图像是更难的,因为好看的人物图片趋向于反映真实世界,而现实世界具有特别多的相互依赖性,因此每个像素都需要在其它像素的上下文中被考虑到。为了使图片看起来好看,即使非写实的图片也需要一致的光源、解剖结构、纹理、视角、风格等等。
这种依赖性也适用于音频、对话、动漫、故事情节的自动生成。我认为生成式模型是当前能够可靠地根据如此复杂的分布生成样本的最佳方式。
让艺术创作变得更容易更快
在使用生成式模型时,通过交互式工具有可能使外行人也能够创作本来需要有经验的艺术家才能创作的图像,或者让艺术家们能够更快地完成他们工作中较为常规的部分。
我并不认为生成模型会很快消除对有创意的艺术家的需求,因为生成模型(以及大多数机器学习模型)专注于对特定的分布建模。这使得生成与训练分布中任何样本都不同的高质量图像(即有创意的图像)是非常困难的。
然而,像本博客中使用的这种工具,使人们能够添加有助于生成更多独特的图片的自定义变更(尤其是如果模型已经学到了一些基本的图形概念(比如照明和透视))。
无监督学习的基本潜能与生成式因素解耦相结合
由于无标签数据的数量远超于有标签数据,并且深度学习是极其需要数据的,我认为今后无监督/半监督学习有逐渐取代有监督方法的潜力。尤其是,我们已经提出了一些针对深度生成模型的方法,用来解耦数据集中的变化因素:图 7 和图 8 展示了 StyleGAN 是如何做到这一点的(至少部分做到)。
实际上,对于「解耦」并没有一个一致的的形式化定义。我的理解是,仅有有限的实验证据表明它对下游任务是有用的。然而,将「解耦」与 GAN 一起使用,让我抱有一种它是有用的乐观态度。尽管生成器不能为它未生成的图像生成内部表征,但是还有几种其它类型的生成模型可以在视觉质量上和 GAN 一较高下,并且可能更适合于下游任务。
实验结果:动漫人物
我在一个名为「Danbooru2018」的动漫数据集上训练出了自己最佳的模型(https://www.gwern.net/Danbooru2018)。我将在数据部分讨论它的优缺点,其中一个主要缺点是缺乏多样性:难以生成男性图像。
下面所有的例子都是用这个工具生成的。
我最初是在 一个 jupyter notebook 中生成了这些图片,但是接着又使用专门的工具显著地加快了图像生成的速度,并且让我以不同的视角来理解模型是如何工作的。下面的图像大致按照生成的复杂程度排序:在不使用该工具的情况下,图 7/8 比图 6 生成起来更麻烦,图 9/10 比图 7/8 更难生成。
图 6 是几张图像的中间潜变量(intermediate latent variable)之间插值的示例。通过使用 GAN 我们可以得到一个很酷的结果:确保插值后的图像与最终图像的质量相似。
图 6:随机潜向量之间的插值
图 7 是通过定位中间潜空间中具有特定意义(在本例中是头发或者眼睛颜色)的向量来修改图像并且向该方向移动的示例。比如,可以将许多黑发人脸图像的平均潜变量值计算出来,减掉其它所有图像的平均潜变量值,即可得到黑色头发的向量。我将会在「训练/后处理」部分进一步讨论。
图 7:在有意义的方向上改变潜变量
图 8 展示了将生成图 7 的想法应用于「嘴巴张开」这一属性的效果。这在某种程度上有用,但是属性并不是完全解耦的:除了嘴巴发生了变化外,图像的每个部分都能看到一些变化。常识告诉我们,一个人可以只动动嘴巴而不显著改变身体的其它部分。一个简单的技巧就是把动漫人物的嘴粘贴到另外一个静态图像上,但是这在改变「嘴巴张开」的向量的同时也会改变皮肤色调或图像风格的情况下并不起作用。
图 8:改变嘴部向量也改变了其它属性
图 9 展示了一个在靠近人物嘴部的空间位置修改特定特征图以产生说话(或咀嚼)的动画,且不引起全局改变的示例。通过少量的手动操作,在不需要多于两个带标签的数据样本的情况下,就可以找到修改后可以产生这种变化的特征图。这些都可以通过该工具完成。
这一过程受到了 DCGAN (https://arxiv.org/abs/1511.06434)论文的启发,通过修改特征图来删除图中的窗口。这些修改是对特定特征图的局部区域做简单的加或减操作。我将在以后的博客中展示如何使用该工具完成这一工作。
图 9:通过空间局部修改产生说话的动画。一旦找到对应于有意义的变化的特征图,无论图像的质量或风格如何,它们都可以被应用于大多数图像。
除了有无发带之外,图 10 展示了和图 9 相同的内容。这一技术可以被应用于许多不同的属性。
图 10:使用局部修改添加/删除发带
实验结果:代码
考虑到我的空闲时间是有限的,并且我的首要目标是学习,我忽略了对大多数项目来说很重要的几个方面:
规划/设计:我起初尽可能少做计划,增加了我想到的功能/变化。
UI 设计:我使用贪心策略来添加我想到的功能。
代码风格:我开展本项目的主要目的不是为了让别人来读我的代码,而是想要尽可能快地得到结果。我曾经从事恶意软件逆向工程的工作,所以我自己本身并不是很害怕调试低质量的代码。快速开发非常适用于本工具,但是在实现深度学习模型方面我的速度要慢得多,因为可能很难发现和调试错误。该项目的代码质量远远不能满足专业项目的要求。
下面给出我的 github 链接,它们仍处于积极开发中,因此某些提交中可能存在一些漏洞:
StyleGAN 的复现:
https://github.com/nolan-dev/stylegan_reimplementation
我制作的工具:
https://github.com/nolan-dev/GANInterface
复现细节
在这一节中我将介绍复现 StyleGAN 和训练模型的技术细节。
复现 StyleGAN
在论文「A Style-Based Generator Architecture for Generative Adversarial Networks」(https://arxiv.org/abs/1812.04948)发布后不久,大概是官方代码发布前的几个月,我开始复现 StyleGAN。
我将在此讨论我碰到的一些挑战和采取的应对方法(假设你熟悉 StyleGAN 和 TensorFlow)。
StyleGAN 以 PGGAN (https://arxiv.org/abs/1710.10196)(我曾经复现过)为基础。这些模型使用了「渐进增大」的训练方式,即判别器和生成器在训练期间不断增添卷积层以处理越来越高的分辨率。
扩展一个模型的做法是比较罕见的——所有我复现过的其它模型在训练期间都不需要改变它们的结构。幸运的是,Tensorflow 有一些比较方便的功能,比如只为模型的一部分加载已保存的权重并随机初始化其余的权重。迁移学习中也会使用这种方法。
我非常喜欢 TensorFlow 2.0 的风格,它将模型创建为继承自「tf.keras.Model」的类。我用这种方式创建了大多数层、生成器、判别器和映射网络。我也试着在动态图执行(eager excution)和传统的基于图的执行之间能够进行切换。动态图执行使得调试更容易,我认为它是一种更好地理解程序的方法(也是一种恶意软件分析中常用的技术)。
不幸的是,现在动态图机执行比在图模式下运行慢得多,所以最终我停止了对该功能的更新。使用「tf.keras.Model」的好处之一就是它在动态图和传统的图模式下都可以工作,因此从理论上讲,再转而使用动态图执行也不会太难。与此同时,我刚刚使用了「tfdebug」命令行界面和「TensorBoard」,在这一点上我相当满意。
StyleGAN 和 PGGAN 有一些重要的区别。有一种方法在使用「自适应实例归一化」操作时,将特定图像的潜在数据作为样式(非空间属性)提供给特征图。理论上讲,这很容易根据论文中所述细节实现,但我选择用「tf.nn.moments」来计算均值和方差,这并不如官方实现的版本那样有效(官方使用较低级别的操作来计算这些值)。
我猜这是由于数值问题造成的,当时我并不想对此进行调试,因此我没有更多地研究它。通常,我很乐于深入研究此类问题,因为研究它们显然使我有机会学更多知识,但是由于这个项目只是我的爱好,因此我必须优先考虑充分利用我的时间。
StyleGAN 也使用了一个中间潜空间,该空间假设通过增加对潜变量值范围的灵活性和依赖性促进解耦(论文中有一些实证证据)。比如说,如果我们假设人群中的男性都没有长头发,那么当对应于性别的潜向量出现在「男性」区域中时,对应于头发长度的潜向量应该永远不会出现在「长发」区域中。如果潜向量是独立的(在没有映射网络时会发生这种情况),我们最终将「长发」和「男性」一起采样,生成器将会生成非男性或是短发来骗过判别器。
这意味着即使头发长度的潜向量处在「长发」区域,当其它潜变量值在其正常范围内时,我们最终有可能生成短发图像。值得注意的是,对一些解耦的定义需要进行「轴对齐」(修改单个潜变量值会带来有意义的变化),我的理解是 StyleGAN 的映射网络促使中间潜空间成为轮流轴对齐解耦旋转之后的形式(修改潜变量会产生有意义的变化)
在我看来,使用中间潜变量值就像通过风格将信息融合到网络中一样有趣。它也很容易实现。除非另有说明,否则本系列博客中提到的潜变量值指的就是中间潜变量值。
如果起初看起来像是论文次要细节的部分变成了最难复现的部分,那就真的太令人烦恼了。StyleGAN 就遇到了这种情况——StyleGAN 和 PGGAN 之间的一个区别就是对双线性上采样(和下采样)还有 R1 regularization(https://arxiv.org/abs/1801.04406)(对判别器的梯度惩罚)的使用不同。
这两种方法都很容易单独实现,但是当我尝试将它们组合时,发现 TensorFlow 无法计算「tf.nn.depthwise_conv2d」操作的二阶导数。
Depth-wise 卷积被用来将卷积核分别应用于每个通道。在卷积神经网络中通常并不需要这样做,因为(除去一些用于移动设备的 CNN)每个卷积核都与前一层的所有通道相连。通常用于实现双线性插值的模糊卷积一次只能处理一个特征图,所以需要进行深度卷积。如果没有实现二阶导,我将无法计算 R1 惩罚,而这需要使用梯度的梯度。
当时我对自动微分还不够了解,不能自己轻易地实现二阶导。我花了一些时间尝试更好的理解自动微分,那时我已经完成了除此部分以外的所有工作,随后不久官方的代码就发布了。英伟达团队在模糊卷积中使用两个「tf.custom_gradient」函数很好地解决了这个问题。
我对 StyleGAN 进行了几次实验调整,并取得了不同程度的成功。
1. 我通过将初始分辨率改为 8x4 并随后增大分辨率来测试矩形图像。
2. 我尝试用 ACGAN 和带有投影判别器的 cGan 来实现「条件 StyleGAN」。
第一个实验在矩形图像上的效果很好,用 ACGAN 和 cGan 实现的「条件 StyleGAN」效果则较差。这可能是由于我的超参数选择不够好,但是通常来说,结果比无条件训练后在潜空间中找到有意义的特征对应的向量的结果差(本文将在「训练/后处理」部分中进行讨论)。
数据
随 StyleGAN 论文一起发布的 FFHQ 数据集含有 70,000 张高分辨率图像。为了更接近生成完整任务的目标,我尝试修改英伟达提供的数据抽取脚本,按照 8:4(高:宽)的比率来提取图像。
除了提取脸部,也从脸部下方提取了相同量的数据。将高度和宽度加倍将需要原始输入尺寸提升 4 倍,而仅将高度加倍则需要将尺寸提升 2 倍。人脸下方的数据也应具有比背景数据更少的变化(差异主要来自不同的衣服),不获取背景数据意味着网络不需要为不属于人物的数据分配容量。
不幸的是,FFHQ 数据集的 70,000 张图片中只有 20,000 张在人脸以下的区域有足够的数据来按照预期的纵横比来创造图片。如图 11 所示,我并不能使用这个小数据及得到特别高质量的结果,但是仍然可能存在提高质量的方法(比如提升收录一张图片的标准)。
图 11:矩形 FFHQ 图像趋于低质量
我也对 GAN 在风格化绘画上的能力很感兴趣,我看到最近「Danbooru2018」(https://www.gwern.net/Danbooru2018)发布了。这一数据集有大量高分辨率的图像且带有非常丰富的标签数据。
它确实有一些潜在的缺点,比如男性图像的数量少得不成比例,这大大降低了该类图像的质量(图 12)。图 12 中的男性图像是从生成的大约 1,000 张图像中则有挑选出来的质量最高的图像。我确实认为这方面有很大的提升空间,尤其是针对平均男性图像使用截断技巧(truncation trick)。
图 12:由于数据集的限制,大多数男性肖像(顶部的图像)画质均较低。即使随意挑选,女性肖像(底部图像)往往具有更高的画质。以上图片均未使用截断技巧来获得更高质量的图像。
该数据也包含很大一部分不适合上班时间浏览的图片(羞羞的污图),尽管我认为生成式模型的一种潜在用途是自动修改媒体内容以使其更适合于不同的受众。
我能够利用收藏该图片的人数,图像分辨率,标签,创建日期等元数据,来选择要收录的候选图片。为了减少差异并提高图像质量,我排除了收藏量很少和六年以前创建的图片。我也只保留分辨率最低为 512x256(高 x 宽)的图片。512x256 是模型的目标分辨率。最后,我过滤掉了一些带有可能增大肖像风格差异的标签的图片,比如「躺着」和「侧身」,或者带有色情暗示的图片。
为了生成数据,我使用了以下两个工具的组合,并修改相应的部分以提取所需纵横比的图像。
https://towardsdatascience.com/animating-ganime-with-stylegan-part-1-4cf764578e
https://github.com/nagadomi/lbpcascade_animeface
这些工具并不总是正确地提取图像,所以我使用了「illustration2vec」(https://github.com/rezoo/illustration2vec )来过滤结果,因为那种无法检测到人的图像很可能是糟糕的。
我还创建了一个能显示大的图像网格的工具,我可以通过它快速手动删除坏的图片,但是这对于包含超过 30,000 张图片的数据集来说太花费时间了。我最终得到的是包含了具有各种质量和标签的图像的几个不同的数据集,图像的数量从 40,000 张到 160,000 张图像不等。完成所有这些工作后,我们得到了一个比我构建的拥有 20,000 张图像的 FFHQ 数据集好得多的一个模型。
训练/后处理
对于每个分辨率,我使用了多达 80 个 epoch 来训练模型,其中 40 个 epoch 用于 PGGAN 的转换阶段,40 个 epoch 用于稳定阶段。对于包含 160,000 张图像的数据集,这个过程花费了一个多月,而且可能是多余的。
我使用 Horovod(https://github.com/horovod/horovod)在两块 Nvidia Titan RTX 显卡上分布训练,在早期的训练步中允许使用较大的批处理大小,且使我不必在每一批中处理少于 16 个样本。
为了收集属性,我生成了成千上万的图像,并用「illustration2vec」对它们进行扫描以得到属性估计。对于每个拥有超过 1,000 张对应图片的属性,我对所有潜变量值取平均,并将从整体平均图像中减去了这些潜变量均值。
该过程创建了一个向量,当它与潜变量相加时,将会促进预期属性的表达。虽然这个过程得到的效果很好(我认为这进一步证明了映射网络解耦了属性),但我仍然对改进这一过程很感兴趣,我可能会使用论文「Interpreting the Latent Space of GANs for Semantic Face Editing」(https://arxiv.org/abs/1907.10786)中描述的技术作出改进。
在某些情况下,我尝试手动解耦相关属性:例如,如果金发和蓝色眼睛相关,从金发向量中减去与蓝眼睛对应的向量可能会有所帮助。然后,我将这些属性向量导出到一个 csv 文件中,我开发的工具可以加载该文件。
结语/一些说明
通过尝试复现那些依赖于我不了解的基础知识的论文,然后学习这些基础知识,并对它们使用的环境,我收获了许多经验。这一过程有点儿像反向传播,意味着我花了 30 多个小时试图在早期完全理解一篇论文。这是我第一次尝试为增进我的理解而制作一个工具,我认为这可能成为我今后的标准策略。
我认为可视化工具是提高对一个主题理解的好方法。源于我的恶意软件分析师的背景,像 Hiew(http://www.hiew.ru/)这样的工具塑造了我对恶意软件的理解,即使起初可视化分析如何有助于窗口可执行文件并不是很直观。
考虑到人类视觉系统可用的带宽和处理能力,我的假设是,当使用图像表示时,我们能够从具有空间局部结构的大多数数据中快速获得大量信息。这也是卷积神经网络似乎能很好地处理的数据类型(考虑到卷积神经网络受到了生物学的启发,这并不奇怪)。这一思想也适用于卷积神经网络本身,它也是我开展此项目的原因之一:希望可视化卷积神经网络的特征图能有助于我更好地理解它们。
在本博客系列的下一部分中,我将更具体地介绍该工具以及如何使用它来创建动画。到那时,我将分享一个该工具的编译版本和一个训练好的模型,从而跟工具进行交互,因为目前仅使用源码进行操作太过于复杂。
原文链接:https://towardsdatascience.com/animating-ganime-with-stylegan-part-1-4cf764578e