前言
编译tensorflow遇到的bug本来就多,在Windows平台上bugs更是加大力度。明明官方教程中在配置完环境后只需执行两行bazel命令,第一行命令却产生不少error。笔者踩了不少坑后,总结出了一些解决方法形成此教程。
1. 配置编译环境
1.1 选择合适的编译配置
Tensorflow源码编译bug多一直被广大用户诟病,不同版本对应不同的编译器,gpu版本还需要安装对应的cuda和cudnn库。因此笔者强烈建议先到官网查看编译配置,具体点击下面链接:Build from source on Windows(https://tensorflow.google.cn/install/source_windows)下面列出前5个官方已经测试过的配置:
CPU
图1.1 tensorflow-cpu的编译配置
GPU
1.2 配置编译环境
- 准备源码。首先直接到github下载tensorflow源码(https://github.com/tensorflow/tensorflow/releases),或者直接git clone后checkout到对应的版本。这里笔者使用了1.14.0-rc1(https://github.com/tensorflow/tensorflow/releases/tag/v1.14.0-rc1),后面的配置也将按照对应的版本来进行,读者注意按需下载。
- 安装msvc编译器。Windows平台下需要安装msvc2017,如果已经安装了VS2019,则需要再补充安装2017的编译工具,在此不做赘述。
- 安装Bazel。Bazel的安装依赖的工具较多,需要VC++、MSYS2、JDK,具体参考Installing Bazel on Windows(https://docs.bazel.build/versions/master/install-windows.html)。配置好依赖后,直接下载Bazel Binary(https://github.com/bazelbuild/bazel/releases)即可,并将Bazel所在的文件夹路径添加到环境变量Path中。注:MSYS2安装好后,要按照官方的说明安装几个库。
- 安装Python。笔者使用的是Anaconda3.5省去其他计算库和pip3的安装。
- * 安装CUDA。如果想编译gpu版本,则需要再加装cuda9.0(https://developer.nvidia.com/cuda-toolkit-archive)和cudnn7.0(https://developer.nvidia.com/rdp/cudnn-archive)。
下面列出笔者所使用的编译配置
软件 | 版本 |
Tensorflow源码 | 1.14.0-rc1 |
Visual Studio | 2019加装msvc2017 |
Bazel | 0.25.0 |
msys2 | x86_64-20190524 |
JDK | 1.8.0_171 |
Python | 3.5.2Anaconda custom (64-bit) |
cuda | 9.0.176_win10 |
cudnn | 9.0-windows10-x64-v7.6.2.24 |
从表中也可以看到,使用了即使用cudnn7.6配合cuda9.0笔者也编译成功了。
02
入门Bazel
在开始编译之前,有必要先了解Bazel的基本操作,出现error时才能有一个大概判断。笔者就曾到stackoverflow上问了两个stupid问题。在此笔者只讲Bazel的几个主要的操作或概念。
2.1 修改编译缓存路径
Tensorflow源码的编译过程产生了大量的缓存,可能占用25G以上的存储空间,而缓存路径默认在C盘。笔者第一次遇到C盘空间不足时,把C盘的Matlab搬出去。折腾几次后老老实实到查看官方文档,才知道可以用<--outputuserroot>指定缓存目录,不过编译过程依然在C盘产生了一些文件占用了几个G。详情可参考Output Directory Layout(https://docs.bazel.build/versions/master/output_directories.html)
2.2 何谓BUILD文件
BUILD中定义了一些编译的目标,包括其名称、头文件、源文件和依赖库等。在tensorflow-1.14.0-rc0/tensorflow/python/BUILD中定义了大量编译目标,各种算子等等的shared object。比如下面这个
cc_library( name = "numpy_lib", srcs = ["lib/core/numpy.cc"], hdrs = ["lib/core/numpy.h"], deps = [ "//tensorflow/core:framework", "//tensorflow/core:lib", "//third_party/py/numpy:headers", "//third_party/python_runtime:headers", ], )
编译第三方库numpy。
2.3 如何定义一个编译目标
用cc_binary、cc_library 或 cc_import可以编译可执行程序、共享库或导入依赖库。值得一提的是,也可以按需要编译动态链接库脱离Tensorflow源码池建立工程。现在假设有如下文件目录:
└──project0 ├── main │ ├── BUILD │ ├── hello-world.cc │ ├── hello-greet.cc │ └── hello-greet.h ├── lib │ ├── BUILD │ ├── hello-time.cc │ └── hello-time.h ├── dll │ ├── third_party.dll │ ├── third_party.lib │ └── third_party.h └── WORKSPACE
WORKSPACE用来指定代码根目录。其中BUILD的定义分别为
# main/BUILD cc_library( name = "hello-greet", srcs = ["hello-greet.cc"], hdrs = ["hello-greet.h"], ) cc_binary( name = "hello-world", srcs = ["hello-world.cc"], deps = [ ":hello-greet", "//lib:hello-time", ], ) # /lib/BUILD cc_import( name = "third_party", hdrs = ["third_party.h"], interface_library = "//dll:third_party.lib", shared_library = "//dll:third_party.dll", ) cc_library( name = "hello-time", srcs = ["hello-time.cc"], hdrs = ["hello-time.h"], deps = [":third_party",], visibility = ["//main:__pkg__"], )
基本的属性有:
属性 | 作用 |
name | 定义目标的唯一标识符或者名称 |
srcs | 指定源文件 |
hdrs | 指定头文件 |
deps | 指定依赖库 |
visibility | 可用来设定对其他目标的可见性,不常用 |
用一个图简单说明上面四个目标的关系:
接着打开命令行,定位到WORKSPACE文件所在目录,执行
bazel build //main:hello-world
编译完成后在/bazel-bin/main/hello-world找到编译完成的可执行文件。详情可参考Introduction to Bazel: Building a C++ Project(https://docs.bazel.build/versions/master/tutorial/cpp.html),C / C++ Rules(https://docs.bazel.build/versions/master/be/c-cpp.html)。