一、实验目的
学习掌握使用TensorFlow定义完整网络结构的方法,掌握使用TensorFlow恢复模型参数的方法,以实时风格迁移算法为例,掌握在CPU平台上使用TensorFlow进行网络推断的方法,理解DLP高性能算子库集成到TensorFlow框架的基本原理掌握在 DLP 平台上使用 TensorFlow 对模型进行量化并实现神经网络推断的方法。
二、背景介绍
· 图像转换网络
图像转换网络由三个卷积层、五个残差块、两个转置卷积层再接一个卷积层构成。除了输出层,所有非残差卷积层后面都加了批归一化(batch normalization,BN)[16] 和 ReLU 操作,输出层使用 tanh 函数将输出像素值限定在 [0, 255] 范围内;第一层和最后一层卷积使用 9 × 9 卷积核,其它卷积层都使用 3 × 3 卷积核;每个残差块中包含两层卷积。
- 残差块
图像转换网络中包含了五个残差块,其基本结构如图所示:输入x经过一个卷积层,再做ReLU,然后经过另一个卷积层得到F(x),再加上x得到输出H(x)=F(x)+x,然后做ReLU得到基本块的最终输出y。当输入x的维度与卷积输出F(x)的维度不同时,需要先对x做恒等变换使二者维度一致,然后再加和。
残差块结构
- 转置卷积
转置卷积又可以称为小数步长卷积,下图是一个转置卷积的示例。输入矩阵InputData是2×2的矩阵,卷积核Kernel的大小为3×3,卷积步长为1,输出OutputData是4×4的矩阵。
转置卷积
- 实例归一化
图像转换网络中,每个卷积计算之后激活函数之前都插入了一种特殊的跨样本的批归一化层。该方法由谷歌的科学家在2015年提出,它使用多个样本做归一化,将输入归一化到加了参数的标准正态分布上。这样可以有效避免梯度爆炸或消失,从而训练出较深的神经网络。批归一化的计算方法如下。
- TensorFlow中模型参数的恢复
在 TensorFlow 中,采用检查点机制(Checkpoint)周期地记录(Save)模型参数等数据并存储到文件系统中,后续当需要继续训练或直接使用训练好的参数做推断时,需要从文件系统中将保存的模型恢复 (Restore) 出来。
三、实验环境
硬件包括CPU和DLP,软件平台包括TensorFlow 1.14,Python 2.7.12,Pillow 4.2.1,Scipy 1.0.0,Numpy 1.16.6,CNML高性能算子库,CNRT运行时库。
四、实验内容
4.1 读取图像
使用scipy.misc模块读入输入图像src并转化成’RGB’模式,返回ndarray类型数组img,如果程序中指定了图片尺寸,就将该图像缩放至指定的尺寸。该部分代码如下:
def scale_img(style_path, style_scale):
scale = float(style_scale)
o0, o1, o2 = scipy.misc.imread(style_path, mode='RGB').shape
scale = float(style_scale)
new_shape = (int(o0 * scale), int(o1 * scale), o2)
style_target = _get_img(style_path, img_size=new_shape)
return style_target
def get_img(src, img_size=False):
img = scipy.misc.imread(src,mode="RGB")
img = scipy.misc.imresize(img,img_size) if img_size else img
return img
4.2 CPU上实现实时风格迁移
为了在CPU上实现实时风格迁移,需要使用图像转换网络对应的pb模型文件处理输入图像,得到风格迁移后的输出图像。主要包括实时风格迁移函数和实时风格迁移主函数的定义等。
实时风格迁移函数:
# TODO:如果 data_in 是保存输入图像的文件路径,则依次将该批次中输入图像文件路径下的 batch_size 张图像读入数组 X;
# 如果 data_in 是已经读入图像并转化成数组形式的数据,则将该数组传递给 X
X = [get_img(path,img_shape) for path in data_in[pos:pos+batch_size]] if is_paths else data_in[pos:pos+batch_size]
start = time.time()
# TODO: 使用 sess.run 来计算 output_tensor
_preds = sess.run(output_tensor,feed_dict={input_tensor:X})
end = time.time()
for j, path_out in enumerate(curr_batch_out):
#TODO:在该批次下调用 utils.py 中的 save_img() 函数对所有风格迁移后的图片进行存储
save_img(path_out,_preds[j])
delta_time = end - start
print("Inference (CPU) processing time: %s" % delta_time)
主函数:
ffwd_different_dimensions(full
if not os.path.isdir(opts.in_path):
if os.path.exists(opts.out_path) and os.path.isdir(opts.out_path):
out_path = os.path.join(opts.out_path,os.path.basename(opts.in_path))
else:
out_path = opts.out_path
# 执行风格迁移预测,输入图像opts.in_path,转换后的图像为out_path,模型文件路径为 opts.model
ffwd_to_img(opts.in_path, out_path, opts.model, device=opts.device)
else:
files = list_files(opts.in_path)
full_in = [os.path.join(opts.in_path,x) for x in files]
full_out = [os.path.join(opts.out_path,x) for x in files]
if opts.allow_different_dimensions:
_in, full_out, opts.model,
device_t=opts.device, batch_size=opts.batch_size)
else :
# 执行风格迁移预测,输入图像的保存路径为 full_in,转换后的图像为 full_out,模型文件路径为 opts.model
ffwd(full_in, full_out, opts.model, device_t=opts.device, batch_size=opts.batch_size)
执行实时风格迁移,运行以下命令:
python ./stu_upload/evaluate_cpu.py --model pb_models/udnie.pb --in-path data/train2014_small/ --out-path out/cpu/
运行输出结果如下:
CPU上实时风格迁移运行结果
4.3 在DLP上实现实时风格迁移
- 模型量化
已经提前训练好的图像转换网络的数据类型为 Float32,需要经过量化后才可以在 DLP上运行。在 fppb_to_intpb 目录下运行以下命令,使用量化工具完成对模型的量化,生成新模型 udnie_int8.pb。
python fppb_to_intpb.py udnie_int8.ini
- 模型推断
通过 DLP 定制的 TensorFlow 版本(其中大部分风格迁移的算子都通过 DLP 的高性能库支持)完成风格迁移模型的前向推断。为了使上层用户不感知底层硬件的迁移,定制的TensorFlow 维持了上层的 Python 接口,用户可以通过 session config 配置 DLP 运行的相关参数以及使用相关接口进行量化。具体的运行时配置信息如下:
os.putenv('MLU_VISIBLE_DEVICES','0')
# get img_shape
def ffwd(data_in, paths_out, model, device_t='', batch_size=1):
assert len(paths_out) > 0
is_paths = type(data_in[0]) == str
# TODO:如果 data_in 是保存输入图像的文件路径,即 is_paths 为 True,则读入第一张图像,由于 pb 模型的输入维度为 1 × 256 × 256 × 3, 因此需将输入图像的形状调整为 256 × 256,并传递给 img_shape;
# 如果 data_in 是已经读入图像并转化成数组形式的数据,即 is_paths 为 False,则直接获取图像的 shape 特征 img_shape
img_shape = get_img(data_in[0], (256, 256, 3)).shape if is_paths else data_in[0].shape
g = tf.Graph()
# setting mlu configurations
config = tf.ConfigProto(allow_soft_placement=True, inter_op_parallelism_threads=1,intra_op_parallelism_threads=1)
config.mlu_options.data_parallelism = 1
config.mlu_options.model_parallelism = 1
config.mlu_options.core_num = 16 # 1 4 16
config.mlu_options.core_version = "MLU270"
config.mlu_options.precision = "int8"
config.mlu_options.save_offline_model = False
在DLP上运行结果如图所示:
DLP上实时风格迁移运行结果
五、实验评估
本次实验在CPU平台上正确实现实时风格迁移的推断过程,给定输入图形,权重参数,可以实时计算并输出风格迁移后的图像,同时程序也输出了对图像进行实时风格迁移所需的时间,在DLP平台上,给定输入图像、权重参数,能够实时输出风格迁移后的图像,DLP和CPU平台上实现实时风格迁移的时间对比如下,
从上图可以看出,两者第一次运行时间均较反常,与·初次启动的IO加载有关,导致启动时间过长,但整体而言在DLP上的运行速度远大于在CPU上的运行速度。