查看原文
其他

CVPR 2019 开源论文 | 基于空间自适应归一化的图像语义合成

武广 PaperWeekly 2021-09-10


作者丨武广

学校丨合肥工业大学硕士生

研究方向丨图像生成


深度学习在算力的推动下不断的发展,随着卷积层的堆叠,模型的层数是越来越深,理论上神经网络中的参数越多这样对数据的拟合和分布描述就能越细致。然而简单的堆叠卷积层又会引起梯度消失和过拟合的问题,伴随着解决方案下残差网络、归一化和非线性层被提出。


本文将对 NVIDIA 近期的论文 Semantic Image Synthesis with Spatially-Adaptive Normalization 进行解读,这篇论文提出了适合保留语义信息的 Spatially-Adaptive Normalization(空间自适应归一化),同时文章在实现细节上也很有参考意义。




论文引入


Batch Normalization (BN) 在 Inception v2 [1] 网络中被提出,它的设计之初是为了解决 Internal Covariate Shift 问题,也就是在训练过程中,隐层的输入分布老是变来变去。因为深层神经网络在做非线性变换前的激活输入值随着网络深度加深或者在训练过程中,其分布逐渐发生偏移或者变动,这导致了训练收敛慢,也就是数据整体分布逐渐往非线性函数的取值区间的上下限两端靠近。所以这导致反向传播时低层神经网络的梯度消失,这是训练深层神经网络收敛越来越慢的本质原因。


我们知道数据白化,是对输入数据分布变换到 0 均值,单位方差的正态分布,这样会使神经网络较快收敛。如果这种"白化处理”作用在每一个隐层上,是不是可以稳定数据分布的同时加速收敛?


这个问题的答案就是 BN 设计的根源了,BN 就是通过一定的规范化手段,对于每个隐层神经元,把逐渐向非线性函数映射后向取值区间极限饱和区靠拢的输入分布强制拉回到均值为 0 方差为 1 的比较标准的正态分布,使得非线性变换函数的输入值落入对输入比较敏感的区域,以此避免梯度消失问题。


掌握了 Batch Normalization 的作用后,其实后期演进的Instance Normalization [2]、Layer Normalization、Group Normalization、Weight Normalization 在原理上的作用是相近的,都是为了稳定模型训练,只不过不同的归一化方法适用于不同的场合。
比如在做图像翻译时,往往设置的 BatchSize 为 1 或者模型训练时 BatchSize 取值很小,这个时候 Batch Normalization 在对这批数据取均值和方差,而这批数据由于量不够,很难描述整体数据,此时就可以尝试使用 Layer Normalization 或者 Group Normalization。


言归正传,回到本文的主题 Spatially-Adaptive Normalization (空间自适应归一化),已经有这么多好用的归一化方法了为啥还要 Spatially-Adaptive Normalization 呢?
说清这个问题前,我们要来看一下 Conditional Batch Normalization (CBN) [3],以图像和其标签为例,CBN 就是通过 BN 将图片底层信息和标签信息结合,这样处理的好处就是让图像的标签(语义信息)指导图像的特征表达。
这种标签信息往往是由热编码或者是低纬的向量通过感知层代替 BN 中的参数 γ 和 β,如果这个标签信息是一张语义分割图呢?这种输入到感知层的方式将不能充分表达图像的语义信息了,此时 Spatially-Adaptive Normalization 便派上用场了,它正是处理这种标签信息是一张语义分割图时如何让图像的标签(语义信息)指导图像的特征表达


总结一下论文的优势:


  • 提出了 Spatially-Adaptive Normalization,实现了给定输入语义布局的情况下合成逼真的图像。
  • 可以根据输入图像和语义图指导合成语义信息下的图像。


Spatially-Adaptive Normalization


如果熟悉 Conditional Batch Normalization 的话,Spatially-Adaptive Normalization(SPADE) 自然是很容易理解的,简单点说就是将 Conditional Batch Normalization 中的"标签”经过多层感知机换成了语义分割图经过卷积层而已。当然你要是不熟悉也没问题,我们接下来详细说明,在说明之前我们看一下 Spatially-Adaptive Normalization 的整体结构。


我们用表示语义分割图,其中 L 表示语义标签的整数集,H 和 W 是图像的高度和宽度。论文的最终目的就是为了学习可以将输入分割掩模 m 转换为逼真图像的模型结构。


设  表示给定一批 N 个样本的深卷积网络的第 i 层激活层。设  是图层中通道的数量。设  和  是各图层中激活层的高度和宽度。Spatially-Adaptive Normalization 可以表示为: 


可以看出以通道方式进行归一化,其中 n∈N, c∈Ci, y∈,  x∈是归一化处理前的输入,为通道 c 中输入的平均值和标准差:



是 Spatially-Adaptive Normalization 学习参数,它们是通过语义分割图 m 经过卷积层得到的参数,此时的是包含语义分割图的语义信息的,同时也是语义分割图 feature map 表示,这样学习到的参数将保留语义分割图的充分的语义信息,也实现了输入的归一化处理。


对于一般的归一化(BN、LN、IN、GN)只是对网络层中输入进行不同维度下求均值和标准差操作,例如Instance Normalization 是对 H 和 W 求均值和标准差,再进行归一化,这是用于图像的风格描述。
假定我们对一个 3 通道的 RGB 图像求像素上的均值和标准差,不同风格的图像呈现的均值和方差是有差异的,比如艳丽的风格往往是像素特别丰富的,此时的图像像素均值自然是大的,阴暗风格对应的像素均值自然是小的。但是这仅仅是对输入的本身进行操作,这个过程没有语义的表示,而 SPADE 将语义分割图为输入提供了丰富的语义信息,这个可以实现输入下语义的传递和表达,这也是 SPADE 真正的意义所在。


语义图像的生成


介绍的 SPADE 只是实现语义图像合成的手段,并不是文章的最终目的,论文的目标是实现根据语义分割图实现语义图像的生成,也就是下面这个过程:



我们看一下完整的模型结构:


模型中的 Image Encoder 其实就是为了实现 VAE [4] 和 GAN [5] 结合的作用,目的是为语义图像的合成提供参考,也就是告诉模型按照语义分割指导合成的图像应该和我的整体风格类似。
比如一批山水画作 Image Encoder 得到的特征隐变量通过 KL 映射到先验分布(高斯分布)上,通过采样根据语义分割图像的指导,解码生成包含语义信息的另一幅山水画。之所以加入 VAE 让图像编码的隐变量映射到先验分布上是为了在测试阶段可以通过随机采样实现语义图像的生成而不是再送入一张指导图像。


接下来我们将对模型细节进一步梳理,主要突出 SPADE 和 SPADE ResBlk 的设计介绍。


SPADE


这部分的结构如下:


首先对语义分割图进行 Resize,这步的作用是为了让语义分割图的输入尺寸和要进行归一化的输入尺寸保持一致,此处的 Resize 都是最近邻下采样实现。得到与输入相同尺寸 map 后,先通过一个 3×3 卷积层得到 128 通道的语义分割图 feature map,这一步主要是为了将原始的语义分割图3通道进行升维到 128 通道,接着就是分别经过两个 3×3 卷积层得到对应的 γ 和 β,此处的 k要根据实际而定。得到 γ 和 β 后就可以和输入(已经和均值和标准差处理过)进行操作。
在源码中,在 SPADE 内部就进行了一个残差操作,图中没有画出而已,结合分析,我们用 tensorflow 对 SPADE 进行实现:

def spade(segmap, x_init, channels, use_bias=True, sn=False, scope='spade'):
    with tf.variable_scope(scope):
        x = param_free_norm(x_init) # 进行均值,标准差计算,(x - x_mean) / x_std

        _, x_h, x_w, _ = x_init.get_shape().as_list()
        _, segmap_h, segmap_w, _ = segmap.get_shape().as_list()

        factor_h = segmap_h // x_h  # 确定下采样的比例
        factor_w = segmap_w // x_w

        segmap_down = down_sample(segmap, factor_h, factor_w) # 最近邻下采样

        # 第一步卷积处理
        segmap_down = conv(segmap_down, channels=128, kernel=5, stride=1
                           pad=2, use_bias=use_bias, sn=sn,scope='conv_128')
        segmap_down = relu(segmap_down)

        # 两个卷积得到segmap_gamma和segmap_beta
        segmap_gamma = conv(segmap_down, channels=channels, kernel=5, stride=1
                            pad=2, use_bias=use_bias, sn=sn,scope='conv_gamma')
        segmap_beta = conv(segmap_down, channels=channels, kernel=5, stride=1
                           pad=2, use_bias=use_bias, sn=sn,scope='conv_beta')

        # 引入残差
        x = x * (1 + segmap_gamma) + segmap_beta

        return x


def param_free_norm(x, epsilon=1e-5):
    x_mean, x_var = tf.nn.moments(x, axes=[12], keep_dims=True)
    x_std = tf.sqrt(x_var + epsilon)

    return (x - x_mean) / x_std


def down_sample(x, scale_factor_h, scale_factor_w):
    _, h, w, _ = x.get_shape().as_list()
    new_size = [h // scale_factor_h, w // scale_factor_w]

    return tf.image.resize_nearest_neighbor(x, size=new_size)


SPADE ResBlk


我们再来看 SPADE ResBlk 的结构:


它是由主路下两个 SPADE 和残差分路 SPADE 构成,分路的 SPADE 主要是为了保证残差块之前和之后的通道数量不同的情况下的操作(这步主要发生在 channel 变化的阶段,如生成器中 1024 到 512 的过程),我们用 tensorflow 实现 SPADE ResBlk:

def spade_resblock(segmap, x_init, channels, use_bias=True, sn=False, scope='spade_resblock'):
    channel_in = x_init.get_shape().as_list()[-1]
    channel_middle = min(channel_in, channels) # 由于是生成阶段,channel是逐渐变小的

    with tf.variable_scope(scope):
        x = spade(segmap, x_init, channel_in, use_bias=use_bias, sn=False, scope='spade_1')
        x = lrelu(x, 0.2)
        x = conv(x, channels=channel_middle, kernel=3, stride=1
                 pad=1, use_bias=use_bias, sn=sn, scope='conv_1')

        x = spade(segmap, x, channels=channel_middle, use_bias=use_bias, 
                  sn=False, scope='spade_2')
        x = lrelu(x, 0.2)
        x = conv(x, channels=channels, kernel=3, stride=1, pad=1
                 use_bias=use_bias, sn=sn, scope='conv_2')

        if channel_in != channels:
            x_init = spade(segmap, x_init, channels=channel_in, use_bias=use_bias, 
                           sn=False, scope='spade_shortcut')
            x_init = conv(x_init, channels=channels, kernel=1, stride=1
                          use_bias=False, sn=sn, scope='conv_shortcut')

        return x + x_init


整个生成器的架构就可以由 SPADE ResBlk 进行设计,判别器首先将语义分割图和真实(生成)进行 concat 再逐步下采样,由于这是图像翻译的过程,默认情况下 batch size 取 1,所以在判别器下不适合用 BN,而使用的 IN 作为归一化处理(个人感觉 LN 得到的效果应该也会不错的)。图像编码器的架构是个下采样过程,也是采用 IN,输出为均值和方差,用于拟合先验分布(高斯分布)。


论文涉及到训练中的损失函数主要是生成对抗损失和拟合先验分布的 KL loss,对抗损失默认采用的是 hinge loss,还涉及到特征匹配损失,生成器和判别器的正则化损失以及衡量生成和真实图像的 VGG loss,感兴趣的可以阅读源码进一步了解:
Pytorch 版本https://github.com/NVlabs/SPADE
TensorFlow 版本:https://github.com/taki0112/SPADE-Tensorflow


实验


实施细节上,采用 Spectral Norm [6] 应用于生成器和判别器中的所有层。生成器和判别器的学习率分别设置为 0.0001 和  0.0004。优化器使用 ADAM 并设置 β1=0 ,β2=0.999。分别在 COCO-Stuff、ADE20K、ADE20K-outdoor、Cityscapes 、FlickrLandscapes 数据集上进行实验。


在定性上,结果还是很有说服力的:



在定量上也展示了优势:



在模型参数上也有一定优势:



总结


论文提出了 Spatially-Adaptive Normalization (空间自适应归一化),该方法对 Conditional Batch Normalization (条件批归一化)进行了改进,将标签信息换成了语义分割图,通过卷积层代替多层感知层为图像合成提供了丰富的语义信息。同时这种转换也不仅仅适用于语义分割图,甚至可以在语音和视频中得到改进和发展。

参考文献


[1] S. Ioffe and C. Szegedy. Batch normalization: Accelerating deep network training by reducing internal covariate shift. In International Conference on Machine Learning (ICML), 2015.

[2] D. Ulyanov, A. Vedaldi, and V. Lempitsky. Instance normalization: The missing ingredient for fast stylization. arxiv 2016. arXiv preprint arXiv:1607.08022, 2016. [3] V. Dumoulin, J. Shlens, and M. Kudlur. A learned representation for artistic style. In International Conference on Learning Representations (ICLR), 2016.

[4] Kingma D P, Welling M. Auto-encoding variational bayes[J]. arXiv preprint arXiv:1312.6114, 2013.

[5] Goodfellow I, Pouget-Abadie J, Mirza M, et al. Generative adversarial nets[C]//Advances in neural information processing systems. 2014: 2672-2680.

[6] T. Miyato, T. Kataoka, M. Koyama, and Y. Yoshida. Spectral normalization for generative adversarial networks. In International Conference on Learning Representations (ICLR), 2018.




点击以下标题查看更多往期内容: 





#投 稿 通 道#

 让你的论文被更多人看到 



如何才能让更多的优质内容以更短路径到达读者群体,缩短读者寻找优质内容的成本呢?答案就是:你不认识的人。


总有一些你不认识的人,知道你想知道的东西。PaperWeekly 或许可以成为一座桥梁,促使不同背景、不同方向的学者和学术灵感相互碰撞,迸发出更多的可能性。 


PaperWeekly 鼓励高校实验室或个人,在我们的平台上分享各类优质内容,可以是最新论文解读,也可以是学习心得技术干货。我们的目的只有一个,让知识真正流动起来。


📝 来稿标准:

• 稿件确系个人原创作品,来稿需注明作者个人信息(姓名+学校/工作单位+学历/职位+研究方向) 

• 如果文章并非首发,请在投稿时提醒并附上所有已发布链接 

• PaperWeekly 默认每篇文章都是首发,均会添加“原创”标志


📬 投稿邮箱:

• 投稿邮箱:hr@paperweekly.site 

• 所有文章配图,请单独在附件中发送 

• 请留下即时联系方式(微信或手机),以便我们在编辑发布时和作者沟通




🔍


现在,在「知乎」也能找到我们了

进入知乎首页搜索「PaperWeekly」

点击「关注」订阅我们的专栏吧



关于PaperWeekly


PaperWeekly 是一个推荐、解读、讨论、报道人工智能前沿论文成果的学术平台。如果你研究或从事 AI 领域,欢迎在公众号后台点击「交流群」,小助手将把你带入 PaperWeekly 的交流群里。


▽ 点击 | 阅读原文 | 下载论文 & 源码

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存