用c++API加载python训练好的tensorflow模型

版权声明: https://blog.csdn.net/b876144622/article/details/79962556
由于在生产环境,很多情况下没有python的环境,只能用c/c++的环境,而且很多情况下是用python训练好模型后,用c++加载模型运行即可,那么如何才能用c++加载模型并运行呢?这里做一些说明。

主要参考两个网址,tensorflow的c++ API官网(https://www.tensorflow.org/api_guides/cc/guide)和Loading a TensorFlow graph with the C++ API(https://medium.com/jim-fleming/loading-a-tensorflow-graph-with-the-c-api-4caaff88463f),按道理,应该只需要参考后者,即可,但我在实践的时候,碰到了一些问题。



1、用python创建graph,并保存成pb文件


import tensorflow as tf
import numpy as np

with tf.Session() as sess:
    a = tf.Variable(5.0, name='a')
    b = tf.Variable(6.0, name='b')
    c = tf.multiply(a, b, name="c")
    sess.run(tf.global_variables_initializer())

    print a.eval() # 5.0
    print b.eval() # 6.0
    print c.eval() # 30.0
    tf.train.write_graph(sess.graph_def, 'models/', 'graph.pb', as_text=False)

执行代码后,会得到model文件夹,在model文件夹下有一个graph.pb文件。



2、用c++ 构建session,读取pb文件,执行


在tensorflow的源码目录中,创建文件夹 /tensorflow-master/tensorflow/user/loader/

在该文件夹下创建loader.cc文件,粘贴如下的代码:

#include "tensorflow/core/public/session.h"
#include "tensorflow/core/platform/env.h"
using namespace tensorflow;

int main(int argc, char* argv[]) {
  // Initialize a tensorflow session
  Session* session;
  Status status = NewSession(SessionOptions(), &session);
  if (!status.ok()) {
    std::cout << status.ToString() << "\n";
    return 1;
  }
 
  // Read in the protobuf graph we exported
  // (The path seems to be relative to the cwd. Keep this in mind
  // when using `bazel run` since the cwd isn't where you call
  // `bazel run` but from inside a temp folder.)
  GraphDef graph_def;
  status = ReadBinaryProto(Env::Default(), "models/graph.pb", &graph_def);
  if (!status.ok()) {
    std::cout << status.ToString() << "\n";
    return 1;
  }

  // Add the graph to the session
  status = session->Create(graph_def);
  if (!status.ok()) {
    std::cout << status.ToString() << "\n";
    return 1;
  }
 
  // Setup inputs and outputs:

  // Our graph doesn't require any inputs, since it specifies default values,
  // but we'll change an input to demonstrate.
  Tensor a(DT_FLOAT, TensorShape());
  a.scalar<float>()() = 3.0;
  Tensor b(DT_FLOAT, TensorShape());
  b.scalar<float>()() = 2.0;
 
  std::vector<std::pair<string, tensorflow::Tensor>> inputs = {
    { "a", a },
    { "b", b },
  };
 
  // The session will initialize the outputs
  std::vector<tensorflow::Tensor> outputs;
 
  // Run the session, evaluating our "c" operation from the graph
  status = session->Run(inputs, {"c"}, {}, &outputs);
  if (!status.ok()) {
    std::cout << status.ToString() << "\n";
    return 1;
  }
 
  // Grab the first output (we only evaluated one graph node: "c")
  // and convert the node to a scalar representation.
  auto output_c = outputs[0].scalar<float>();
 
  // (There are similar methods for vectors and matrices here:
  // https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/public/tensor.h)
 
  // Print the results
  std::cout << outputs[0].DebugString() << "\n"; // Tensor<type: float shape: [] values: 30>
  std::cout << output_c() << "\n"; // 30
 
  // Free any resources used by the session
  session->Close();
  return 0;
}



3、创建BUILD文件 (输入命令, touch BUILD)
粘贴如下的代码:


load("//tensorflow:tensorflow.bzl", "tf_cc_binary")
tf_cc_binary(
    name = "loader",
    srcs = ["loader.cc"],
    deps = [
        "//tensorflow/cc:cc_ops",
        "//tensorflow/cc:client_session",
        "//tensorflow/core:tensorflow",
    ],
)

这里的代码和那边博客里的代码有区别,按照博客里的代码,会出错,不能编译成功,用这个代码后,编译成功了。这里的代码时从官网C++ API介绍从copy出来的。

这里出现的问题如下,


用bazel build :loader 时

在bazel-bin/tensorflow/loader下不能找到可执行文件./loader

在build时,也是会报错的,提示如下:

collect2: error: ld returned 1 exit status

Target //tensorflow/heke/loader:loader failed to build


用bazel run :loader会报错,同上

collect2: error: ld returned 1 exit status
Target //tensorflow/heke/loader:loader failed to build




最后,在/user/loader目录下,有两个文件
BUILD
loader.cc


4、编译运行


a)、配置tensorflow-master中的./configure

b)、在loader目录下,执行 bazel build :loader

c)、进入到tensorflow-master/bazel-bin/tensorflow/heke/loader,并把前面用python生成的model/graph.pb 复制到loader目录下

d)、运行./loader



这里运行的时候,也可以直接用bazel run :loader来运行,结合了编译和运行两个阶段

注:这里用的c++代码的编译方式和前面的博客用c++API加载python训练好的tensorflow模型 里用的编译不一致,那么相当于提供了两种c++代码的编译方式。都可以尝试。这里也试过用libtensorflow.so库的方式来编译,但有一些bug,看下能否解决吧。



用 gcc gcc -I /usr/local/include/tf -L /usr/local/lib train.cc -ltensorflow 报需要用-std=c++11,


以及/usr/local/include/tf/third_party/eigen3/unsupported/Eigen/CXX11/Tensor:1:42: fatal error: unsupported/Eigen/CXX11/Tensor: No such file or directory 的错误




用gcc -std=c++11 -I /usr/local/include/tf -L /usr/local/lib train.cc -ltensorflow 就不会报-std=c++11的错误,但是下面的错误继续会报。


这个错误,目前先解决不了,后续再看吧




其实上面的问题,不是关键,这个应该可以通过安装eigen,并在编译时,指定eigen的目录可以解决。




更重要的错误在于,用gcc编译时,目前只有tensorflow/c/c_api.h支持的最好,其它的都需要导入各种各样的头文件。会提醒缺少各种各样的头文件。这个问题目前好像还比较难解决,可以参见https://github.com/tensorflow/tensorflow/issues/2412 中asimshankar的回复。
阅读更多

扫码向博主提问

keep_forward

非学,无以致疑;非问,无以广识
去开通我的Chat快问
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页