本篇博客记录笔者最近在在线推理服务中使用 Tensorflow C++ 接口的若干心得和疑(tu)惑(cao),整个流程包括创建 session ,加载 graph ,填充 tensor ,运行 session ,等等。注意,因为 tensorflow 2.0 没有普及,考虑稳定性,本篇博客代码均基于 tensorflow 1.12 。
1. session
1.1 session & client_session
我们知道 tensorflow 所有节点都处于 graph,而 graph 则和 session 绑定,所以线上的实时预测服务在初始化时需要创建 session 并载入 graph。网上找到的很多例子都是用 session
,而官网上只提供了 client_session
的接口,两者的主要区别在于 Run()
函数的参数不一样,client_session
如下:
Status Run(
const FeedType & inputs,
const std::vector< Output > & fetch_outputs,
const std::vector< Operation > & run_outputs,
std::vector< Tensor > *outputs
) const
这个 FeedType 定义如下:
typedef std::unordered_map<Output, Input::Initializer, OutputHash> FeedType;
对比 session
的 Run()
:
virtual Status Run(const RunOptions& run_options,
const std::vector<std::pair<string, Tensor> >& inputs,
const std::vector<string>& output_tensor_names,
const std::vector<string>& target_node_names,
std::vector<Tensor>* outputs, RunMetadata* run_metadata);
看出区别没有?哈希表的 key 不同,一个是自定义类 Output,一个是 string。
我们要运行会话进行预测,不妨把模型当成一个黑盒子,那么关键的步骤有两步,喂数据和取结果,而喂数据要给不同的 placeholder
喂不同的数据,取结果则需要知道从哪个 operation
取结果,所以关键是要有一个哈希表记录 placeholder 或者 operation。client_session
是用 tensorflow c++ api 的 Placeholder 对象或者 Operation 对象作为哈希表的 key ,而 session
则是用 string。
所以,训练可以用 client_session
或者 session
,而预测只能用 session
。因为如果是训练的话,可以掉用 tf c++ api 创建 session
graph
placeholder
,可以得到 Placeholder
对象再 Run()
,但是预测过程是从模型文件中建立 graph,无法得到 placeholder 对象,所以也就无法使用 client_session
了。而 session
此时可以大展身手了,只需要训练方为需要输入和输出的节点命名,预测方就可以通过名称找到对应的节点,喂数据或者取结果就都可以进行了。
值得注意的是,client_session
也是通过封装 session
来实现的。所以为什么 tensorflow 官方文档只有 client_session
而没有 session
,实在令人困惑啊。
1.2 创建 session
tensorflow::NewSession()
可用于创建 session
tensorflow::SessionOptions options;
auto session = std::unique_ptr<tensorflow::Session>(tensorflow::NewSession(options));
2. load graph
关于图,模型文件在存储图时将图给“骨肉分离”了。骨为结构,存储节点与节点之间的连接,肉为数值,存储 variable 大小,有篇博客很好地解释了 tf 的图:Tensorflow框架实现中的“三”种图。有两种方式加载图。
方法一