我正在关注如何在Android上部署和使用预先训练的Tensorflow图表(模型)Google's example。此示例在以下位置使用.pb
文件:
https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip
是指向自动下载的文件的链接。
该示例显示了如何将.pb
文件加载到Tensorflow会话并使用它来执行分类,但它似乎没有提到如何生成这样的.pb
文件,之后训练图形(例如,在Python中)。
有没有关于如何做到这一点的例子?
答案 0 :(得分:34)
编辑: freeze_graph.py
脚本是TensorFlow存储库的一部分,现在可用作生成协议缓冲区的工具,该协议缓冲区表示来自现有TensorFlow的“冻结”训练模型GraphDef
和已保存的检查点。它使用与下面描述的相同的步骤,但它更容易使用。
目前这个过程没有很好的记录(并且需要改进),但大致的步骤如下:
tf.Graph
g_1
。Session.run()
)。tf.Graph
的新g_2
中,使用在步骤2中获取的相应numpy数组的值为每个变量创建tf.constant()
个张量使用tf.import_graph_def()
将g_1
中的节点复制到g_2
,然后使用input_map
参数将g_1
中的每个变量替换为相应的tf.constant()
在步骤3中创建的input_map
张量。您可能还希望使用return_elements
指定新的输入张量(例如,用input pipeline替换tf.placeholder()
)。使用g_2.as_graph_def()
参数指定预测输出张量的名称。
调用[jwplayer7sc file=”https://www.youtube.com/watch?v=c74KCFIaRIw” title=”efzef” description=”zef” image=”zefzef”]
获取图表的协议缓冲区表示。
(注意:生成的图表在图表中会有额外的节点用于培训。虽然它不是公共API的一部分,但您可能希望使用内部graph_util.extract_sub_graph()
功能从图中剥离这些节点。)
答案 1 :(得分:16)
除了我以前使用freeze_graph()
的答案之外,只有将其称为脚本才有用,有一个非常好的功能可以为您完成所有繁重的工作,并且适合从您调用正常模型训练代码。
convert_variables_to_constants()
做了两件事:
假设sess
是您的tf.Session()
而"output"
是预测节点的名称,以下代码会将您的最小图表序列化为文本和二进制原型。
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)
答案 2 :(得分:4)
我无法弄清楚如何实现mrry描述的方法。但在这里我是如何解决它的。我不确定这是否是解决问题的最佳方法,但至少它解决了这个问题。
由于write_graph也可以存储常量的值,因此在使用write_graph函数编写图形之前,我将以下代码添加到python中:
for v in tf.trainable_variables():
vc = tf.constant(v.eval())
tf.assign(v, vc, name="assign_variables")
这会创建在训练后存储变量值的常量,然后创建张量“ assign_variables ”以将它们分配给变量。现在,当您调用write_graph时,它将以常量的形式将变量的值存储在文件中。
唯一剩下的部分是在c代码中调用这些张量“ assign_variables ”,以确保为变量分配了存储在c代码中的常量值。文件。这是一种方法:
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);
}
答案 3 :(得分:4)
这是对@Mostafa的回答的另一种看法。运行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
}
这样你只有一个命名的op在C ++端运行,所以你不必乱用迭代节点。
答案 4 :(得分:1)
刚刚发现这篇文章,非常有用,谢谢!我也使用@ 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我使用&#34; var_hack&#34;作为我在python中的变量名
答案 5 :(得分:1)
我在Tensorflow代码库中找到了一个freeze_graph()
函数,在执行此操作时可能会有所帮助。根据我的理解,在序列化GraphDef之前将变量与常量交换,因此当您从C ++加载此图时,它不再需要设置变量,您可以直接将其用于预测。
这似乎是最干净的选择。