在 DeepDream 图像生成算法的学习中,我们通过修改像素值试图使神经网络中卷积核的激活最大化。但是,这并不具备灵活生成指定风格图像的功能,因此,本节我们继续学习神经风格迁移算法。在神经风格迁移中,我们需要一个内容图像和一个风格图像,我们的目标是保持内容图像的同时融和风格图像中的风格样式,以组合这两个图像生成全新图像。
在进行实战前,我们首先了解神经风格迁移的相关原理。我们以与 DeepDream
算法类似的方式修改原始图像,但是,不同的是,在神经风格迁移模型中需要将损失值分为内容损失和风格损失。内容损失用于衡量生成的图像与内容图像之间的差异程度。风格损失用于衡量风格图像与生成的图像之间的关联程度。
尽管上述损失可以根据图像间的差异进行计算,但实际上,我们使用图像在网络层中的激活而不是原始图像来计算损失值。例如,第 2
层的内容损失是内容图像和生成的图像通过第 2
层神经网络层得到的激活之间的平方差。
计算内容损失较为简单,接下来我们继续学习如何计算生成图像和风格图像之间的相似度。我们将使用一种称为 gram
矩阵的技术,gram
矩阵用于计算生成图像和风格图像之间的相似度,相似度的计算方法如下:
L G M ( S , G , l ) = 1 4 N l 2 M l 2 ∑ i j ( G M [ l ] ( S ) i j − G M [ l ] ( G ) i j ) 2 L_{GM}(S,G,l)=\frac1 {4N_l^2M_l^2}\sum_{ij}(GM[l](S)_{ij}-GM[l](G)_{ij})^2 LGM(S,G,l)=4Nl2Ml21ij∑(GM[l](S)ij−GM[l](G)ij)2
其中 G M [ l ] GM[l] GM[l] 是风格图像 S S S 和生成图像 G G G 在第 l l l 层的 gram
矩阵值。gram
矩阵是通过将矩阵与其自身的转置矩阵相乘得到的。
计算得到风格损失和内容损失后,最终生成的修改图像是使总损失最小的图像,即令风格和内容损失的加权平均值最小。
在了解了神经风格迁移的基本原理之后,我们继续对模型的运行流程进行分析,主要通过以下步骤实现神经风格迁移:
gram
矩阵值gram
矩阵值gram
矩阵值的平方差,得到的结果即为风格损失了解了模型原理和运算流程后,在本节中,我们利用 Keras
实现神经风格迁移。
(1) 导入所需要的库,以及用于神经风格迁移的内容图像和风格图像:
from keras.applications import vgg19
from keras import backend as K
import numpy as np
import cv2
from PIL import Image
from matplotlib import pyplot as plt
import tensorflow as tf
tf.compat.v1.disable_eager_execution()
style_img = cv2.imread('Vincent_van_Gogh_779.jpg')
style_img = cv2.cvtColor(style_img, cv2.COLOR_BGR2RGB)
style_shape = style_img.shape
content_img = cv2.imread('3.jpeg')
content_img = cv2.cvtColor(content_img, cv2.COLOR_BGR2RGB)
content_shape = content_img.shape
(2) 为了便于理解神经风格迁移算法的效果,在生成图像前先查看风格和内容图像:
plt.subplot(211)
# 为了进行对比,将风格图像进行缩放,以与内容图像具有相同的尺寸
plt.imshow(cv2.resize(style_img, (content_shape[1], content_shape[0])))
plt.title('Style image')
plt.axis('off')
plt.subplot(212)
plt.imshow(content_img)
plt.title('Content image')
plt.axis('off')
plt.show()
(3) 初始化 VGG19
模型,以便获取输入图像在网络层中的特征输出:
from keras.applications import vgg19
model = vgg19.VGG19(include_top=False, weights='imagenet')
(4) 定义图像预处理和逆处理函数:
def preprocess_image(img):
img = np.array(img).astype(float)
img = np.expand_dims(img, axis=0)
# 数据预处理
img[:, :, :, 0] -= 103.939
img[:, :, :, 1] -= 116.779
img[:, :, :, 2] -= 123.68
img = img[:, :, :, ::-1] / 255
return img
def deprocess_image(x):
x = x[:, :, :, ::-1] * 225
x[:, :, :, 0] += 103.939
x[:, :, :, 1] += 116.779
x[:, :, :, 2] += 123.68
x = np.clip(x, 0, 255).astype('uint8')[0]
x = Image.fromarray(x).resize((content_shape[1], content_shape[0]))
return x
(5) 使用定义的 preprocess_image
函数对内容图像进行预处理,并将其输入到模型中以提取 VGG19
模型 block3_conv4
层的特征值:
content_img = preprocess_image(content_img)
get_3rd_layer_output = K.function([model.layers[0].input],[model.get_layer('block3_conv4').output])
layer_output_base = get_3rd_layer_output([content_img])[0]
在以上代码中,我们定义了一个函数 get_3rd_layer_output
用于获取输入图像并提取预定义层的输出特征。
(6) 接下来,定义需要提取用于计算内容和风格损失的神经网络图层,并为每一网络层分配相应权重:
layer_contributions_content = {'block3_conv4': 0.1}
layer_contributions_style = {'block1_conv1':5,
'block2_conv1':10,
'block3_conv1':10,
'block4_conv1':15,
'block5_conv1':15}
在以上代码中,我们定义了用于计算内容和风格损失的网络层,并为这些网络分配了不同权重,用于计算总损失。
(7) 定义 gram
矩阵和风格损失函数:
我们首先定义函数 gram_matrix
,用于计算输入的 gram
矩阵:
def gram_matrix(x):
# 对图像进行展平
features = K.batch_flatten(K.permute_dimensions(x, (2, 0 ,1)))
# 计算特征矩阵和特征转置矩阵的点积
gram = K.dot(features, K.transpose(features))
return gram
在以下代码中,我们将按照“神经风格迁移原理”小节中介绍的风格损失方程的定义计算风格损失:
def style_loss(style, combination):
S = gram_matrix(style)
C = gram_matrix(combination)
channels = 3
size = content_img.shape[0] * content_img.shape[1]
return K.sum(K.square(S - C)) / (4. * (pow(channels, 2)) * (pow(size, 2)))
(8) 初始化损失值函数,计算内容损失:
layer_dict = dict([(layer.name, layer) for layer in model.layers])
loss = K.variable(0.)
for layer_name in layer_contributions_content:
coeff = layer_contributions_content[layer_name]
activation = layer_dict[layer_name].output
scaling = K.prod(K.cast(K.shape(activation), 'float32'))
loss = loss + coeff * K.sum(K.square(activation - layer_output_base)) / scaling
在以上代码中,根据内容神经网络层 layer_contributions_content
的损失来更新损失值,layer_output_base
是将原始图像通过内容神经网络层时的输出。激活 activations
(即基于修改后的图像提取到的特征)和 layer_output_base
之间的差异越大,则图像内容损失越大。
然后,计算风格损失:
for layer_name in layer_contributions_style:
coeff = layer_contributions_style[layer_name]
activation = layer_dict[layer_name].output
scaling = K.prod(K.cast(K.shape(activation), 'float32'))
style_layer_output = K.function([model.layers[0].input], [model.get_layer(layer_name).output])
layer_output_style = style_layer_output([preprocess_image(style_img)])[0][0]
loss = loss + style_loss(layer_output_style, activation[0])
在以上代码中,我们以与计算内容损失相同的方式计算风格损失,不同的是,风格损失的计算使用不同的神经网络层。
(9) 构建函数将输入图像映射到损失值和相应的梯度变化值:
input_image = model.input
grads = K.gradients(loss, input_image)[0]
grads /= K.maximum(K.mean(K.abs(grads)), 1e-7)
outputs = [loss, grads]
fetch_loss_and_grads = K.function([input_image], outputs)
def eval_loss_and_grads(img):
outs = fetch_loss_and_grads([img])
loss_value = outs[0]
grad_values = outs[1]
return loss_value, grad_values
以上代码以与 DeepDream 算法类似,使用以上代码可以获取损失和梯度变化值以生成风格迁移图像。
(9) 最后,在多个 epochs
内运行模型,观察生成的图像:
img = content_img.copy()
for i in range(2000):
step=0.001
loss_value, grad_values = eval_loss_and_grads(img)
print('...Loss value at', i, ':', loss_value)
img = img - step * grad_values
if((i+1)%100 == 0):
img2 = img.copy()
img2 = deprocess_image(img2)
plt.imshow(img2)
plt.axis('off')
plt.show()
使用以上代码生成的图像,得到内容图像和风格图像的融合后的风格迁移图片:
可以通过选择不同的神经网络层来计算内容和风格损失,并为不同网络层分配不同的权重系数,观察生成图像的差别。
使用神经风格迁移算法生成图像的核心思想与 DeepDream 算法类似,通过获取损失和梯度变化值以生成风格迁移图像,将内容图像和风格参考图像混合在一起。在本节中,首先介绍了神经风格迁移的核心思想与风格迁移图像的生成流程,然后利用 Keras
从零开始实现了神经风格迁移算法,可以通过修改模型中的超参数来生成不同观感的图像。
Keras深度学习实战(1)——神经网络基础与模型训练过程详解
Keras深度学习实战(2)——使用Keras构建神经网络
Keras深度学习实战(3)——神经网络性能优化技术
Keras深度学习实战(4)——深度学习中常用激活函数和损失函数详解
Keras深度学习实战(5)——批归一化详解
Keras深度学习实战(6)——深度学习过拟合问题及解决方法
Keras深度学习实战(7)——卷积神经网络详解与实现
Keras深度学习实战(8)——使用数据增强提高神经网络性能
Keras深度学习实战(9)——卷积神经网络的局限性
Keras深度学习实战(10)——迁移学习详解
Keras深度学习实战(11)——可视化神经网络中间层输出
Keras深度学习实战(12)——面部特征点检测
Keras深度学习实战(13)——目标检测基础详解
Keras深度学习实战(14)——从零开始实现R-CNN目标检测
Keras深度学习实战(15)——从零开始实现YOLO目标检测
Keras深度学习实战(16)——自编码器详解
Keras深度学习实战(17)——使用U-Net架构进行图像分割
Keras深度学习实战(18)——语义分割详解
Keras深度学习实战(19)——使用对抗攻击生成可欺骗神经网络的图像
Keras深度学习实战(20)——DeepDream模型详解