Tensorflow 在 C++ 中导出和运行图的不同方法

Posted

技术标签:

【中文标题】Tensorflow 在 C++ 中导出和运行图的不同方法【英文标题】:Tensorflow Different ways to Export and Run graph in C++ 【发布时间】:2016-06-01 05:40:24 【问题描述】:

要将经过训练的网络导入 C++,您需要导出网络才能执行此操作。查了很多,几乎没有找到相关信息,明确了应该使用freeze_graph()才能做到。

感谢新的 0.7 版本的 Tensorflow,他们添加了 documentation 。

查看文档后,我发现类似的方法很少,你能告诉freeze_graph()和有什么区别: tf.train.export_meta_graph,因为它具有相似的参数,但它似乎也可以用于将模型导入 C++(我只是猜想不同之处在于,对于通过这种方法输出的文件,您只能使用 import_graph_def() 还是别的什么? )

还有一个关于如何使用write_graph()的问题: 在文档中graph_defsess.graph_def 给出,但在freeze_graph() 的示例中它是sess.graph.as_graph_def()。这两者有什么区别?

此问题与this issue.有关

谢谢!

【问题讨论】:

看起来像一个名为“Tensorflow Serving”的独立库提供了这个功能:tensorflow.github.io/serving/serving_basic.html。不过,我仍然想弄清楚如何在普通的 Tensorflow 中做到这一点! 【参考方案1】:

这是我利用 TF 0.12 中引入的 V2 检查点的解决方案。

无需将所有变量转换为常量或freeze the graph。

为了清楚起见,V2 检查点在我的目录models 中如下所示:

checkpoint  # some information on the name of the files in the checkpoint
my-model.data-00000-of-00001  # the saved weights
my-model.index  # probably definition of data layout in the previous file
my-model.meta  # protobuf of the graph (nodes and topology info)

Python 部分(保存)

with tf.Session() as sess:
    tf.train.Saver(tf.trainable_variables()).save(sess, 'models/my-model')

如果您使用tf.trainable_variables() 创建Saver,您可以为自己节省一些头痛和存储空间。但也许一些更复杂的模型需要保存所有数据,然后将此参数删除到Saver,只需确保您在创建图表之后创建Saver。给所有变量/层赋予唯一的名称也是非常明智的,否则你可以在different problems 中运行。

Python 部分(推理)

with tf.Session() as sess:
    saver = tf.train.import_meta_graph('models/my-model.meta')
    saver.restore(sess, tf.train.latest_checkpoint('models/'))
    outputTensors = sess.run(outputOps, feed_dict=feedDict)

C++ 部分(推理)

请注意,checkpointPath 不是任何现有文件的路径,只是它们的公共前缀。如果您错误地将.index 文件的路径放在那里,TF 不会告诉您这是错误的,但它会在推理过程中由于未初始化的变量而死。

#include <tensorflow/core/public/session.h>
#include <tensorflow/core/protobuf/meta_graph.pb.h>

using namespace std;
using namespace tensorflow;

...
// set up your input paths
const string pathToGraph = "models/my-model.meta"
const string checkpointPath = "models/my-model";
...

auto session = NewSession(SessionOptions());
if (session == nullptr) 
    throw runtime_error("Could not create Tensorflow session.");


Status status;

// Read in the protobuf graph we exported
MetaGraphDef graph_def;
status = ReadBinaryProto(Env::Default(), pathToGraph, &graph_def);
if (!status.ok()) 
    throw runtime_error("Error reading graph definition from " + pathToGraph + ": " + status.ToString());


// Add the graph to the session
status = session->Create(graph_def.graph_def());
if (!status.ok()) 
    throw runtime_error("Error creating graph: " + status.ToString());


// Read weights from the saved checkpoint
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());


// and run the inference to your liking
auto feedDict = ...
auto outputOps = ...
std::vector<tensorflow::Tensor> outputTensors;
status = session->Run(feedDict, outputOps, , &outputTensors);

【讨论】:

嗨,@peci1,感谢您的工作。你能告诉我如何编译这个 c++ 代码吗?我应该使用巴泽尔吗?你能展示任何简单的示例命令吗? @YWPKwon 该示例不是完整或独立的代码。您应该以对您有用的方式将其合并(例如,将其包含在 main 方法中)。然后可以使用 bazel 以标准方式进行编译,或者您可以查看我的“hack”如何(错误)使用 Python pip 的安装来获得 C++ 环境。它位于:github.com/tradr-project/tensorflow_ros 并使用 catkin 构建工具 (github.com/ros/catkin)。 @YWPKwon 要使用 TF 构建 C++ 代码,您还可以使用此处描述的 bazel:tensorflow.org/api_guides/cc/guide,或使用此处描述的 CMake:github.com/FloopCZ/tensorflow_cc @Floop 你的“build.sh”也依赖于 bazel。我写了几个示例如何在C, C++, Python and Go here 中运行模式 因为这个解决方案是很久以前写的,并且 tensorflow 实际上已经发展了,有没有现代方法可以用 C++ 做(仅)预测部分?如果是这样,您能否分享文档或教程的链接?【参考方案2】:

对于预测(以及所有其他操作),您可以执行以下操作:

首先在python中你应该命名你的变量或操作以供将来使用

self.init = tf.initialize_variables(tf.all_variables(), name="nInit")

训练后,当您分配变量时,计算so..时会遍历所有变量并将其作为常量保存到图表中。 (用那个冻结工具几乎可以做到这一点,但我通常自己做,检查下面py和cpp中的“name=nWeights”)

def save(self, filename):
    for variable in tf.trainable_variables():
        tensor = tf.constant(variable.eval())
        tf.assign(variable, tensor, name="nWeights")

    tf.train.write_graph(self.sess.graph_def, 'graph/', 'my_graph.pb', as_text=False)

现在使用 c++ 并加载我们的图形并从保存的常量中加载变量:

void load(std::string my_model) 
        auto load_graph_status =
                ReadBinaryProto(tensorflow::Env::Default(), my_model, &graph_def);

        auto session_status = session->Create(graph_def);

        std::vector<tensorflow::Tensor> out;
        std::vector<string> vNames;

        int node_count = graph_def.node_size();
        for (int i = 0; i < node_count; i++) 
            auto n = graph_def.node(i);

            if (n.name().find("nWeights") != std::string::npos) 
                vNames.push_back(n.name());
            
        

        session->Run(, vNames, , &out);

现在您已加载所有神经网络权重或其他变量。

同样,您可以执行其他操作(还记得名字吗?);制作适当大小的输入和输出张量,用数据填充输入张量并像这样运行会话:

auto operationStatus = session->Run(input, "put_your_operation_here", , &out);

【讨论】:

我已经用 C++ 编写了运行代码,在我的问题中,我问的是不同的方式和它们的区别?顺便说一句,您提出的方法几乎是技巧,但正确的方法是使用freeze_graph() 代码来冻结节点。 说实话我还没有时间测试冻结图。当我在做我的项目时,根本没有明确的 api 或工具来做这件事( @Alex Joz 我已经尝试过您的解决方案。但是,我有变量,例如几个批量标准化的移动均值和方差,它们不是可训练的变量,仍应存储。所以我用'for variable in tf.trainable_variables():'替换了'for variable in tf.global_variables():'。问题是 C++ 中生成的网络总是在不同的样本上给出相同的输出。知道这可能来自哪里或如何纠正它吗? 所以,它的行为肯定与 python 代码不同,不是吗?你确定吗?另外,我建议你不要使用这种方法,除了研究案例。自动冻结图导出效果很好。 没有错,但是很老了。请查看其他一些现代方法来执行此操作,您将节省很多时间 =)【参考方案3】:

对于TensorFlow v2,建议使用tensorflow::LoadSavedModel,它可以获取通过Python API mode.save()保存的模型(SavedModel)。所以你不需要使用FreezeSavedModelGrapeDef

假设你的 TensorFlow 模型文件保存在model/ 目录中:

#include <tensorflow/cc/saved_model/loader.h>
#include <tensorflow/cc/saved_model/tag_constants.h>
#include <tensorflow/cc/tools/freeze_saved_model.h>

using namespace std;
using namespace tensorflow;

//namespace tf = tensorflow;

int main() 

    const std::string export_dir = "model/";

    SavedModelBundle model_bundle;
    SessionOptions session_options = SessionOptions();
    RunOptions run_options = RunOptions();
    Status status = LoadSavedModel(session_options, run_options, export_dir, kSavedModelTagServe,
                                &model_bundle);

    if (status.ok()) 
        std::cout << "Session successfully loaded: " << status;
    
    else 
        std::cerr << "Failed: " << status;
    
return 0;

【讨论】:

以上是关于Tensorflow 在 C++ 中导出和运行图的不同方法的主要内容,如果未能解决你的问题,请参考以下文章

在 Microsoft Azure 中导出和导入入站安全规则

如何通过 DataGrip IDE 在单个文件中导出和导入 MySQL 模式

在另一个js文件中导出和重用一个js文件的重载函数[重复]

在 python 3.4 中导入 tensorflow 时出错“无法加载本机 TensorFlow 运行时”

TensorFlow 在 Python CLI 和 IPython CLI 中导入,但不在 IPython QtConsole 中

如何在 Python 中导入 tensorflow lite 解释器?