Keras训练模型在C++tensorflow上的运用实践(visual studio2015)

       说到深度学习一般都是在python环境上实施的。几大深度学习框架中Keras代码简洁性,十分利于上手。而可惜的是Keras本身没有C++接口,而一般的生产环境都需要C++环境支持。所以能够在C++上部属深度学习模型至关重要。

所需环境:

1.visual studio 2015(必须2015以上,2010不可以!)

2.tensorflow C++接口(lib文件和include)

3.opencv

4.jsoncpp(配置方法参考:https://blog.csdn.net/z8110/article/details/81152910

步骤1:tensorflow的visual studio上的配置

        tensorflow C++接口的编译和配置的坑有好多,我尝试了多次也没有成功,有兴趣的同学可以自己尝试编译。

         我是最终直接下载了别人配置好的项目,他的包里已经包含了tensorflow C++环境所需要的lib和include文件。

        (链接:https://pan.baidu.com/s/1fwP7-D1-Usz1Ec5cY-RaYg 密码:0hlb)

步骤2: Keras训练模型转成tensorflow用的pb模型:直接上代码,最终生成了1个pb模型文件

from keras.models import load_model
import tensorflow as tf
from keras import backend as K
from tensorflow.python.framework import graph_io
from models.triplet_model import TripletModel

 
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
 
 
"""----------------------------------配置路径-----------------------------------"""
h5_model_path='./experiments/triplet_mnist/checkpoints/triplet_loss_model.h5'#Keras训练模型
output_path='./experiments/triplet_mnist/checkpoints/'#转换后pb模型的地址
pb_model_name='triplet_loss_model.pb'#转换后pb模型的文件名
 
 
"""----------------------------------导入keras模型------------------------------"""
K.set_learning_phase(0)
net_model = load_model(h5_model_path,custom_objects={'triplet_loss': TripletModel.triplet_loss})
 
#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=[net_model.output.op.name])
graph_io.write_graph(frozen_graph, output_path, pb_model_name, as_text=False)

步骤三:python环境下用tensorflow验证导出的pb文件是否正常:

# coding: utf-8
import tensorflow as tf
import numpy as np
import cv2

#创建图
with tf.Graph().as_default():
    output_graph_def=tf.GraphDef()

#导入pb模型
pb_file_path='./experiments/triplet_mnist/checkpoints/triplet_loss_model.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计算结果(我的这个模型需要3张图片,每张图片生成1个128位的向量)
jpg_path="image_data/221M50A03410000Q-RS_2700_1550.jpg"
with tf.Session() as sess:
    init=tf.global_variables_initializer()
    sess.run(init)
    op=sess.graph.get_operations()   
    #for i, m in enumerate(op): #查看模型层次结构
    #    print('op{}:'.format(i),m.values())
    input_anc=sess.graph.get_tensor_by_name("anc_input:0")#名称要和模型里名称一致
    input_pos=sess.graph.get_tensor_by_name("pos_input:0")#名称要和模型里名称一致
    input_neg=sess.graph.get_tensor_by_name("neg_input:0")#名称要和模型里名称一致
    outputVec=sess.graph.get_tensor_by_name('model_1_1/dense_4/Relu:0')#名称要和模型里名称一致
    img=cv2.imread(jpg_path,0)
    img=img.astype(np.float32)
    feed_dict={input_anc:np.reshape(img,(1,140,140,1)),
               input_pos:np.reshape(img,(1,140,140,1)),
               input_neg:np.reshape(img,(1,140,140,1))
              }
    img_out_vec=sess.run(outputVec,feed_dict)
print(img_out_vec)

最终运行发现模型可以正常运行。(正常生成128位向量,且和h5模型生成的结果一致)

步骤四:在步骤一里下载的项目中添加源代码,注意输入输出的tensor名称要与pb模型里的相一致。

#include "stdafx.h"
#include <fstream>
#include <utility>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <iostream>
 
#include "tensorflow/cc/ops/const_op.h"
#include "tensorflow/cc/ops/image_ops.h"
#include "tensorflow/cc/ops/standard_ops.h"
 
#include "tensorflow/core/framework/graph.pb.h"
#include "tensorflow/core/framework/tensor.h"
 
#include "tensorflow/core/graph/default_device.h"
#include "tensorflow/core/graph/graph_def_builder.h"
 
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/stringpiece.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/lib/io/path.h"
#include "tensorflow/core/lib/strings/stringprintf.h"
 
#include "tensorflow/core/public/session.h"
#include "tensorflow/core/util/command_line_flags.h"
 
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/init_main.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/types.h"
#include "json/json.h"
#include "opencv2/opencv.hpp"

 
using namespace tensorflow::ops;
using namespace tensorflow;
using namespace std;
using namespace cv;
using tensorflow::Flag;
using tensorflow::Tensor;
using tensorflow::Status;
using tensorflow::string;
using tensorflow::int32 ;
 

void CVMat_to_Tensor(Mat img,Tensor* output_tensor,int input_rows,int input_cols)
{
    //imshow("input image",img);
    //图像进行resize处理
    resize(img,img,cv::Size(input_cols,input_rows));
    //imshow("resized image",img);
 
    //归一化
    //img.convertTo(img,CV_32FC1);
    //img=1-img/255;
 
    //创建一个指向tensor的内容的指针
    float *p = output_tensor->flat<float>().data();
 
    cv::Mat tempMat(input_rows, input_cols, CV_32FC1, p);
    img.convertTo(tempMat,CV_32FC1);
 
//    waitKey(0);
 
}

vector<tensorflow::Tensor> image_to_vec(Mat img,string model_path)
{
	string input_tensor_name1 = "anc_input";
	string input_tensor_name2 = "pos_input";
	string input_tensor_name3 = "neg_input";
	//string output_tensor_name = "concatenate_6/concat";
	string output_tensor_name = "model_1_1/dense_4/Relu";
	/*--------------------------------创建session------------------------------*/
	Session* session;
	SessionOptions opts;
	//opts.config.mutable_gpu_options


	Status status = NewSession(SessionOptions(), &session);//创建新会话Session
    /*--------------------------------从pb文件中读取模型--------------------------------*/
	GraphDef graphdef; //Graph Definition for current model

	Status status_load = ReadBinaryProto(Env::Default(), model_path, &graphdef); //从pb文件中读取图模型;
	if (!status_load.ok()) {
		cout << "ERROR: Loading model failed..." << model_path << std::endl;
		cout << status_load.ToString() << "\n";
		exit(-1);
	}
	Status status_create = session->Create(graphdef); //将模型导入会话Session中;
	if (!status_create.ok()) {
		cout << "ERROR: Creating graph in session failed..." << status_create.ToString() << std::endl;
		exit(-1);
	}
	cout << "<----Successfully created session and load graph.------->" << endl;

	/*---------------------载入测试图片--------------------------------*/
	clock_t startTime, endTime;
	cout << endl << "<----------------loading test_image------------------->" << endl;
	//Mat img = imread(image_path, 0);
	if (!img.data)
	{
		cout << "loading test_image failed" << endl;
		exit(-1);
	}
	//imshow("picture", img);
	if (img.empty())
	{
		cout << "can't open the image!!!!!!!" << endl;
		exit(-1);
	}
	int input_height = 140;
	int input_width = 140;
	Tensor input_tensor1(DT_FLOAT, TensorShape({ 1,input_height,input_width,1 }));
	Tensor input_tensor2(DT_FLOAT, TensorShape({ 1,input_height,input_width,1 }));
	Tensor input_tensor3(DT_FLOAT, TensorShape({ 1,input_height,input_width,1 }));
	//将Opencv的Mat格式的图片存入tensor
	CVMat_to_Tensor(img, &input_tensor1, input_height, input_width);

	//Mat Z = Mat::zeros(input_height, input_width, CV_8UC1);
	CVMat_to_Tensor(img, &input_tensor2, input_height, input_width);
	CVMat_to_Tensor(img, &input_tensor3, input_height, input_width);
	//cout << input_tensor1.DebugString() << endl;
	//cout << input_tensor2.DebugString() << endl;
	//cout << input_tensor3.DebugString() << endl;
	/*-----------------------------------用网络进行测试-----------------------------------------*/
	cout << endl << "<-------------Running the model with test_image--------------->" << endl;
	//前向运行,输出结果一定是一个tensor的vector
	vector<tensorflow::Tensor> outputs;
	string output_node = output_tensor_name;
	//cout << "Session Running......" << endl;
	Status status_run = session->Run({ { input_tensor_name1, input_tensor1 },
	                                   { input_tensor_name2, input_tensor2 },
	                                   { input_tensor_name3, input_tensor3 } },
	                                   { output_node },
                                       {},
		                               &outputs);
	//cout << "Session complet......" << endl;
	if (!status_run.ok()) {
		cout << "ERROR: RUN failed..." << std::endl;
		cout << status_run.ToString() << "\n";
		exit(-1);
	}
	session->Close();
	return outputs;

}
 
int main(int argc, char** argv )
{
    /*--------------------------------1.配置关键信息(图片路径和模型路径)---------------*/
    //string image_path="D:/testTensorflow/test/Debug/25100-000078-RS_550_1800.jpg";
	//string image_path = *argv[1];
	string model_path = "D:/testTensorflow/test/Debug/triplet_loss_model.pb";
	//cout<<argv[3]<<endl;

	/*---------------------------------2.从json文件中读取样本图片特征向量---------------*/
	Json::Reader reader;
	Json::Value root;	//从文件中读取
	ifstream is;
	is.open("Anchorbase.json", ios::binary);
	if (reader.parse(is, root))
		cout << "<--------------load json secsess!--------------->" << endl;
	else
		cout << "load json failed!" << endl;

	/*--------------------------------3.获得图片特征向量(outputs[0])-------------------*/
	vector<tensorflow::Tensor> outputs;
	//Mat img = imread(image_path, 0);
	Mat img = imread(argv[1], 0);
	outputs=image_to_vec(img,model_path); //把输出值给提取出来
    cout << "Output tensor size:" << outputs.size() << std::endl;
	//for (std::size_t i = 0; i < outputs.size(); i++) {
    //    cout << outputs[i].DebugString()<<std::endl;
    //}
    Tensor t = outputs[0];                   // Fetch the first tensor
    auto tmap = t.tensor<float, 2>();        // Tensor Shape: [batch_size, target_class_num]


	/*--------------------------------4.比较测试图片和样本图片特征向量的距离----------*/
	auto labels=root.getMemberNames();//样本标签名称
	auto iter = labels.begin();//开头标签的指针
	// 比较和所有特征向量的距离获取距离最小的作为品类判定
	double dist = 100;//距离
	String pridict_label;//预测结果
	for(auto iter=labels.begin();iter!=labels.end();iter++)
	{
		double sum = 0;
		for (int j = 0; j < 128; j++)
		{
			sum=sum	+abs(root[*iter][j].asDouble() - tmap(0, j));
			//cout << "Class " << tmap(0, j) << "," << std::endl;
		}
		if (sum < dist)
		{
			dist = sum;
			pridict_label = *iter;
		}
	}
	cout << "Pridicted result:" << pridict_label<<endl;
	system("pause");
    return 0;
}

步骤五:编译生成可执行的exe文件,如果包含目录,库目录等设置正确的话则可以正确生成exe文件。但是exe文件编译完毕执行时会提示丢失tensorflow.dll,不要怕只要把库目录下的dll文件拷贝到exe文件同一目录下即可。

运行效果图:

参考:https://blog.csdn.net/qq_25109263/article/details/81285952

           https://blog.csdn.net/zsyddl2/article/details/80973388

猜你喜欢

转载自blog.csdn.net/u012160945/article/details/84583840