[关闭]
@Team 2019-11-24T09:18:14.000000Z 字数 11596 阅读 248

深入理解风格迁移三部曲(三)--FUNIT

陈扬


FUNIT: Few-Shot Unsupervised Image-to-Image Translation

project:https://nvlabs.github.io/FUNIT/

作者:陈扬

简介

无监督的图像到图像转换方法学习利用图像的非结构化(UNlabel)数据集将给定类中的图像映射到不同类中的类似图像。在ICCV2019上,NVIDIA-Lab发表了Image-to-image最新的研究成果,基于少量类别学习的FUNIT.笔者在CVPR2020的投稿中正好也大量涉及到了image2image的实验,其中样本不均衡是对unpair-image2image任务来说同样是一个很大的问题,而few-shot Learning则是其中一种极端情况.于此同时,paper中令我影响深刻的是作者做了大量的Ablation Study ,充分的实验证明了算法的有效性以及鲁棒性.


前言

在我们展开深入理解FUNIT之前,我们来看一下他的前身UNIT,由NVIDIA-Lab在2017年提出,该文章首次提Image-Image Translation这个概念,将计算机视觉和计算机图形学的许多任务总结进去,分为一对多和多对一的两类转换任务,包括CV里的边缘检测,图像分割,语义标签以及CG里的mapping labels or sparse user inputs to realistic images.

image-20191124143200833

该文章定义了作为两个图像域.传统的supervised Image-to-image 通过对图像域进行采样,求其联合概率分布

,通过Encoder-Decoder的思想,作者定义了两个E和G,希望使得z=E(X)在latent space上近可能的分布一致.意味着当我们同时对
时,我们希望得出:

这样,我们得到了两个Domain下image的一致表示,再通过令,从latent space中重构,

因此,我们两个采样下的经过后得到了,再把:

通过Adv_loss对抗学习跨域生成图片的效果.

可能细心的你以及发现了这是不是很类似VAE-GAN吗?是的.

作者通过联合训练4个网络的三个来训练整个网络:


VAE的目标是minimize source domain to latent space's KL diversity and latent space to destination domain's KL diversity(我觉得中文太拗口了,这句话实在是说不来)来最小化变分上界,VAE的定义如下:

对抗:GAN_LOSS被用于确保翻译图像类似图像在目标域.定义如下:

循环一致性:由于shared latent-space假设暗含了循环一致性约束,因此我们在提出的框架中实施循环一致性约束,以进一步规范不适定的无监督图像间转换问题。产生的信息处理流称为循环重建流,定义如下:

训练好的网络,我们可以通过对latent sapce的latent variable重编码,进而把输入图像迁移到各个域中:

image-20191124154047242

image-20191124153813482


few-shot

虽然UNIT以及其变种(cycleGAN等等)已经表现得非常成功,现有的无监督图像到图像转换模型在两个方面受到限制。首先,如果在训练时只给出很少的图像,它们的样本效率低,产生差的转换输出。

其次,学习的模型仅限于在两个类之间转换图像。尽管新任务与原始任务之间存在相似性,但是用于一个转换任务的训练模型不能直接重用于新任务。

例如,即使猫与老虎有很大的相似性,也不能将哈士奇与猫的转换模型重新用于哈士奇 与老虎的转换。

image-20191124154554577

同样,作者通过few-shot学习得到的图像同样可以应用于少样本分类,在Ablation Study中作者做了非常详细的比较,进而验证其有效性.


算法描述

前提假设:为了训练 FUNIT,我们使用来自一组对象类(例如各种动物物种的图像)的图像,称为源类。我们不假设任何两个类别之间配对图像的存在.

使用源类图像来训练多级无监督图像到图像的转换模型。

在测试过程中,我们为模型提供了 一些来自新对象类的图像,称为目标类。该模型必须利用少 数目标图像将任何源类图像转换为目标类的同类图像。

框架由条件图像生成器 G 和多任务对抗判别器 D 组成。它采用一个图像作为输入,我们的生成 器 G 同时采用内容图像 和一组类别图像K:{}作为输入,并通过生成器G产生输出图像:


我们将 G 称为少样本图像转换器。G 将输入内容图像映射到输出图像,使得 x 看起来像属于对象类的图像,并且具有纹理结构上的相似性。设 分别表示源类集和目标类集。训练时,
在测试时,G 从未看到的目标类
获取一些图像作为类图像,并将从任何源类采样的 图像映射到目标类 的类似图像。

多任务对抗判别器

判别器 D 通过同时解决多个对抗分类任务来训练。 每个任务是二分类任务,确定输入图像是源类的实际图像还来自 G 生成的转换输出.这个对抗训练实际上是类似前面提到过的,不在赘述.


框架设计

Image From FUNIT_chs

少样本图像转换器由三个子网络组成:内容编码器(Content Encoder),类编码器(Class Encoder)和解码器(Decoder).


内容编码器由几个 2D 卷积层组成,后跟几个ResBlock。它将输入内容图像 x 映射到内容潜码 ,其代表空间特征映射。

image-20191124161458061

  1. class ContentEncoder(nn.Module):
  2. def __init__(self, downs, n_res, input_dim, dim, norm, activ, pad_type):
  3. super(ContentEncoder, self).__init__()
  4. self.model = []
  5. self.model += [Conv2dBlock(input_dim, dim, 7, 1, 3,
  6. norm=norm,
  7. activation=activ,
  8. pad_type=pad_type)]
  9. for i in range(downs):
  10. self.model += [Conv2dBlock(dim, 2 * dim, 4, 2, 1,
  11. norm=norm,
  12. activation=activ,
  13. pad_type=pad_type)]
  14. dim *= 2
  15. self.model += [ResBlocks(n_res, dim,
  16. norm=norm,
  17. activation=activ,
  18. pad_type=pad_type)]
  19. self.model = nn.Sequential(*self.model)
  20. self.output_dim = dim
  21. def forward(self, x):
  22. return self.model(x)

类编码器由几个 2D 卷积层组成,后面是样本轴平滑操作。具体地说,它首先映射 K个类别图像

映射到中间潜在向量,然后计算中间潜在向量的平均值,以获得最终的潜码

image-20191124162041845

  1. class ClassModelEncoder(nn.Module):
  2. def __init__(self, downs, ind_im, dim, latent_dim, norm, activ, pad_type):
  3. super(ClassModelEncoder, self).__init__()
  4. self.model = []
  5. self.model += [Conv2dBlock(ind_im, dim, 7, 1, 3,
  6. norm=norm,
  7. activation=activ,
  8. pad_type=pad_type)]
  9. for i in range(2):
  10. self.model += [Conv2dBlock(dim, 2 * dim, 4, 2, 1,
  11. norm=norm,
  12. activation=activ,
  13. pad_type=pad_type)]
  14. dim *= 2
  15. for i in range(downs - 2):
  16. self.model += [Conv2dBlock(dim, dim, 4, 2, 1,
  17. norm=norm,
  18. activation=activ,
  19. pad_type=pad_type)]
  20. self.model += [nn.AdaptiveAvgPool2d(1)]
  21. self.model += [nn.Conv2d(dim, latent_dim, 1, 1, 0)]
  22. self.model = nn.Sequential(*self.model)
  23. self.output_dim = dim
  24. def forward(self, x):
  25. return self.model(x)

解码器由几个自适应实例正规化(AdaIN)和残差块Resblock组成,后面跟着一些上 采样卷 积层。 AdaIN 残余 块是使用 AdaIN [18]作为正则化层的残余块。对于每个样本,AdaIN 首先将每个通道中样本的激活函数标准化为零均值和单位方差。然后它会缩放激活使用一组标量和偏置组成的学习仿射变换(通过两层全连接网络FC,使用自适应地计算仿射变换参数)。

image-20191124162355095

  1. class Decoder(nn.Module):
  2. def __init__(self, ups, n_res, dim, out_dim, res_norm, activ, pad_type):
  3. super(Decoder, self).__init__()
  4. self.model = []
  5. self.model += [ResBlocks(n_res, dim, res_norm,
  6. activ, pad_type=pad_type)]
  7. for i in range(ups):
  8. self.model += [nn.Upsample(scale_factor=2),
  9. Conv2dBlock(dim, dim // 2, 5, 1, 2,
  10. norm='in',
  11. activation=activ,
  12. pad_type=pad_type)]
  13. dim //= 2
  14. self.model += [Conv2dBlock(dim, out_dim, 7, 1, 3,
  15. norm='none',
  16. activation='tanh',
  17. pad_type=pad_type)]
  18. self.model = nn.Sequential(*self.model)
  19. def forward(self, x):
  20. return self.model(x)
  21. class MLP(nn.Module):
  22. def __init__(self, in_dim, out_dim, dim, n_blk, norm, activ):
  23. super(MLP, self).__init__()
  24. self.model = []
  25. self.model += [LinearBlock(in_dim, dim, norm=norm, activation=activ)]
  26. for i in range(n_blk - 2):
  27. self.model += [LinearBlock(dim, dim, norm=norm, activation=activ)]
  28. self.model += [LinearBlock(dim, out_dim,
  29. norm='none', activation='none')]
  30. self.model = nn.Sequential(*self.model)
  31. def forward(self, x):
  32. return self.model(x.view(x.size(0), -1))

生成器整体代码:

  1. class FewShotGen(nn.Module):
  2. def __init__(self, hp):
  3. super(FewShotGen, self).__init__()
  4. nf = hp['nf']
  5. nf_mlp = hp['nf_mlp']
  6. down_class = hp['n_downs_class']
  7. down_content = hp['n_downs_content']
  8. n_mlp_blks = hp['n_mlp_blks']
  9. n_res_blks = hp['n_res_blks']
  10. latent_dim = hp['latent_dim']
  11. self.enc_class_model = ClassModelEncoder(down_class,
  12. 3,
  13. nf,
  14. latent_dim,
  15. norm='none',
  16. activ='relu',
  17. pad_type='reflect')
  18. self.enc_content = ContentEncoder(down_content,
  19. n_res_blks,
  20. 3,
  21. nf,
  22. 'in',
  23. activ='relu',
  24. pad_type='reflect')
  25. self.dec = Decoder(down_content,
  26. n_res_blks,
  27. self.enc_content.output_dim,
  28. 3,
  29. res_norm='adain',
  30. activ='relu',
  31. pad_type='reflect')
  32. self.mlp = MLP(latent_dim,
  33. get_num_adain_params(self.dec),
  34. nf_mlp,
  35. n_mlp_blks,
  36. norm='none',
  37. activ='relu')
  38. def forward(self, one_image, model_set):
  39. # reconstruct an image
  40. content, model_codes = self.encode(one_image, model_set)
  41. model_code = torch.mean(model_codes, dim=0).unsqueeze(0)
  42. images_trans = self.decode(content, model_code)
  43. return images_trans
  44. def encode(self, one_image, model_set):
  45. # extract content code from the input image
  46. content = self.enc_content(one_image)
  47. # extract model code from the images in the model set
  48. class_codes = self.enc_class_model(model_set)
  49. class_code = torch.mean(class_codes, dim=0).unsqueeze(0)
  50. return content, class_code
  51. def decode(self, content, model_code):
  52. # decode content and style codes to an image
  53. adain_params = self.mlp(model_code)
  54. assign_adain_params(adain_params, self.dec)
  55. images = self.dec(content)
  56. return images

通过使用这种转换器设计,我们的目标是使用内容编码 器提取具有类不变的潜在表示(例如,对象姿势)并使用类编码器提取类特定的潜在表示(例如,对象外观)。通过经由 AdaIN 层将类别潜码馈送到解码器,我们让类图像控制 全局外观(例如,对象外观),而内容图像确定局部结构(例如,眼睛的位置)。


损失函数的设计

我们通过解决由下式给出的极小极大优化问题来训练所提 出的 FUNIT 框架:


其中, 分别是 GAN 损失,内容图像重建损失和特征匹配损失。

对抗损失仅使用类的相应二分类预测分数来计算损失。


内容重建损失有助于 G 学习转换模型。 具体地,当对输入内容图像和输入类图像使用相同图像时(在这种情况下 K = 1),损失促使 G 生成与输入相同的输出图像(重构一致性,在cycleGAN在叫identity Loss).

特征匹配损失使训练正常化。 我们首先通过从 D 重新移 动最后一个(预测)层来构造一个特征提取器,称为 。 然后我们使用 从转换输出 和 类图像 中提取特征并最小化 :


实验部分

我非常想重点讲一下实验部分,我看完这篇文章的实验部分做的太好了,给了我在今后的科研中一个非常好的榜样.

重点说一下评价指标啊,在image-to-image这个领域据我所知,肉眼观察法是最好的评价指标,但是他也会带来一些个人的主观性,我们看看作者是如何通过实验说服我的:

先放一张漂亮的大图:

image-20191124164040054

性能指标。作者使用几个标准进行评估。首先,作者测量转换是否类似于目标类的图像。其次,作者检查在转换期间是否保留了类不变内容。第三,作者量化输出图像的写实照片。最后,作者测量该模型是否可用于生成目标类的图像分布。

image-20191124164918260

量化对比:User performance score(肉眼观察法):

image-20191124164847559

少样本分类准确度:

image-20191124164500507

image-20191124164530895

Inception Score(我很困惑这个指标真的有用吗?)和FID(这个确实还有点用):

image-20191124164640897

Ablation Study

(都能坚持看到这里了都,我觉得英文说得更明白了😁):

we analyze impact of the content image reconstruction loss weight on the Animal Faces dataset.The table shows that λR = 0.1 provides a good trade-off, and we used it as the default value throughout the paper. Interestingly, a very small weight value λR = 0.01 results in degrading performance on both
content preservation and translation accuracy.

image-20191124165155042

image-20191124165220954

Latent Space Interpolation

we use t-SNE to visualize the class code in a two dimensional space. It can be seen that images from similar classes are grouped together in the class embedding space.

image-20191124165306611

we find that by interpolating between two source classes (Siamese cat and Tiger) we can sometimes generate a target class (Tabby cat) that the model has never observed. This suggests that the class encoder learns a general class-specific representa- tion, thus enabling generalization to novel classes.

总结

这篇文章很好的结合了few-shot解决UNpair image-to-image的样本不均衡的问题,更重要的是其及其接近实际的生产应用环境,是一篇无论是从理论上还是实际应用角度出发来看,都具有重大指导意义的文章,笔者同样很喜欢这个工作.

如果您是一位C9高校的老师,看到了这篇文章,可以在知乎上联系,笔者目前正就读于中国海洋大学计算机科学与技术专业,发表CCF_C类一作一篇,SCI_1区3作,一篇文章在投,希望有大佬把我领走了(-╹▽╹-).

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注