**
基于Tensorflow 2.0实现的图片风格迁移
**
- 摘 要 神经风格迁移是一种优化技术,用于将两个图像(一个内容图像和一个风格参考图像)混合在一起,使输出的噪声图像看起来像内容图像,
但是使用了风格参考图像的风格。 这是通过优化输出图像以匹配内容图像的内容统计数据和风格参考图像的风格统计数据来实现的。
这些统计数据可以使用卷积网络从图像中提取。
本次论文采用Tensorflow2.0实现简单的图片风格迁移,采用官方VGG19训练模型,对内容、风格图片进行训练,通过增加训练次数与噪声图片生成次数达到减小图片内容损失以及风格损失的目的;实验表明通过增加训练次数与噪声图片生成次数可以明显的减小图片的内容以及风格损失。 - 关键词:神经网络;风格迁移;卷积网络;VGG19;Tensorflow2.0
**
1 绪论
**
- 所谓图像风格迁移,即给定内容图片A,风格图片B,能够生成一张具有A图片内容和B图片风格的图片C。比如说,我们可以使用梵高先生的名画《星夜》
- 作为风格图片,来与其他图片生成具有《星夜》风格新图片。举两个简单的例子:利用梵高的名画——《星夜》作为风格图片,一个常见的动漫图片作为内容图片。分别计算出内容图片与风格图片的内容特征,再基于风格图片生成的的噪声图片风格特征与内容图片融合生成一张新的噪声图片即风格迁移后的图片。
图1-1 风格图片以及迁移前后对比图
- 针对原Demo对其训练次数以及训练后融合次数进行修改,将训练次数改为150次(原Demo为100次),融合次数改为30次(原Demo为20次)。随着训练次数和融合次数增加图片的内容损失以及风格损失有着明显的降低。下图为修改后对比图:
图1-2 训练次数与融合次数不同对比图
**
; 2 项目
**
2.1 项目器材
- 联想拯救者Y7000笔记本Windows10系统,16GB内存,主硬盘128GB,CPU(Intel@Core i5-8300H),显卡(NVIDIA GTX 1050 Ti 4GB)
2.2 项目环境
- Anaconda3-5.1.0+PyCharm+Python3.6
2.3 项目内容
2.3.1 项目包含文件
- 本项目一共包含四个Python文件以及一个TXT文件,其中model.py文件为模型初始化文件,即调用官方VGG19模型文件;settings.py文件为整个项目设置相关参数的重要文件,主要设置内容特征层loss加权系数、风格特征层loss加权系数、内容图片和风格图片输入路径、生成图片输出路径、内容和特征总loss加权系数、输出图片大小以及训练次数等;utils.py文件主要作用为对图片进行解码、归一化处理;train.py文件主要作用为计算风格和内容特征、计算风格和内容loss、加速训练以及生成噪声图片(即风格迁移后的图片);requirements.txt文件可通过修改文件中涉及相关Python包的版本,达到在不同电脑上也能正常运行的目的。
2.3.2 实验过程
- 首先查看运行设备的Python环境以及项目涉及的相关附件包版本,通过修改requirements.txt文件,将涉及包版本修改与运行设备中相关版本。如下图所示:
图2.3.2—1 修改requirements.txt文件
- 其次通过settings.py文件设置图片的输入与输出位置和输出图片的大小以及相关训练epoch个数以及epoch训练次数。如下图所示:
图2.3.2—2 修改相关参数
- 运行train.py文件,等待模型训练,训练结束后可在输出文件夹中查看输出风格迁移后的图片。如下图:
图2.3.2—3 风格迁移后的输出图片
**
; 3 项目所遇问题
**
3.1 版本兼容性问题
- 由于python以及相关附件包版本不同,部分相关函数的调用方法有所改变。以至于在初步运行Demo时总是报错,其中报错最多的为找不到某一个附件包内所涉及的函数调用。通过在CSDN网站内大量查询解决办法,最终确定为版本不兼容,更新项目所涉及相关附件包后得以解决。
其中在正式确定本次项目Demo前,在运行其它类似项目时遇到以下问题: - Tensorflow与keras版本有相关对应关系[1]。
-
例如keras2.3.1对应tensorflow2.1、2.0.1.15.
-
Tensorflow与python版本有对应关系。[2]
- 例如windows环境下tensorflow1.6.0对应python3.5-3.6
- Pytorch和Torchvision以及python版本有严格对应关系[3]。
- 例如torch1.4.0对应torchvision0.5.0,python2.7或者python3.5—3.8。
3.2 图片输出问题
- 在原项目源码中发现对于风格迁移后的噪声图片输出大小做出了严格的限制(即将图片压缩后输出),在查询相关知识点后,发现可以通过python的图像处理库——PIL修改输出图片的大小[4];PIL提供了功能丰富的方法,比如格式转换、旋转、裁剪、改变尺寸、像素处理、图片合并等。源码将输出图片大小定为450*300,通过PIL库中Image读取内容噪声图片,再通过相关方法将输出噪声图片大小改为与内容图片大小。
图3.2—1 修改噪声图片输出大小
; 3.3 epoch训练问题
- 原项目中epoch训练个数为20个,训练次数为100次,从输出的噪声图片看,输出的噪声图片对风格、内容特征的损失比较少,但再将epoch训练个数增加至30个、训练次数增加至150次后,输出的噪声图片的内容、风格特征损失达到一个理想值,当继续增加epoch训练个数和训练次数时提升并不明显,最终确定epoch训练个数为30个训练次数为150次。
结 论
- 原项目中[5]重点提到了以梵高名画《星空》作为风格图片进行相关的图像风格融合,且风格与内容的损失比较少。但该项目对于其它风格的图像迁移也有着不错的表现,例如赛博朋克风格、素描风格等,其中赛博朋克风格在建筑上的表现更佳,对于其它类型的图片表现不够理想,素描风格的整体表现都不错。
- 该项目所训练的模型不止能对于绘画内的风格迁移,摄影类也能很好的进行风格迁移,但对于摄影类(即真实类)的风格、内容损失较大,能够明显的感受到有很大的突兀感,不够真实。其绘画(素描)类的风格迁移表现不够理想,突兀感很强烈,风格损失过大。
赛博朋克凤格:
素描风格:
- 该项目还有着很大的优化空间,当风格、内容图像特点比较多时,项目训练时间也会更长。后续可以通过提升电脑性能以及优化源码完成对训练时间的缩短。
; 参考文献
- [1]tensorflow和keras版本对应关系(blog.csdn.net/weixin_40109345/article/details/106730050?spm=1001.2014.3001.5502)
- [2]各操作系统与Tensorflow版本配套关系(blog.csdn.net/u014797226/article/details/80632440?spm=1001.2014.3001.5502)
- [3]Pytorch和Torchvision版本对应(blog.csdn.net/qq_40263477/article/details/106577790?spm=1001.2014.3001.5502)
- [4]Python「PIL」:调整图片大小(blog.csdn.net/qq_41297934/article/details/105302393?spm=1001.2014.3001.5502)
- [5]有趣的深度学习——使用TensorFlow 2.0实现图片神经风格迁移
(https://blog.csdn.net/aaronjny/article/details/104879258)
附录
- model.py
import typing
import tensorflow as tf
import settings
def get_vgg19_model(layers):
"""
创建并初始化vgg19模型
:return:
"""
vgg = tf.keras.applications.VGG19(include_top=False, weights='imagenet')
outputs = [vgg.get_layer(layer).output for layer in layers]
model = tf.keras.Model([vgg.input, ], outputs)
model.trainable = False
return model
class NeuralStyleTransferModel(tf.keras.Model):
def __init__(self, content_layers: typing.Dict[str, float] = settings.CONTENT_LAYERS,
style_layers: typing.Dict[str, float] = settings.STYLE_LAYERS):
super(NeuralStyleTransferModel, self).__init__()
self.content_layers = content_layers
self.style_layers = style_layers
layers = list(self.content_layers.keys()) + list(self.style_layers.keys())
self.outputs_index_map = dict(zip(layers, range(len(layers))))
self.vgg = get_vgg19_model(layers)
def call(self, inputs, training=None, mask=None):
outputs = self.vgg(inputs)
content_outputs = []
for layer, factor in self.content_layers.items():
content_outputs.append((outputs[self.outputs_index_map[layer]][0], factor))
style_outputs = []
for layer, factor in self.style_layers.items():
style_outputs.append((outputs[self.outputs_index_map[layer]][0], factor))
return {'content': content_outputs, 'style': style_outputs}
- settings.py
from PIL import Image
CONTENT_LAYERS = {'block4_conv2': 0.5, 'block5_conv2': 0.5}
STYLE_LAYERS = {'block1_conv1': 0.2, 'block2_conv1': 0.2, 'block3_conv1': 0.2, 'block4_conv1': 0.2,
'block5_conv1': 0.2}
CONTENT_IMAGE_PATH = './images/test_0.jpg'
STYLE_IMAGE_PATH = './images/style_2.jpeg'
OUTPUT_DIR = './output/'
CONTENT_LOSS_FACTOR = 1
STYLE_LOSS_FACTOR = 100
img_pillow = Image.open(CONTENT_IMAGE_PATH)
WIDTH = img_pillow.width
HEIGHT = img_pillow.height
EPOCHS = 1
STEPS_PER_EPOCH = 100
LEARNING_RATE = 0.03
- utils.py
import tensorflow as tf
import settings
image_mean = tf.constant([0.485, 0.456, 0.406])
image_std = tf.constant([0.299, 0.224, 0.225])
def normalization(x):
"""
对输入图片x进行归一化,返回归一化的值
"""
return (x - image_mean) / image_std
def load_images(image_path, width=settings.WIDTH, height=settings.HEIGHT):
"""
加载并处理图片
:param image_path: 图片路径
:param width: 图片宽度
:param height: 图片长度
:return: 一个张量
"""
x = tf.io.read_file(image_path)
x = tf.image.decode_jpeg(x, channels=3)
x = tf.image.resize(x, [height, width])
x = x / 255.
x = normalization(x)
x = tf.reshape(x, [1, height, width, 3])
return x
def save_image(image, filename):
x = tf.reshape(image, image.shape[1:])
x = x * image_std + image_mean
x = x * 255.
x = tf.cast(x, tf.int32)
x = tf.clip_by_value(x, 0, 255)
x = tf.cast(x, tf.uint8)
x = tf.image.encode_jpeg(x)
tf.io.write_file(filename, x)
- train.py
import os
import numpy as np
from tqdm import tqdm
import tensorflow as tf
from model import NeuralStyleTransferModel
import settings
import utils
model = NeuralStyleTransferModel()
content_image = utils.load_images(settings.CONTENT_IMAGE_PATH)
style_image = utils.load_images(settings.STYLE_IMAGE_PATH)
target_content_features = model([content_image, ])['content']
target_style_features = model([style_image, ])['style']
M = settings.WIDTH * settings.HEIGHT
N = 3
def _compute_content_loss(noise_features, target_features):
"""
计算指定层上两个特征之间的内容loss
:param noise_features: 噪声图片在指定层的特征
:param target_features: 内容图片在指定层的特征
"""
content_loss = tf.reduce_sum(tf.square(noise_features - target_features))
x = 2. * M * N
content_loss = content_loss / x
return content_loss
def compute_content_loss(noise_content_features):
"""
计算并当前图片的内容loss
:param noise_content_features: 噪声图片的内容特征
"""
content_losses = []
for (noise_feature, factor), (target_feature, _) in zip(noise_content_features, target_content_features):
layer_content_loss = _compute_content_loss(noise_feature, target_feature)
content_losses.append(layer_content_loss * factor)
return tf.reduce_sum(content_losses)
def gram_matrix(feature):
"""
计算给定特征的格拉姆矩阵
"""
x = tf.transpose(feature, perm=[2, 0, 1])
x = tf.reshape(x, (x.shape[0], -1))
return x @ tf.transpose(x)
def _compute_style_loss(noise_feature, target_feature):
"""
计算指定层上两个特征之间的风格loss
:param noise_feature: 噪声图片在指定层的特征
:param target_feature: 风格图片在指定层的特征
"""
noise_gram_matrix = gram_matrix(noise_feature)
style_gram_matrix = gram_matrix(target_feature)
style_loss = tf.reduce_sum(tf.square(noise_gram_matrix - style_gram_matrix))
x = 4. * (M ** 2) * (N ** 2)
return style_loss / x
def compute_style_loss(noise_style_features):
"""
计算并返回图片的风格loss
:param noise_style_features: 噪声图片的风格特征
"""
style_losses = []
for (noise_feature, factor), (target_feature, _) in zip(noise_style_features, target_style_features):
layer_style_loss = _compute_style_loss(noise_feature, target_feature)
style_losses.append(layer_style_loss * factor)
return tf.reduce_sum(style_losses)
def total_loss(noise_features):
"""
计算总损失
:param noise_features: 噪声图片特征数据
"""
content_loss = compute_content_loss(noise_features['content'])
style_loss = compute_style_loss(noise_features['style'])
return content_loss * settings.CONTENT_LOSS_FACTOR + style_loss * settings.STYLE_LOSS_FACTOR
optimizer = tf.keras.optimizers.Adam(settings.LEARNING_RATE)
noise_image = tf.Variable((content_image + np.random.uniform(-0.2, 0.2, (1, settings.HEIGHT, settings.WIDTH, 3))) / 2)
@tf.function
def train_one_step():
"""
一次迭代过程
"""
with tf.GradientTape() as tape:
noise_outputs = model(noise_image)
loss = total_loss(noise_outputs)
grad = tape.gradient(loss, noise_image)
optimizer.apply_gradients([(grad, noise_image)])
return loss
if not os.path.exists(settings.OUTPUT_DIR):
os.mkdir(settings.OUTPUT_DIR)
for epoch in range(settings.EPOCHS):
with tqdm(total=settings.STEPS_PER_EPOCH, desc='Epoch {}/{}'.format(epoch + 1, settings.EPOCHS)) as pbar:
for step in range(settings.STEPS_PER_EPOCH):
_loss = train_one_step()
pbar.set_postfix({'loss': '%.4f' % float(_loss)})
pbar.update(1)
utils.save_image(noise_image, '{}/{}.jpg'.format(settings.OUTPUT_DIR, epoch + 1))
- requirements.txt
tensorflow==2.0.0
numpy==1.19.5
tqdm==4.62.3
转载请注明来源
Original: https://blog.csdn.net/YiXuanYa9527/article/details/122207074
Author: YiXuanYa9527
Title: 基于Tensorflow 2.0实现的图片风格迁移
相关阅读
Title: BERT模型做word2vec
BERT模型做word2vec
*
- 一、模型下载
- 二、环境配置
-
+
* 已测试可运行环境:
* 环境配置
- 三、Bert模型简单应用
-
+
* 启动BertClient
* bert模型简单demo
- BUG记录
一、模型下载
BERT模型,GitHub源码,下载地址
https://github.com/google-research/bert
bert模型
PS:Multilingual 是多语言模型,最后一个是中文模型(只有字级别的) 其中 Uncased 是字母全部转换成小写,而Cased是保留了大小写
根据自已的需求下载相应的文件并解压
二、环境配置
已测试可运行环境:
- python 3.6
- tensorflow 1.14.0
- tensorflow-gpu 1.14.0
; 环境配置
极简配置Tensorflow,无需单独下载CUDA,cuDNN
- 使用Anaconda 创建一个python版本为3.6的新虚拟环境
- 点击右侧箭头,open terminal
- 终端输入
conda install -c aaronzs tensorflow-gpu==xxx
, 也可以不指定版本
等待下载完成 - 完成后输入
conda list
如果有cudatoolkit
和cudnn
说明成功 - 打开pycharm,新建项目,新建项目时环境选择刚刚配置的conda虚拟环境
- 项目新建完成后,使用terminal输入以下命令,安装bert-service支持包
pip install bert-serving-client
pip install bert-serving-server
注意tensorflow与CUDA、python版本相对应
至此,所需环境全部配置完成
PS:环境配置参考
TensorFlow-GPU极简———不用安装cuda、cuDNN!!!
BERT初体验
三、Bert模型简单应用
启动BertClient
terminal中输入
bert-serving-start -model_dir E:\Python-Project\uncased_L-12_H-768_A-12\uncased_L-12_H-768_A-12\ -num_worker=1
其中,model-dir 后面输入已解压的bert模型存放路径,最好用绝对路径,不容易出错
启动
只有当最后出现"start the sink、ready、listen"时才是启动成功了
只有启动了bert-serving服务,才能使用bert模型
bert模型简单demo
得到'dog'的词向量,1*768维
from bert_serving.client import BertClient
import numpy as np
import tensorflow as tf
tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR)
client = BertClient()
x = client.encode(['dog'])
print(np.shape(x))
print(x)
BUG记录
- bert模型可以启动,但是卡在了一半处,始终运行不到最后——没有正确的安装tensorflow、或者tensorflow-gpu,总之是环境问题
- 启动graph失败!——可能是tensorflow-gpu版本问题,一开始我使用1.15版报了这个错误,然后降级到1.14后不再报错
- 莫名其妙的python启动错误!——可能是以为电脑上python版本太多太乱,我重新建了一个项目,将代码copy过去,重新启动,正常!
Original: https://blog.csdn.net/wuxiemimei/article/details/123032424
Author: 光头强12138
Title: BERT模型做word2vec