Keras:Keras訓練模型的C++呼叫嘗試
阿新 • • 發佈:2019-02-01
最近遇到一個專案中需要使用Keras進行訓練然後還要用C++去呼叫模型.但是Keras沒有C++介面,因此目前是將Keras模型轉換為TensorFlow模型然後再使用TensorFlow的C++介面進行呼叫.
為了快速驗證效果,這裡只使用原來圖片中的2個分類同時每個分類中只使用少部分圖片進行訓練.
資料集存放目錄為:
------------------\tmp
----------\train
------\0
------\1
----------\validation
------\0
------\1
----------\test
------\0
------\1
Keras模型
Keras訓練部分(分別將訓練模型的網路結構和網路權重存放在json檔案和h5檔案中)
Keras模型訓練部分
#!/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)
Keras模型驗證
使用測試集中的資料驗證模型,這裡只使用了其中一個分類進行驗證.
#!/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.]]
......
Keras訓練模型轉換為tensorflow二進位制模型
多方查詢,在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……
參考: