组合图:C 是否有TensorFlow Import_graph_def当量

Combining graphs: is there a TensorFlow import_graph_def equivalent for C++?

本文关键字:graph def 当量 Import TensorFlow 是否 组合图      更新时间:2023-10-16

我需要使用自定义输入和输出层扩展导出的模型。我发现这可以很容易地完成:

with tf.Graph().as_default() as g1: # actual model
    in1 = tf.placeholder(tf.float32,name="input")
    ou1 = tf.add(in1,2.0,name="output")
with tf.Graph().as_default() as g2: # model for the new output layer
    in2 = tf.placeholder(tf.float32,name="input")
    ou2 = tf.add(in2,2.0,name="output")
gdef_1 = g1.as_graph_def()
gdef_2 = g2.as_graph_def()
with tf.Graph().as_default() as g_combined: #merge together
    x = tf.placeholder(tf.float32, name="actual_input") # the new input layer
    # Import gdef_1, which performs f(x).
    # "input:0" and "output:0" are the names of tensors in gdef_1.
    y, = tf.import_graph_def(gdef_1, input_map={"input:0": x},
                             return_elements=["output:0"])
    # Import gdef_2, which performs g(y)
    z, = tf.import_graph_def(gdef_2, input_map={"input:0": y},
                             return_elements=["output:0"])
sess = tf.Session(graph=g_combined)
print "result is: ", sess.run(z, {"actual_input:0":5}) #result is: 9

这很好。

但是,我需要将指针作为网络输入,而不是传递任意形状的数据集。问题是,我无法想到python内部的任何解决方案(定义和传递指针(,当使用C++ Api开发网络时,我找不到与tf.import_graph_def函数等同的网络。

这在C 中具有不同的名称,还是有其他方法可以合并C 中的两个图形/模型?

感谢您的建议

它不像python那样容易。

您可以用类似的东西加载GraphDef

#include <string>
#include <tensorflow/core/framework/graph.pb.h>
#include <tensorflow/core/platform/env.h>
tensorflow::GraphDef graph;
std::string graphFileName = "...";
auto status = tensorflow::ReadBinaryProto(
    tensorflow::Env::Default(), graphFileName, &graph);
if (!status.ok()) { /* Error... */ }

然后您可以使用它来创建一个会话:

#include <tensorflow/core/public/session.h>
tensorflow::Session *newSession;
auto status = tensorflow::NewSession(tensorflow::SessionOptions(), &newSession);
if (!status.ok()) { /* Error... */ }
status = session->Create(graph);
if (!status.ok()) { /* Error... */ }

或扩展现有图的图:

status = session->Extend(graph);
if (!status.ok()) { /* Error... */ }

这样,您可以将几个GraphDef S放入同一图。但是,没有其他设施可以提取特定的节点,也没有避免名称碰撞 - 您必须自己找到节点,并且必须确保GraphDef S没有冲突的OP名称。例如,我使用此函数查找所有与给定正则表达式匹配的名称的节点,并按名称排序:

#include <vector>
#include <regex>
#include <tensorflow/core/framework/node_def.pb.h>
std::vector<const tensorflow::NodeDef *> GetNodes(const tensorflow::GraphDef &graph, const std::regex &regex)
{
    std::vector<const tensorflow::NodeDef *> nodes;
    for (const auto &node : graph.node())
    {
        if (std::regex_match(node.name(), regex))
        {
            nodes.push_back(&node);
        }
    }
    std::sort(nodes.begin(), nodes.end(),
              [](const tensorflow::NodeDef *lhs, const tensorflow::NodeDef *rhs)
              {
                  return lhs->name() < rhs->name();
              });
    return nodes;
}

可以通过直接操纵要组合的两个图的graphDefs中的nodedefs来实现这一点。基本算法是定义两个GraphDef,使用占位符将输入到第二个GraphDef,然后将其重定向到第一个GraphDef的输出。这将类似于通过将第二电路的输入连接到第一电路的输出来串联的两个电路。

首先,定义了示例GraphDef,以及观察GraphDefs内部的实用程序。重要的是要注意,两个GraphDefs的所有节点都必须具有唯一的名称。

Status Panel::SampleFirst(GraphDef *graph_def) 
{
    Scope root = Scope::NewRootScope();
    Placeholder p1(root.WithOpName("p1"), DT_INT32);
    Placeholder p2(root.WithOpName("p2"), DT_INT32);
    Add add(root.WithOpName("add"), p1, p2);
    return root.ToGraphDef(graph_def);
}
Status Panel::SampleSecond(GraphDef *graph_def)
{
    Scope root = Scope::NewRootScope();
    Placeholder q1(root.WithOpName("q1"), DT_INT32);
    Placeholder q2(root.WithOpName("q2"), DT_INT32);
    Add sum(root.WithOpName("sum"), q1, q2);
    Multiply multiply(root.WithOpName("multiply"), sum, 4);
    return root.ToGraphDef(graph_def);
}
void Panel::ShowGraphDef(GraphDef &graph_def)
{
    for (int i = 0; i < graph_def.node_size(); i++) {
        NodeDef node_def = graph_def.node(i);
        cout << "NodeDef name is " << node_def.name() << endl;
        cout << "NodeDef op is " << node_def.op() << endl;
        for (const string& input : node_def.input()) {
            cout << "t input: " << input << endl;
        }
    }
}

现在创建了两个GraphDef,第二个GraphDef的输入连接到第一个GraphDef的输出。这是通过在节点上迭代并识别第一个操作节点来完成的,该节点的输入是占位符,并将这些输入重定向到第一个GraphDef的输出。然后将节点添加到第一个GraphDef以及所有后续节点。结果是第二个GraphDef。

附加的第一个GraphDef
Status Panel::Append(vector<Tensor> *outputs)
{
    GraphDef graph_def_first;
    GraphDef graph_def_second;
    TF_RETURN_IF_ERROR(SampleFirst(&graph_def_first));
    TF_RETURN_IF_ERROR(SampleSecond(&graph_def_second));
    for (int i = 0; i < graph_def_second.node_size(); i++) {
        NodeDef node_def = graph_def_second.node(i);
        if (node_def.name() == "sum") {
            node_def.set_input(0, "p1");
            node_def.set_input(1, "add");
        }
        *graph_def_first.add_node() = node_def;
    }
    ShowGraphDef(graph_def_first);
    unique_ptr<Session> session(NewSession(SessionOptions()));
    TF_RETURN_IF_ERROR(session->Create(graph_def_first));
    Tensor t1(2);
    Tensor t2(3);
    vector<pair<string, Tensor>> inputs = {{"p1", t1}, {"p2", t2}};
    TF_RETURN_IF_ERROR(session->Run(inputs, {"multiply"}, {}, outputs));
    return Status::OK();
}

此特定图将采用两个输入2和3,并将它们添加在一起。然后,该(5(的总和将再次添加到第一个输入(2(,然后乘以4,以获得28的结果。(((2 3( 2( * 4 = 4 = 28。