迁移学习实践 深度学习打造图像的别样风格

简介: 神经风格传递的原理是定义两个距离函数,一个描述两幅图像的内容如何不同,一个描述两幅图像之间的风格差异。然后,给定三个图像,一个期望的样式图像,一个期望的内容图像,和输入图像(用内容图像初始化),我们尝试转换输入图像,以最小化与内容图像的内容距离和它与样式图像的样式距离

一、前言


训练环境:Googlecolab训练时长:<15min论文地址:https://arxiv.org/abs/1508.06576


在本教程中,我们将学习如何使用深度学习来创作另一种(毕加索或梵高式)风格的图像,这就是所谓的神经类型迁移!这是列昂·盖茨的论文中概述的一种技术:一种艺术风格的神经算法,非常值得一读。


那么,问题来了,什么是neural style transfer?


答:neural style transfer是一种优化技术,用于取三张图片:内容图片,样式参考图片(如来自同一著名画家的艺术作品),和你想要改造风格的输入图片,将三张图片混合在一起,这样输入图片转化成集内容图片的内容与样式参考图片的样式于一体的别样图片。


例如,让我们来看看这只海龟和葛饰北斋的《神奈川巨浪》:



如果葛饰北斋决定用这种风格来画这只乌龟,那会是什么样子呢?会是这样的吗?



这是魔法还是深度学习?幸运的是,这并不涉及任何巫术:样式转换是一种有趣的技术,它展示了神经网络的功能和内部表示。


神经风格传递的原理是定义两个距离函数,一个描述两幅图像的内容如何不同,一个描述两幅图像之间的风格差异。然后,给定三个图像,一个期望的样式图像,一个期望的内容图像,和输入图像(用内容图像初始化),我们尝试转换输入图像,以最小化与内容图像的内容距离和它与样式图像的样式距离。总之,我们将获取基本输入图像、要匹配的内容图像和要匹配的样式图像。我们将通过反向传播最小化内容和样式之间的距离(损失)来转换基本输入图像,创建一个匹配内容图像的内容样式图像的样式的图像。


其中将涉及的具体概念:在这个过程中,我们将围绕以下概念建立实践经验和发展直觉。


  • Eager Execution——使用 TensorFlow 的命令式编程环境,可以立即评估操作。
  • 使用函数API定义模型 ,我们将构建模型的一个子集,它将使我们能够使用函数API访问必要的中间激活。
  • 利用一个预训练模型的特征图—学习如何使用预训练模型及其特征图
  • 创建自定义训练循环——我们将研究如何设置优化器来最小化给定的输入参数损失
  • 我们将按照一般步骤来执行风格转换:可视化数据、基本预处理/准备我们的数据、设置损失函数、创建模型、损失函数优化。


二、代码实践


下载图片(环境:Google colab)


importosimg_dir='/tmp/nst'ifnotos.path.exists(img_dir):
os.makedirs(img_dir)
!wget--quiet-P/tmp/nst/https://upload.wikimedia.org/wikipedia/commons/d/d7/Green_Sea_Turtle_grazing_seagrass.jpg!wget--quiet-P/tmp/nst/https://upload.wikimedia.org/wikipedia/commons/0/0a/The_Great_Wave_off_Kanagawa.jpg!wget--quiet-P/tmp/nst/https://upload.wikimedia.org/wikipedia/commons/b/b4/Vassily_Kandinsky%2C_1913_-_Composition_7.jpg!wget--quiet-P/tmp/nst/https://upload.wikimedia.org/wikipedia/commons/0/00/Tuebingen_Neckarfront.jpg!wget--quiet-P/tmp/nst/https://upload.wikimedia.org/wikipedia/commons/6/68/Pillars_of_creation_2014_HST_WFC3-UVIS_full-res_denoised.jpg!wget--quiet-P/tmp/nst/https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg/1024px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg!wget--quiet-P/tmp/nst/https://ucc.alicdn.com/images/user-upload-01/20210403204754742.jpg



代码运行后,tmp下的 nst 文件夹里有了我们请求下载的图片。


导入需要的依赖库和配置


importmatplotlib.pyplotaspltimportmatplotlibasmplmpl.rcParams['figure.figsize'] = (10,10)
mpl.rcParams['axes.grid'] =FalseimportnumpyasnpfromPILimportImageimporttimeimportfunctools


%tensorflow_version1.ximporttensorflowastffromtensorflow.python.keras.preprocessingimportimageaskp_imagefromtensorflow.python.kerasimportmodelsfromtensorflow.python.kerasimportlossesfromtensorflow.python.kerasimportlayersfromtensorflow.python.kerasimportbackendasK


我们将从启用 Eager execution 开始。Eager execution允许我们以最清晰和最易读的方式完成这项技术。


tf.enable_eager_execution()
print("Eager execution: {}".format(tf.executing_eagerly()))


定义我们的图片路径


# Set up some global values herecontent_path='/tmp/nst/chongzi.jpg'style_path='/tmp/nst/The_Great_Wave_off_Kanagawa.jpg'


可视化输入图像


defload_img(path_to_img):
max_dim=512img=Image.open(path_to_img)
long=max(img.size)
scale=max_dim/longimg=img.resize((round(img.size[0]*scale), round(img.size[1]*scale)), Image.ANTIALIAS)
img=kp_image.img_to_array(img)
# We need to broadcast the image array such that it has a batch dimension img=np.expand_dims(img, axis=0)
returnimg


defimshow(img, title=None):
# Remove the batch dimensionout=np.squeeze(img, axis=0)
# Normalize for display out=out.astype('uint8')
plt.imshow(out)
iftitleisnotNone:
plt.title(title)
plt.imshow(out)


plt.figure(figsize=(10,10))
content=load_img(content_path).astype('uint8')
style=load_img(style_path).astype('uint8')
plt.subplot(1, 2, 1)
imshow(content, 'Content Image')
plt.subplot(1, 2, 2)
imshow(style, 'Style Image')
plt.show()



我们期望输出的图像是输入图片转化成集内容图片的内容与样式参考图片的样式于一体的别样图片


准备数据:让我们创建便捷化加载和预处理图像流程的方法。根据 VGG 训练流程,我们执行与预期相同的预处理流程。VGG网络在图像上进行训练,每个通道归一化的平均值为[103.939,116.779,123.68](通道BGR)。


defload_and_process_img(path_to_img):
img=load_img(path_to_img)
img=tf.keras.applications.vgg19.preprocess_input(img)
returnimg


为了查看优化的输出,我们需要执行逆预处理步骤。此外,由于我们的优化图像的值可能在负无穷和正无穷之间的任何地方,我们必须剪切,以保持我们的值在 0-255 范围内。


defdeprocess_img(processed_img):
x=processed_img.copy()
iflen(x.shape) ==4:
x=np.squeeze(x, 0)
assertlen(x.shape) ==3, ("Input to deprocess image must be an image of ""dimension [1, height, width, channel] or [height, width, channel]")
iflen(x.shape) !=3:
raiseValueError("Invalid input to deprocessing image")
# perform the inverse of the preprocessing stepx[:, :, 0] +=103.939x[:, :, 1] +=116.779x[:, :, 2] +=123.68x=x[:, :, ::-1]
x=np.clip(x, 0, 255).astype('uint8')
returnx


定义内容和样式表示:为了获得图像的内容和样式表示,我们将查看模型中的一些中间层。随着我们深入模型,这些中间层代表越来越高的阶特征。在这种情况下,我们使用网络架构VGG19,一个预先训练的图像分类网络。这些中间层对于从图像定义内容和样式的表示是必要的。对于输入图像,我们将尝试匹配这些中间层上相应的样式和内容目标表示。


为什么需要中间层?为了让一个网络执行图像分类(我们的网络已经接受了这样的训练),它必须理解图像。这涉及到将原始图像作为输入像素,并通过将原始图像像素转换为复杂的图像特性理解来构建内部表示。这也是卷积神经网络能够很好地泛化的部分原因:它们能够捕获类(例如,猫vs狗)中的不变性和定义特征,而这些不变性和定义特征对背景噪声和其他滋扰是不可知的。因此,在输入原始图像和输出分类标签之间的某个地方,模型充当一个复杂的特征提取器;因此,通过访问中间层,我们能够描述输入图像的内容和样式。具体来说,我们将从我们的网络中拉出这些中间层:



# Content layer where will pull our feature mapscontent_layers= ['block5_conv2'] 
# Style layer we are interested instyle_layers= ['block1_conv1',
'block2_conv1',
'block3_conv1', 
'block4_conv1', 
'block5_conv1'               ]
num_content_layers=len(content_layers)
num_style_layers=len(style_layers)


构建模型:在这种情况下,我们加载VGG19,并将我们的输入张量输入到模型中。这将允许我们提取内容、样式和生成的图像的特征映射(以及随后的内容和样式表示)。我们使用VGG19,正如论文中建议的那样。此外,由于 VGG19 是一个相对简单的模型(与ResNet、Inception等相比),所以功能映射实际上更适合于样式转换。


为了访问与我们的样式和内容特性映射对应的中间层,我们获得了相应的输出,并使用 Keras 函数API,使用所需的输出激活来定义模型。使用函数式 API 定义模型只需定义输入和输出:model = Model(inputs, outputs)。


defget_model():
""" Creates our model with access to intermediate layers.   This function will load the VGG19 model and access the intermediate layers.   These layers will then be used to create a new model that will take input image  and return the outputs from these intermediate layers from the VGG model.   Returns:    returns a keras model that takes image inputs and outputs the style and       content intermediate layers.   """# Load our model. We load pretrained VGG, trained on imagenet datavgg=tf.keras.applications.vgg19.VGG19(include_top=False, weights='imagenet')
vgg.trainable=False# Get output layers corresponding to style and content layers style_outputs= [vgg.get_layer(name).outputfornameinstyle_layers]
content_outputs= [vgg.get_layer(name).outputfornameincontent_layers]
model_outputs=style_outputs+content_outputs# Build model returnmodels.Model(vgg.input, model_outputs)


在上面的代码片段中,我们将加载预先训练好的图像分类网络。然后我们获取前面定义的感兴趣的层。然后,我们通过将模型的输入设置为图像,将输出设置为样式和内容层的输出来定义模型。换句话说,我们创建了一个模型,它将获取输入图像并输出内容和样式中间层!




计算Content Loss:我们将在每一层添加我们的Content Loss。这样,当我们通过模型(在 Eager 中是简单的模型input_image!)提供输入图像时,每次迭代都将正确地计算通过模型的所有内容损失,因为我们正在急切地执行,所以将计算所有的梯度。



其中我们通过一些因子 wl 加权每一层损失的贡献。在我们的例子中,我们将每个层的权重相等(wl=1/|L|)。


Computing style loss


同样,我们将损失作为距离度量。


defgram_matrix(input_tensor):
# We make the image channels first channels=int(input_tensor.shape[-1])
a=tf.reshape(input_tensor, [-1, channels])
n=tf.shape(a)[0]
gram=tf.matmul(a, a, transpose_a=True)
returngram/tf.cast(n, tf.float32)
defget_style_loss(base_style, gram_target):
"""Expects two images of dimension h, w, c"""# height, width, num filters of each layer# We scale the loss at a given layer by the size of the feature map and the number of filtersheight, width, channels=base_style.get_shape().as_list()
gram_style=gram_matrix(base_style)
returntf.reduce_mean(tf.square(gram_style-gram_target))# / (4. * (channels ** 2) * (width * height) ** 2)


应用样式迁移


运行梯度下降:如果你不熟悉梯度下降/反向传播或需要复习,你绝对应该看看这个令人敬畏的资源。在这种情况下,我们使用 Adam 优化器来最小化我们的损失。我们迭代地更新我们的输出图像,使其损失最小化。我们不更新与我们的网络相关的权值,而是训练我们的输入图像,使损失最小化。为了做到这一点,我们必须知道如何计算损失和梯度。


请注意L-BFGS,如果您熟悉这个算法推荐,不是本教程中使用本教程因为背后的主要动机是为了说明与渴望执行最佳实践,通过使用亚当,我们可以证明autograd/梯度带功能自定义训练循环。


接下来我们将定义一个小助手函数,它将加载内容和样式图像,并通过我们的网络转发它们,然后该网络将输出模型中的内容和样式特征表示。


defget_feature_representations(model, content_path, style_path):
"""Helper function to compute our content and style feature representations.  This function will simply load and preprocess both the content and style   images from their path. Then it will feed them through the network to obtain  the outputs of the intermediate layers.   Arguments:    model: The model that we are using.    content_path: The path to the content image.    style_path: The path to the style image  Returns:    returns the style features and the content features.   """# Load our images in content_image=load_and_process_img(content_path)
style_image=load_and_process_img(style_path)
# batch compute content and style featuresstyle_outputs=model(style_image)
content_outputs=model(content_image)
# Get the style and content feature representations from our model  style_features= [style_layer[0] forstyle_layerinstyle_outputs[:num_style_layers]]
content_features= [content_layer[0] forcontent_layerincontent_outputs[num_style_layers:]]
returnstyle_features, content_features


计算损失和梯度:这里我们用 tf.GradientTape 计算梯度。它允许我们通过跟踪操作来利用自动微分来计算后面的梯度。它记录前向传递过程中的操作,然后计算出损失函数相对于后向传递的输入图像的梯度。


defcompute_loss(model, loss_weights, init_image, gram_style_features, content_features):
"""This function will compute the loss total loss.  Arguments:    model: The model that will give us access to the intermediate layers    loss_weights: The weights of each contribution of each loss function.       (style weight, content weight, and total variation weight)    init_image: Our initial base image. This image is what we are updating with       our optimization process. We apply the gradients wrt the loss we are       calculating to this image.    gram_style_features: Precomputed gram matrices corresponding to the       defined style layers of interest.    content_features: Precomputed outputs from defined content layers of       interest.  Returns:    returns the total loss, style loss, content loss, and total variational loss  """style_weight, content_weight=loss_weights# Feed our init image through our model. This will give us the content and # style representations at our desired layers. Since we're using eager# our model is callable just like any other function!model_outputs=model(init_image)
style_output_features=model_outputs[:num_style_layers]
content_output_features=model_outputs[num_style_layers:]
style_score=0content_score=0# Accumulate style losses from all layers# Here, we equally weight each contribution of each loss layerweight_per_style_layer=1.0/float(num_style_layers)
fortarget_style, comb_styleinzip(gram_style_features, style_output_features):
style_score+=weight_per_style_layer*get_style_loss(comb_style[0], target_style)
# Accumulate content losses from all layers weight_per_content_layer=1.0/float(num_content_layers)
fortarget_content, comb_contentinzip(content_features, content_output_features):
content_score+=weight_per_content_layer*get_content_loss(comb_content[0], target_content)
style_score*=style_weightcontent_score*=content_weight# Get total lossloss=style_score+content_scorereturnloss, style_score, content_score


然后计算梯度就简单了


defcompute_grads(cfg):
withtf.GradientTape() astape: 
all_loss=compute_loss(**cfg)
# Compute gradients wrt input imagetotal_loss=all_loss[0]
returntape.gradient(total_loss, cfg['init_image']), all_loss


优化循环


importIPython.displaydefrun_style_transfer(content_path, 
style_path,
num_iterations=1000,
content_weight=1e3, 
style_weight=1e-2): 
# We don't need to (or want to) train any layers of our model, so we set their# trainable to false. model=get_model() 
forlayerinmodel.layers:
layer.trainable=False# Get the style and content feature representations (from our specified intermediate layers) style_features, content_features=get_feature_representations(model, content_path, style_path)
gram_style_features= [gram_matrix(style_feature) forstyle_featureinstyle_features]
# Set initial imageinit_image=load_and_process_img(content_path)
init_image=tf.Variable(init_image, dtype=tf.float32)
# Create our optimizeropt=tf.train.AdamOptimizer(learning_rate=5, beta1=0.99, epsilon=1e-1)
# For displaying intermediate images iter_count=1# Store our best resultbest_loss, best_img=float('inf'), None# Create a nice config loss_weights= (style_weight, content_weight)
cfg= {
'model': model,
'loss_weights': loss_weights,
'init_image': init_image,
'gram_style_features': gram_style_features,
'content_features': content_features  }
# For displayingnum_rows=2num_cols=5display_interval=num_iterations/(num_rows*num_cols)
start_time=time.time()
global_start=time.time()
norm_means=np.array([103.939, 116.779, 123.68])
min_vals=-norm_meansmax_vals=255-norm_meansimgs= []
foriinrange(num_iterations):
grads, all_loss=compute_grads(cfg)
loss, style_score, content_score=all_lossopt.apply_gradients([(grads, init_image)])
clipped=tf.clip_by_value(init_image, min_vals, max_vals)
init_image.assign(clipped)
end_time=time.time() 
ifloss<best_loss:
# Update best loss and best image from total loss. best_loss=lossbest_img=deprocess_img(init_image.numpy())
ifi%display_interval==0:
start_time=time.time()
# Use the .numpy() method to get the concrete numpy arrayplot_img=init_image.numpy()
plot_img=deprocess_img(plot_img)
imgs.append(plot_img)
IPython.display.clear_output(wait=True)
IPython.display.display_png(Image.fromarray(plot_img))
print('Iteration: {}'.format(i))        
print('Total loss: {:.4e}, ''style loss: {:.4e}, ''content loss: {:.4e}, ''time: {:.4f}s'.format(loss, style_score, content_score, time.time() -start_time))
print('Total time: {:.4f}s'.format(time.time() -global_start))
IPython.display.clear_output(wait=True)
plt.figure(figsize=(14,4))
fori,imginenumerate(imgs):
plt.subplot(num_rows,num_cols,i+1)
plt.imshow(img)
plt.xticks([])
plt.yticks([])
returnbest_img, best_loss


模型跑起来!


best, best_loss=run_style_transfer(content_path, style_path, num_iterations=1000)


可视化输出

我们对输出图像进行“deprocess”,以去除对其进行的处理,展示。


defshow_results(best_img, content_path, style_path, show_large_final=True):
plt.figure(figsize=(10, 5))
content=load_img(content_path) 
style=load_img(style_path)
plt.subplot(1, 2, 1)
imshow(content, 'Content Image')
plt.subplot(1, 2, 2)
imshow(style, 'Style Image')
ifshow_large_final: 
plt.figure(figsize=(10, 10))
plt.imshow(best_img)
plt.title('Output Image')
plt.show()


show_results(best, content_path, style_path)



迁移学习打造图像的别样风格,一只玉米螟的图像也能成为艺术品,看起来还不错吧!


推荐阅读:


Neural_Style_Transfer_with_Eager_Execution

https://zhuanlan.zhihu.com/p/93388054

https://blog.csdn.net/weixin_43264420/article/details/104441258

目录
相关文章
|
8天前
|
机器学习/深度学习 人工智能 TensorFlow
人工智能浪潮下的自我修养:从Python编程入门到深度学习实践
【10月更文挑战第39天】本文旨在为初学者提供一条清晰的道路,从Python基础语法的掌握到深度学习领域的探索。我们将通过简明扼要的语言和实际代码示例,引导读者逐步构建起对人工智能技术的理解和应用能力。文章不仅涵盖Python编程的基础,还将深入探讨深度学习的核心概念、工具和实战技巧,帮助读者在AI的浪潮中找到自己的位置。
|
12天前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习中的卷积神经网络:从理论到实践
【10月更文挑战第35天】在人工智能的浪潮中,深度学习技术以其强大的数据处理能力成为科技界的宠儿。其中,卷积神经网络(CNN)作为深度学习的一个重要分支,在图像识别和视频分析等领域展现出了惊人的潜力。本文将深入浅出地介绍CNN的工作原理,并结合实际代码示例,带领读者从零开始构建一个简单的CNN模型,探索其在图像分类任务中的应用。通过本文,读者不仅能够理解CNN背后的数学原理,还能学会如何利用现代深度学习框架实现自己的CNN模型。
|
9天前
|
机器学习/深度学习 数据采集 自然语言处理
深入浅出深度学习:从理论到实践
【10月更文挑战第38天】本文旨在通过浅显易懂的语言和直观的代码示例,带领读者探索深度学习的奥秘。我们将从深度学习的基本概念出发,逐步深入到模型构建、训练以及应用实例,让初学者也能轻松入门。文章不仅介绍了深度学习的原理,还提供了实战操作指南,帮助读者在实践中加深理解。无论你是编程新手还是有一定基础的学习者,都能在这篇文章中找到有价值的内容。让我们一起开启深度学习之旅吧!
|
11天前
|
机器学习/深度学习 自然语言处理 语音技术
深度学习的奇妙之旅:从理论到实践
【10月更文挑战第36天】在本文中,我们将一起探索深度学习的神秘世界。我们将首先了解深度学习的基本概念和原理,然后通过一个简单的Python代码示例,学习如何使用深度学习库Keras进行图像分类。无论你是深度学习的初学者,还是有一定基础的学习者,都可以从这篇文章中获得新的知识和启示。
|
16天前
|
机器学习/深度学习 监控 PyTorch
深度学习工程实践:PyTorch Lightning与Ignite框架的技术特性对比分析
在深度学习框架的选择上,PyTorch Lightning和Ignite代表了两种不同的技术路线。本文将从技术实现的角度,深入分析这两个框架在实际应用中的差异,为开发者提供客观的技术参考。
35 7
|
19天前
|
机器学习/深度学习 编解码 算法
什么是超分辨率?浅谈一下基于深度学习的图像超分辨率技术
超分辨率技术旨在提升图像或视频的清晰度,通过增加单位长度内的采样点数量来提高空间分辨率。基于深度学习的方法,如SRCNN、VDSR、SRResNet等,通过卷积神经网络和残差学习等技术,显著提升了图像重建的质量。此外,基于参考图像的超分辨率技术通过利用高分辨率参考图像,进一步提高了重建图像的真实感和细节。
|
14天前
|
机器学习/深度学习 人工智能 自动驾驶
深度学习的奇迹:如何用神经网络识别图像
【10月更文挑战第33天】在这篇文章中,我们将探索深度学习的奇妙世界,特别是卷积神经网络(CNN)在图像识别中的应用。我们将通过一个简单的代码示例,展示如何使用Python和Keras库构建一个能够识别手写数字的神经网络。这不仅是对深度学习概念的直观介绍,也是对技术实践的一次尝试。让我们一起踏上这段探索之旅,看看数据、模型和代码是如何交织在一起,创造出令人惊叹的结果。
26 0
|
18天前
|
机器学习/深度学习 自动驾驶 大数据
深入探索深度学习:理论与实践
【10月更文挑战第29天】本文将深入探讨深度学习的理论与实践,包括其基本概念、发展历程、关键技术以及应用场景。我们将从浅入深,逐步解析深度学习的内在机制,并通过实例展示其在实际应用中的强大能力。无论你是深度学习的初学者,还是已经在该领域有所建树的研究者,都能在本文中找到有价值的信息。让我们一起踏上深度学习的探索之旅吧!
|
8天前
|
机器学习/深度学习 人工智能 算法
深度学习在图像识别中的应用与挑战
本文探讨了深度学习技术在图像识别领域的应用,重点分析了卷积神经网络(CNN)的工作原理及其在处理图像数据方面的优势。通过案例研究,展示了深度学习如何提高图像识别的准确性和效率。同时,文章也讨论了当前面临的主要挑战,包括数据不足、过拟合问题以及计算资源的需求,并提出了相应的解决策略。
|
3天前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习中的卷积神经网络(CNN)及其在图像识别中的应用
本文旨在通过深入浅出的方式,为读者揭示卷积神经网络(CNN)的神秘面纱,并展示其在图像识别领域的实际应用。我们将从CNN的基本概念出发,逐步深入到网络结构、工作原理以及训练过程,最后通过一个实际的代码示例,带领读者体验CNN的强大功能。无论你是深度学习的初学者,还是希望进一步了解CNN的专业人士,这篇文章都将为你提供有价值的信息和启发。

热门文章

最新文章

下一篇
无影云桌面