用C++调用tensorflow在python下训练好的模型(centos7)
本文主要參考博客https://blog.csdn.net/luoyexuge/article/details/80399265?[1]?
bazel安裝參考:https://blog.csdn.net/luoyi131420/article/details/78585989?[2]
首先介紹下自己的環境是centos7,tensorflow版本是1.7,python是3.6(anaconda3)。
要調用tensorflow c++接口,首先要編譯tensorflow,要裝bazel,要裝protobuf,要裝Eigen;然后是用python訓練模型并保存,最后才是調用訓練好的模型,整體過程還是比較麻煩,下面按步驟一步步說明。
1.安裝bazel?
以下是引用的[2]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
實際上我自己只缺jdk工具,加上我沒有sudo權限,我自己是在網上直接下的jdk-8,鏈接是?
http://www.oracle.com/technetwork/java/javase/downloads/java-archive-javase8-2177648.html?
然后解壓,最后將其路徑添加到環境變量中:?
export JAVA_HOME=/home/guozitao001/tools/jdk1.8.0_171?
export PATH=$JAVA_HOME/bin:$PATH
然后去git上下載bazel的安裝文件https://github.com/bazelbuild/bazel/releases,具體是文件bazel-0.15.0-installer-linux-x86_64.sh。?
(1) 終端切換到.sh文件存放的路徑,文件添加可執行權限:?
$?chmod +x bazel-0.5.3-installer-linux-x86_64.sh?
(2)然后執行該文件:?
$?./bazel-0.5.3-installer-linux-x86_64.sh –user?
注意:–user選項表示bazel安裝到HOME/bin目錄下,并設置.bazelrc的路徑為HOME/.bazelrc。?
安裝完成后執行bazel看是否安裝成功,這里我并沒有添加環境變量就可以直接運行,大家根據自己需要添加。
2.安裝protobuf
下載地址:https://github.com/google/protobuf/releases ,我下載的是3.5.1版本,如果你是下載新版的tensorflow,請確保protobuf版本也是最新的,安裝步驟: cd /protobuf ./configure make sudo make install 安裝之后查看protobuf版本: protoc --version- 1
- 2
- 3
- 4
- 5
- 6
- 7
根據[1]的作者采坑經歷所說,protoc一定要注意版本要和tensorflow匹配,總之這里3.5.1的protoc和tensorflow1.7是能夠匹配的。
3.安裝Eigen
wget http://bitbucket.org/eigen/eigen/get/3.3.4.tar.bz2 下載之后解壓放在重新命名為eigen3,我存放的路徑是,/Users/zhoumeixu/Downloads/eigen3- 1
- 2
這個沒什么好多說的,如果wget失敗就直接用瀏覽器或者迅雷下載就是了。
4.tensorflow下載以及編譯:?
1下載TensorFlow ,使用 git clone - –recursive?https://github.com/tensorflow/tensorflow?
2.下載bazel工具(mac下載installer-darwin、linux用installer-linux)?
3. 進入tensorflow的根目錄?
3.1 執行./configure 根據提示配置一下環境變量,這個不大重要。?
要GPU的話要下載nvidia驅動的 盡量裝最新版的驅動吧 還有cudnn version為5以上的 這些在官網都有提及的?
3.2 有顯卡的執行 ” bazel build –config=opt –config=cuda //tensorflow:libtensorflow_cc.so ”?
沒顯卡的 ” –config=cuda ” 就不要加了?
bazel build –config=opt //tensorflow:libtensorflow_cc.so。?
編譯成功后會有bazel成功的提示。?
3.3這里編譯完過后,最后調用tensorflow模型的時候的時候提示文件tensorflow/tensorflow/core/platform/default/mutex.h缺2個頭文件:nsync_cv.h,nsync_mu.h,仔細查找后,發現這兩個頭文件在python的site-papackages里面,它只是沒找到而已,所以我們在mutex.h中將這兩個頭文件的路徑補充完整:?
這樣之后調用就不會提示缺少頭文件了。
4.python訓練tensorflow模型:?
下面訓練tensorflow模型的pb模型,[1]作者做了個簡單的線性回歸模型及生成pb格式模型代碼:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
注意這里一定要把需要輸入和輸出的變量要以string形式的name在tf.graph_util.convert_variables_to_constants中進行保存,比如說這里的inputs和outputs。得到一個后綴為pb的文件?
然后加載該模型,驗證是否成功保存模型:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
運行結果:[[14.998546]], 該結果完全符合預期。
5.C++項目代碼,一共有4個文件
model_loader_base.h:
#ifndef CPPTENSORFLOW_MODEL_LOADER_BASE_H #define CPPTENSORFLOW_MODEL_LOADER_BASE_H #include <iostream> #include <vector> #include "tensorflow/core/public/session.h" #include "tensorflow/core/platform/env.h" using namespace tensorflow; namespace tf_model { /** * Base Class for feature adapter, common interface convert input format to tensors * */ class FeatureAdapterBase{ public: FeatureAdapterBase() {}; virtual ~FeatureAdapterBase() {}; virtual void assign(std::string, std::vector<double>*) = 0; // tensor_name, tensor_double_vector std::vector<std::pair<std::string, tensorflow::Tensor> > input; }; class ModelLoaderBase { public: ModelLoaderBase() {}; virtual ~ModelLoaderBase() {}; virtual int load(tensorflow::Session*, const std::string) = 0; //pure virutal function load method virtual int predict(tensorflow::Session*, const FeatureAdapterBase&, const std::string, double*) = 0; tensorflow::GraphDef graphdef; //Graph Definition for current model }; } #endif //CPPTENSORFLOW_MODEL_LOADER_BASE_H- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
ann_model_loader.h:
#ifndef CPPTENSORFLOW_ANN_MODEL_LOADER_H #define CPPTENSORFLOW_ANN_MODEL_LOADER_H#include "model_loader_base.h" #include "tensorflow/core/public/session.h" #include "tensorflow/core/platform/env.h" using namespace tensorflow; namespace tf_model { /** * @brief: Model Loader for Feed Forward Neural Network * */ class ANNFeatureAdapter: public FeatureAdapterBase { public: ANNFeatureAdapter(); ~ANNFeatureAdapter(); void assign(std::string tname, std::vector<double>*) override; // (tensor_name, tensor) }; class ANNModelLoader: public ModelLoaderBase { public: ANNModelLoader(); ~ANNModelLoader(); int load(tensorflow::Session*, const std::string) override; //Load graph file and new session int predict(tensorflow::Session*, const FeatureAdapterBase&, const std::string, double*) override; }; } #endif //CPPTENSORFLOW_ANN_MODEL_LOADER_H- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
ann_model_loader.cpp:
#include <iostream> #include <vector> #include <map> #include "ann_model_loader.h" //#include <tensor_shape.h> using namespace tensorflow; namespace tf_model { /** * ANNFeatureAdapter Implementation * */ ANNFeatureAdapter::ANNFeatureAdapter() { } ANNFeatureAdapter::~ANNFeatureAdapter() { } /* * @brief: Feature Adapter: convert 1-D double vector to Tensor, shape [1, ndim] * @param: std::string tname, tensor name; * @parma: std::vector<double>*, input vector; * */ void ANNFeatureAdapter::assign(std::string tname, std::vector<double>* vec) { //Convert input 1-D double vector to Tensor int ndim = vec->size(); if (ndim == 0) { std::cout << "WARNING: Input Vec size is 0 ..." << std::endl; return; } // Create New tensor and set value Tensor x(tensorflow::DT_FLOAT, tensorflow::TensorShape({1, ndim})); // New Tensor shape [1, ndim] auto x_map = x.tensor<float, 2>(); for (int j = 0; j < ndim; j++) { x_map(0, j) = (*vec)[j]; } // Append <tname, Tensor> to input input.push_back(std::pair<std::string, tensorflow::Tensor>(tname, x)); } /** * ANN Model Loader Implementation * */ ANNModelLoader::ANNModelLoader() { } ANNModelLoader::~ANNModelLoader() { } /** * @brief: load the graph and add to Session * @param: Session* session, add the graph to the session * @param: model_path absolute path to exported protobuf file *.pb * */ int ANNModelLoader::load(tensorflow::Session* session, const std::string model_path) { //Read the pb file into the grapgdef member tensorflow::Status status_load = ReadBinaryProto(Env::Default(), model_path, &graphdef); if (!status_load.ok()) { std::cout << "ERROR: Loading model failed..." << model_path << std::endl; std::cout << status_load.ToString() << "\n"; return -1; } // Add the graph to the session tensorflow::Status status_create = session->Create(graphdef); if (!status_create.ok()) { std::cout << "ERROR: Creating graph in session failed..." << status_create.ToString() << std::endl; return -1; } return 0; } /** * @brief: Making new prediction * @param: Session* session * @param: FeatureAdapterBase, common interface of input feature * @param: std::string, output_node, tensorname of output node * @param: double, prediction values * */ int ANNModelLoader::predict(tensorflow::Session* session, const FeatureAdapterBase& input_feature, const std::string output_node, double* prediction) { // The session will initialize the outputs std::vector<tensorflow::Tensor> outputs; //shape [batch_size] // @input: vector<pair<string, tensor> >, feed_dict // @output_node: std::string, name of the output node op, defined in the protobuf file tensorflow::Status status = session->Run(input_feature.input, {output_node}, {}, &outputs); if (!status.ok()) { std::cout << "ERROR: prediction failed..." << status.ToString() << std::endl; return -1; } //Fetch output value std::cout << "Output tensor size:" << outputs.size() << std::endl; for (std::size_t i = 0; i < outputs.size(); i++) { std::cout << outputs[i].DebugString(); } std::cout << std::endl; Tensor t = outputs[0]; // Fetch the first tensor int ndim = t.shape().dims(); // Get the dimension of the 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: Get Final Prediction Label and Probability 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); } } // Log std::cout << "Final class id: " << output_class_id << std::endl; std::cout << "Final value is: " << output_prob << std::endl; (*prediction) = output_prob; // Assign the probability to prediction return 0; } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
main.cpp:
#include <iostream> #include "tensorflow/core/public/session.h" #include "tensorflow/core/platform/env.h" #include "ann_model_loader.h"using namespace tensorflow; int main(int argc, char* argv[]) { if (argc != 2) { std::cout << "WARNING: Input Args missing" << std::endl; return 0; } std::string model_path = argv[1]; // Model_path *.pb file // TensorName pre-defined in python file, Need to extract values from tensors std::string input_tensor_name = "inputs"; std::string output_tensor_name = "outputs"; // Create New Session Session* session; Status status = NewSession(SessionOptions(), &session); if (!status.ok()) { std::cout << status.ToString() << "\n"; return 0; } // Create prediction demo tf_model::ANNModelLoader model; //Create demo for prediction if (0 != model.load(session, model_path)) { std::cout << "Error: Model Loading failed..." << std::endl; return 0; } // Define Input tensor and Feature Adapter // Demo example: [1.0, 1.0, 1.0, 1.0, 1.0] for Iris Example, including bias int ndim = 5; std::vector<double> input; for (int i = 0; i < ndim; i++) { input.push_back(1.0); } // New Feature Adapter to convert vector to tensors dictionary tf_model::ANNFeatureAdapter input_feat; input_feat.assign(input_tensor_name, &input); //Assign vec<double> to tensor // Make New Prediction double prediction = 0.0; if (0 != model.predict(session, input_feat, output_tensor_name, &prediction)) { std::cout << "WARNING: Prediction failed..." << std::endl; } std::cout << "Output Prediction Value:" << prediction << std::endl; return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
將這四個文件放在同一個路徑下,然后還需要添加一個Cmake的txt文件:
cmake_minimum_required(VERSION 2.8) project(cpptensorflow) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x -g -fopenmp -fno-strict-aliasing") link_directories(/home/xxx/tensorflow/bazel-bin/tensorflow) include_directories( /home/xxx/tensorflow /home/xxx/tensorflow/bazel-genfiles /home/xxx/tensorflow/bazel-bin/tensorflow /home/xxx/tools/eigen3 ) add_executable(cpptensorflow main.cpp ann_model_loader.h model_loader_base.h ann_model_loader.cpp) target_link_libraries(cpptensorflow tensorflow_cc tensorflow_framework)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
這里注意cmake_minimum_required(VERSION 2.8)要和自己系統的cmake最低版本相符合。
然后在當前目錄下建立一個build的空文件夾:?
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
到此基本就結束了,最后感謝下作者[1],我真是差點被搞瘋了。。。
?
原文:https://blog.csdn.net/gzt940726/article/details/81053378
轉載于:https://www.cnblogs.com/Ph-one/p/9516490.html
總結
以上是生活随笔為你收集整理的用C++调用tensorflow在python下训练好的模型(centos7)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CTU 2017 J - Punchin
- 下一篇: heap 的一些用法