Tensorflow C++ 从训练到部署(1):环境搭建
很多人使用 Tensorflow 作为自己深度学习的实验工具,然而它只能用 Python 来训练和预测,对于实际生产而言,我们更多地会用 C++ 来放入自己工程中。例如一个典型的流程如下:
1)在训练环节,我们仍然希望使用 Python 接口
2)在预测环节,我们使用 C++ 接口获取结果
这几篇博客我们就尝试按照上面的方式,完成从环境搭建、训练到部署的一整套流程。
0、系统环境
Ubuntu 16.04
Tensorflow 1.10.1
ProtoBuf 3.6.1
1、安装依赖
1)安装 JDK 8
下载地址:
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
或者到我的网盘下载:
[Ubuntu] https://pan.baidu.com/s/1B2wpEVVqtP6JwBfEvEQsnw
[Mac] https://pan.baidu.com/s/15zxVi7uJDmnJk6gQfov1Cw
2)安装依赖库:
使用如下命令:
1 | sudo apt-get install pkg-config zip g++ zlib1g-dev unzip python |
3)安装 Bazel:
在 github 上下载 bazel-
https://github.com/bazelbuild/bazel/releases
使用如下命令安装:
1 2 | chmod +x bazel-<version>-installer-linux-x86_64.sh ./bazel-<version>-installer-linux-x86_64.sh --user |
参考提示可以添加可执行文件目录(通常不需要):
4)安装 protobuf
使用如下命令安装(我这里安装版本为 3.6.1,你可以根据自己需要修改,其实对于 1.10.1 来说,安装 3.6.0 会省去后面的一些步骤):
1 2 3 4 5 6 7 | wget -t 0 -c https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-all-3.6.1.tar.gz tar zxvf protobuf-all-3.6.1.tar.gz cd protobuf-3.6.1/ ./autogen.sh ./configure make -j4 sudo make install |
5)安装 Eigen (可选,你也可以在后面实际工程中再指定)
使用如下命令安装,我们这里使用的版本是 Eigen 3.3.5:
1 2 3 4 5 6 7 8 | wget -t 0 -c https://github.com/eigenteam/eigen-git-mirror/archive/3.3.5.zip unzip 3.3.5.zip cd eigen-git-mirror-3.3.5/ mkdir build cd build cmake .. make sudo make install |
2、编译 tensorflow
在 C++ 工程中我们需要引用 libtensorflow_cc.so 库,这个库在安装 tensorflow 时是没有安装到系统的,只能通过编译源码进行安装。
0)下载 tensorflow 1.10.1源码
地址:https://github.com/tensorflow/tensorflow/archive/v1.10.1.zip
或者到我的网盘下载:
https://pan.baidu.com/s/1kmsDM4YLbAEZDIjPvNuaAQ
下载并解压缩。
1)修改 tensorflow 里面的 protobuf 和 eigen 版本配置(当然如果你安装的 protobuf 就是 3.6.0 就不需要这些步骤了):
在刚才的步骤中,我们已经安装了自己的一个 protobuf 版本,然而比较坑的是 tensorflow 自己也会下载一个版本,同时内部也有自己定义的版本号,这些版本很可能都不是统一的。最后会造成版本冲突。因此我们这里建议预先修改 tensorflow 源码将内部的版本统一成我们系统的版本。
打开 tensorflow/workspace.bzl
搜索:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | tf_http_archive( name = "protobuf_archive", urls = [ "https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.0.tar.gz", "https://github.com/google/protobuf/archive/v3.6.0.tar.gz", ], sha256 = "50a5753995b3142627ac55cfd496cebc418a2e575ca0236e29033c67bd5665f4", strip_prefix = "protobuf-3.6.0", ) # We need to import the protobuf library under the names com_google_protobuf # and com_google_protobuf_cc to enable proto_library support in bazel. # Unfortunately there is no way to alias http_archives at the moment. tf_http_archive( name = "com_google_protobuf", urls = [ "https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.0.tar.gz", "https://github.com/google/protobuf/archive/v3.6.0.tar.gz", ], sha256 = "50a5753995b3142627ac55cfd496cebc418a2e575ca0236e29033c67bd5665f4", strip_prefix = "protobuf-3.6.0", ) tf_http_archive( name = "com_google_protobuf_cc", urls = [ "https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.0.tar.gz", "https://github.com/google/protobuf/archive/v3.6.0.tar.gz", ], sha256 = "50a5753995b3142627ac55cfd496cebc418a2e575ca0236e29033c67bd5665f4", strip_prefix = "protobuf-3.6.0", ) |
修改成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | tf_http_archive( name = "protobuf_archive", urls = [ "https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.1.tar.gz", "https://github.com/google/protobuf/archive/v3.6.1.tar.gz", ], sha256 = "3d4e589d81b2006ca603c1ab712c9715a76227293032d05b26fca603f90b3f5b", strip_prefix = "protobuf-3.6.1", ) # We need to import the protobuf library under the names com_google_protobuf # and com_google_protobuf_cc to enable proto_library support in bazel. # Unfortunately there is no way to alias http_archives at the moment. tf_http_archive( name = "com_google_protobuf", urls = [ "https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.1.tar.gz", "https://github.com/google/protobuf/archive/v3.6.1.tar.gz", ], sha256 = "3d4e589d81b2006ca603c1ab712c9715a76227293032d05b26fca603f90b3f5b", strip_prefix = "protobuf-3.6.1", ) tf_http_archive( name = "com_google_protobuf_cc", urls = [ "https://mirror.bazel.build/github.com/google/protobuf/archive/v3.6.1.tar.gz", "https://github.com/google/protobuf/archive/v3.6.1.tar.gz", ], sha256 = "3d4e589d81b2006ca603c1ab712c9715a76227293032d05b26fca603f90b3f5b", strip_prefix = "protobuf-3.6.1", ) |
打开 tensorflow/contrib/cmake/external/protobuf.cmake
搜索:
1 | set(PROTOBUF_TAG v3.6.0) |
修改成:
1 | set(PROTOBUF_TAG v3.6.1) |
打开 tensorflow/tools/ci_build/install/install_proto3.sh
搜索:
1 | PROTOBUF_VERSION="3.6.0" |
修改成:
1 | PROTOBUF_VERSION="3.6.1" |
打开 tensorflow/tools/ci_build/protobuf/protobuf_optimized_pip.sh
搜索:
1 | PROTOBUF_VERSION="3.3.1" |
修改成:
1 | PROTOBUF_VERSION="3.6.1" |
Tensorflow C++ 版本编译时需要 Eigen 3.3 以上版本,在之前的步骤中我们已经安装了 Eigen 3.3.5 版本,这里我们同样做出修改(这一步可选,因为 Eigen 仅是一个头文件,你可以在后续工程中指定使用你自己的 Eigen 库,这样就不用 tensorflow 的 eigen 了):
搜索:
1 2 3 4 5 6 7 8 9 10 | tf_http_archive( name = "eigen_archive", urls = [ "https://mirror.bazel.build/bitbucket.org/eigen/eigen/get/fd6845384b86.tar.gz", "https://bitbucket.org/eigen/eigen/get/fd6845384b86.tar.gz", ], sha256 = "d956415d784fa4e42b6a2a45c32556d6aec9d0a3d8ef48baee2522ab762556a9", strip_prefix = "eigen-eigen-fd6845384b86", build_file = clean_dep("//third_party:eigen.BUILD"), ) |
修改成:
1 2 3 4 5 6 7 8 9 10 | tf_http_archive( name = "eigen_archive", urls = [ "https://bitbucket.org/eigen/eigen/get/3.3.5.tar.gz", "https://github.com/eigenteam/eigen-git-mirror/archive/3.3.5.tar.gz", ], sha256 = "992855522dfdd0dea74d903dcd082cdb01c1ae72be5145e2fe646a0892989e43", strip_prefix = "eigen-eigen-3.3.5", build_file = clean_dep("//third_party:eigen.BUILD"), ) |
2)安装所需要的库
1 | sudo apt-get install autoconf automake libtool curl make g++ unzip zlib1g-dev git python |
3)Bazel编译源码
进入 tensorflow 目录,运行:
1 | ./configure |
然后使用如下命令编译:
1 | bazel build //tensorflow:libtensorflow_cc.so |
或者使用如下命令编译 CPU 指令集加速版本(需要根据你的 CPU 类型修改参数):
1 | bazel build -c opt --copt=-mavx --copt=-mavx2 --copt=-mfma --copt=-mfpmath=both --copt=-msse4.2 --config=monolithic //tensorflow:libtensorflow_cc.so |
如果出现以下显示则编译成功:
同时编译 libtensorflow_framework.so:
1 | bazel build -c opt --copt=-mavx --copt=-mavx2 --copt=-mfma --copt=-mfpmath=both --copt=-msse4.2 --config=monolithic //tensorflow:libtensorflow_framework.so |
如果出现以下显示则编译成功:
4)使用以下脚本一键安装:
1 | source tensorflow/contrib/makefile/build_all_linux.sh |
3、拷贝到系统目录下(建议)
为了后续方便使用,逐行执行以下命令,拷贝头文件和库文件到系统目录下:
1 2 3 4 5 6 7 | sudo mkdir -p /usr/local/include/tf/tensorflow sudo cp -r bazel-genfiles/ /usr/local/include/tf sudo cp -r tensorflow/cc /usr/local/include/tf/tensorflow sudo cp -r tensorflow/core /usr/local/include/tf/tensorflow sudo cp -r third_party /usr/local/include/tf sudo cp bazel-bin/tensorflow/libtensorflow_cc.so /usr/local/lib sudo cp bazel-bin/tensorflow/libtensorflow_framework.so /usr/local/lib |
PS:如果想要卸载请运行如下命令:
1 2 | sudo rm -r /usr/local/include/tf sudo rm /usr/local/lib/libtensorflow_*.so |
至此,我们完成了从编译 Tensorflow C++ 库到安装到系统目录下的流程。
常见错误:
1)错误:tensorflow/tensorflow/core/public/session.h:22:60: fatal error: tensorflow/core/framework/device_attributes.pb.h: No such file or directory
缺少其他文件往往是由于没有完整编译tensorflow所导致,比如之前某次bazel构建没有清空。建议运行 bazel clean 后重新编译试试。
2)错误:fatal error: google/protobuf/arena.h: No such file or directory
没有正确安装 protobuf,请参照步骤1中的4)进行安装。并且按照步骤2中1)正确修改 tensorflow 中的版本配置和你系统中一致。
3)错误:error: #error This file was generated by an older version of protoc
因为 tensorflow 本身在安装时也会下载一个 protobuf 到自己目录下,如果跟系统版本不匹配则会出现问题。
修改
tensorflow/tensorflow/contrib/makefile/download_dependencies.sh 文件:
有这样一行:
1 | PROTOBUF_URL="https://mirror.bazel.build/github.com/google/protobuf/archive/396336eb961b75f03b25824fe86cf6490fb75e3a.tar.gz" |
修改为和你相同的版本:
1 | PROTOBUF_URL="https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-all-3.6.1.tar.gz" |
检查系统 protobuf 版本:
1 | libprotoc 3.6.1 |
输出为:
1 | protoc --version |
查找有几个 protobuf 安装在系统里:
1 | sudo find / -name protoc |
你也可以单独运行每个 protoc 看看版本是不是一致。
删除 Bazel 缓存:
1 | sudo rm -r ~/.cache/bazel/ |
然后重新进行步骤 2 中的 2)之后的操作。
4)错误:fatal error: tensorflow/core/framework/tensor.pb.h: No such file or directory
出现这一错误很可能是没有自动编译 libtensorflow_framework.so,建议重新运行步骤2中的3)和4)再试。
5)错误:cannot execute binary file: Exec format error
如果在执行 bazel 安装脚本:
1 | ./bazel-<version>-installer-linux-x86_64.sh --user |
时遇到上面的问题,可以尝试在执行脚本之前加上 sudo 权限:
1 | sudo ./bazel-<version>-installer-linux-x86_64.sh --user |
6)错误:fatal error: unsupported/Eigen/CXX11/Tensor: No such file or directory
如果在编译包含 tensorflow c++ 的项目时遇到如下错误:
1 2 3 4 5 | In file included from /usr/local/include/tf/tensorflow/core/framework/tensor.h:19:0, from /usr/local/include/tf/tensorflow/core/public/session.h:24, from /home/skylook/Develop/tensorflow_cpp/simple/load_simple_net.cpp:1: /usr/local/include/tf/third_party/eigen3/unsupported/Eigen/CXX11/Tensor:1:42: fatal error: unsupported/Eigen/CXX11/Tensor: No such file or directory compilation terminated. |
这是因为 tensorflow 安装的 eigen 版本较旧,最好使用 eigen 3.3 或以上版本编译。
参考文献:
[1] http://mathmach.com/2017/10/09/tensorflow_c++_api_prediction_first/
[2] https://github.com/formath/tensorflow-predictor-cpp/
[3] https://github.com/tensorflow/tensorflow/issues/1890
[4] https://github.com/PatWie/tensorflow-cmake
[5] https://zhuanlan.zhihu.com/p/31283000
Tensorflow C++ 从训练到部署系列文章目录
Tensorflow C++ 从训练到部署(3):使用 Keras 训练和部署 CNN |
Tensorflow C++ 从训练到部署(2):简单图的保存、读取与 CMake 编译 |
Tensorflow C++ 从训练到部署(1):环境搭建 |
记得安装patch
你好, 我想问一下怎么在后续中指定自己的eigen库? 我想要改写一下eigen的一个地方, 能用在TensorFlow里..
您好!我在make调用tensorflow的c++工程文件时,出现了
error: ‘infinity’ is not a member of ‘Eigen::NumTraits’
这样的报错信息。infinity应该在2010-7-15之后的eigen版本中应该就有了。我查看download文件中的eigen库是3.3.90(最新的也才3.3.7)。希望能等到您的回复!!!!
您好!我在make调用编译后的tensorflow的c++文件时,出现了
core/framework/numeric_types.h:79:28: error: ‘infinity’ is not a member of ‘Eigen::NumTraits’这样的报错信息。我查看了在2010-7-15之后发布的eigen版本中,应该有infinity的。查看tensorflow/contrib/makefile/downloads文件中的eigen库,显示是3.3.90版本(最新的也才3.3.7)。希望得到您的回复!!