风格迁移
编写:张礼俊/SlyneD
校对:毛丽
总校对与审核:寒小阳
这份作业里面我们会实现风格迁移的技巧,来自于这篇文章 “Image Style Transfer Using Convolutional Neural Networks” (Gatys et al., CVPR 2015).
主要的想法就是拿两张图,然后生成一张新图片来反应了一张图的内容和另一张图的艺术风格。我们首先要定义一个损失函数来在深度网络的特征空间中分别匹配相应图片的内容和风格,然后在图片本身上做梯度下降。
我们用来做特征提取器的深度网络是SqueezeNet, 一个在ImageNet上面训练的小模型。你可以用任何一个网络,但是我们选择用SqueezeNet,因为它又小又有效。
这里有一个示例图片,你将会在这个作业的最后面的时候生成它。
Setup
%load_ext autoreload
%autoreload 2
from scipy.misc import imread, imresize
import numpy as np
from scipy.misc import imread
import matplotlib.pyplot as plt
# Helper functions to deal with image preprocessing
from cs231n.image_utils import load_image, preprocess_image, deprocess_image
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "2" # 设置使用的GPU
%matplotlib inline
def get_session():
"""Create a session that dynamically allocates memory."""
# See: https://www.tensorflow.org/tutorials/using_gpu#allowing_gpu_memory_growth
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
session = tf.Session(config=config)
return session
def rel_error(x,y):
return np.max(np.abs(x - y) / (np.maximum(1e-8, np.abs(x) + np.abs(y))))
# Older versions of scipy.misc.imresize yield different results
# from newer versions, so we check to make sure scipy is up to date.
def check_scipy():
import scipy
vnum = int(scipy.__version__.split('.')[1])
assert vnum >= 16, "You must install SciPy >= 0.16.0 to complete this notebook."
check_scipy()
加载预训练的SqueezeNet模型,这个模型是从PyTorch上转换过来的,详见cs231n/classifiers/squeezenet.py
参考模型架构。
要想使用SqueezeNet, 你首先需要下载模型参数。切换到cs231n/datasets
目录下,运行get_squeezenet_tf.sh
。注意,如果你之前跑过get_assignment3_data.sh
,那么SqueezeNet应该已经下载好了。
from cs231n.classifiers.squeezenet import SqueezeNet
import tensorflow as tf
tf.reset_default_graph() # remove all existing variables in the graph
sess = get_session() # start a new Session
# Load pretrained SqueezeNet model
SAVE_PATH = 'cs231n/datasets/squeezenet.ckpt'
#if not os.path.exists(SAVE_PATH):
# raise ValueError("You need to download SqueezeNet!")
model = SqueezeNet(save_path=SAVE_PATH, sess=sess)
# Load data for testing
content_img_test = preprocess_image(load_image('styles/tubingen.jpg', size=192))[None]
style_img_test = preprocess_image(load_image('styles/starry_night.jpg', size=192))[None]
answers = np.load('style-transfer-checks-tf.npz')
INFO:tensorflow:Restoring parameters from cs231n/datasets/squeezenet.ckpt
计算损失函数
我们将计算我们损失函数的三个组成部分。损失函数是三个部分的加权和: 内容损失 + 风格损失 + 整体多样性损失。 你会在下面的函数中计算这些加权的部分。
内容损失
我们希望生成一张图片,这张图片可以反映一张图片的内容和另一个张图片的风格。为此,我们要把这二者都加入到我们的损失函数中去。我们希望可以惩罚对于内容图片的内容的偏移,以及对风格图片的风格偏移。我们可以用这样的混合的损失函数来进行梯度下降,注意不是在模型的参数上,而是在我们的原始图片的像素值上进行梯度下降。
我们首先来写一下内容的损失函数。 内容损失衡量的是生成的图片的特征图和源图片的特征图的差异程度。 我们只关心网络的某一个层的内容表示(比如,层 ℓ ℓ ),特征图是 Aℓ∈R1×Cℓ×Hℓ×Wℓ A ℓ ∈ R 1 × C ℓ × H ℓ × W ℓ . Cℓ C ℓ 是 ℓ ℓ 这层的通道(filters/channels)数量, Hℓ H ℓ and Wℓ W ℓ 是高和宽. 我们将会在reshape之后的特征图上来计算(把二维图像拉伸成一维)。假设 Fℓ∈RNℓ×Mℓ F ℓ ∈ R N ℓ × M ℓ 是当前图片的特征图 以及 Pℓ∈RNℓ×