☰
TensorFlow 1.10 CPU版C++库编译指南:Bazel+VS2015实战
2026/10/14 8:37:57 网站建设 项目流程

简介:这份资源是CPU版TensorFlow 1.10.0编译产出的C++ API库文件,面向需要在Windows平台用C++调用TensorFlow进行推理与训练的开发者,尤其适合使用VS2015-64位环境、希望直接跑通训练模型或加载GPU训练所得模型的工程人员。压缩包为rar格式,共2388个文件,约80.87MB,其中以2248个h头文件为主体,另含exe可执行文件、txt说明、lib与dll库文件,以及core、tensor、eigen、sparse、fft、svd、qr、cholesky等模块相关文件,覆盖矩阵运算、线性代数与神经网络等依赖,目录结构完整,便于按模块检索与集成。已有519人学习下载。资源省去从源码编译的繁琐过程,可直接在VS2015中配置引用,用于编写C++程序调用TensorFlow跑例程、加载并运行训练好的模型,同时兼容GPU训练产出的模型,作者亲测可用,适合需要快速搭建C++推理环境的开发者参考使用。

1. 为什么今天还要折腾 CPU 版 TensorFlow 1.10 的 C++ 库

如果你手上有一个 2018 年前后落地的工业视觉项目,模型是冻结好的.pb,推理代码是 C++ 写的,部署机器是一台没有独显、也不允许随便装运行时的工控机,那你大概率绕不开一个名字:CPU 版的 TensorFlow 1.10.0,配上 VS2015 编译出 C++ API 的库文件。这不是怀旧,是很多产线设备至今还在跑这套组合,换框架的成本远高于把库编出来。

TensorFlow 1.10 的 C++ API 和 Python API 是两套东西:Python 侧import tensorflow就能用,C++ 侧必须自己用 Bazel 从源码编译出tensorflow.dll、tensorflow.lib和一堆头文件,再用 VS2015 链接进自己的工程。标题里的每个词都是硬约束——CPU 版意味着不用 CUDA、不碰 cuDNN;1.10.0 意味着 API 和目录结构锁死在这个版本;VS2015 意味着 MSVC 14.0 工具链,和后面 VS2017/2019 的 ABI 不通用。

这篇笔记面向两类人:一类是第一次要从源码编 TensorFlow C++ 库、被 Bazel 和 MSVC 折腾到怀疑人生的新手;另一类是编过但总在链接期翻车、想知道参数边界和踩坑点的熟手。我会把选型理由、编译步骤、参数含义、排错路径讲透,让你能照着复现,而不是对着报错发呆。

2. 编译前的环境选型:为什么是 Bazel + MSVC 14.0 这套组合

2.1 TensorFlow 1.10 的构建系统为什么只能用 Bazel

TensorFlow 1.x 的官方构建入口就是 Bazel,tensorflow/BUILD、tensorflow/contrib/BUILD这些文件是构建图的唯一真相来源。CMake 在 1.10 时代对 C++ API 的支持是残缺的,社区那几个 CMake 脚本只能编出部分算子,遇到libtensorflow_cc.so这种完整目标就歇了。所以想拿到完整的 C++ API,Bazel 不是可选项,是唯一路径。

Bazel 版本也要卡死。TensorFlow 1.10 官方推荐 Bazel 0.15.0 到 0.16.1 之间,我一般用 0.15.2。用 0.17 以上会在解析WORKSPACE时因为http_archive规则变更直接报错,用 0.14 以下又会缺--config相关能力。这个版本区间是血泪经验,别想着用新版 Bazel 编老 TensorFlow,玄学报错能让你查一整天。

2.2 MSVC 14.0 工具链和 Python 2.7 的绑定关系

TensorFlow 1.10 的 Windows 构建脚本tensorflow/contrib/cmake和 Bazel 的msvc配置,默认锚定的是 VS2015 的 MSVC 14.0。原因很实际:1.10 的tensorflow/core/platform/default/integral_types.h和一批std::相关头文件在 MSVC 14.1(VS2017)下会有std::string_view之类的兼容问题,官方没做适配。你硬用 VS2017 编,可能在编译protobuf或eigen时就挂掉。

Python 侧必须是 2.7,且是 64 位。Bazel 构建过程中会调用 Python 脚本生成pywrap相关代码,1.10 的脚本是 Python 2 语法,用 Python 3 会在print语句和unicode处理上直接报语法错误。我一般装一个干净的 Python 2.7.15,把python.exe加进 PATH,并在configure时明确指定路径。

2.3 CPU 版编译要关掉哪些开关

CPU 版的核心是--config=opt加上--config=noaws --config=nogcp --config=nohdfs这类裁剪开关,把用不到的云存储、GPU 支持全关掉,减少依赖和编译时间。关键配置项在configure脚本里交互式回答:

配置项CPU 版取值说明
CUDA supportN关掉 GPU,避免拉 cuDNN 依赖
Compute capability留空不填,填了会尝试编 GPU 算子
TensorFlow C++ APIY必须开,否则不生成 C++ 头文件
Optimization-march=native按本机 CPU 指令集优化,跨机部署改 -mavx2 等
Python pathC:\Python27\python.exe指向 2.7 解释器

-march=native在编译机和部署机 CPU 不同代时会触发非法指令,这是部署期最常见的翻车点。如果部署机型号杂,老老实实写-mavx -mfma这种保守组合。

3. 从源码到 tensorflow.lib:一步步编出 C++ API 库

3.1 拉源码、切分支、装 Bazel 0.15.2

先把源码和工具链准备好。TensorFlow 1.10.0 的 tag 是v1.10.0,别用 master,master 的 API 已经变了。

# 拉取 TensorFlow 源码并切到 1.10.0 git clone https://github.com/tensorflow/tensorflow.git cd tensorflow git checkout v1.10.0 # 下载 Bazel 0.15.2 的 Windows 安装包(bazel-0.15.2-windows-x86_64.exe) # 安装后把 bazel.exe 所在目录加入 PATH,验证版本 bazel version # 期望输出:Build label: 0.15.2

这里的关键是git checkout v1.10.0,切错分支后面所有编译都是白费。Bazel 装好后用bazel version确认,如果显示的不是 0.15.x,先解决版本问题再往下走。源码目录不要放在带中文或空格的路径下,Bazel 对路径里的空格处理有 bug,会在生成genrule时莫名失败。

3.2 跑 configure 脚本:CPU 版的关键回答

在源码根目录执行python configure.py,这是交互式的,每个回答都影响后续编译。

# 在 tensorflow 源码根目录执行 python configure.py # 交互回答示例(CPU 版): # Please specify the location of python. [Default is ...]: C:\Python27\python.exe # Do you wish to build TensorFlow with CUDA support? [y/N]: N # Do you wish to build TensorFlow with OpenCL support? [y/N]: N # Do you wish to build TensorFlow with MKL support? [y/N]: N # Please specify optimization flags: -mavx -mfma # Do you wish to build TensorFlow with Apache Kafka platform support? [y/N]: N

CUDA support选 N 之后,脚本不会再问 compute capability,这是 CPU 版省事的地方。MKL support选 N 是因为 MKL 在 1.10 的 Windows 构建里经常和 MSVC 运行库冲突,除非你明确需要它加速矩阵运算,否则关掉更稳。optimization flags我填-mavx -mfma,兼顾性能和部署兼容性,比-march=native安全。

回答完会在源码根目录生成.tf_configure.bazelrc,里面记录了 Python 路径、优化选项等。这个文件是后续bazel build的配置来源,改配置就改它,不用重跑 configure。

3.3 用 bazel build 编出 C++ API 目标

CPU 版 C++ API 的构建目标是//tensorflow:libtensorflow_cc.so,但 Windows 下我们要的是.dll和.lib,对应目标//tensorflow:libtensorflow_cc.so在 Windows 上会生成tensorflow.dll和导入库。命令如下:

# 编译 C++ API 动态库,CPU 版 bazel build --config=opt \ --config=noaws --config=nogcp --config=nohdfs \ //tensorflow:libtensorflow_cc.so # 同时编出 C API 库(可选,某些工程用 C API 更省事) bazel build --config=opt \ //tensorflow:libtensorflow.so

--config=opt开启优化,--config=noaws等裁剪掉云存储依赖,能显著减少编译时间和链接错误。第一次编译大概 40 分钟到 1 小时,取决于机器。编译产物在bazel-bin/tensorflow/下,Windows 上你会看到tensorflow.dll、tensorflow.lib(导入库)以及tensorflow_cc.dll之类的文件,具体命名随 Bazel 版本略有差异。

编译过程中如果卡在protobuf或eigen的编译上,多半是 MSVC 版本不对或 Bazel 版本不对,回到 2.1、2.2 检查。

3.4 收集头文件和库文件,整理成可链接的目录

编完之后,头文件散落在源码树和bazel-genfiles里,需要收集。我一般建一个tf_cpu_1.10目录,结构如下:

# 整理产物目录结构(在源码根目录执行) mkdir -p tf_cpu_1.10/include mkdir -p tf_cpu_1.10/lib # 拷贝核心头文件 cp -r tensorflow/core tf_cpu_1.10/include/tensorflow/ cp -r tensorflow/cc tf_cpu_1.10/include/tensorflow/ cp -r bazel-genfiles/tensorflow/core/framework tf_cpu_1.10/include/tensorflow/core/ # 拷贝库文件 cp bazel-bin/tensorflow/tensorflow.dll tf_cpu_1.10/lib/ cp bazel-bin/tensorflow/tensorflow.lib tf_cpu_1.10/lib/

头文件里tensorflow/core/framework下的*.pb.h是 Bazel 生成的,必须从bazel-genfiles拷,源码树里没有。漏拷这个目录,VS2015 里会报tensorflow/core/framework/graph.pb.h: No such file or directory。库文件里tensorflow.lib是导入库,链接时用;tensorflow.dll是运行时用,部署时要和 exe 放一起。

4. VS2015 工程配置:让 C++ 代码真正链接上 tensorflow.lib

4.1 新建工程并设置包含目录、库目录

打开 VS2015,新建一个 Win32 控制台工程,配置选 Release x64。在项目属性里设置:

  • C/C++ -> 常规 -> 附加包含目录:加上tf_cpu_1.10/include
  • 链接器 -> 常规 -> 附加库目录:加上tf_cpu_1.10/lib
  • 链接器 -> 输入 -> 附加依赖项:加上tensorflow.lib

这三步是基础,但很多人漏了 x64 配置。TensorFlow 1.10 的 Windows 构建默认是 64 位,你的工程如果选 Win32,链接时会报模块计算机类型“x86”与目标计算机类型“x64”冲突。这个报错很直白,但新手容易忽略配置管理器里的平台选择。

4.2 写一个最小 C++ 推理代码验证链接

用一段加载.pb模型并跑一次推理的代码验证库是否可用:

// minimal_infer.cpp #include <tensorflow/core/public/session.h> #include <tensorflow/core/platform/env.h> #include <tensorflow/core/framework/tensor.h> #include <iostream> int main() { using namespace tensorflow; // 创建 Session,CPU 版默认用本机 CPU Session* session; Status status = NewSession(SessionOptions(), &session); if (!status.ok()) { std::cerr << "NewSession failed: " << status.ToString() << std::endl; return 1; } // 加载冻结图,路径按实际模型改 GraphDef graph_def; status = ReadBinaryProto(Env::Default(), "model.pb", &graph_def); if (!status.ok()) { std::cerr << "ReadProto failed: " << status.ToString() << std::endl; return 1; } status = session->Create(graph_def); if (!status.ok()) { std::cerr << "Create graph failed: " << status.ToString() << std::endl; return 1; } // 构造输入张量,形状要和模型输入节点一致 Tensor input(DT_FLOAT, TensorShape({1, 224, 224, 3})); // 这里省略填充数据的逻辑,实际用 input.flat<float>() 写入 std::vector<std::pair<std::string, Tensor>> inputs = {{"input:0", input}}; std::vector<Tensor> outputs; status = session->Run(inputs, {"output:0"}, {}, &outputs); if (!status.ok()) { std::cerr << "Run failed: " << status.ToString() << std::endl; return 1; } std::cout << "Inference OK, output dim: " << outputs[0].shape().DebugString() << std::endl; session->Close(); delete session; return 0; }

NewSession创建会话,ReadBinaryProto读.pb,session->Create把图加载进会话,session->Run执行推理。输入节点名input:0和输出节点名output:0必须和冻结图里的一致,用错名字会报Node not found。TensorShape({1, 224, 224, 3})是 NHWC 格式,CPU 版默认布局,别写成 NCHW。

4.3 运行时 DLL 缺失和常见链接错误的处理

编译通过但运行时报找不到 tensorflow.dll,把tf_cpu_1.10/lib/tensorflow.dll拷到 exe 同目录,或者加进系统 PATH。如果报无法解析的外部符号,检查附加依赖项里tensorflow.lib是否拼对,以及工程平台是不是 x64。

链接期还可能遇到LNK2038: 检测到“RuntimeLibrary”的不匹配项,这是 TensorFlow 库用的运行库(/MD)和你工程设置(/MT)不一致。在C/C++ -> 代码生成 -> 运行库里改成多线程 DLL (/MD),和 TensorFlow 对齐。

5. 避坑与排查:编译和链接期最常见的 5 个翻车点

5.1 Bazel 版本不对导致 WORKSPACE 解析失败

现象:执行bazel build时立刻报ERROR: Skipping '//tensorflow:libtensorflow_cc.so': no such package或WORKSPACE:... http_archive rule相关错误。

原因:Bazel 0.17 以上改了http_archive的参数校验,TensorFlow 1.10 的WORKSPACE文件按老规则写的,解析不过。

解决:卸载新版 Bazel,装 0.15.2,bazel version确认后重试。别试图改WORKSPACE去适配新版,改动会牵连一堆依赖。

5.2 Python 3 环境导致 configure 脚本语法错误

现象:跑python configure.py时报SyntaxError: invalid syntax,指向print语句或unicode。

原因:1.10 的 configure 脚本是 Python 2 写的,系统默认python指向了 Python 3。

解决:用绝对路径C:\Python27\python.exe configure.py执行,或在 PATH 里把 Python 2.7 放前面。装完 Python 2.7 后确认是 64 位版本,32 位会在后续 Bazel 构建时找不到匹配的库。

5.3 头文件漏拷导致 VS2015 报找不到 graph.pb.h

现象:VS2015 编译时报无法打开包括文件: "tensorflow/core/framework/graph.pb.h"。

原因:graph.pb.h是 Bazel 在构建时生成的,不在源码树里,只在bazel-genfiles/tensorflow/core/framework/下。

解决:把bazel-genfiles/tensorflow/core/framework整个目录拷进tf_cpu_1.10/include/tensorflow/core/,保持相对路径一致。类似的还有*.pb.cc对应的头文件,一并拷全。

5.4 运行库不匹配引发 LNK2038

现象:链接时大量LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MT_StaticRelease”不匹配值“MD_DynamicRelease”。

原因:TensorFlow 官方构建用/MD(动态运行库),你的工程默认可能是/MT。

解决:项目属性C/C++ -> 代码生成 -> 运行库改成多线程 DLL (/MD)。如果工程里引了其他静态库,也要确认它们同样是/MD编的,否则冲突会转移。

5.5 部署机 CPU 指令集不支持导致非法指令

现象:编译机跑得好好的 exe,拷到部署机一运行就崩,报0xC000001D: Illegal Instruction。

原因:编译时用了-march=native,生成的指令用了编译机支持但部署机不支持的 AVX512 等指令。

解决:重新 configure,优化选项改成保守组合如-mavx -mfma,重新编译。如果部署机更老,连 AVX 都没有,就写-msse4.2。这个坑在跨机型部署时几乎必踩,提前确认部署机 CPU 型号能省很多事。

6. 进阶技巧:把 C++ API 库做成可复用工程模板

编一次库不容易,每次新建工程都手动配包含目录、库目录、依赖项太费劲。我的习惯是做一个 VS2015 的属性表(.props),把 TensorFlow 相关的路径和依赖全写进去,新工程导入这个属性表就行。

属性表里关键节点是IncludePath、LibraryPath和AdditionalDependencies,用相对路径指向tf_cpu_1.10,这样整个tf_cpu_1.10目录连同属性表可以一起拷给同事,换台机器只要改属性表里的根路径。属性表内容大致如下:

<!-- tensorflow_cpu_1.10.props 关键片段 --> <Project> <PropertyGroup> <TFRoot>$(MSBuildThisFileDirectory)..\tf_cpu_1.10</TFRoot> </PropertyGroup> <ItemDefinitionGroup> <ClCompile> <AdditionalIncludeDirectories>$(TFRoot)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> <AdditionalLibraryDirectories>$(TFRoot)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalDependencies>tensorflow.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> </Project>

$(MSBuildThisFileDirectory)是属性表自身所在目录,用相对路径定位tf_cpu_1.10,这样整个目录树可以整体迁移。导入方式是在 VS2015 的属性管理器里右键工程,选添加现有属性表,选中这个.props文件。

另一个进阶点是验证库的完整性。编完之后别急着写业务代码,先用 4.2 那段最小推理代码跑通一次,确认NewSession、ReadBinaryProto、Run三个环节都不报错。这一步能提前暴露 90% 的链接和运行时问题,比在业务代码里 debug 高效得多。

我自己的教训是:第一次编 1.10 的 C++ 库时,图省事用了系统里的 Python 3 和 Bazel 0.19,结果 configure 报语法错误、build 报 WORKSPACE 解析失败,来回折腾了一下午才回到正确版本组合。后来我养成习惯,动手前先把 Bazel 版本、Python 版本、MSVC 版本三个数字写在纸上核对一遍,确认无误再敲第一条命令。这个习惯帮我省下的时间,远比核对本身花的多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询