TensorFlow C++ API 教程
TensorFlow 不仅提供了 Python API,还提供了一个功能强大的 C++ API,使用 C++ API 的主要优势在于:

- 性能: C++ 编译后的代码执行效率极高,适合对延迟和吞吐量有极致要求的场景。
- 部署: 可以将训练好的模型轻松集成到 C++ 编写的应用程序中,例如游戏引擎、嵌入式设备、移动 App 后端等,无需依赖 Python 解释器。
- 跨平台: C++ 代码可以编译并在各种平台上运行(Windows, Linux, macOS, Android, iOS 等)。
第一部分:核心概念
在开始写代码之前,理解几个核心概念至关重要。
tensorflow::Session (会话)
这是 TensorFlow C++ API 的核心。Session 对象负责执行计算图,你可以把它想象成一个“引擎”,你把计算图(模型)加载进去,然后给它输入数据,它就会输出结果。
tensorflow::GraphDef (计算图定义)
GraphDef 是一个协议缓冲区,它以序列化的形式存储了整个计算图的结构,它包含了所有的操作(Operations/Op)和张量(Tensors),当你训练好一个模型后,通常会将其保存为 GraphDef 格式(通常是 .pb 文件),然后在 C++ 程序中加载它。
tensorflow::Tensor (张量)
Tensor 是数据的基本单位,类似于一个多维数组,在 C++ 中,你需要创建 Tensor 对象来作为模型的输入,并从模型的输出中获取 Tensor 对象,你需要指定 Tensor 的数据类型(如 DT_FLOAT, DT_INT32)和形状。

第二部分:环境准备
在编写代码之前,你必须确保你的系统已经正确安装了 TensorFlow 的 C++ 库。
安装 Bazel (构建工具)
TensorFlow 使用 Bazel 作为其官方的构建系统,你需要先安装 Bazel。
在 Ubuntu/Debian 上:
# 添加 Bazel 的 GPG key sudo apt install apt-transport-https curl gnupg curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor | sudo tee /etc/apt/keyrings/bazel.gpg > /dev/null echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bazel.gpg] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list sudo apt update && sudo apt install bazel
在 macOS 上 (使用 Homebrew):

brew install bazelisk # 然后创建一个符号链接 sudo ln -s $(which bazelisk) /usr/local/bin/bazel
编译 TensorFlow C++ 库
最简单的方法是直接从源码编译 TensorFlow,这会自动为你生成所有需要的库文件。
# 克隆 TensorFlow 仓库 git clone https://github.com/tensorflow/tensorflow.git cd tensorflow # 配置编译选项 (选择适合你系统的) # 只构建 C++ 库,不构建 Python Wheel ./configure # 编译,这可能需要很长时间 bazel build //tensorflow:libtensorflow_cc.so
编译成功后,你会在 bazel-bin/tensorflow/ 目录下找到 libtensorflow_cc.so (Linux) 或 libtensorflow_cc.dylib (macOS) 等库文件。
第三部分:第一个 C++ 程序:加载并运行一个预训练模型
我们将创建一个简单的 C++ 程序,加载一个预训练好的 GraphDef 文件,并执行它。
步骤 1: 获取一个预训练模型
为了方便演示,我们使用一个非常简单的加法模型,这个模型接受两个输入 a 和 b,输出它们的和 c。
创建一个 Python 脚本 create_add_model.py 来生成这个模型文件:
# create_add_model.py
import tensorflow as tf
# 创建一个简单的计算图
a = tf.constant(3, name="a")
b = tf.constant(4, name="b")
c = tf.add(a, b, name="c")
# 创建一个会话并运行
with tf.Session() as sess:
result = sess.run(c)
print(f"Python 运行结果: {result}") # 应该输出 7
# 将计算图保存为 GraphDef 文件
graph_def = sess.graph.as_graph_def()
with tf.gfile.GFile("add_model.pb", "wb") as f:
f.write(graph_def.SerializeToString())
print("模型已保存为 add_model.pb")
运行这个 Python 脚本:
python create_add_model.py
执行后,你会得到一个名为 add_model.pb 的文件,这就是我们 C++ 程序要加载的模型。
步骤 2: 编写 C++ 代码
创建一个名为 main.cpp 的文件,内容如下:
// main.cpp
#include <iostream>
#include <tensorflow/core/public/session.h>
#include <tensorflow/core/framework/tensor.h>
int main() {
// 1. 创建一个新的 Session
tensorflow::Session* session;
tensorflow::SessionOptions options;
TF_CHECK_OK(tensorflow::NewSession(options, &session));
// 2. 加载 GraphDef 文件
tensorflow::GraphDef graph_def;
TF_CHECK_OK(ReadBinaryProto(tensorflow::Env::Default(), "add_model.pb", &graph_def));
// 3. 将 GraphDef 导入到 Session 中
TF_CHECK_OK(session->Create(graph_def));
// 4. 准备输入
// 我们的模型有两个输入: "a" 和 "b"
// 创建两个 Tensor 对象来存放输入值
tensorflow::Tensor a_tensor(tensorflow::DT_INT32, tensorflow::TensorShape());
a_tensor.scalar<int>()() = 20; // 设置输入 a 的值为 20
tensorflow::Tensor b_tensor(tensorflow::DT_INT32, tensorflow::TensorShape());
b_tensor.scalar<int>()() = 22; // 设置输入 b 的值为 22
std::vector<std::pair<std::string, tensorflow::Tensor>> inputs = {
{ "a", a_tensor },
{ "b", b_tensor }
};
// 5. 准备输出
// 模型有一个输出: "c"
std::vector<tensorflow::Tensor> outputs;
// 6. 运行计算图
TF_CHECK_OK(session->Run(inputs, {"c"}, {}, &outputs));
// 7. 处理输出
// outputs[0] 就是我们得到的 "c" 的值
tensorflow::Tensor output = outputs[0];
auto output_value = output.scalar<int>()();
std::cout << "C++ 运行结果: " << output_value() << std::endl; // 应该输出 42
// 8. 关闭 Session
session->Close();
return 0;
}
步骤 3: 编译和链接 C++ 代码
我们需要使用 Bazel 来编译 main.cpp,创建一个名为 BUILD 的文件,与 main.cpp 在同一目录下。
BUILD 文件内容:
# BUILD
cc_binary(
name = "run_add_model",
srcs = ["main.cpp"],
deps = [
"//tensorflow/core:tensorflow", # 依赖 TensorFlow 核心库
],
)
编译命令:
在包含 main.cpp, add_model.pb 和 BUILD 文件的目录下,运行:
bazel build //:run_add_model
运行程序:
编译成功后,可执行文件会在 bazel-bin/run_add_model (Linux) 或 bazel-bin/run_add_model.exe (Windows) 下。
./bazel-bin/run_add_model
预期输出:
C++ 运行结果: 42
恭喜!你已经成功使用 C++ 运行了你的第一个 TensorFlow 模型!
第四部分:处理更复杂的输入(如图像)
在实际应用中,你经常需要处理像图像这样的多维数据,下面是一个如何将图像数据送入模型的例子。
假设我们有一个模型,它接受一个 float 类型的 1D 张量(可以看作一个特征向量),并输出一个类别。
准备 C++ 代码
// main_image.cpp
#include <iostream>
#include <tensorflow/core/public/session.h>
#include <tensorflow/core/framework/tensor.h>
int main() {
// 假设我们有一个已经加载和创建好的 session
// ... (省略 session 的创建和图加载部分,与上面例子相同) ...
// 1. 准备输入数据
// 假设模型输入节点名为 "input_layer"
// 输入是一个形状为 [1, 784] 的 float32 张量 (一个展平的 28x28 图像)
// 注意:第一个维度 1 通常代表 "batch size"
tensorflow::Tensor input_tensor(tensorflow::DT_FLOAT, tensorflow::TensorShape({1, 784}));
// 获取张量的底层 Eigen 张量,方便填充数据
auto input_map = input_tensor.tensor<float, 2>();
// 填充一些随机数据作为示例
for (int i = 0; i < 784; ++i) {
input_map(0, i) = i * 0.01f; // 填充一些示例值
}
std::vector<std::pair<std::string, tensorflow::Tensor>> inputs = {
{ "input_layer", input_tensor }
};
// 2. 准备输出
// 假设模型输出节点名为 "output_layer"
std::vector<tensorflow::Tensor> outputs;
// 3. 运行
TF_CHECK_OK(session->Run(inputs, {"output_layer"}, {}, &outputs));
// 4. 处理输出
// 假设输出是一个形状为 [1, 10] 的张量,代表 10 个类别的概率
tensorflow::Tensor output = outputs[0];
auto output_map = output.tensor<float, 2>();
std::cout << "输出向量 (前5个元素):" << std::endl;
for (int i = 0; i < 5; ++i) {
std::cout << " Class " << i << ": " << output_map(0, i) << std::endl;
}
return 0;
}
编译和运行
更新你的 BUILD 文件以包含新的源文件:
# BUILD
cc_binary(
name = "run_image_model",
srcs = ["main_image.cpp"],
deps = [
"//tensorflow/core:tensorflow",
],
)
然后编译并运行:
bazel build //:run_image_model ./bazel-bin/run_image_model
第五部分:从 SavedModel 加载
SavedModel 是 TensorFlow 推荐的模型保存格式,它不仅包含 GraphDef,还包含了变量值和签名信息,使得加载和使用模型更加方便。
保存为 SavedModel
修改之前的 create_add_model.py 文件:
# create_add_model_saved.py
import tensorflow as tf
# 创建计算图
a = tf.constant(3, name="a")
b = tf.constant(4, name="b")
c = tf.add(a, b, name="c")
# 定义一个签名
# 这告诉 TensorFlow 哪些输入和输出是重要的
@tf.function(input_signature=[tf.TensorSpec(shape=[], dtype=tf.int32, name="a_in"),
tf.TensorSpec(shape=[], dtype=tf.int32, name="b_in")])
def add_func(a_in, b_in):
return tf.add(a_in, b_in, name="c_out")
# 创建一个 SavedModel
builder = tf.saved_model.builder.SavedModelBuilder("add_model_saved")
builder.add_meta_graph_and_variables(
tf.compat.v1.Session(),
[tf.saved_model.SERVING],
signature_def_map={
'my_add_signature': add_func.get_concrete_function().get_signature_def()
},
main_op=tf.compat.v1.tables_initializer()
)
builder.save()
运行它:python create_add_model_saved.py,这会创建一个名为 add_model_saved 的目录。
C++ 加载 SavedModel
C++ 加载 SavedModel 的方式略有不同,需要指定 SavedModelBundle。
// main_saved_model.cpp
#include <iostream>
#include <tensorflow/core/public/session.h>
#include <tensorflow/core/framework/tensor.h>
#include <tensorflow/core/platform/env.h>
#include <tensorflow/core/saved_model/loader.h> // 新增头文件
int main() {
// 使用 SavedModelLoader 加载模型
tensorflow::SavedModelBundle bundle;
// 第一个参数是运行设备,第二个是标签,第三个是输出路径
TF_CHECK_OK(tensorflow::LoadSavedModel(tensorflow::SessionOptions(),
tensorflow::RunOptions(),
"add_model_saved",
{"serve"}, // SavedModel 的标签
&bundle));
// bundle.session 就是加载好的 Session 对象
tensorflow::Session* session = bundle.session.get();
// 准备输入
tensorflow::Tensor a_in(tensorflow::DT_INT32, tensorflow::TensorShape());
a_in.scalar<int>()() = 100;
tensorflow::Tensor b_in(tensorflow::DT_INT32, tensorflow::TensorShape());
b_in.scalar<int>()()() = 200;
std::vector<std::pair<std::string, tensorflow::Tensor>> inputs = {
{ "a_in", a_in },
{ "b_in", b_in }
};
// 准备输出
std::vector<tensorflow::Tensor> outputs;
// 运行
// 使用签名的输出名称 "c_out"
TF_CHECK_OK(session->Run(inputs, {"c_out"}, {}, &outputs));
// 处理输出
tensorflow::Tensor output = outputs[0];
std::cout << "SavedModel 运行结果: " << output.scalar<int>()() << std::endl; // 应该输出 300
return 0;
}
更新 BUILD 文件并编译运行,即可看到成功加载 SavedModel 的效果。
总结与最佳实践
-
选择合适的加载方式:
- 如果你的模型非常简单,只有图结构,没有变量,用
GraphDef即可。 - 对于绝大多数现代模型(特别是训练好的模型),强烈推荐使用
SavedModel,它更健壮,包含所有必要信息。
- 如果你的模型非常简单,只有图结构,没有变量,用
-
错误处理:
TF_CHECK_OK是一个非常方便的宏,它会检查 TensorFlow API 的返回值,如果失败则立即终止程序,在生产环境中,你可能需要更精细的错误处理。 -
性能: 对于高性能场景,考虑使用
tensorflow::TensorBuffer和Eigen库(TensorFlow 内部使用)来更高效地操作张量数据,避免不必要的拷贝。 -
依赖管理: Bazel 是官方推荐的构建工具,它能很好地处理 TensorFlow 的复杂依赖关系,如果你想在其他项目中使用 TensorFlow,也可以考虑使用
vcpkg或 Conan 等包管理器来安装 TensorFlow 的 C++ 库。
这个教程为你提供了使用 TensorFlow C++ API 的坚实基础,你可以基于此,探索更高级的功能,如 GPU 加速、自定义操作等。
