赞
踩
本文是
[1]
的译文, 按照作者Connor Shorten
的说法, 此博客讨论的是StyleGAN2的诸如weight demodulation, path length regularization和去掉progressive growing等信息。虽然我去年底自己复现过StyleGAN2的pytorch版, 但对这些内容也有些忘记了,借此机会复习下。
对于StyleGAN不了解的小伙伴,建议先看下别人写的关于StyleGAN的基本介绍,然后再来看本文,相信会收获更多~
来自Nvidia 赫尔辛基实验室的Karras等人提出的StyleGAN1的架构在Flicker-Faces-HQ (FFHQ)等数据集上表现非常惊人,相比之前的DCGAN和Conditional GAN等结构。
以FFHQ数据集为例,StyleGAN不但可以生成逼真的人脸(几乎无懈可击),而且能够对人脸进行解耦, 以便对人脸进行编辑(19年的论文,截至2020年10月中旬,引用已破千(截至2020年10月20日,是1232.),在twitter上也经常有用StyleGAN的latent space做文章的工作,比较典型的厉害人物有CUHK的沈宇军和周博磊)。
StyleGAN生成的人脸图像
在StyleGAN中, Frechet Inception Distance (FID) 是最常用的衡量图片真实性的指标(由法国科学家提出,也称遛狗绳距离)。
第一代StyleGAN
[2]
的实验表,可见,StyleGAN1在FFHQ最好的FID为4.40,FID越低越好。
第二代StyleGAN
[3]
的实验表,可见,StyleGAN2在FFHQ上的表现比第一代更好,其中,FID值由4.40提升到2.84. 算的上是巨大的提升了。
本文主要讨论StyleGAN2相比StyleGAN1在架构上的改变,正是这些改变使得FID指标有了显著提升~,定性观察上面,StyleGAN2的artifacts出现频率也显著比StyleGAN1低。
并基于很多人对latent space进行魔改的需求,对latent space进行平滑操作。
原作者搞了个17分钟的解释StyleGAN2的视频在下面,如果感兴趣的小伙伴,可以饭墙去看看[4]
karras和ming-yu liu等大佬非常擅长用改造的normalization层进行image synthesis任务(如GauGAN和StyleGAN)。如GauGAN中的SPADE和StyleGAN的AdaIN。
对StyleGAN,karras等人使用AdaIN (adaptive instance normalization)来控制source vector w w w对生成人脸的影响程度。
对StyleGAN2来讲,作者们调整了AdaIN的使用,从而有效的避免了水印artifacts(water droplet artifacts)。
AdaIN是被用于快速Neural Style Transfer的normalization层。在Neural Style Transfer任务中,研究者们发现:AdaIN可以显著的解耦(remarkable disentanglement) low-level的"style"特征和high-level的"content"特征。
图像来自<Arbitrary Style Transfer in real-time with Adaptive Instance Normalization> authored by Xun Huang and Serge Belongie.
不同于一般的风格迁移(Style Transfer)任务,AdaIN的出现使得风格迁移任务从受限于一种风格或者需要lengthy optimization process的情况中摆脱了出来。
AdaIN表明,仅通过归一化统计就可以将风格(style)和内容(content)结合起来。
Karras等人在StyleGAN2中解释了AdaIN这种归一化方法会"discard information in feature maps encoded in the relative magnitudes of activations"。
生成器(Generator)通过将信息sneaking在这些层中来克服信息丢失的问题,但是这却带来了水印问题(water-droplet artifacts)。
对于为什么鉴别器(Discriminator)不能识别水印,作者与读者有同样的困惑。
在StyleGAN2中,AdaIN被重构为Weight Demodulation, 如下所示:
a,b为StyleGAN的细节图,c和d为StyleGAN2的细节图。可以看出
[5]
,normalization中不再需要mean,只计算std即可。并将noise模块移除style box中,如下所示[5]
Weight demodulation的处理流程如下
① 如上图里面的©所示,Conv 3x3
后面的Mod std
被用于对卷积层的权重进行scaling(缩放)
w
i
j
k
′
=
s
i
×
w
i
j
k
w_{ijk}^{'} = s_{i} \times w_{ijk}
wijk′=si×wijk, 这里的
i
i
i表示第
i
i
i个特征图。
② 接着对卷积层的权重进行demod
σ
j
=
(
∑
i
,
k
(
w
i
j
k
′
)
2
)
\sigma_j = \sqrt (\sum_{i,k} (w_{ijk}^{'})^2)
σj=(
i,k∑(wijk′)2)
那么,得到新的卷积层权重为
加一个小的
ϵ
\epsilon
ϵ是为了避免分母为0,保证数值稳定性。尽管这种方式与Instance Norm并非在数学上完全等价,但是weight demodulation同其它normalization 方法一样,使得输出特征图有着standard的unit和deviation。
此外,将scaling参数挪为卷积层的权重使得计算路径可以更好的并行化。这种方式使得训练加速了约40%(从每秒处理37张图片到每秒处理61张图片—不知道作者用的是什么机器)。
StyleGAN图像对鼻子和眼睛等面部特征有很强的位置偏好。作者Karras等人将此归因于progressive growing training (同Karras作品PGGAN[6]
,18年的论文,引用已经有2200左右了,Karras太强了。。。)
Progressive growing指的是:“先训一个小分辨率的图像,训好了之后再逐步过渡到更高分辨率的图像。然后稳定训练当前分辨率,再逐步过渡到下一个更高的分辨率。”
尽管对研究者来说,复现progressive growing非常令人头大(因为Karras他们全都是用tensorflow 1.x写的这些网络)。这需要复杂的循环和策略设计。但是总的来说,progressive growing还是属于一种比较直观的对高分辨率图像合成的coarse-to-fine的方式。这是因为一般的GAN在训练超高分辨率( 102 4 2 1024^2 10242)的图像生成上面相当不稳定,按照作者的话说:
The discriminator will easily distinguish real and fake images, resulting in the generator unable to learn anything during training.
不同于StyleGAN第一代使用progressive growing的策略,StyleGAN2开始寻求其它的设计以便于让网络更深,并有着更好的训练稳定性。
对Resnet结构而言,网络加深是通过skip connection实现的。所以StyleGAN2采用了类似ResNet的残差连接结构(residual block)。对于这些设计,我们使用双线性滤波对前一层进行上/下采样,并尝试学习下一层的残差值(residual value)。
如下图所示,Karras等人使用来自TomTom公司和Adobe研究院的作者提出的“Multi-Scale Gradients for Generative Adversarial Networks” MSG-GAN[7]
来revisit之前的progressive growing的方式,从而更好的利用不同尺度的信息。
正是受MSG-GAN的启发,StyleGAN2设计了一个新的架构来利用图像生成的多个尺度信息(不需要像progressive growing那样麻烦了),他们通过一个resnet风格的跳跃连接在低分辨率的特征映射到最终生成的图像(下图的绿色block)
StyleGAN2的网络结构(受MSG-GAN启发)。
Here is the performance improvement using different approaches. FID越低越好,可以看出对G output加skip和对D 增加residual的组合效果最好。
作者表明MSG-GAN的方式同progressive growing类似,训练的早期更多地依赖低频/分辨率来对最终的输出产生影响。下图显示了特征图对最后输出的贡献情况。对这一点的研究促使Karras等人扩大网络规模,使1024x1024的分辨率对最终输出贡献更大。
StyleGAN1对FFHQ数据集使用了R1正则化。实验结果表明,在评估计算代价的时候,regularization是可以忽略的。实际上,即使每隔16个mini-batch使用regularization的方式,模型效果依旧很不错,因此,在StyleGAN2中,我们可以使用lazy regularization的策略,即对数据分布开始跑偏的时候才使用R1 regularization,其它时候不使用。
Path Length Regularization的意义是使得latent space的插值变得更加smooth和线性。简单来说,当在latent space中对latent vector进行插值操作时,我们希望对latent vector的等比例的变化直接反映到图像中去。即:“在latent space和image space应该有同样的变化幅度(线性的latent space)”(比如说,当你对某一组latent vector进行了5个degree的偏移,那么反映在最后的生成图像中,应该是同样的效果, 如下图所示)。
Karras等人通过对生成器(Generator) 增加了一个loss项来达到这个目标。
代码如下,简单来说就是计算生成器生成的图像对其latent vector的梯度(Jacobian矩阵),
∣
∣
J
w
T
y
∣
∣
2
||J_w^Ty||_2
∣∣JwTy∣∣2就是代码中的pl_lengths
,
a
a
a为pl_mean_var
的移动平均: pl_mean_var
= pl_mean_var
+ 0.01 * (
m
e
a
n
mean
mean(pl_lengths
) - pl_mean_var
)。
这种方法"dramatically facilitates projecting images back into the latent space"(让图像得到更加线性的对应latent vector)。
#---------------------------------------------------------------------------- # Non-saturating logistic loss with path length regularizer from the paper # "Analyzing and Improving the Image Quality of StyleGAN", Karras et al. 2019 def G_logistic_ns_pathreg(G, D, opt, training_set, minibatch_size, pl_minibatch_shrink=2, pl_decay=0.01, pl_weight=2.0): _ = opt latents = tf.random_normal([minibatch_size] + G.input_shapes[0][1:]) labels = training_set.get_random_labels_tf(minibatch_size) fake_images_out, fake_dlatents_out = G.get_output_for(latents, labels, is_training=True, return_dlatents=True) fake_scores_out = D.get_output_for(fake_images_out, labels, is_training=True) loss = tf.nn.softplus(-fake_scores_out) # -log(sigmoid(fake_scores_out)) # Path length regularization. with tf.name_scope('PathReg'): # Evaluate the regularization term using a smaller minibatch to conserve memory. if pl_minibatch_shrink > 1: pl_minibatch = minibatch_size // pl_minibatch_shrink pl_latents = tf.random_normal([pl_minibatch] + G.input_shapes[0][1:]) pl_labels = training_set.get_random_labels_tf(pl_minibatch) fake_images_out, fake_dlatents_out = G.get_output_for(pl_latents, pl_labels, is_training=True, return_dlatents=True) # Compute |J*y|. pl_noise = tf.random_normal(tf.shape(fake_images_out)) / np.sqrt(np.prod(G.output_shape[2:])) pl_grads = tf.gradients(tf.reduce_sum(fake_images_out * pl_noise), [fake_dlatents_out])[0] pl_lengths = tf.sqrt(tf.reduce_mean(tf.reduce_sum(tf.square(pl_grads), axis=2), axis=1)) pl_lengths = autosummary('Loss/pl_lengths', pl_lengths) # Track exponential moving average of |J*y|. with tf.control_dependencies(None): pl_mean_var = tf.Variable(name='pl_mean', trainable=False, initial_value=0.0, dtype=tf.float32) pl_mean = pl_mean_var + pl_decay * (tf.reduce_mean(pl_lengths) - pl_mean_var) pl_update = tf.assign(pl_mean_var, pl_mean) # Calculate (|J*y|-a)^2. with tf.control_dependencies([pl_update]): pl_penalty = tf.square(pl_lengths - pl_mean) pl_penalty = autosummary('Loss/pl_penalty', pl_penalty) # Apply weight. # # Note: The division in pl_noise decreases the weight by num_pixels, and the reduce_mean # in pl_lengths decreases it by num_affine_layers. The effective weight then becomes: # # gamma_pl = pl_weight / num_pixels / num_affine_layers # = 2 / (r^2) / (log2(r) * 2 - 2) # = 1 / (r^2 * (log2(r) - 1)) # = ln(2) / (r^2 * (ln(r) - ln(2)) # reg = pl_penalty * pl_weight return loss, reg #----------------------------------------------------------------------------
感谢阅读StyleGAN2的重要特性分析,谢谢~
[1] StyleGAN2–medium
[2] StyleGAN: A Style-Based Generator Architecture for Generative Adversarial Networks
[3] StyleGAN2: Analyzing and Improving the Image Quality of StyleGAN
[4] StyleGANv2 Explained!
[5] GAN — StyleGAN & StyleGAN2 – Jonathan Hui
[6] PGGAN
[7] MSG-GAN
[8] StyleGAN2-pytorch
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。