可随意转载! Update2022.08.11

前言

图片风格迁移(Style Transfer)最近5年非常火,它的前序技术叫纹理迁移(Texture Transfer)是图形学的一个重要分支,一般的方法是:先做纹理迁移,再做图像重建(Image Reconstruction)。

大神Gatys把神经网络引入了纹理迁移领域,利用VGG分类算法(附录)做图片高层语义的提取,然后训练过程中同时最小化原图和风格图的内容+风格相似性,图片重建得到迁移结果。

具体来说,Gatys提出了Gram矩阵,利用Grams矩阵特性优化风格相似。同时用VGG优化内容相似。经过多轮训练迭代内容和风格同时相似,这就是神经风格迁移(Neural Style Transfer)。

为什么Gram矩阵能表示(represent)风格?

论文《Demystifying Neural Style Transfer》的解释是:最小化Gram矩阵等同于最小化最大均值差异(maximum mean discrepancy, MMD)。通俗的说,最小化Gram矩阵就是最小化原图和风格图的激活分布(activation distributions),而这个激活分布就是style。

MMD:对于分布f1、f2,假如MMD=0,那么f1和f2就是同分布。

BN模块的均值和方差(mean & variance)正好包含了MMD的性状,因此最小化L2 Norm等同于最小化Gram矩阵。

网上StyleGAN pytorch版本比较多,本文以代码比较清晰、比较流行的rosinality版本为例。

一、G网络解读

1. style模块

这个style模块就是Z空间转W空间的网络。由一个PixelNorm接n_mlp个(8)EqualLinear。

        layers = [PixelNorm()]

        for i in range(n_mlp):
            layers.append(
                EqualLinear(
                    style_dim, style_dim, lr_mul=lr_mlp, activation="fused_lrelu"
                )
            )

        self.style = nn.Sequential(*layers)

注意:这个不是StyleConv

注意:StyleGAN2不需要PixelNorm,这个实现应该是多余了。

2. G网络尺寸

从4×4 逐步递增到1024×1024,这里是定义了以尺寸为key的map,在后面组建StyleBlock的时候会用到。

        self.channels = {
            4: 512,
            8: 512,
            16: 512,
            32: 512,
            64: 256 * channel_multiplier,
            128: 128 * channel_multiplier,
            256: 64 * channel_multiplier,
            512: 32 * channel_multiplier,
            1024: 16 * channel_multiplier,
        }

self.channels[4]等于512。

3. conv1(StyleConv)

是StyleConv(上图的灰色块),具体是ModulatedConv2d + NoiseInjection组合

self.conv1 = StyledConv(
            self.channels[4], self.channels[4], 3, style_dim, blur_kernel=blur_kernel
        )

4. ToRGB

在PGGAN中使用的是1×1的conv,目的是保证输出到下一层的通道数是3。这里改成了ModulatedConv2d(请看下面的函数解析部分),作用跟原来1×1卷积相同。

self.to_rgb1 = ToRGB(self.channels[4], style_dim, upsample=False)
class ToRGB(nn.Module):
    def __init__(self, in_channel, style_dim, upsample=True, blur_kernel=[1, 3, 3, 1]):
        super().__init__()
        ...
        self.conv = ModulatedConv2d(in_channel, 3, 1, style_dim, demodulate=False)
        self.bias = nn.Parameter(torch.zeros(1, 3, 1, 1))

    def forward(self, input, style, skip=None):
        out = self.conv(input, style)
        out = out + self.bias
        ...

5. G block

17层noise,16层StyleConv,8层ToRGB

每个Block由2个StyleConv和1个to_rgb构成。

        # 4x4,8x8,16x16,32x32,64x64,128x128,256x256,512x512 总共8个block
        # 17层对应4x4~1024x1024共9种分辨率
        for layer_idx in range(self.num_layers):
            res = (layer_idx + 5) // 2
            shape = [1, 1, 2 ** res, 2 ** res]
            self.noises.register_buffer(f"noise_{layer_idx}", torch.randn(*shape))

        # i取值范围[3,10]
        for i in range(3, self.log_size + 1):
            # 这里用到了前面定义的channels map,比如2的三次方:8
            out_channel = self.channels[2 ** i]

            # 8个style_block
            self.convs.append(
                StyledConv(
                    in_channel,
                    out_channel,
                    3,
                    style_dim,
                    upsample=True,
                    blur_kernel=blur_kernel,
                )
            )

            # 8个style_block
            self.convs.append(
                StyledConv(
                    out_channel, out_channel, 3, style_dim, blur_kernel=blur_kernel
                )
            )

            # 8个ToRGB
            self.to_rgbs.append(ToRGB(out_channel, style_dim))
2个StyleBlock+1个toRGB

二、工具类/方法解读

EqualLinear模块

出自progressiveGAN,跟F.linear基本一样只是改成了高斯weight。

ConstantInput

高斯常量输入

self.input = ConstantInput(self.channels[4])        

ModulatedConv2d

考虑到最上图c部分,modulation、conv、norm组合,就是这个函数。modulation缩放了特征图等同于修改了conv的权重参数。IN的目的应该是归一化这个缩放,作者假定输入激活层是独立同分布(i.i.d),归一化目标可以直接用下面标准差实现:

输出本质可以用conv层的权重的标准差来直接表示

以上就是这个函数的原理。

Blur

《Making Convolutional Networks Shift-Invariant Again》论文参数了信号处理中利用低通滤波(low-pass filtering)消除混叠(anti-aliasing)的常见方法(Blur),并证明了在CNN中它也是很有作用的。

Upsample/Downsample

make_kernel

upfirdn2d

NoiseInjection

todo

FusedLeakyReLU

todo

附录

Norm为什么可以影响Style,什么是AdaIN?(英文)

所有stylegan比较(英文)

可视化StyleGAN2(英文)

anti-aliased论文(英文)

VGG网络结构

各种Norm理解

BN

IN

CIN

条件IN。

参数γ和β从vector中取

AdaIN

自适应IN,可以迁移任意多个styles。

将Content image的feature 转换,使其与style image的feature有相同的方差和均值即可实现style transfer

把前面算法的γ和β替换了风格图的均值和标准差