由于工作需要,最近要将一个keras模型部署在c++环境下,然而keras只有python接口。如果要支持c++,需要将将Keras的.h5 模型先转换成Tensorflow的.pb模型,然后通过Tensorflow的C++接口来加载使用。
由于Keras底层本来就是调用的Tensorflow,所以整个转换过程也很简单快速。
概括起来就是冻结模型,保存参数。
可以是自己训练生成的,也可以是直接从网上下载的预训练模型。
比如VGG之类较常用模型,也可以通过keras接口直接加载预训练模型。
from keras.applications.vgg16 import VGG16
keras_model = VGG16(weights='imagenet',input_shape=(224, 224, 3),pooling='max',include_top=False)
keras_model .save('./vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5')
from keras.models import load_model
from keras import backend as K
def freeze_session(session, keep_var_names=None, output_names=None, clear_devices=True):
from tensorflow.python.framework.graph_util import convert_variables_to_constants
graph = session.graph
with graph.as_default():
freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference(keep_var_names or []))
output_names = output_names or []
output_names += [v.op.name for v in tf.global_variables()]
input_graph_def = graph.as_graph_def()
if clear_devices:
for node in input_graph_def.node:
node.device = ""
frozen_graph = convert_variables_to_constants(session, input_graph_def,
output_names, freeze_var_names)
return frozen_graph
import tensorflow as tf
from tensorflow.python.framework import graph_io
#设置路径
h5_model_path='./vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5'
pb_model_name='vgg16_weights_tf_dim_ordering_tf_kernels_notop.pb'
#加载keras模型
K.set_learning_phase(0)
net_model = load_model(h5_model_path)
print('input is :', net_model.input.name)
print ('output is:', net_model.output.name)
#冻结并保存为Tensorflow模型
sess = K.get_session()
frozen_graph = freeze_session(K.get_session(), output_names=[net_model.output.op.name])
graph_io.write_graph(frozen_graph, output_path, pb_model_name, as_text=False)
模型转换完成后,我们需要确认模型转换是否正确。
验证方案:读取一张图片,按输入尺寸缩放后,验证VGG模型提取的特征是否一致。
(1)用Keras模型预测
# 读取文件*
img = cv2.imread("11.jpg")
img = cv2.resize(img, (224, 224), interpolation=cv2.INTER_NEAREST)
img = np.asarray(img, 'f')
img = np.expand_dims(img, axis=0)
*# 导入模型*,也可以loadmodel本地模型
keras_VGG = VGG16(weights='imagenet',input_shape=(224, 224, 3),pooling='max',include_top=False)
*# 预测向量*
feature = keras_VGG.predict(img)
print(feature)
(2)用Tensorflow模型预测
import numpy as np
from keras.applications.vgg16 import VGG16
import tensorflow as tf
import cv2
*# 读取文件*
img = cv2.imread("11.jpg")
img = cv2.resize(img, (224, 224), interpolation=cv2.INTER_NEAREST)
img = np.asarray(img, 'f')
img = np.expand_dims(img, axis=0)
with tf.Graph().as_default():
output_graph_def = tf.GraphDef()
*# 打开.pb模型*
with open("vgg16_weights_tf_dim_ordering_tf_kernels_notop.pb", "rb") as f:
output_graph_def.ParseFromString(f.read())
tensors = tf.import_graph_def(output_graph_def, name="")
print("tensors:", tensors)
*#建立会话执行预测*
with tf.Session() as sess:
init = tf.global_variables_initializer()
sess.run(init)
op = sess.graph.get_operations()
input_x = sess.graph.get_tensor_by_name("input_1:0") # 具体名称看上一段代码的input.name
print("input_X:", input_x)
out_softmax = sess.graph.get_tensor_by_name("global_max_pooling2d_1/Max:0") # 具体名称看上一段代码的output.name
print("Output:", out_softmax)
feature = sess.run(out_softmax, feed_dict={input_x: np.reshape(img, (1, 224, 224, 3))})
print(feature)
执行后可以看到,输入相同的情况下,他们的输出结果都是一样的,从而可以证明,模型转换是正确的。具体数据我就不贴出来了,可自行测试。