Tensorflow C++ API调用Python预训练模型

作者: 415lab_MoMo | 来源:发表于2017-08-12 15:10 被阅读6388次

    最近一段时间研究了如何打通tensorflow线下使用python训练深度学习模型,然后线上使用c++调用预先训练好的模型完成预测的流程。毕竟深度学习模型上线是需要考虑效率的,目前来说c++的效率还是python所不能比的。
    这篇文章基于tensorflow 1.2版本写的,tensorflow 1.2版本及以上提供了一种更加方便的c++ API调用python API训练好的模型。但这方面的资料比较少,自己也踩了不少坑,于是写了一个简单的使用tensorflow c++ API调用线下python API训练好的模型的demo,以及如何配置环境和编译。

    大体的流程如下:

    • 1.使用tensorflow python API编写和训练自己的模型,训练完成后,使用tensorflow saver 将模型保存下来。
    • 2.使用tensorflow c++ API 构建新的session,读取python版本保存的模型,然后使用session->run()获得模型的输出。
    • 3.编译和运行基于tensorflow c++ API写的代码。

    安装Bazel

    Bazel是一个类似于Make的工具,是Google为其内部软件开发的特点量身定制的工具,如今Google使用它来构建内部大多数的软件。
    在编译 tensorflow c++的时候,需要利用bazel来进行编译的,理论上是可以使用Cmake等其工具来编译的,但是我尝试了好久没有成功,所以最后还是使用了google的bazel进行编译。希望有大神可以把编译方法告诉我~
    安装方法按照官方教程走就行。我采用的是直接编译二进制文件的方法,这个最简单直接,首先下载对应版本的二进制文件,然后执行下面的命令即可:

    $ chmod +x bazel-version-installer-os.sh
    $ ./bazel-version-installer-os.sh --user
    

    下载Tensorflow源码

    我们需要将Tensorflow源码下载到本地,后续编译tensorflow c++ 代码需要在这个目录下进行。在这里需要说明的一点是,本文采用的c++ API载入python 预训练模型的方法,是基于tensorflow1.2版本。所以需要下载tensorflow 1.2版本及以上,直接从github上clone即可:

    $ git clone -b r1.2 https://github.com/tensorflow/tensorflow.git
    

    使用Tensorflow Python API线下定义模型和训练

    这里的话我写了一个十分简单的基于tensorflow的demo:res=a*b+y,代码如下:

    # -*-coding:utf-8 -*-
    import numpy as np
    import tensorflow as tf
    import sys, os
    
    if __name__ == '__main__':
        train_dir = os.path.join('demo_model/', "demo")
        a = tf.placeholder(dtype=tf.int32, shape=None, name='a')
        b = tf.placeholder(dtype=tf.int32, shape=None, name='b')
        y = tf.Variable(tf.ones(shape=[1], dtype=tf.int32), dtype=tf.int32, name='y')
        res = tf.add(tf.multiply(a, b), y, name='res')
        with tf.Session() as sess:
            feed_dict = dict()
            feed_dict[a] = 2
            feed_dict[b] = 3
            fetch_list = [res]
            sess.run(tf.global_variables_initializer())
            saver = tf.train.Saver()
            # 训练和保存模型
            res = sess.run(feed_dict=feed_dict, fetches=fetch_list)
            saver.save(sess, train_dir)
    
            print("result: ", res[0])
    
    

    运行结果如下:

    result:  [7]
    

    模型保存在了demo_model/下,里面的包含四个文件:

    checkpoint  #模型checkpoint中的一些文件名的信息
    demo.data-00000-of-00001  #模型中保存的各个权重
    demo.index  #可能是保存的各个权重的索引
    demo.meta  #模型构造的图的拓扑结构
    

    使用python API 载入和运行模型的代码如下:

    # -*- coding:utf-8 -*-
    import tensorflow as tf
    import os
    
    if __name__ == '__main__':
        with tf.Session() as sess:
            saver = tf.train.import_meta_graph('demo_model/demo.meta')
            saver.restore(sess, tf.train.latest_checkpoint('demo_model/'))
            # sess.run()
            graph = tf.get_default_graph()
            a = graph.get_tensor_by_name("a:0")
            b = graph.get_tensor_by_name("b:0")
            feed_dict = {a: 2, b: 3}
    
            op_to_restore = graph.get_tensor_by_name("res:0")
            print(sess.run(fetches=op_to_restore, feed_dict=feed_dict))
    

    运行结果如下:

    [7]
    

    使用Tensorflow c++ API读入预训练模型

    关于tensorflow c++ API的教程网上资料真的很少,我只能一边看着官方文档一边查着Stack Overflow慢慢写了,有些API我现在也不是很清楚怎么用,直接上代码吧:

    //
    // Created by MoMo on 17-8-10.
    //
    #include <iostream>
    #include "tensorflow/core/public/session.h"
    #include "tensorflow/core/protobuf/meta_graph.pb.h"
    #include "tensorflow/cc/client/client_session.h"
    #include "tensorflow/cc/ops/standard_ops.h"
    #include "tensorflow/core/framework/tensor.h"
    
    using namespace std;
    using namespace tensorflow;
    
    int main()
    {
        const string pathToGraph = "demo_model/demo.meta";
        const string checkpointPath = "demo_model/demo";
        auto session = NewSession(SessionOptions());
        if (session == nullptr)
        {
            throw runtime_error("Could not create Tensorflow session.");
        }
    
        Status status;
    
    // 读入我们预先定义好的模型的计算图的拓扑结构
        MetaGraphDef graph_def;
        status = ReadBinaryProto(Env::Default(), pathToGraph, &graph_def);
        if (!status.ok())
        {
            throw runtime_error("Error reading graph definition from " + pathToGraph + ": " + status.ToString());
        }
    
    // 利用读入的模型的图的拓扑结构构建一个session
        status = session->Create(graph_def.graph_def());
        if (!status.ok())
        {
            throw runtime_error("Error creating graph: " + status.ToString());
        }
    
    // 读入预先训练好的模型的权重
        Tensor checkpointPathTensor(DT_STRING, TensorShape());
        checkpointPathTensor.scalar<std::string>()() = checkpointPath;
        status = session->Run(
                {{ graph_def.saver_def().filename_tensor_name(), checkpointPathTensor },},
                {},
                {graph_def.saver_def().restore_op_name()},
                nullptr);
        if (!status.ok())
        {
            throw runtime_error("Error loading checkpoint from " + checkpointPath + ": " + status.ToString());
        }
    
    //  构造模型的输入,相当与python版本中的feed
        std::vector<std::pair<string, Tensor>> input;
        tensorflow::TensorShape inputshape;
        inputshape.InsertDim(0,1);
        Tensor a(tensorflow::DT_INT32,inputshape);
        Tensor b(tensorflow::DT_INT32,inputshape);
        auto a_map = a.tensor<int,1>();
        a_map(0) = 2;
        auto b_map = b.tensor<int,1>();
        b_map(0) = 3;
        input.emplace_back(std::string("a"), a);
        input.emplace_back(std::string("b"), b);
    
    //   运行模型,并获取输出
        std::vector<tensorflow::Tensor> answer;
        status = session->Run(input, {"res"}, {}, &answer);
    
        Tensor result = answer[0];
        auto result_map = result.tensor<int,1>();
        cout<<"result: "<<result_map(0)<<endl;
    
        return 0;
    }
    
    

    使用tensorflow c++ API读入预先训练的模型的大体的流程就是这样 ,复杂的模型,可能会需要构造更加复杂的输入和输出,读入部分一样。

    编译和运行

    代码写了,最后一步就是编译和运行了。在这里我采用的是bazel进行编译运行,这里需要写一个BUILD文件,内容如下:

    cc_binary(
        name = "demo",#目标文件名
        srcs = ["demo.cc"],#源代码文件名
        deps = [
            "//tensorflow/cc:cc_ops",
            "//tensorflow/cc:client_session",
            "//tensorflow/core:tensorflow"
            ],
    )
    

    然后将代码,BUILD文件一起放在我们下载下来的tensorflow的源码的tensorflow/tensorflow/demo目录下,demo目录为自己新建的。执行如下命令进行编译运行:

    bazel build -c opt --copt=-mavx --copt="-ggdb" --copt="-g3" demo/...
    

    经过漫长的编译过程,大概30分钟。会在tensorflow/bazel-bin/tensorflow/demo生成可执行文件demo,之后将我们预先训练好的模型放入相同的目录,运行即可,下面是运行结果:

    result: 7
    

    总结

    整个tensorflow线下使用python训练深度学习模型,然后线上使用c++调用预先训练好的模型完成预测的流程,基本介绍完了。从这个过程可以看出tensorflow的强大之处,开发者在开发之处考虑到了落地工业界,提供了这样一套线上和线下打通的流程,十分方便。

    相关文章

      网友评论

      • 钢铁复仇者Stark:你好,博主。我向请教一下生成可执行文件demo,之后应该如何运行啊?
        ea8d783fbdf5:同问,可执行的文件demo是什么格式的?
      • b96685760fbf:请问编译tf_cc.so库之后,在引用头文件时仍然找不到meta_graph.pb.h该怎么办?在对应文件夹里只找到了meta_graph.proto
      • 2d29dfc6790d:赞,这一块我们也做了一些调研,在c++下已实现cnn模型的inference,这一块资料太少了,后面希望有问题可以和作者探讨下。
        冫令苩開:你好,我是刚入坑的小白一枚,可以给个demo参考一下,c++下怎么调用cnn模型的inference吗?谢谢。我是检测,利用SSD做检测。
        冫令苩開:你好,我是刚入坑的小白一枚,可以给个demo参考一下,c++下怎么调用cnn模型的inference吗?谢谢。我是检测,利用SSD做检测。
        415lab_MoMo: @李永超_fc31 我也是刚刚开始研究这个,你实现CNN的interface了,厉害👍以后有问题我们可以多交流交流~
      • 鱼缸外的鱼1003:支持!!

      本文标题:Tensorflow C++ API调用Python预训练模型

      本文链接:https://www.haomeiwen.com/subject/iazdrxtx.html