有没有关于如何生成保存经过训练的Tensorflow图的protobuf文件的例子
我正在看Google如何在Android上部署和使用预训练的Tensorflowgraphics(模型):
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/android
此示例在以下位置使用.pb文件: [这是到自动下载的文件的链接 ] https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip
该示例显示了如何将.pb文件加载到Tensorflow会话并使用它来执行分类,但是没有(?)提及如何在graphics被训练后(例如在Python中)生成这样的.pb文件。
有没有关于如何做的例子?
编辑:作为TensorFlow存储库的一部分的freeze_graph.py
脚本现在用作从现有的TensorFlow GraphDef
和保存的检查点生成代表“冻结”训练模型的协议缓冲区的工具。 它使用如下所述的相同步骤,但使用起来更容易。
目前这个过程没有很好的文档logging(并且需要细化),但是大致的步骤如下:
- build立和训练您的模型作为一个
tf.Graph
称为g_1
。 - 获取每个variables的最终值并将它们存储为numpy数组(使用
Session.run()
)。 - 在一个名为
g_2
的新的tf.Graph
,使用在步骤2中获取的相应numpy数组的值为每个variables创buildtf.constant()
张量。 -
使用
tf.import_graph_def()
将g_1
节点复制到g_2
,并使用input_map
参数将input_map
每个variablesreplace为步骤3中创build的相应tf.constant()
张量。您可能还想使用input_map
来指定一个新的input张量(例如,用tf.placeholder()
replaceinputpipe道 )。 使用return_elements
参数指定预测输出张量的名称。 -
调用
g_2.as_graph_def()
来获得graphics的协议缓冲表示。
( 注意:生成的图将在图中有额外的节点用于训练,尽pipe它不是公共API的一部分,但您可能希望使用内部graph_util.extract_sub_graph()
函数从图中graph_util.extract_sub_graph()
这些节点。
另外我的以前的答案使用freeze_graph()
,这是只有当你把它称为一个脚本是好的,有一个非常好的function,将为你做所有繁重的工作,适合从你的正常模型训练代码中调用。
convert_variables_to_constants()
做两件事:
- 它通过用常量replacevariables来冻结权重
- 它删除与前馈预测无关的节点
假设sess
是你的tf.Session()
和"output"
是你的预测节点的名字,下面的代码会将你的最小graphics序列化成文本和二进制protobuf。
from tensorflow.python.framework.graph_util import convert_variables_to_constants minimal_graph = convert_variables_to_constants(sess, sess.graph_def, ["output"]) tf.train.write_graph(minimal_graph, '.', 'minimal_graph.proto', as_text=False) tf.train.write_graph(minimal_graph, '.', 'minimal_graph.txt', as_text=True)
我无法弄清楚如何实现由mrry描述的方法。 但在这里,我是如何解决它的。 我不确定这是否是解决问题的最好方法,但至less可以解决这个问题。
由于write_graph也可以存储常量的值,所以在用write_graph函数写图之前,我将下面的代码添加到了python中:
for v in tf.trainable_variables(): vc = tf.constant(v.eval()) tf.assign(v, vc, name="assign_variables")
这创build了常量,它们在被训练之后存储variables的值,然后创build张量“ assign_variables ”来将它们赋值给variables。 现在,当你调用write_graph的时候,它会把variables的值以常量的forms存储在文件中。
剩余的部分是在c代码中调用这些张量“ assign_variables ”,以确保您的variables被分配了存储在文件中的常量值。 这是一个办法:
Status status = NewSession(SessionOptions(), &session); std::vector<tensorflow::Tensor> outputs; char name[100]; for(int i = 0;status.ok(); i++) { if (i==0) sprintf(name, "assign_variables"); else sprintf(name, "assign_variables_%d", i); status = session->Run({}, {name}, {}, &outputs); }
这是另一个@ Mostafa的答案。 运行tf.assign
操作的更tf.assign
是将它们存储在tf.group
。 这是我的Python代码:
ops = [] for v in tf.trainable_variables(): vc = tf.constant(v.eval()) ops.append(tf.assign(v, vc)); tf.group(*ops, name="assign_trained_variables")
而在C ++中:
std::vector<tensorflow::Tensor> tmp; status = session.Run({}, {}, { "assign_trained_variables" }, &tmp); if (!status.ok()) { // Handle error }
这样你只有一个命名操作在C ++端运行,所以你不必乱七八糟的迭代节点。
刚刚发现这个post,这是非常有用的谢谢! 我也用@ Mostafa的方法,虽然我的C ++代码有点不同:
std::vector<string> names; int node_count = graph.node_size(); cout << node_count << " nodes in graph" << endl; // iterate all nodes for(int i=0; i<node_count; i++) { auto n = graph.node(i); cout << i << ":" << n.name() << endl; // if name contains "var_hack", add to vector if(n.name().find("var_hack") != std::string::npos) { names.push_back(n.name()); cout << "......bang" << endl; } } session.Run({}, names, {}, &outputs);
NB我在python中使用“var_hack”作为我的variables名称
我在Tensorflow代码库中find了一个freeze_graph()
函数,这可能会有帮助。 根据我的理解,在序列化GraphDef之前用常量交换variables,所以当你从C ++中加载这个图时,它没有需要设置的variables,你可以直接使用它来进行预测。
还有一个testing和指南中的一些说明。
这似乎是这里最干净的select。