大家好,欢迎来到IT知识分享网。
神经风格迁移大法好,人人都能变梵高,但实际上这可是个技术活。虽然神经风格迁移NST从概念上很容易理解,但想生成高质量的图片却实属不易。为了获得良好结果,必须正确实施其中许多复杂的细节和隐藏的技巧。
本文就将深入研究神经风格迁移原理,并详细讨论这些技巧。
关于NST的介绍这里就不再赘述了,你可以通过官方PyTorch教程了解NST。不过如同其它介绍性文章一样,它们的执行结果都较为普通(如下图)。笔者将展示能提高迁移质量的编码教程,但是先说一些题外话。
(上图是两种不同方法实现的神经风格迁移质量比较。左上图是要匹配其内容的图像。左下图是要匹配其风格的图像。中间图是由PyTorch教程实现的迁移结果,右图是由本文所述步骤实现的迁移结果。很明显,右图视觉质量更高,且更忠实于左上方图像的风格。)
问:为什么Gram矩阵能度量风格?
Gatys等人的文章(介绍神经风格迁移)大都简单易懂。然而他们却没有回答这个问题:为什么选用Gram矩阵来表示风格(即纹理)?
在较高层上,Gram矩阵能测量同一层中不同特征图之间的相关性。特征图即卷积层激活后的输出。例如,如果一个卷积层有64个滤波器,将生成64个特征图。Gram矩阵能测量层中不同特征图之间的相关性(相似性),而无需关心像素的确切位置。
为了阐明Gram矩阵为什么能度量纹理,假设有两个滤波器,一个用于检测蓝色事物,一个用于检测螺旋。将这两个滤波器应用于输入图片以产生两个滤波图,并测量其相关性。如果两个滤波图高度相关,那么基本可以确定图片中所有螺旋都是蓝色的。这意味着图片纹理是由蓝色螺旋组成,而非红色、绿色或是黄色螺旋。
尽管这项解释仍令笔者有些不解,但Gram矩阵能度量风格似乎是纹理合成社区公认的事实。此外,不能否认由Gram得到的结果确实令人印象深刻的。
修复PyTorch执行
提高迁移质量的第一步是修复PyTorch教程执行。该教程内容试图忠于Gatys等人的文章,却遗漏了一些细节。一方面,文章作者将 MaxPool2d替换为AvgPool2d,因为他们发现AvgPool2d能生成更高质量的结果。
另一细节是,该教程计算的是卷积输出而非ReLu激活函数的ContentLoss(内容损失)和StyleLoss(风格损失)。这有点吹毛求疵,因为笔者在实验中并没有观察出使用卷积和ReLu之间有什么大区别。
VGG19网络及其各层
该教程与文章之间最大的差别是它对于ContentLoss 和StyleLoss分别使用了“错误”的层。笔者之所以在错误一词上打了引号,是因为层的选择是比较主观的一件事,很大程度上取决于最令人喜欢的风格。
也就是说,可以使用一些经验法则来指导决策的制定。在度量内容相似性时,若content_img与input_img之间存在完美的像素匹配,较低层往往激活程度最高。越深入网络,层越不关心匹配是否精确,相反,当特征处于正确位置时,层会被高度激活。
为了将每层的内容都进行可视化展示,可以设置style_weight=0,并使用不同的层作为content_layer,基于随机的input_img运行训练过程。
VGG19网络各层的响应内容可视化。图片越往右,层在网络中的位置越深。
教程使用第4层卷积作为内容层。如上图所示,对于内容来说该层数可能太低了,因为处于这个深度的网络仍在关心像素匹配是否精确。Gatys等人使用conv4_2替代conv2_2,因为conv4_2更关心整体特征分布而非单个像素。
对于风格来说,低层捕捉较小的重复特征,较高层捕捉更抽象的整体特征。因此,为了对style_img的整体风格进行迁移(从低层细节到整体图案),应该包含网络中所有深度的层。
该教程使用前5个卷积层,但它们在网络中的位置太低,不大能捕捉到整体特征。Gaty等人使用 conv1_1、 conv2_1、conv3_1、conv4_1和conv5_1,在整个网络体系中形成了良好的层分布。可以使用与内容层相同的方法,对每个层选择要优化的样式进行可视化。
为了实现这一点,设置content_weight=0,指定想要使用的style_layers,然后基于随机的input_img运行训练过程。
PyTorch教程选择的层生成的风格(左);Gatys等人的文章选择的层生成的风格(右)
意料之中,教程选择的层优化的风格捕捉低层次的重复性特征,但不能捕捉高层次的整体性特征。
提高迁移质量
截至目前,我们实现的修复质量应该已经相当接近Gatys等人的文章中所看到的质量。现在将进一步深入,采取以下措施以生成更好的图像。
笔者对文章所做的第一个修改就是将优化程序从L-BFGS转换为Adam。文章表述L-BFGS能生成更高质量的迁移。
但笔者在实验中并未发现这两者的使用效果有什么差异,此外Adam的稳定性似乎更好,尤其在训练大量步骤时或者有着庞大的style_weight时。在这些情况下,L-BFGS可能会报错,这大概是因为梯度爆炸(笔者并未深入研究)。
另一个轻微调整是将mse_loss (即L2loss)转换为l1_loss。笔者想不出L2_loss对于风格迁移有什么好处(除了在0处具有可微性),因为平方值将会对异常值做出严重干扰。
正如前一节所述,我们并不关心像素匹配是否精确,并且可以容忍生成图像中出现一些异常值。实际上,当风格特征和内容特征混合在一起时,异常值可能会使得视觉效果更佳。最后,《特征可视化》(Feature Visualization,接触相关话题必须阅读的一篇文章)的作者也许是出自类似原因,在其任务中使用l1_loss。
实际上,许多用于生成高质量特征可视化(FV)的技巧都优雅地过渡到了神经风格迁移上。
FV和NST在概念上十分相似,只在生成input_img的方式上有所不同。在NST中,input_img经过优化后,可与content_img 和style_img以相同的方式激活网络中不同层。而FV则不使用content_img 或style_img,而是生成input_img,最大程度地刺激不同层的神经元。
积极的数据增强使生成图片的右上角产生旋转伪影
笔者从FV中借用了一个小技巧,即在input_img上使用数据增强。这与常规分类任务的工作原理完全相同:每一步都要对input_img进行增强(如旋转、裁剪、缩放等),然后运行模型并计算损失。
通过每一步的增强,我们迫使input_img生成抗微小扰动的鲁棒性特征。这些特征应该包含较少的高频伪影,并更具有视觉吸引力。然而,我发现《特征可视化》文章所使用的增强过大,必须适当缩小。即使如此,生成图片(上图)边缘仍然有一些旋转伪影。消除这些伪影的最简单方法就是将边缘的一些像素剪切掉。
笔者做出的最后一个修改是将content_layer转换为conv3_2,而非Gatys等人使用的conv4_2。笔者读过的大多数文章也推荐使用conv4_2,但是笔者发现使用conv4_2会忽略掉一些细节,且生成图片的风格会覆盖内容。
另一方面,conv3_2可以保留这些细节,而无需对像素完美性进行过度惩罚。
进一步质量提升
现在已经讨论了笔者的神经风格迁移代码中所实施的全部技巧。在原有的PyTorch教程基础上,已经极大提高了迁移质量。此外,content_weight和 style_weight对于特定的图像选择具有更高的鲁棒性。
例如,笔者发现如果没有对PyTorch教程进行适当的调整,一组图像的 style_weight很难轻松迁移到另一组图像上。
也就是说,去除生成图片中的高频噪音可能会得到更好的结果。文章《可微的图像参数化》(DifferentiableImage Parameterization,接触相关话题必须阅读的另一篇文章)提出了很有趣的方法。
该文中,作者先在(去噪)傅里叶空间而非(噪声)像素空间中对input_img 参数化。因为input_img 由梯度下降法生成,对输入去相关化相当于一个预调节器,可通过梯度下降法更快找到最小值,从而使优化更易(与在监督学习任务中去除相关特征类似)。除了类似的简短解释如在去相关空间中发现的最小值更宽泛、更健壮外,我并不清楚为什么这能提高迁移质量。
更简单的方法是通过直接或间接惩罚来抑制高频噪声。直接惩罚可通过将input_img的全变差损失添加到优化对象中来实现。相反,通过在每次梯度下降步骤后对input_img进行模糊,或者在将噪音应用于input_img之前对梯度进行模糊,可实现间接惩罚。
这两种方法存在的问题是,它们还会对真正的高频特性造成不利影响。可通过在训练时缩减总变化损失或模糊量来进行一定程度上的改善。
笔者的初始目标是使用机器学习生成媒介形象图片。但在反复实验中,无意发现了一些很惊人的事。对笔者来说,整个过程中最有趣的部分是神经网络的端到端可微性。我们很容易地就能“颠倒”一个原本用于区分猫狗的模型,并用其生成无数种不同风格的图像,你还可以尝试用随机森林模型进行操作。
用神经风格迁移生成好看的图片了,这个过程听起来简单,但想要得到高质量的结果,确实是个技术活,可见即使有机器学习辅助,成为梵高依然不是件容易的事。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/80038.html