使用VGG19迁移学习实现图像风格迁移
2020.3.15 更新:
使用Python 3.7 + TensorFlow 2.0的实现:
有趣的深度学习——使用TensorFlow 2.0实现图片神经风格迁移 (https://blog.csdn.net/aaronjny/article/details/104879258)
转载请注明出处:https://blog.csdn.net/aaronjny/article/details/79681080
一直想要做个图像风格迁移来玩玩的,感觉还是蛮有意思的。
所谓图像风格迁移,即给定内容图片A,风格图片B,能够生成一张具有A图片内容和B图片风格的图片C。
比如说,我们可以使用梵高先生的名画《星夜》 作为风格图片,来与其他图片生成具有《星夜》风格新图片。emmm,夭寿啦,机器帮你画世界名画啦。。。
举两个生成的例子:
均使用《星夜》作为风格图片(可以替换,我以《星夜》为例):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PBbLpmXy-1584256961127)(https://raw.githubusercontent.com/AaronJny/nerual_style_change/master/sample/input_style_1.jpg)]
示例1:
网络上找到的一张风景图片。
内容图片:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hBlBxLwz-1584256961128)(https://raw.githubusercontent.com/AaronJny/nerual_style_change/master/sample/input_content_1.jpg)]
生成图片:
生成图片的尺寸比较小,没办法,我的显卡太差了,尺寸大一点的话显卡内存不足。
示例2:
嗷嗷嗷,狼人嚎叫~
内容图片:
生成图片:
效果还凑合吧,可以接受。
下面记录实现过程。
一.获取预训练的vgg19模型
VGG19是Google DeepMind发表在ICLR 2015上的论文《VERY DEEP CONVOLUTIONAL NETWORK SFOR LARGE-SCALE IMAGE RECOGNITION》中提出的一种DCNN结构。
众所周知,CNN在图片处理上表现良好,VGG19提出后,也被用在图像处理上。我这里要用到的VGG19模型就是在imagenet数据集上预训练的模型。
一般认为,深度卷积神经网络的训练是对数据集特征的一步步抽取的过程,从简单的特征,到复杂的特征。
训练好的模型学习到的是对图像特征的抽取方法,所以在imagenet数据集上训练好的模型理论上来说,也可以直接用于抽取其他图像的特征,这也是迁移学习的基础。自然,这样的效果往往没有在新数据上重新训练的效果好,但能够节省大量的训练时间,在特定情况下非常有用。
预训练好的VGG19模型可以从这里下载,模型大小500M+。
二.模型编写
这里的模型基本上就是VGG19模型,只是稍微做了一些修改。
我们要从预训练的模型中,获取卷积层部分的参数,用于构建我们自己的模型。VGG19中的全连接层舍弃掉,这一部分对提取图像特征基本无用。
要注意的是,我这里提取出来的VGG参数全部是作为constant(即常量)使用的,也就是说,这些参数是不会再被训练的,在反向传播的过程中也不会改变。
另外,输入层要设置为Variable,我们要训练的就是这个。最开始输入一张噪音图片,然后不断地根据内容loss和风格loss对其进行调整,直到一定次数后,该图片兼具了风格图片的风格以及内容图片的内容。当训练结束时,输入层的参数就是我们生成的图片。
附一张VGG结构图:
这个代码里主要是定义VGG,至于LOSS在训练过程中进行说明。
models.py
# -*- coding: utf-8 -*-
# @Time : 18-3-23 下午12:20
# @Author : AaronJny
# @Email : Aaron__7@163.com
import tensorflow as tf
import numpy as np
import settings
import scipy.io
import scipy.misc
class Model(object):
def __init__(self, content_path, style_path):
self.content = self.loadimg(content_path) # 加载内容图片
self.style = self.loadimg(style_path) # 加载风格图片
self.random_img = self.get_random_img() # 生成噪音内容图片
self.net = self.vggnet() # 建立vgg网络
def vggnet(self):
# 读取预训练的vgg模型
vgg = scipy.io.loadmat(settings.VGG_MODEL_PATH)
vgg_layers = vgg['layers'][0]
net = {
}
# 使用预训练的模型参数构建vgg网络的卷积层和池化层
# 全连接层不需要
# 注意,除了input之外,这里参数都为constant,即常量
# 和平时不同,我们并不训练vgg的参数,它们保持不变
# 需要进行训练的是input,它即是我们最终生成的图像
net['input'] = tf.Variable(np.zeros([1, settings.IMAGE_HEIGHT, settings.IMAGE_WIDTH, 3]), dtype=tf.float32)
# 参数对应的层数可以参考vgg模型图
net['conv1_1'] = self.conv_relu(net['input'], self.get_wb(vgg_layers, 0))
net['conv1_2'] = self.conv_relu(net['conv1_1'], self.get_wb(vgg_layers, 2))
net['pool1'] = self.pool(net['conv1_2'])
net['conv2_1'] = self.conv_relu(net['pool1'], self.get_wb(vgg_layers, 5))
net['conv2_2'] = self.conv_relu(net['conv2_1'], self.get_wb(vgg_layers, 7))
net['pool2'] = self.pool(net['conv2_2'])
net['conv3_1'] = self.conv_relu(net['pool2'], self.get_wb(vgg_layers, 10))
net['conv3_2'] = self.conv_relu(net['conv3_1'], self.get_wb(vgg_layers, 12))
net['conv3_3'] = self.conv_relu(net['conv3_2'], self.get_wb(vgg_layers, 14))
net['conv3_4'] = self.conv_relu(net['conv3_3'], self.get_wb(vgg_layers, 16))
net['pool3'] = self.pool(net['conv3_4'])
net['conv4_1'] = self.conv_relu(net['pool3'], self