1. 程式人生 > >Keras:Keras訓練模型的C++呼叫嘗試

Keras:Keras訓練模型的C++呼叫嘗試

最近遇到一個專案中需要使用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……

參考: