Advertisement

Pytorch学习笔记-第八章

阅读量:

Pytorch学习笔记-第八章风格迁移

  • utils

    • Gram矩阵
  • PackerVGG

  • transformer

  • main

    • 训练
    • 生成图片

记录一下个人学习和使用Pytorch中的一些问题。强烈推荐 《深度学习框架PyTorch:入门与实战》.写的非常好而且作者也十分用心,大家都可以看一看,本文为学习第八章风格迁移的学习笔记。

主要分析实现代码里面main,transformer,PackedVGG,utils这4个代码文件完成整个项目模型结构定义,训练及生成,还有输出展示的整个过程。

utils

这是项目中用到的一些额外工具包,对visdom封装更方便显示多个点以及网格显示图片这个老朋友就不多说了。此外还有一个图像里面常用的BN操作(注意,这里的BN时IBN既每个样本一个标准化,而不是整批的标准化,而且BN的均值标准差不是一般的0.5时专人计算过的imagenet上的均值和标准差)的函数以及获取封装的从路径获取风格图片的函数,还有一个在这种风格迁移任务里用到的计算Gram矩阵的函数。

Gram矩阵

Gram是用来描述图像整体风格特征的一种矩阵。假设输入的图像尺寸是BxCxHxW,那么得到的Gram矩阵的尺寸是BxCxC既只于输入的通道数有关。矩阵取值由如下公式计算
在这里插入图片描述
其中F_{ik}代表第i个输入通道(实际上输入的是多个特征图,所以就是第i个特征图)的第k个像素,同时可以发现该计算和位置信息无关,也就是说原始图片随机打散后生成新的特征图重新计算Gram矩阵也是一致的。说明Gram提取是整体风格而不是结构信息。代码实现计算Gram矩阵如下。

复制代码
    def gram_matrix(y):
    """
    Input shape: b,c,h,w
    Output shape: b,c,c
    """
    (b, ch, h, w) = y.size()
    features = y.view(b, ch, w * h)
    features_t = features.transpose(1, 2)
    #选择转置维度
    gram = features.bmm(features_t) / (ch * h * w)
    #batch矩阵乘法
    return gram
    
    
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

其中用到了一个小技巧,对于CxHxW的输入来说如果我们把它调整为CxHW的二维矩阵,这个二维矩阵与自己的转置矩阵相乘的结果就是Gram矩阵。

PackerVGG

该文件主要是封装了一个预训练好了的VGG16网络,是用来提取我们想要的风格的特征的。看它的前向传播函数,最后返回的不是一般意义上最后分类结果,可是把VGG16前4个卷积块的输出(高层次特征图)当作结果返回。我们计算风格loss也是在VGG输出的特征图上计算,看生成图片和原始风格图片高层特征是否相近。

复制代码
    class Vgg16(torch.nn.Module):
    def __init__(self):
        super(Vgg16, self).__init__()
        #自带的vgg16由两个nn.Sequential,一个用来提取特征的feature,一个分类的classifier,我们这里用featurn即可
        features = list(vgg16(pretrained=True).features)[:23]
        # the 3rd, 8th, 15th and 22nd layer of \ 
        # self.features are: relu1_2,relu2_2,relu3_3,relu4_3
        self.features = nn.ModuleList(features).eval()
    
    def forward(self, x):
        results = []
        for ii, model in enumerate(self.features):
            x = model(x)
            if ii in {3, 8, 15, 22}:
            #前4个卷积块的输出
                results.append(x)
    
        vgg_outputs = namedtuple("VggOutputs", ['relu1_2', 'relu2_2', 'relu3_3', 'relu4_3'])
        #返回一个命名元组,可以通过obj.value的方式访问元素
        return vgg_outputs(*results)
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

transformer

迁移部分的网络结构如下
在这里插入图片描述
对着结构图可以很清晰的看懂代码文件,要注意的地方也有几点:

  1. 没有采用一般的用0补齐图片边缘,而是采用边缘反射的像素补齐
复制代码
    reflection_padding = int(np.floor(kernel_size / 2))
        self.reflection_pad = nn.ReflectionPad2d(reflection_padding)
    
    
      
      
    
    AI写代码
  1. 上采样不是直接用的反卷积,而是Upsample再卷积来扩大尺寸。
复制代码
    class UpsampleConvLayer(nn.Module):
    """UpsampleConvLayer
    instead of ConvTranspose2d, we do UpSample + Conv2d
    see ref for why.
    ref: http://distill.pub/2016/deconv-checkerboard/
    """
    
    def __init__(self, in_channels, out_channels, kernel_size, stride, upsample=None):
        super(UpsampleConvLayer, self).__init__()
        self.upsample = upsample
        reflection_padding = int(np.floor(kernel_size / 2))
        self.reflection_pad = nn.ReflectionPad2d(reflection_padding)
        self.conv2d = nn.Conv2d(in_channels, out_channels, kernel_size, stride)
    
    def forward(self, x):
        x_in = x
        if self.upsample:
        #插值上采样,默认使用最近邻
            x_in = t.nn.functional.interpolate(x_in, scale_factor=self.upsample)
        out = self.reflection_pad(x_in)
        out = self.conv2d(out)
        return out
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码
  1. 没有全连接层,全是卷积,所以不要在意图片尺寸,都可以输入。

main

该文件除了一些设置的参数之外,就是训练和结果生成函数了。

训练

前面的说明已经铺垫好了,现在要来正式训练一下网络了,整体训练过程如下。
在这里插入图片描述
整个网络架构如下图。
在这里插入图片描述
需要注意的是(3)中计算风格loss,是所有风格图片和生成图片的Gram矩阵的均方误差之和。

生成图片

直接读取模型参数,然后读取原始图片送入模型保存输出结果即可。这个内容实现的网络叫做Fast Neural Style,正是因为它存在一个训练好的模型,可以快速完成风格迁移;而最初Neural Style文章里,类似GAN的操作是不同调整输出的,虽然输出的结果更好了,但是相当耗时间。

全部评论 (0)

还没有任何评论哟~