【摘要】详细分析了基于Keras实现图片风格迁移的生成算法,提供了GUI实现工具;同时,比较了生成式深度学习跟常规深度学习的差异,拓展对神经网络的认识。
该实现算法及代码,主要取自Keras 之父写的 《Python 深度学习》,有推荐价值。在此基础上,本人开发了一个用tkinter写的实用小工具,通过设置目标图片,风格图片,按开始转换,即可生成风格迁移后的新图片。
源代码可通过以下Github地址获取
https://github.com/EdwinZhang1970/Python/tree/master/Netural%20Style%20Transfer
2. 取图片在模型中的更靠顶部的层激活信息,作为图片的内容特征值,因为是全局的,因此,可以只取一层就可以了。本例算法取的是conv5_2, 代码中为block5_conv2
content_layer = 'block5_conv2'
3. 取图片在模型中的更靠底部的层激活信息,作为图片的风格特征值,因为是局部的,因此,需要多取几层,以获得更全面的风格特征信息。本例中取的是 conv1_1, conv2_1, conv3_1, conv4_1, conv5_1, 代码中为block1_conv1, block2_conv1, block3_conv1, block4_conv1, block5_conv1
style_layers = ['block1_conv1',
'block2_conv1',
'block3_conv1',
'block4_conv1',
'block5_conv1']
def content_loss(base, combination):
return K.sum(K.square(combination - base))
2. 让生成图片和风格图片,在靠近底部依次向上所选的多个层激活(风格信息),让它们在这几个层保持类似的同层相互关系(不是层的具体内容,而是该层元素之间的相互关系),从而做到生成图片与风格图片的风格一致.
通过 gram_matrix()函数,可计算获得输入矩阵的格拉姆矩阵,格拉姆矩阵是原始矩阵的相互关系的映射
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, img_height, img_width):
S = gram_matrix(style)
C = gram_matrix(combination)
channels = 3
size = img_height * img_width
return K.sum(K.square(S - C)) / (4. * (channels ** 2) * (size ** 2))
3. 提高生成图片的空间连续性,让生成图片本身更像完整的图片,而不是单个像素点的随意堆积
def total_variation_loss(x, img_height, img_width):
a = K.square(
x[:, :img_height - 1, :img_width - 1, :] - x[:, 1:, :img_width - 1, :])
b = K.square(
x[:, :img_height - 1, :img_width - 1, :] - x[:, :img_height - 1, 1:, :])
return K.sum(K.pow(a + b, 1.25))
这段代码咋看很复杂,细看还是很简单的。简化a, 只需抽取变量a中有变化的第1维进行分析
K.square(x[:img_height-1] – x[1:])
= (x[0]-x[1])**2 +(x[1]-x[2])**2 + … + (x[img_height-1]-x[img_height])**2
可以看出,公式就是图片中,所有点与其相邻点的距离和(不包括最后一条边)。使相邻点的之间的差异损失越小,就达到了使图片像素点越连续的目的。
4. 总的损失值,是以上三种损失的加权合计结果。本例设置的缺省权重系数为
total_variation_weight = 1e-4
content_weight = 0.025
style_weight = 1.
loss += content_weight * content_loss(target_image_features, combination_features)
for layer_name in style_layers:
... ...
sl = style_loss(style_reference_features, combination_features, img_height, img_width)
loss += (style_weight / len(style_layers)) * sl
loss += total_variation_weight * total_variation_loss(combination_image, img_height, img_width)
其中,style_weigth表示的是总的风格权重,它将会被平均分配到各个风格层参与计算。
调整权重,可以获得不同的生成结果。比如,加大content_weight, 将会使生成图看起来更像目标图。
1. 获得目标图片,风格图片的常量信息(因此整个处理过程,赋值后不会再改变)
target_image = K.constant(preprocess_image(target_image_path, img_height, img_width))
style_reference_image = K.constant(preprocess_image(style_reference_image_path, img_height, img_width))
2. 设置生成图片的占位符。(整个处理过程,都在不断地调整特征值),初始值为生成图片的数据
combination_image = K.placeholder((1, img_height, img_width, 3))
3. 装载预训练的VGG19模型及在imagenet数据集上获得的权重系数
model = vgg19.VGG19(input_tensor=input_tensor,
weights='imagenet',
include_top=False)
4. 利用预训练模型,提取目标图片,风格图片,生成图片的各层激活值,并根据损失函数的定义,计算损失值和梯度
5. 利用优化函数fmin_l_bfgs_b()进行优化,获得优化结果,优化结果中包括生成图的特征数据。对生成图特征数据进行图片还原处理,即可获得可显示的图片效果。每一轮优化计算都会有一个结果,本文提供的工具将实时显示每轮优化的图片效果。
最核心的大功臣是L-BFGS-B优化算法,感谢科学家设计了各种优化算法,感谢Scipy的大师们实现了这些算法。
1. 应用场景
a) 常规:根据单一样本的特征值,预测其自身的标签值,获得回归,分类信息。实现对现有模式和规则的提取和使用,完成对已生成的样本数据的分析。
b) 此例:综合分析多个相关样本的特征值,生成新的样本特征值。在现有模式和规则的基础上,进行融合,获得新的模式和规则,生成创新的样本数据。
2. 样本特征值的获取
a) 常规:对获得的文本,图片,音频,视频,测量,传感等原始数据,进行清理,数值化,浮点化,归一化等处理,只需要获得数据的基本特征值,然后转成可输入计算模型的向量数据,输入模型进行计算。
b) 此例:提取原始向量数据在预训练模型中的不同层激活输出,并对这些层激活进行建模处理,获得样本的内容特征,纹理风格特征,然后以这些提取的高级特征值为基础,完成接下来的模型优化算法运算。
3. 损失函数的设计
a) 常规:统计样本的计算标签值与实际标签值的差异,若标签值为回归值,可用mse, mae;若标签为单标签二分类 或 多标签多分类,可用 binary_crossentropy; 若标签值为单标签多分类,可用categorical_crossentropy
b) 此例:损失函数 = 生成图片跟目标图片的内容损失 * 权重 + 生成图片跟风格图片的风格损失 * 权重 + 生成图片本身的像素化损失 * 权重。 完全是特定场景下的独有损失函数。
4. 优化算法的设计
a) 常规:基于梯度下降的算法及改良算法:SGD, RMSProp, Adagrad, Adam ,
b) 此例:L-BFGS-B算法,适应于有约束的多元函数优化算法。注:scipy中的optimize子包中提供了常用的最优化算法函数实现
5. 模型及学习成果的设计
a) 常规:利用CNN提取样本空间信息,利用RNN提取样本时序信息,利用RPN推荐样本区域位置信息,再利用DNN进行特征值与标签值的线性回归映射等,学习获得使损失函数值最小的模型权重系数值
b) 此例:利用现有的预训练模型提取特征值,并进行建模处理,学习获得使损失函数值最小的样本数据特征值
6. 批量喂入模型的目的(小技巧)
a) 常规:一个批次中的样本是独立的,序号随机,相互没有关系,在训练时,大家共同为减少损失函数值做贡献;在预测时,各顾各计算自己的标签值
b) 此例:借用批次概念,将3个相关样本数据,按规定的次序,放在一个批次中同时计算(而不是分3次分别计算),然后再根据规定的序号取出相应的输出值,用于后续的处理。
7. 计算能力的影响(小感触)
a) 常规:现代神经网络技术的应用和发展,得益于算法改进,计算机算能提高,大数据获得。因此,人工智能企业的价值评估是:研发团队的人才论文情况,看算法能力;GPU服务器的超算集群情况,看计算能力;标注数据的投入情况,看数据能力。
b) 此例:我用64G内存的服务器,一个计算轮次为3~4秒;用8G内存的笔记本,一个计算轮次是800多秒。没有好的计算机,看来不行。
小结:
深度学习框架提供了大量常规算法模块,使我们可以像搭积木一样,快速搭建神经网络来满足一些常用场景的应用。而更多的实际场景,将需要根据具体的原始业务数据,提取特定的高级样本特征,设计各种特定的损失函数,优化算法,以获得业务所需要的标签数据或特征数据,满足特定场景的业务需要和性能需要。
其中的每个步骤,都是算法在起主导作用。利用常用算法搭积木,设计解决方案,是我们学习的基础。同时,更多的应用场景,要求我们根据具体数据和需求,选用,设计和创建特定算法,以满足产业级应用的需要。