在windows环境下离线编译tensorflow2.2.0的C++动态链接库

环境

系统: windows
python:3.7
cuda:10.1
cudnn:7.6
vs:2019
GPU:2080ti

记录一下在win10系统下如何离线编译tensorflow2.2.0的C++动态链接库,为使用C++版本的tensorflow调用pb模型进行推理做准备。

首先给出编译时主要参考的两篇文章:
Windows编译Tensorflow2.2.0 C++ dll (CPU bazel)
Windows10 Bazel 编译 Tensorflow 2 C++ dll 和 lib 文件

上面两篇文章记录了使用bazel编译tensorflow的C++动态链接库的步骤和注意事项,

一、准备工作

1.1、第三方软件安装

安装msys2
下载msys2-x86_64-20210725.exe,直接安装即可。

Windows编译Tensorflow2.2.0 C++ dll (CPU bazel)这篇文章说装完msys2之后要把anaconda下的patch.exe文件拷贝到msys2的安装目录下,我在自己的anaconda下没有找到这个文件,最后发现没有影响,因为下面会再用msys2安装一次patch。

打开msys2.exe,安装patch,unzip,参考文章里也安装了git和grep,
git是用来拉取github上的程序的,如tensorflow,grep是为了搜索文件。安装patch和unzip是必须要做的,后续编译的时候要用到patch。git和grep可以根据自己情况看是否安装。

pacman -Syu
pacman -Su
pacman -S patch, unzip, grep

前两个命令是用来更新基本库。

好了重点来了,如果你像我一样工作环境是在内网该咋办呢?

pacman是支持离线安装本地的包的。命令如下

pacman -U 本地文件路径

我们可以在一个有网络的环境下安装msys2,然后使用pacman在线安装patch、unzip,在线安装时,pacman会将安装包下载到本地,路径如下D:\msys64\var\cache\pacman\pkg。

找到patch和unzip的安装包,在离线环境下的电脑上使用离线的方式(-U)安装即可。(-Syu和-Su在离线情况下可以不执行)

pacman -U 本地文件路径

bazel

下载bazel2.0.0,直接重名bazel.exe,我放在了D盘下的bazel文件夹中,不需要将bazel.exe放在msys2的安装目录下

vs2019

正常安装vs2019。需要记住一些安装信息,在设置环境变量的时候会用到。

1.2、系统环境变量

在系统变量的path中添加"D:\bazel",也就是bazel.exe所在的路径。

添加5个新的变量

BAZEL_SH
BAZEL_VC
BAZEL_VS
BAZEL_VC_FULL_VERSION  
BAZEL_WINSDK_VERSION

其中BAZEL_VC_FULL_VERSION的值可以在你VS的安装目录下的VC\Tools\MSVC路径下找到(Enterprise表示我安装的是企业版,不同版本不一样)。

BAZEL_VC_WINSDK_VERSION的值可以在安装VS的时候找到(参考1中有介绍),如果你之前已经安装过VS2019,可以随便打开一个项目,然后再属性页的常规选项中找到Windows SDK版本号。

二、编译tensorflow动态链接库

2.1、配置文件

这一步有两个重点的地方需要注意
1、修改 bazel 默认输出目录. 如果你的 C 盘够大, 就不需要改,个人建议一定要改,我C盘原来有20个G的空间,一开始以为不需要改,结果编译的时候直接快占满了,而且不可能一次性编译通过,多编译几次后C盘就满了,后来改到空间充足的D盘才通过。

修改方法是在.bazelrc文件的最后一行加上

startup --output_usr_root=D:/tensorflow_lib

这样就把bazel的输出目录改到了D盘下的tensorflow_lib目录下。

2、编译GPU版本时需要注意选择GPU对应的算力,下图是部分型号的算力对照表,我的GPU是2080ti,所以算力选的就是7.5,第一次编译没注意这个直接写了7.0导致编译不通过。

NDVIDIA给出的算力对照表

执行配置文件脚本

python configure.py

每一步的参数设置可以见给出的两篇参考文章。

2.2、bazel编译
编译dll

bazel build --config=opt --config=cuda //tensorflow:tensorflow_cc.dll

编译lib

bazel build --config=opt --config=cuda //tensorflow:tensorflow_cc_dll_import_lib

生成包含文件include

bazel build --config=opt --config=cuda //tensorflow:install_headers

编译dll是重头戏,几乎不可能一次通过的,尤其是离线安装的朋友。如果你也是离线安装或者在线安装的时候某些包下载老是中断请参考最后一节。

三、离线编译问题总结

3.1、离线安装包位置指定

编译dll时的主要问题是某些安装包下载不下来,tensorflow有特别多的依赖包需要下载,而我是完全离线的环境,所以需要将tensorflow依赖的包下载好后放到本地,然后将编译文件中对应的依赖包的下载路径指定到本地,这样tensorflow编译的时候就可以找到本地下载好的文件了。

参考文章1介绍可以搭建本地服务器来解决上述问题,可以百度如何在Win10上使用SSL服务。我一开始就是这样做的,后来发现可以不用搭建本地服务器,直接指定本地文件的地址即可。方法如下

首先将下载好的依赖包放在一个指定的路径下:如D:\tensorflow_install_package,里面有39个文件(没错,我是在自己的笔记本上手动下载了这39个文件)。

将文件夹路径D:\tensorflow_install_package粘贴至浏览器的搜索栏中,回车后页面中将会列出该路径下的所有文件,如下图所示

我们随便单击一个文件就可以发现该文件会被浏览器下载下来。所以该文件的本地地址就是:file:///D:/tensorflow_install_package/文件名。

接下来就是将编译文件中的对应依赖包的本地地址添加到其对应的下载列表中(注意:不要删除之前的下载路径,且本地地址要放在列表后面,否则会报错)。

我以llvm举例,该文件的下载信息在tensorflow文件夹下的worksapce.bzl中,下载列表为LLVM_URLS(其他包大都是urls字段),原来有两个下载路径,我们在有网络的环境下选择一个将llvm包下载下来,然后放到编译环境的电脑的D:\tensorflow_install_package下,将本地地址 file:///D:/tensorflow_install_package/llvm-project-387c3f74fd8efdc0be464b0e1a8033cc1eeb739c.tar.gz 添加到LLVM_URLS列表的最后。如下图所示。
请添加图片描述

在编译的过程中,如果遇到某个依赖包没有下载完成会提示该包安装失败的信息,可以知道哪个包没下载,还有这个包的下载信息的路径,根据这些信息找到对应的workspace.bzl,单独下载,然后指定本地路径即可。

依赖包的信息一般在./tensorflow/workspace.bzl和./third_party文件夹中对应的第三方包文件夹下的workspace.bzl文件中,总之如果某个包安装有问题,找到对应的workspace.bzl,按照上述操作执行即可。

3.2、几个比较特殊的问题

io_bazel_rules_docker 拉取失败
有几个依赖包的安装信息是在编译时才生成的,编译时会将依赖包下载解压到2.1节我们指定的bazel输出目录,如io_bazel_rules_docker,这个包的信息是在bazel输出目录(截图的目录是tensroflow_debug,这是我在编译debug版本的时候截的图,release版本的目录是tensorflow_lib不影响,后面的路径都是一致的。)下的26orbg4z/external/bazel_toolchains/repositorier/repositories.bzl中。这就是我在3.1节中说的,如果某个包安装失败,可以从错误信息中找到这个包的安装信息。
请添加图片描述

这个io_bazel_rules_docker是用git clone命令拉取的,但是我是离线环境下安装的,所以编译时会失败,需要单独下载后放在本地,但是还有一个问题,我们看这个包的下载信息是通过git_repository配置的,其中remote指定了该包对应的远程仓库,然后我不知道怎么指定为本地的仓库,所以干脆改成http_archive格式,如下图所示,(再次提醒空白地方要用空格,不要用tab键)
请添加图片描述
upb问题
upb的安装信息也是在bazel的输出目录中的,找到对应的下载信息即可。
请添加图片描述

注意upb的下载信息的地址是放在url字段中的,不是urls,因此需要将原来的url注释掉。
请添加图片描述
rules_java问题
前面我们说了如果某个包安装失败的话我们可以根据提示信息来找到该包的下载路径,然后修改即可,但是我在编译到rules_java时,错误信息只有该包的下载路径,没有说这个包的下载信息是在那个.bzl文件的。找遍了所有的.bzl文件都没有找到,后来实在没办法了,我就自己写了rules_java的下载信息。其中sha256是一个哈希值,是为了保证文件的一致性,可以百度一下如何查看文件的sha256值。如果sha256值不对就无法正确解压。

llvm无法完全解压
llvm是一个比较大的包,有100多M,参考文章1中也建议这个包太大最好提前下载到本地。但是在编译到这一步时我遇到了压缩文件不能解压到bazel的输出路径中的问题,后来发现是权限不够,需要用管理员权限解压。但是我怎么知道bazel时如何设置使用管理员权限解压文件的呢?

我的解决办法是在win10的命令行中使用管理员权限先把llvm压缩包解压,然后用7-Zip重新压缩回去,这样就解决权限问题了(注意压缩包的格式要还是要.tar.gz,否则会报格式错误)。

请确保你的离线环境中的解压工具是7-Zip,WinRAR的话即便解决了管理员权限的问题可能还是会遇到解压不完全的问题。

修改本地路径时,要用空格不能用tab
修改本地路径时遇到空白的地方记得要用空格,不要用tab键代替。

四、测试

如果在VS上配置tensorflow网上有很多教程,这里就不再展示。

4.1、测试代码

#include <iostream>
#include "tensorflow/core/public/session.h"
#include "tensorflow/core/platform/env.h"

using namespace std;
using namespace tensorflow;

int main()
{
    tensorflow::Tensor inp_tensor_0 = tensorflow::Tensor(DT_FLOAT, TensorShape({ 2,2 }));
    inp_tensor_0.tensor<float, 2>()(0, 0) = 1;
    inp_tensor_0.tensor<float, 2>()(0, 1) = 2;
    inp_tensor_0.tensor<float, 2>()(1, 0) = 2;
    inp_tensor_0.tensor<float, 2>()(1, 1) = 3;
    cout << inp_tensor_0.tensor<float, (2)>() << endl;
    return 0;
}

4.2、测试bug

无法打开包括文件: “google/protobuf/port_def.inc”

这个bug在参考文章2中有提到,就是将anaconda中安装的tensorflow里的google文件夹拷贝到tensorflow的include文件夹下,只不过我的路径跟参考文章2提到的不一致,我的anaconda中的google是在Lib\site-packages\tensorflow\include下。

::max有问题

原因是tensorflow中的min和max方法与<windows.h>中传统的min/max宏定义有冲突。在预处理器中添加NOMINMAX即可。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GHZhao_GIS_RS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值