/ KERAS, DEEPLEARNING, 环境搭建

Keras训练模型部署到C++环境并生成DLL

一. 准备工作

  • 在Windows上搭建完成Tensorflow的C++环境,这里参考本人的上一篇博客

  • 在windows上部署opencv3的C++环境

  • python3的keras和tf2.X的环境:pip3 install keras==2.4.3pip3 install tensorflow==2.3.1

  • python3的opencv环境:pip3 install opencv-python

二. python模型训练

python模型构建:Kaggle猫狗分类

python完整代码放在本人的git仓库上面

2.1 模型训练,保存.h5格式的模型文件

在keras的model.save()模型保存的函数中,只支持2种保存方式:

  • .h5格式的文件进行保存模型整个结构及其权重。

  • 以文件夹(包含assets saved_model.pb variables)来保存,模型架构和训练配置(包括优化器、损失和指标)存储在 saved_model.pb 中。权重保存在 variables/ 目录下。

因此使用.h5格式来存储模型结构及权重。

2.2 h5文件转pt文件

将训练好的模型以.h5格式进行存储,再通过转成.pt格式的模型文件,提供C++的tensorflow调用模型。

from keras.models import load_model
import tensorflow as tf
import tensorflow.python.keras.backend as K
from tensorflow.python.framework import graph_io
# 针对tf2.x来说不支持freezegraph的,这里需要使用tf1的方式
tf.compat.v1.disable_eager_execution()

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.compat.v1.global_variables()).difference(keep_var_names or []))
        output_names = output_names or []
        output_names += [v.op.name for v in tf.compat.v1.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


"""----------------------------------配置路径-----------------------------------"""
h5_model_path = 'model/model.h5'
pb_model_name = 'model.pt'
output_path = '.'

"""----------------------------------导入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)

"""----------------------------------保存为.pb格式------------------------------"""
sess = K.get_session()
frozen_graph = freeze_session(K.get_session(),output_names=[out.op.name for out in net_model.outputs])
graph_io.write_graph(frozen_graph, output_path, pb_model_name, as_text=False)

2.3 测试pt模型文件

对同一个图片分别利用.h5模型文件和.pt模型文件进行预测。.pt格式的模型预测代码如下:

def pred_with_pt(img, pb_file_path):
    with tf.Graph().as_default():
        output_graph_def = tf.compat.v1.GraphDef()

        # 打开.pb模型
        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:", tensors)

        # 在一个session中去run一个前向
        with tf.compat.v1.Session() as sess:
            init = tf.compat.v1.global_variables_initializer()
            sess.run(init)

            op = sess.graph.get_operations()

            input_x = sess.graph.get_tensor_by_name("conv2d_input:0")  # 具体名称看上一段代码的input.name
            print("input_X:", input_x)

            out_softmax = sess.graph.get_tensor_by_name("dense/Softmax:0")  # 具体名称看上一段代码的output.name
            print("Output:", out_softmax)

            img_out_softmax = sess.run(out_softmax,
                                       feed_dict={input_x: img})

            return img_out_softmax

三. C++ 模型预测

3.1 将模型预测类进行封装并生成动态链接库DLL

  • VS2019部署(参考本人的上一篇博客):新建一个动态链接库(DLL),新建一个头文件tf_clf.h和源文件tf_clf.cpp,这里是生成DLL包(点击生成 ==> 重新生成DLLTF

  • 这里要把tensorflow_cc.dll放到生成的x64/release里面

  • 这里还需要在Release属性页里配置C/C++的预处理器(防止后面编译时出现这种错误tstring.h(350,40): error C2589: “(”:“::”):

    _XKEYCHECK_H
    NOMINMAX
    
  • 头文件tf_clf.h中声明类的导出

class __declspec(dllexport) TFClf;
class TFClf {
private:
vector<float> mean = { 103.939,116.779,123.68 };
int resize_col = 224;
int resize_row = 224;
string input_tensor_name = "conv2d_input";
string output_tensor_name = "dense/Softmax";
Point draw_point = Point(50, 50);

public:
string image_path, model_path;
TFClf(string img, string model) :image_path(img), model_path(model) {}
void mat_to_tensor(Mat img, Tensor* output_tensor);
Mat preprocess_img(Mat img);
void model_pred();
void show_result_pic(Mat img, int output_class_id, double output_prob);
};
  • 源文件tf_clf.cpp完成类的具体实现,注意这里要#include "pch.h"

3.2 新建一个项目测试DLL

  • 新建一个控制台的空项目,并将打包好的DllTF.dllDllTF.lib复制到工程中

  • 配置属性管理器:这里需要在Release | x64添加之前配置好的opencv_release.propstf_release.props

  • 这里要把tensorflow_cc.dll放到生成的x64/release里面

  • 新建一个头文件tf_clf.h

#pragma once
#ifndef TF_CLF_H

#endif // !TF_CLF_H

#pragma comment(lib,"DllTF.lib")
class __declspec(dllexport) TFClf;


class TFClf {
private:
    vector<float> mean = { 103.939,116.779,123.68 };
    int resize_col = 224;
    int resize_row = 224;
    string input_tensor_name = "conv2d_input";
    string output_tensor_name = "dense/Softmax";
    Point draw_point = Point(50, 50);

public:
    string image_path, model_path;
    TFClf(string img, string model) :image_path(img), model_path(model) {}
    void mat_to_tensor(Mat img, Tensor* output_tensor);
    Mat preprocess_img(Mat img);
    void model_pred();
    void show_result_pic(Mat img, int output_class_id, double output_prob);
};
  • 新建一个源文件main.cpp
# include "tf_clf.h"

int main() {
string model_path = "D:/yeyan/pycharm_project/dogcat/model/model.pt";
string img_path = "D:/yeyan/pycharm_project/dogcat/data/test_set/test_set/cats/cat.4001.jpg";
TFClf clf = TFClf(img_path, model_path);
clf.model_pred();
}