最近遇到一个项目中需要使用Keras进行训练然后还要用C++去调用模型.但是Keras没有C++接口,因此目前是将Keras模型转换为TensorFlow模型然后再使用TensorFlow的C++接口进行调用.
为了快速验证效果,这里只使用原来图片中的2个分类同时每个分类中只使用少部分图片进行训练.
数据集存放目录为:
------------------\tmp
----------\train
------\0
------\1
----------\validation
------\0
------\1
----------\test
------\0
------\1
Keras训练部分(分别将训练模型的网络结构和网络权重存放在json文件和h5文件中)
#!/usr/bin/python
# coding:utf8
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same',input_shape=(64, 64, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(loss='binary_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
train_datagen = ImageDataGenerator(rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
test_datagen = ImageDataGenerator(rescale=1. / 255)
train_generator = train_datagen.flow_from_directory(
'tmp/train', # this is the target directory
target_size=(64, 64),
batch_size=32,
class_mode='binary')
# this is a similar generator, for validation data
validation_generator = test_datagen.flow_from_directory(
'tmp/validation',
target_size=(64, 64),
batch_size=32,
class_mode='binary')
model.fit_generator(
train_generator,
steps_per_epoch=50,
epochs=10,
validation_data=validation_generator,
validation_steps=25)
model.save_weights('tmp/first_try.h5')
model.save('tmp/first_try.hdf5')
json_string = model.to_json()
open('tmp/first_try.json','w').write(json_string)
使用测试集中的数据验证模型,这里只使用了其中一个分类进行验证.
#!/usr/bin/python
# coding:utf8
# # 模型结构的恢复
from keras.models import model_from_yaml
from keras.preprocessing import image
from keras.preprocessing.image import load_img
import numpy as np
import os
yaml_string = open('tmp/first_try.json').read()
model = model_from_yaml(yaml_string)
# 打印模型
# model.summary()
# 加载权重
classifier = model.load_weights('tmp/first_try.h5')
# # 遍历图像
rootdir = 'tmp/test/1'
list = os.listdir(rootdir)
for i in range(0,len(list)):
path = os.path.join(rootdir, list[i])
if os.path.isfile(path):
print (path)
# 加载图像
img = load_img(path, target_size=(64, 64))
img = image.img_to_array(img) / 255.0
img = np.expand_dims(img, axis=0)
predictions = model.predict(img)
if predictions[0][0] > 0.1:
print (predictions)
else:
print ('Error!!!')
输出:
tmp/test/1/pandas_0_435.jpg
[[1.]]
tmp/test/1/pandas_0_453.jpg
[[1.]]
tmp/test/1/pandas_0_422.jpg
[[1.]]
tmp/test/1/pandas_0_483.jpg
[[1.]]
tmp/test/1/pandas_0_479.jpg
[[1.]]
tmp/test/1/pandas_0_464.jpg
[[1.]]
tmp/test/1/pandas_0_612.jpg
[[1.]]
tmp/test/1/pandas_0_571.jpg
[[1.]]
tmp/test/1/pandas_0_519.jpg
[[1.]]
......
多方查找,在github上找到一个将Keras训练模型转换为tensorflow二进制模型的方法(github::eras_to_tensorflow),亲测可用,在这里记录一下吧:
#!/usr/bin/python
# coding:utf8
##-------keras模型保存为tensorflow的二进制模型-----------
import sys
from keras.models import load_model
import tensorflow as tf
import os
import os.path as osp
from keras import backend as K
from tensorflow.python.framework.graph_util import convert_variables_to_constants
def freeze_session(session, keep_var_names=None, output_names=None, clear_devices=True):
# 将会话状态冻结为已删除的计算图,创建一个新的计算图,其中变量节点由在会话中获取其当前值的常量替换.
# session要冻结的TensorFlow会话,keep_var_names不应冻结的变量名列表,或者无冻结图中的所有变量
# output_names相关图输出的名称,clear_devices从图中删除设备以获得更好的可移植性
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
output_fld = sys.path[0] + '/tmp/'
if not os.path.isdir(output_fld):
os.mkdir(output_fld)
weight_file_path = osp.join(sys.path[0], 'tmp/first_try.hdf5')
K.set_learning_phase(0)
net_model = load_model(weight_file_path)
print('input is :', net_model.input.name)
print ('output is:', net_model.output.name)
# 获得当前图
sess = K.get_session()
# 冻结图
frozen_graph = freeze_session(sess, output_names=[net_model.output.op.name])
from tensorflow.python.framework import graph_io
graph_io.write_graph(frozen_graph, output_fld, 'new_tensor_model.pb', as_text=False)
print('saved the constant graph (ready for inference) at: ', osp.join(output_fld, 'new_tensor_model.pb'))
print (K.get_uid())
输出pb文件,打印出:
('input is :', u'conv2d_1_input:0')
('output is:', u'activation_5/Sigmoid:0')
Converted 24 variables to const ops.
('saved the constant graph (ready for inference) at: ', '/home/w/mycode/pythoncode/tmp/new_tensor_model.pb')
#!/usr/bin/python
# coding:utf8
import tensorflow as tf
import numpy as np
import os
import cv2
def predict(jpg_path, pb_file_path):
with tf.Graph().as_default():
output_graph_def = tf.GraphDef()
with open(pb_file_path, "rb") as f:
output_graph_def.ParseFromString(f.read())
tensors = tf.import_graph_def(output_graph_def, name="")
# print (tensors)
with tf.Session() as sess:
init = tf.global_variables_initializer()
sess.run(init)
sess.graph.get_operations()
input_x = sess.graph.get_tensor_by_name("conv2d_1_input:0") # 具体名称看上一段代码的input.name
out_softmax = sess.graph.get_tensor_by_name("activation_5/Sigmoid:0") # 具体名称看上一段代码的output.name
img = cv2.imread(jpg_path, 1)
img_out_softmax = sess.run(out_softmax,feed_dict={input_x: np.array(img).reshape((-1, 64, 64, 3)) / 255.0})
# print (img_out_softmax)
return img_out_softmax
pb_path = 'tmp/new_tensor_model.pb'
rootdir = 'tmp/test/0'
if __name__ == '__main__':
list = os.listdir(rootdir)
for i in range(0,len(list)):
path = os.path.join(rootdir, list[i])
if os.path.isfile(path):
print (path)
index = predict(path, pb_path)
print (index)
输出:
tmp/test/0/pandas_0_702.jpg
[[1.6394738e-11]]
tmp/test/0/pandas_0_737.jpg
[[5.2570143e-11]]
tmp/test/0/pandas_0_663.jpg
[[7.964132e-14]]
tmp/test/0/pandas_0_661.jpg
[[1.4841452e-10]]
tmp/test/0/pandas_0_693.jpg
[[5.834242e-15]]
tmp/test/0/pandas_0_723.jpg
[[3.9710753e-13]]
tmp/test/0/pandas_0_711.jpg
[[1.6867729e-12]]
......
然后就是TensorFlow的C++接口编译问题,还在进行中……
另外,看到github上有朋友直接将Keras模型直接转化为C++可点调用的文本文件github::keras2cpp.在QT中尝试了一下,发现样例可以用:
但是从keras.model.cc文件中的代码片段中可以看出,目前应该只是针对Keras1.x版本.
Layer *l = 0L;
if(layer_type == "Convolution2D") {
l = new LayerConv2D();
} else if(layer_type == "Activation") {
l = new LayerActivation();
} else if(layer_type == "MaxPooling2D") {
l = new LayerMaxPooling();
} else if(layer_type == "Flatten") {
l = new LayerFlatten();
} else if(layer_type == "Dense") {
l = new LayerDense();
} else if(layer_type == "Dropout") {
continue; // we dont need dropout layer in prediciton mode
}
if(l == 0L) {
cout << "Layer is empty, maybe it is not defined? Cannot define network." << endl;
return;
}
l->load_weights(fin);
m_layers.push_back(l);
}
fin.close();
}
而且后端是Theano.因此没有使用该方法.
还得继续编译一下TensorFlow……
参考:
github::eras_to_tensorflow
github::keras2cpp