张量流 在C++中导出和运行图的不同方法
Tensorflow Different ways to Export and Run graph in C++
要将训练好的网络导入C++您需要导出网络才能这样做。在搜索了很多并且几乎没有找到任何关于它的信息之后,澄清了我们应该使用 freeze_graph() 来做到这一点。
感谢新的 0.7 版本的 Tensorflow,他们添加了它的文档。
查看文档后,我发现类似的方法很少,您能说出freeze_graph()
和: tf.train.export_meta_graph
因为它具有类似的参数,但似乎它也可用于将模型导入C++(我只是猜测区别在于,对于使用此方法的文件输出,您只能使用import_graph_def()
或者它是其他东西?
还有一个关于如何使用write_graph()
的问题:在文档中,graph_def
由sess.graph_def
给出,但在freeze_graph()
的示例中,它是sess.graph.as_graph_def()
。这两者有什么区别?
与这个问题有关。
谢谢!
利用TF 0.12中引入的V2检查点的解决方案。
无需将所有变量转换为常量或冻结图形。
为了清楚起见,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)
蟒蛇部分(保存)
with tf.Session() as sess:
tf.train.Saver(tf.trainable_variables()).save(sess, 'models/my-model')
如果您使用 tf.trainable_variables()
创建Saver
,您可以为自己节省一些麻烦和存储空间。但也许一些更复杂的模型需要保存所有数据,然后删除此参数以Saver
,只要确保在创建图形后创建Saver
即可。为所有变量/层指定唯一的名称也是非常明智的,否则您可能会遇到不同的问题。
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);
对于预测(以及所有其他操作),您可以执行以下操作:
首先,在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);
对于 TensorFlow v2,建议使用tensorflow::LoadSavedModel
,它可以采用通过 Python API mode.save()
保存的模型 (SavedModel
)。所以你不需要使用FreezeSavedModel
和GrapeDef
.
假设您的 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;
}
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 通过方法访问结构
- 最小硬币更换问题(自上而下方法)
- C++为构建时间获取QDateTime的可靠方法
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 处理多个异常集合的C++方法
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 有什么方法可以遍历结构吗
- 当类在C++中定义时,有什么方法可以"register"类吗?
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 使用std::函数映射对象方法
- 有符号的int和int-有没有一种方法可以在C++中区分它们
- C++从另一个类访问公共静态向量的正确方法是什么
- C++优先级队列,按对象的唯一指针的特定方法升序排列
- 没有为自己的结构调用列表推回方法
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 在类定义之后定义一个私有方法
- 枚举环境变量的惯用C++14/C++17方法
- 张量流 在C++中导出和运行图的不同方法