3.开始编译
完成了上面的内容后,下面才真正开始编译源码。第三节只讲编译操作,编译错误的处理在第四节。
3.1 项目编译配置
命令行定位到/tensorflow-1.14.0-rc0/,执行
python configure.py
这个脚本会询问诸如使用哪个路径下的Python、是否要支持cuda、用哪个版本的cuda、是否要支持xla等,读者可根据需要做配置。脚本正常运行后,目录中生成.tf_configure.bazelrc
文件,保存了刚才的配置,比如这样:
build --action_env PYTHON_BIN_PATH="D:/Python/Anaconda3/python.exe" build --action_env PYTHON_LIB_PATH="D:/Python/Anaconda3/lib/site-packages" build --python_path="D:/Python/Anaconda3/python.exe" build:xla --define with_xla_support=true build --action_env TF_NEED_OPENCL_SYCL="0" build --action_env TF_NEED_ROCM="0" build --action_env TF_NEED_CUDA="1" build --action_env TF_NEED_TENSORRT="0" build --action_env CUDA_TOOLKIT_PATH="C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v9.0" build --action_env TF_CUDA_COMPUTE_CAPABILITIES="3.5,7.0" build --action_env TF_CUDA_CLANG="0" build --config=cuda build:opt --copt=/arch:AVX build:opt --define with_default_optimizations=true build --config monolithic build --copt=-w --host_copt=-w build --copt=-DWIN32_LEAN_AND_MEAN --host_copt=-DWIN32_LEAN_AND_MEAN --copt=-DNOGDI --host_copt=-DNOGDI build --verbose_failures build --distinct_host_configuration=false build:v2 --define=tf_api_version=2 test --flaky_test_attempts=3 test --test_size_filters=small,medium test --test_tag_filters=-benchmark-test,-no_oss,-oss_serial test --build_tag_filters=-benchmark-test,-no_oss test --test_tag_filters=-no_windows,-gpu test --build_tag_filters=-no_windows,-gpu build --action_env TF_CONFIGURE_IOS="0"
3.2 编译whl安装包生成器
在生成tensorflow-python的whl安装包之前,首先要编译其生成器。使用下面的命令:
- CPU版本tensorflow
bazel build --config=opt --output_user_root=f:mpazel //tensorflow/tools/pip_package:build_pip_package
- GPU版本tensorflow
bazel build --config=opt --config=cuda --output_user_root=f:mpazel --define=no_tensorflow_py_deps=true//tensorflow/tools/pip_package:build_pip_package
编译时,CPU和内存占用可能达到100%,注意电脑散热。此外,如果RAM少于16GB,编译时Win10的UI可能直接卡主,可以在build命令添加--local_ram_resources=2048
限制内存使用。而根据CPU主频越高和核数越多,编译耗时越短,笔者的笔记本(i7@2.6Ghz x4)花了十几个小时...... 编译完成后,在tensorflow-1.14.0-rc0/bazel-bin/tensorflow/tools/pip_package
中,可以找到build_pip_package.exe
,它就是用来生成whl安装包的工具。
3.3 生成whl安装包并安装和试用
利用千辛万苦得到的build_pip_package.exe
生成安装包:
bazel-binensorflowoolspip_packageuild_pip_package f:/tmp/tensorflow_pkg
安装tensorflow-python:
pip3 install f:/tmp/tensorflow_pkg/tensorflow-1.14.0rc0-cp35-cp35m-win_amd64.whl
成功安装后,怀着激动的心情,做个乘法哈哈
C:Usersme>ipython Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul 5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)] Type 'copyright', 'credits' or 'license' for more information IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: import tensorflow as tf In [2]: sess = tf.InteractiveSession() In [3]: a = tf.ones([10000,10000]) In [4]: b = 2*tf.ones([10000,10000]) In [5]: c = a*b In [6]: c.eval() Out[6]: array([[2., 2., 2., ..., 2., 2., 2.], [2., 2., 2., ..., 2., 2., 2.], [2., 2., 2., ..., 2., 2., 2.], ..., [2., 2., 2., ..., 2., 2., 2.], [2., 2., 2., ..., 2., 2., 2.], [2., 2., 2., ..., 2., 2., 2.]], dtype=float32)
4. 解决各种可能的编译错误
然而实际上编译过程并不是两句cmd命令就可以完成的orz。不知道为什么谷歌一个大公司,把编译搞得这么痛苦。下面是一位暴躁老哥在github issue的吐槽,他用了“What a pain in the ass.”
如果遇到的问题本文找不到,可以到官网找找解决思路Build and install error messages(https://tensorflow.google.cn/install/errors)。
4.1 error回顾
虽然中断编译,只要不bazel clean
,都不会编译from scratch,但是依然有些文件重新编译,非常耗时。笔者花了三天,断断续续才编译完,有个别算子不知为何编译时间需要两三个小时以上。相比于CPU版本的编译,GPU版本的error多了不少。但是为了速度,当然要编译GPU版本啦。查看报错时,有两点要注意:
1) 如果error信息中出现中文乱码,需要设置cmd的编码;
2) 如果出现(??????),再运行一次编译命令,可能看到(Permisson Denied),此时重启计算机可继续编译。
4.1.1 error C2118: 负下标
笔者编译时遇到的第一个error来自azel-tensorflow-1.14.0-rc0externalgrpcsrccoresialtshandshakerhandshaker.pb.c(118):error C2118:负下标
不论如何,先打开问题文件看看:
C:Usersme>ipython Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul 5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)] Type 'copyright', 'credits' or 'license' for more information IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: import tensorflow as tf In [2]: sess = tf.InteractiveSession() In [3]: a = tf.ones([10000,10000]) In [4]: b = 2*tf.ones([10000,10000]) In [5]: c = a*b In [6]: c.eval() Out[6]: array([[2., 2., 2., ..., 2., 2., 2.], [2., 2., 2., ..., 2., 2., 2.], [2., 2., 2., ..., 2., 2., 2.], ..., [2., 2., 2., ..., 2., 2., 2.], [2., 2., 2., ..., 2., 2., 2.], [2., 2., 2., ..., 2., 2., 2.]], dtype=float32)
看到这么长一个assert,着实吓了一跳。还好谷歌在注释里说了此处出现error,这需要定义PB_FIELD_16BIT
以处理数值超过八位的问题,那么负下标也就可以理解了。那么我们只需按照注释说的,在handshaker.pb.h
定义PB_FIELD_16BIT
即可:
#define PB_FIELD_16BIT 65536
4.1.2 找不到area.h
估苟了一下,发现是缺少c-ares库( https://c-ares.haxx.se ),下载解压,把
ares.h、ares_build.h、are_rules.h、ares_version.h
添加到MSVC的包含目录里C:ProgramFiles(x86)MicrosoftVisualStudio14.0VCinclude
,然后继续编译。
4.1.3 LNK2019
仔细观察所有出链接错误的函数名末尾都有_ares,推测是缺少c-ares的DLL。上一个问题里我们仅仅添加了头文件,因此最后链接时找不到库。我们需要自己安装这个库。根据该库的README.msvc
,我们需要打开命令行定位到C:ProgramFiles(x86)MicrosoftVisualStudio14.0VCin
利用文件夹中的nmake编译c-ares源码,假设c-ares路径为f:c-ares,执行以下命令:
nmake -f f:c-aresMakefile.msvc
编译完成的动态链接库及三个例程在f:c-aresmsvc
中,我们只需要msvccaresdll-release
里的库。在我讲库文件加入到MSVC的目录中后,依然报错。在查看了Bazel文档后发现可以在BUILD中,利用上文第二节提到的cc_import()
手动导入库。我们先将c-ares库拷贝到tensorflow-1.14.0-rc0ensorflowpython
,接着在tensorflowpythonBUILD
的4588行,tf_py_wrap_cc
前手动导入cares库:
cc_import( name = "mycares", hdrs = ["myares/ares.h"], interface_library = "myares/msvc/cares/dll-release/cares.lib", shared_library = "myares/msvc/cares/dll-release/cares.dll", )
再次编译不再出现LNK2019,然后迎接新error。
4.1.4 找不到cudart64_.dll
很明显这是cuda库的dll,奇怪的是cudart64_.dll
末尾的下划线,一般来说下划线之后都会接上版本号之类的符号。笔者用的是cuda9.0,那么本应该是cudart64_90.dll
,故猜测是没有给Bazel指定cuda版本。一番估苟之后,看到一位老哥说给Bazel两个flag指定cuda和cudnn。在3.1节里提到的.tf_configure.bazelrc
文件里保存了编译配置,将两个flag加进去:
#tensorflow-1.14.0-rc0/.tf_configure.bazelrc ...... build --action_env TF_CUDNN_VERSION=7 build --action_env TF_CUDA_VERSION=9.0
继续编译不再找不到,然后迎接新error。
4.1.5 DLL load failed: 找不到指定的模块
这次同样是找不到库,但是没有说是那个库[汗]。仔细观察Traceback:
ImportError: Traceback (most recent call last): File "?C:UsersstyzcAppDataLocalTempBazel.runfiles_t3w1g11q unfilesorg_tensorflowensorflowpythonpywrap_tensorflow.py", line 58, in <module> from tensorflow.python.pywrap_tensorflow_internal import * File "?C:UsersstyzcAppDataLocalTempBazel.runfiles_t3w1g11q unfilesorg_tensorflowensorflowpythonpywrap_tensorflow_internal.py", line 28, in <module> _pywrap_tensorflow_internal = swig_import_helper() File "?C:UsersstyzcAppDataLocalTempBazel.runfiles_t3w1g11q unfilesorg_tensorflowensorflowpythonpywrap_tensorflow_internal.py", line 24, in swig_import_helper _mod = imp.load_module('_pywrap_tensorflow_internal', fp, pathname, description) File "D:PythonAnaconda3libimp.py", line 242, in load_module return load_dynamic(name, filename, file) File "D:PythonAnaconda3libimp.py", line 342, in load_dynamic return _load(spec) ImportError: DLL load failed: 找不到指定的模块。
发现问题来自Python脚本的import语句,加载_pywrap_tensorflow_internal
库失败。一开始笔者以为是相对路径有问题导致脚本找不到库,直接把库拷贝到pywrap_tensorflow_internal.py
所在文件夹和WORKSPACE文件夹,但都没有用。又尝试指定绝对路径加载也失败。尝试多次后,猜测不一定是_pywrap_tensorflow_internal
库找不到,而是该库的依赖库找不到。因此笔者用DependencyWalker
查看其依赖库,发现有不少缺失的库。但是,有一个库比较瞩目,它就是4.1.3里我们自己编译的c-ares库
这就可以理解了,之前我们只在BUILD中指定了绝对路径导入了该库,而现在Python脚本从环境变量里的路径是找不到该库的。因此手动将cares.dll拷贝到其中一个环境变量路径里,比如c:Windows
,等待了几分钟之后,编译完成。再安装3.3的步骤就就可以安装tensorlfow-python了。