📜  TensorFlow中的样式传输

📅  最后修改于: 2021-01-11 10:52:25             🧑  作者: Mango

TensorFlow中的样式传输

神经样式转换( NST )是一类软件算法,用于处理数字图像或视频,或采用其他图像的外观或视觉样式。当我们实现算法时,我们定义了两个距离。一个用于内容( Dc ),另一个用于形式( Ds )。

在本主题中,我们将实现一个基于深度神经网络的人工系统,该系统将创建高感知质量的图像。该系统将使用神经表示来分离,重新组合内容图像(样式图像)作为输入,并在使用样式图像的艺术风格进行打印时返回内容图像。

神经样式转移是一种优化技术,主要用于拍摄两个图像(内容图像和样式参考图像)并将它们融合。因此,输出图像看起来像内容图像,以匹配内容图像的内容统计信息和样式参考图像的样式统计信息。这些统计数据是使用卷积网络从图像中得出的。

神经样式转移算法的工作

当我们实现给定算法时,我们定义了两个距离。一个用于样式(Ds),另一个用于内容(Dc)。 Dc测量两个图像之间的内容不同,Ds测量两个图像之间的样式不同。我们将第三张图像作为输入,并将其转换为最小化其与内容图像的内容距离和与样式图像的样式距离。

需要图书馆

import tensorflow as tf  
#we transform and models because we will modify our images and we will use pre-trained model VGG-19   
from torchvision import transforms, models  from PIL 
import Image  
import matplotlib.pyplot as plt  
import numpy as np  

VGG-19模型

VGG-19型号类似于VGG-16型号。西蒙尼扬Zisserman推出VGG模型。 VGG-19接受了ImageNet数据库中超过一百万张图像的培训。该模型具有19层深度神经网络,可以将图像分类为1000个对象类别。

高层架构

神经风格转换使用相关的卷积神经网络。然后定义一个损失函数,将两个图像完全融合以创建视觉上吸引人的艺术品,NST定义了以下输入:

  • 内容图片(c)-我们要将样式转移到的图片
  • 样式图像-我们要从中移动方法的图像
  • 输入图像(g)-包含最终结果的图像。

该模型的架构相同,所计算的损失如下所示。我们不需要对下图中发生的事情有深刻的了解,因为我们将在接下来的几节中详细介绍每个组件。这个想法是对发生样式转移的工作流有一个高层次的理解。

下载并加载相关的VGG-16

我们将从此网页借用VGG-16砝码。我们将需要下载vgg16_weights.npz文件,并将其替换到项目主目录中名为vgg的文件夹中。我们只需要卷积和池化层。明确地说,我们将加载要用作NST网络的前七个卷积层。我们可以使用笔记本中提供的load_weights(…)函数来完成此操作。

注意:我们必须尝试更多层。但是请注意我们的CPU和GPU的内存限制。

# This function takes in a file path to the file containing weights
# and an integer that denotes how many layers to be loaded.
vgg_layers=load_weights(os.path.join('vgg','vgg16_weights.npz'),7)

定义功能以建立样式转移网络

我们定义了几个函数,这些函数将帮助我们稍后在给定输入的情况下完全定义CNN的计算图。

创建TensorFlow变量

我们将numpy数组加载到TensorFlow变量中。我们正在创建以下变量:

  • 内容图片( tf.placeholder )
  • 样式图片( tf.placeholder )
  • 生成的图像( tf.Variable and trainable = True )
  • 预先训练的体重和偏见( tf.Variable and trainable = False )

确保我们保持生成的图像可训练,同时保持预训练的权重以及权重和偏差保持冻结。我们展示了定义输入和神经网络权重的两个函数。

def define_inputs (input_shape):
"""
This function defines the inputs (placeholders) and image to be generated (variable)
"""
content = tf.placeholder(name='content' , shape=input_shape, dtype=tf.float32)
style= tf.placeholder(name='style', shape=input_shape, dtype=tf.float32)
generated= tf.get_variable(name='generated', initializer=tf.random_normal_initalizer=tf.random_normal_initiallizer(), shape=input_shape, dtype=tf.float32, trainable=true)
return {'content':content,'style,'generated': generated}
def define_tf_weights():
"""
This function defines the tensorflow variables for VGG weights and biases
"""
for k, w_dict in vgg_layers.items():
w, b=w_dict['weights'], w_dict['bias']
with tf.variable_scope(k):
  tf.get_variable(name='weights', initializer=tf.constant(w, dtype=tf.float32), trainable=false)
tf.get_variable(name='bias', initializer=tf.constant(b, dtype=tf.float32), trainable=False)

计算VGG净输出

Computing the VGG net output
Def build_vggnet(inp, layer_ids, pool_inds, on_cpu=False):
"This function computes the output of full VGG net """
    outputs = OrderedDict()
    
    out = inp


for lid in layer_ids:
        with tf.variable_scope(lid, reuse=tf.AUTO_REUSE):
            print('Computing outputs for the layer {}'.format(lid))
            w, b = tf.get_variable('weights'), tf.get_variable('bias')
            out = tf.nn.conv2d(filter=w, input=out, strides=[1,1,1,1], padding='SAME')
out = tf.nn.relu(tf.nn.bias_add(value=out, bias=b))
            outputs[lid] = out


        if lid in pool_inds:
            with tf.name_scope(lid.replace('conv','pool')):
                out = tf.nn.avg_pool(input=out, ksize=[1,2,2,1], strides=[1, 2, 2, 1], padding='SAME')
                outputs[lid.replace('conv','pool')] = out


return outputs

损失函数

在本节中,我们定义了两个损失函数:风格损失函数和内容函数。内容丢失函数可确保在生成的图像和内容图像之间高层的激活类似。

内容成本函数

内容成本函数可确保将内容图像中存在的内容捕获到生成的图像中。已经发现,CNN在较高级别捕获有关内容的信息,其中较低级别更关注单像素值。

A ^ l_ {ij}(I)是使用图像I实现的第l层,第i个特征图和第j个位置的激活。然后将内容损失定义为

内容丢失背后的直觉

如果我们将神经网络学到的东西可视化,则有证据表明,在存在各种对象的情况下,较高层中的不同特征图会被激活。因此,如果两个图像具有相同的内容,则它们在顶层中具有相似的激活。

我们将内容成本定义如下。

def define_content_loss(inputs, layer_ids, pool_inds, c_weight):
c_outputs= build_vggnet (inputs ["content"], layer_ids, pool_inds)
g_outputs= build_vggnet (inputs ["generated"], layer_ids, pool_inds)
content_loss= c_weight * tf.reduce_mean(0.5*(list(c_outputs.values())[-1]-list(g_outputs.values())[-1])**2)

风格损失函数

它定义了样式丢失函数,需要更多的工作。为了从VGG网络中获取样式信息,我们将使用完整的CNN层。样式信息是通过测量图层中要素图之间存在的相关量来衡量的。在数学上,样式损失定义为:

风格失落的直觉

通过上面的方程组,这个想法很简单。主要目标是为原始图像和样式图像计算样式矩阵。

然后,将样式损失定义为两个样式矩阵之间的均方根差。

       def define_style_matrix(layer_out):
 """
    This function computes the style matrix, which essentially computes
    how correlated the activations of a given filter to all the other filers.
    Therefore, if there are C channels, the matrix will be of size C x C
    """
    n_channels = layer_out.get_shape().as_list()[-1]
    unwrapped_out = tf.reshape(layer_out, [-1, n_channels])
     style_matrix = tf.matmul(unwrapped_out, unwrapped_out, transpose_a=True)
    return style_matrix
    
    def define_style_loss(inputs, layer_ids, pool_inds, s_weight, layer_weights=None):
     """ 
    This function computes the style loss using the style matrix computed for
     the style image and the generated image 
     """ 
    c_outputs = build_vggnet(inputs["style"], layer_ids, pool_inds)
    g_outputs = build_vggnet(inputs["generated"], layer_ids, pool_inds)
    
     c_grams = [define_style_matrix(v) for v in list(c_outputs.values())]
    g_grams = [define_style_matrix(v) for v in list(g_outputs.values())]
        
        if layer_weights is None:
            style_loss =  s_weight * \
                tf.reduce_sum([(1.0/len(layer_ids)) * tf.reduce_mean((c - g)**2) for c,g in zip(c_grams, g_grams)])
        else:
            style_loss = s_weight * \