安装好C++版的TensorFlow之后,我们就可以用C++来部署python训练好的TensorFlow模型了。安装C++版的TensorFlow的教程可以 参考这里 。部署TensorFlow模型主要分为两步,第一步是用python训练模型,然后保存模型为 .pb 格式的二进制文件;第二步则是在C++中加载python保存的模型并进行预测。

1、python训练模型并保存

2、使用C++加载python训练好的模型并进行预测

1、python训练模型并保存

这里我们以mnist数据集为例,训练一个三层的多层感知机对手写数字图片进行识别,具体的训练代码如下:

#coding=utf-8
import pickle
import numpy as np
import tensorflow as tf
import time 
import os
from tensorflow.python.framework.graph_util import convert_variables_to_constants
# 定义一个mnist数据集的类
class mnistReader():  
    def __init__(self, mnistPath, onehot = True):  
        self.mnistPath = mnistPath
        self.onehot = onehot  
        self.batch_index = 0
        print ('read:',self.mnistPath)
        fo = open(self.mnistPath, 'rb')
        self.train_set,self.valid_set,self.test_set = pickle.load(fo, encoding='bytes')
        fo.close()        
        self.data_label_train = list(zip(self.train_set[0], self.train_set[1]))
        np.random.shuffle(self.data_label_train)       
    # 获取下一个训练集的batch
    def next_train_batch(self, batch_size = 100):
        if self.batch_index < len(self.data_label_train)/batch_size:  
            print ("batch_index:",self.batch_index )
            datum = self.data_label_train[self.batch_index*batch_size:(self.batch_index+1)*batch_size]  
            self.batch_index+=1  
            return self._decode(datum, self.onehot)  
        else:  
            self.batch_index=0  
            np.random.shuffle(self.data_label_train)  
            datum=self.data_label_train[self.batch_index*batch_size:(self.batch_index+1)*batch_size]  
            self.batch_index+=1  
            return self._decode(datum,self.onehot)          
    # 获取测试集的数据
    def test_data(self):
        tdata, tlabel = self.test_set
        data_label_test=list(zip(tdata,tlabel))
        return self._decode(data_label_test,self.onehot)
    # 把一个batch的训练数据转换为可以放入模型训练的数据 
    def _decode(self, datum, onehot):  
        rdata=list()                 
        rlabel=list()  
        if onehot:  
            for d,l in datum:   
                img = np.reshape(d, (28,28))
                img = np.expand_dims(img, 2)           
                rdata.append(img) 
                hot = np.zeros(10)    
                hot[int(l)] = 1                   
                rlabel.append(hot)  
        else:  
            for d,l in datum:  
                img = np.reshape(d, (28,28))                
                img = np.expand_dims(img,2)            
                rdata.append(img)
                rlabel.append(int(l))  
        return rdata,rlabel  
#多层感知机模型(只有一个隐藏层)
def multi_perceptron():      
    batch_size = 100                              # batch大小
    height = 28                                   # 图片高度
    width = 28                                    # 图片宽度
    channel = 1                                   # 图片通道数
    in_units = 784                                # 多层感知机的输入
    h1_units=300                                  # MLP隐藏层的输出节点数
    mnist_path = "E:/testdata/mnist.pkl"          # mnist数据集路径
    save_path = "./output"                        #保存模型的路径
    images = tf.placeholder(tf.float32, shape = [None, height, width, channel],name = "images")
    labels =  tf.placeholder(tf.float32,[None, 10], name = "labels")
    w1=tf.Variable(tf.truncated_normal([in_units,h1_units],stddev=0.1))
    b1=tf.Variable(tf.zeros([h1_units]))     
    w2=tf.Variable(tf.truncated_normal([h1_units,10],stddev=0.1))   
    b2=tf.Variable(tf.zeros([10]))           
    # 网络各层的计算
    inputs = tf.reshape(images, [-1, in_units])
    hidden1 = tf.nn.relu(tf.matmul(inputs, w1) + b1)         
    hidden1_drop = tf.nn.dropout(hidden1, 0.75)         
    logits = tf.nn.softmax(tf.matmul(hidden1_drop, w2) + b2, name = "logits") 
    # 定义交叉熵为损失函数和优化器
    cross_entropy=tf.reduce_mean(-tf.reduce_sum(labels*tf.log(logits),reduction_indices=[1])) 
    train_step=tf.train.AdagradOptimizer(0.3).minimize(cross_entropy)  
    # 开始训练
    print ("begin training ...\n")
    sess = tf.InteractiveSession()              # 创建会话  
    tf.global_variables_initializer().run()     # 初始化全局变量
    mnist = mnistReader(mnistPath = mnist_path)
    for i in range(1000):
        image_batch, label_batch = mnist.next_train_batch()        
        train_step.run({images:image_batch, labels:label_batch})        
    # 计算测试集上的准确率
    correction_prediction = tf.equal(tf.argmax(labels, 1), tf.argmax(logits, 1))     
    accuracy = tf.reduce_mean(tf.cast(correction_prediction, tf.float32))
    test_data,test_label = mnist.test_data()
    print (accuracy.eval({images : test_data, labels :test_label}))
    # 保存模型的参数
    saver = tf.train.Saver() 
    saver.save(sess, os.path.join(save_path, "model.ckpt"))  
    graph = convert_variables_to_constants(sess, sess.graph_def, ["logits"])
    tf.train.write_graph(graph, save_path, 'model.pb',as_text=False)
    print ("save model sucessful")
if __name__ == '__main__':
    multi_perceptron()       # 多层感知机得到的准确率大概为0.98左右

模型训练好之后,保存模型文件为 ./output/model.pb

2、使用C++加载python训练好的模型并进行预测

(1) 文件结构如下,需要将python保存的模型复制到build文件夹下:

├── src
| └── hello.cpp
├── CMakeLists.txt
├── build
| └──model.pb
| └──digit.jpg

(2)hello.cpp的内容为(C++程序主要参考 官网的实现例子 ):

#include <fstream>
#include <utility>
#include <vector>
#include <Eigen/Core>
#include <Eigen/Dense>
#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/platform/env.h"
#include "tensorflow/core/platform/init_main.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/types.h"
#include "tensorflow/core/public/session.h"
#include "tensorflow/core/util/command_line_flags.h"
using namespace std;
using namespace tensorflow;
using namespace tensorflow::ops;
using tensorflow::Flag;
using tensorflow::Tensor;
using tensorflow::Status;
using tensorflow::string;
using tensorflow::int32;
//读取一个文件放入一个tensor之中
static Status ReadEntireFile(tensorflow::Env* env, const string& filename,Tensor* output) {
	tensorflow::uint64 file_size = 0;
	TF_RETURN_IF_ERROR(env->GetFileSize(filename, &file_size));          //获取文件的大小
	string contents;
	contents.resize(file_size);                                          //文件内容
	std::unique_ptr<tensorflow::RandomAccessFile> file;	                 //创建一个指向文件的智能指针
	TF_RETURN_IF_ERROR(env->NewRandomAccessFile(filename, &file));       //将这个指针指向文件
	tensorflow::StringPiece data;                                        //声明stringpiece类型的data存放文件内容
	TF_RETURN_IF_ERROR(file->Read(0, file_size, &data, &(contents)[0])); //将文件读入data之中
	if (data.size() != file_size) {
            return tensorflow::errors::DataLoss("Truncated read of '", filename,"' 
                                                expected ", file_size, " got ", data.size());
  	output->scalar<string>()() = string(data);                           //将data的值赋给output的tensor
	return Status::OK();
//从图像读取数据并放入tensor之中
Status ReadTensorFromImageFile(const string& file_name, const int input_height,
                               const int input_width, const float input_mean,
                               const float input_std,
                               std::vector<Tensor>* out_tensors) {
	auto root = tensorflow::Scope::NewRootScope();
	using namespace ::tensorflow::ops;               // NOLINT(build/namespaces)
	string input_name = "file_reader";
	string output_name = "normalized";
	//把文件读入一个叫input的tensor中
	Tensor input(tensorflow::DT_STRING, tensorflow::TensorShape());     //声明一个string类型的tensor,名为input
	TF_RETURN_IF_ERROR(ReadEntireFile(tensorflow::Env::Default(), file_name, &input)); //把文件读入这个tensor中
    //用一个placeholder来读取input的数据
	auto file_reader = Placeholder(root.WithOpName("input"), tensorflow::DataType::DT_STRING);
	std::vector<std::pair<string, tensorflow::Tensor>> inputs = {{"input", input},};
	//确定是什么类型的文件并且进行解码
	const int wanted_channels = 1;
	tensorflow::Output image_reader;
	if (tensorflow::str_util::EndsWith(file_name, ".png")) {
		image_reader = DecodePng(root.WithOpName("png_reader"), file_reader,DecodePng::Channels(wanted_channels));
	else if (tensorflow::str_util::EndsWith(file_name, ".gif")) {
        // gif decoder returns 4-D tensor, remove the first dim
    	image_reader = Squeeze(root.WithOpName("squeeze_first_dim"),DecodeGif(root.WithOpName("gif_reader"), file_reader));
	else if (tensorflow::str_util::EndsWith(file_name, ".bmp")) {
    	image_reader = DecodeBmp(root.WithOpName("bmp_reader"), file_reader);
	else {
        // Assume if it's neither a PNG nor a GIF then it must be a JPEG.
    	image_reader = DecodeJpeg(root.WithOpName("jpeg_reader"), file_reader,DecodeJpeg::Channels(wanted_channels));
    //现在把图像数据转为float,这样我们才对它进数据操作
	auto float_caster = Cast(root.WithOpName("float_caster"), image_reader, tensorflow::DT_FLOAT);
    // The convention for image ops in TensorFlow is that all images are expected
    // to be in batches, so that they're four-dimensional arrays with indices of
    // [batch, height, width, channel]. Because we only have a single image, we
    // have to add a batch dimension of 1 to the start with ExpandDims().
	auto dims_expander = ExpandDims(root.WithOpName("expand"), float_caster, 0);
    // Bilinearly resize the image to fit the required dimensions.
    // auto resized = ResizeBilinear(                               //图像进行双线性插值到固定尺寸
    //root, dims_expander,
    //Const(root.WithOpName("size"), {input_height, input_width}));
    // Subtract the mean and divide by the scale.                  //减去图像均值
    //Div(root.WithOpName(output_name), Sub(root, resized, {input_mean}),
    //{input_std});
//	float input_max = 255;
//	Div(root.WithOpName("div"),dims_expander,input_max);           //图像除以255,进行归一化处理
    // This runs the GraphDef network definition that we've just constructed, and
    // returns the results in the output tensor.
	tensorflow::GraphDef graph;
	TF_RETURN_IF_ERROR(root.ToGraphDef(&graph));
	std::unique_ptr<tensorflow::Session> session(tensorflow::NewSession(tensorflow::SessionOptions()));
	TF_RETURN_IF_ERROR(session->Create(graph));
	TF_RETURN_IF_ERROR(session->Run({inputs}, {"expand"}, {}, out_tensors));
  	return Status::OK();
//主函数
int main(int argc, char** argv ){
	Session* session;
  	Status status = NewSession(SessionOptions(), &session);           //创建新会话Session
	string model_path="model.pb";                                     //保存的模型路径
	GraphDef graphdef;                                                //当前模型的图定义
	Status status_load = ReadBinaryProto(Env::Default(), model_path, &graphdef); //从pb文件中读取图模型;
	if (!status_load.ok()) {
    	std::cout << "ERROR: Loading model failed..." << model_path << std::endl;
    	std::cout << status_load.ToString() << "\n";
      	return -1;
  	Status status_create = session->Create(graphdef);               //将图模型导入会话Session中;
	if (!status_create.ok()) {
    	std::cout << "ERROR: Creating graph in session failed..." << status_create.ToString() << std::endl;
    	return -1;
	cout << "Session successfully created."<< endl;
	//开始进行预测
	string image_path= argv[1];                 //图片路径
	int input_height =28;                       //高度
	int input_width=28;                         //宽度
	int input_mean=0;                           //均值
	int input_std=1;                            //方差
	std::vector<Tensor> resized_tensors;        //用于保存读取的图片的tensor数组
	//从图片读取数据并放入一个tensor之中
	cout<<"begin load image..."<<endl;
	Status read_tensor_status = ReadTensorFromImageFile(image_path, input_height, input_width, input_mean,
                          				   			    input_std, &resized_tensors);
    if (!read_tensor_status.ok()) {
  		LOG(ERROR) << read_tensor_status;
    	cout<<"resing error"<<endl;
    	return -1;
	cout<<"load image successful!"<<endl;
	const Tensor& resized_tensor = resized_tensors[0];
	std::cout << resized_tensor.DebugString()<<endl;          //打印出输入模型的tensor形状
	vector<tensorflow::Tensor> outputs;
	string output_node = "logits";                            //这里的输出名logits要和模型的输出相匹配
	//开始预测,这里的输入名images要和模型的输入相匹配
	Status status_run = session->Run({{"images", resized_tensor}}, {output_node}, {}, &outputs);
	if (!status_run.ok()) {
		std::cout << "ERROR: RUN failed..."  << std::endl;
    	std::cout << status_run.ToString() << "\n";
    	return -1;
	//取出输出值
    std::cout << "Output tensor size:" << outputs.size() << std::endl;
    for (std::size_t i = 0; i < outputs.size(); i++) {
    	std::cout << outputs[i].DebugString()<<endl;         //打印出模型输出的tensor的形状
  	Tensor t = outputs[0];                    // 取出第一个tensor
  	int ndim2 = t.shape().dims();             // 得到tensor的维度
  	auto tmap = t.tensor<float, 2>();         // Tensor Shape: [batch_size, target_class_num]
  	int output_dim = t.shape().dim_size(1);   // Get the target_class_num from 1st dimension
  	std::vector<double> tout;
	// Argmax:获取最终的预测label和概率值
  	int output_class_id = -1;
  	double output_prob = 0.0;
  	for (int j = 0; j < output_dim; j++){
        std::cout << "Class " << j << " prob:" << tmap(0, j) << "," << std::endl;
        if (tmap(0, j) >= output_prob) {
              output_class_id = j;
              output_prob = tmap(0, j);
    // 打印日志
 	std::cout << "Final class id: " << output_class_id << std::endl;
  	std::cout << "Final class prob: " << output_prob << std::endl;
     //auto f=t.shaped<float,2>({2,5});
     //cout << f <<endl;
     //Eigen::array<int, 1> reduction_dims{0};
     //cout<< f.maximum(reduction_dims)<<endl;
     //Eigen::array<int, 2> offsets = {0, 0};
     //Eigen::array<int, 2> extents = {2, 1};
     //auto slice = f.slice(offsets, extents);
     //float *index = slice.data();
     //cout << "slice" << endl << slice << endl;
     //cout << slice.argmax()<<endl;
	return 0;

(3)CMakeLists的内容为

cmake_minimum_required (VERSION 2.8.8)           # cmake的最低版本号
project (tf_test)                                # 工程名
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++11 -W")  # 设置编译器,这里设为C++编译器
link_directories(path_to_tensorflow/bazel-bin/tensorflow)   # 链接库搜索目录
include_directories(                              # 头文件的搜索目录
   path_to_tensorflow/
   path_to_tensorflow/bazel-genfiles
   path_to_tensorflow/bazel-bin/tensorflow
   path_to_tensorflow/tensorflow/contrib/makefile/downloads/nsync/public
   path_to_tensorflow/tensorflow/contrib/makefile/downloads/absl
   path_to_tensorflow/tensorflow/contrib/makefile/gen/protobuf/include
   /usr/local/include/eigen3
add_executable(tf_test  hello.cpp)                # 将源码编译为目标文件
target_link_libraries(tf_test tensorflow_cc tensorflow_framework) # 把动态链接库链接到目标文件中

(4)编译和运行

cd build
cmake ..
./tf_test digit.jpg

  输出结果为:

Session successfully created.
begin load image...
load image successful!
Tensor<type: float shape: [1,28,28,1] values: [[[253][21][0]]]...>
Output tensor size:1
Tensor<type: float shape: [1,10] values: [0 1 0...]...>
Class 0 prob:0,
Class 1 prob:1,
Class 2 prob:0,
Class 3 prob:0,
Class 4 prob:0,
Class 5 prob:0,
Class 6 prob:0,
Class 7 prob:0,
Class 8 prob:0,
Class 9 prob:0,
Final class id: 1
Final class prob: 1

参考:https://blog.csdn.net/zwx1995zwx/article/details/79064064

          https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/label_image/main.cc

安装好C++版的TensorFlow之后,我们就可以用C++来部署python训练好的TensorFlow模型了。安装C++版的TensorFlow的教程可以参考这里。部署TensorFlow模型主要分为两步,第一步是用python训练模型,然后保存模型为.pb格式的二进制文件;第二步则是在C++中加载python保存的模型并进行预测。1、python训练模型并保存2、使用C++加...
tensorflow作为一个著名的开源深度学习框架,其在python平台的神经网络模型搭建、训练和测试等是很齐全的,但其在C++端的设计方面性能相对较差。在实际工程中,C++项目具有更省时的优点,因此基于C++tensorflow的开发是很重要的。 一些比较知名的网络模型,例如yolo-v3系列等的C++代码较为全面,一定程度属于定制的。 然而对于一些自己编写的网络模型的支持方面,我们往往需要自己来配置环境。坦白说,笔者在配置环境方面花费了很多时间,参考了很多优秀的资料,但最后都不尽如意。作为一个小白
tensorflow目前支持最好的语言还是python,但大部分服务都用C++ or Java开发,一般采用动态链接库(.so)方式调用算法,因此tensorflow的c/c++ API还是有必要熟悉下,而且经过本人测试,相同算法,c接口相比python速度更快。 下面讲解如何让程序调用tensorflow c/c++库 1.编译库 先在github上下载tensorflow源码,执行...
在实际项目部署过程中,会需要tensorflow c或者c++本, 网上一通查,说自己编译会各种坑,投机取巧。。在拿到别人编译好的c和c++本调用报错后,开始痛定思痛,躲不过的坑 自己来趟好了。。 先说下电脑环境: linux18.04 cuda 10.0 cudnn 7.6.5 make 4.1 protobuf 3.7 ############环境安装############# 怕tensorflow本高 容易出错,保守选择 tensroflow 1.13.1本, 并选择
摘要: 最近在研究如何使用tensorflow c++ API调用tensorflow python环境下训练得到的网络模型文件。参考了很多博客,文档,一路上踩了很多坑,现将自己的方法步骤记录下来,希望能够帮到有需要的人!(本文默认读者对python环境下tensorflow的使用已经比较熟悉了) 方法简要梳理如下: 安装bazel,然后使用bazel编译tensorflow源码,产生我们
tensorflow_service tensorflow是被广泛应用的深度学习框架,提供丰富的API接口,可以省去很多自己的开发工作。python本的tensorflow是被应用最多的。但是python的执行效率偏低。有很多公司后台是用C++编写的,为了更好的将深度模型应用到线上,通常需要进行模型在线inference。 最近在做tensorflow模型C++线上inference, 模型训练仍然利用python tensorflow验证效果,实际上线时,采用更加高效的C++ API进行服务。将经
有时候,我们需要将TensorFlow模型导出为单个文件(同时包含模型架构定义与权重),方便在其他地方使用(如在c++部署网络)。利用tf.train.write_graph()默认情况下只导出了网络的定义(没有权重),而利用tf.train.Saver().save()导出的文件graph_def与权重是分离的,因此需要采用别的方法。 我们知道,graph_def文件中没有包含网络中的Variable值(通常情况存储了权重),但是却包含了constant值,所以如果我们能把Variable转换为constant,即可达到使用一个文件同时存储网络架构与权重的目标。 我们可以采用以下方式冻结
现在的深度学习框架一般都是基于 Python 来实现,构建、训练、保存和调用模型都可以很容易地在 Python 下完成。但有时候,我们在实际应用这些模型的时候可能需要在其他编程语言下进行,本文将通过直接调用 TensorFlow 的 C/C++ 接口来导入 TensorFlow 预训练好的模型。 1.环境配置 点此查看 C/C++ 接口的编译 2. 导入预定义的图和训练好的参数值 // se
将Keras训练的.hdf5模型固化为.pb模型 from tensorflow.python.framework import graph_io from keras.models import load_model from keras import backend as K import tensorflow as tf def freeze_session(session,...
一、编译TensorFlowc++接口 在调用TensorFlowc++接口之前,首先要安装bazel、protobuf、Eigen等软件,然后下载TensorFlow源码进行编译,整体过程还是比较麻烦。 1、配置C++tensorflow使用时的第三方依赖 (1)protobuf下载及安装 Protobuf这玩意儿是重中之重,它的本与tensorflow本密切相关,它的本错...
花了整整一天的时间,终于编译好了c++本的tensorflow,下面将给出我的整个编译过程,我只能说过程很艰辛,查了很多资料,不过最后总算是成功了,功夫不负有心人。下面将给我的编译过程: 参考博客: https://blog.csdn.net/jiugeshao/article/details/79144438 https://www.jianshu.com/p/3549c8e9cc15
CNN(卷积神经网络)是一种在计算机视觉领域常用的深度学习模型,可以对图像进行特征提取和分类。而手写字体识别是一种常见的应用场景,在数字识别、文字识别等方面都有广泛的应用。TensorFlow是一种基于数据流图的开源机器学习框架,其支持各种机器学习算法的实现,并能够在多种硬件和操作系统上运行。因此, CNN和TensorFlow结合使用可以有效提高手写字体识别的准确率和效率。 在应用CNN进行手写字体识别时,首先需要对训练数据进行处理和标记。接着,构建CNN网络模型,通过反向传播算法进行训练,不断优化模型参数,提高识别准确率。然后,将模型部署TensorFlow平台上,加快运行速度,提高效率。 在实际应用中,CNN和TensorFlow结合使用可以实现高效的手写字体识别。例如,在自动化识别银行支票、快递单等场景中,可以通过CNN模型提取特征并使用TensorFlow优化模型,实现较高的识别率和较快的处理速度。总的来说, CNN和TensorFlow结合使用为手写字体识别提供了一种强有力的解决方案,并在实际应用中得到了广泛的应用和验证。