Pytorch编译

以下大部分内容来源于Pytorch源码编译简明指南,并加上自己踩得一些坑。

获取源代码

编译首先是要获取源代码。

官方获取源代码是最好的方式,从Pytorch的github官网可以下载最新的代码。

记住,从官方克隆最新的代码的时候要加入recursive这个参数,因为Pytorch本身需要很多的第三方库参与编译:

1
git clone --recursive https://github.com/pytorch/pytorch

当然如果你想编译特定版本的Pytorch,需要如下操作:

1
2
3
4
git clone --recursive https://github.com/pytorch/pytorch
cd pytorch
git checkout v1.0.0
git submodule update --recursive

上面这条命令是下载并更新第三方库,我们要保证需要的第三方库都下载完毕,不然在编译过程中会中断。

目录结构

以下是Pytorch源码包展开的目录结构(只展示了主要的一些文件夹),其中主要的源码都在以下展示的文件夹中:

20190106163251

核心文件夹

核心文件夹主要是c10、aten、torch、caffe2.

为什么将c10放到最前面呢?

因为官方已经表明c10目录是最重要的源代码文件夹,也就是几乎所有的源代码都与这里的代码有关系,比如我们的类型定义,Pytorch最重要的Tensor的内存分配方式等等,都在这个文件夹中,官方也说到了,之后会慢慢将Aten中的代码移至这个文件夹,也就是说这个文件夹将包含Pytorch中最核心的代码。

Aten文件夹则包含了一些实现了Tensor的底层(和c10类似),也包括了很多的层前向代码和后向实现的代码(例如卷积层的前向和后向操作代码),包括CPU和GPU端,总之都是C++的核心操作代码。

torch文件夹也同样重要,其中主要包含了一些稍微高层些的操作函数,例如torch.ones等,有C++和Python端,也包括了Python核心代码和包装代码,如果我们使用python版Pytorch的话,与这些代码接触就比较密切了。

Caffe2则不用多说,caffe2则主要针对移动端设计了很多优化后的运算代码,模型融合、模型量化等等的代码,其后端有QNNPACK等一些针对移动端的底层运算库(有开发人员说GLOW也在caffe2后端考虑之内)。

third_party

Pytorch毕竟是大型的深度学习库,所以需要的依赖库也是有很多的,其中有很多我们耳熟能详的数值计算库(eigen、gemmlowp)、模型转换库(onnx、onnx-tensorrt)、并行训练库(gloo、nccl)、自家的底层端实现库(QNNPACK)以及绑定python端的pybind11等一系列所依赖的库。

当然还有很多库这里就不一一介绍了,总之,我们在编译的时候,Pytorch的编译代码会根据我们的设置在编译的时候,自动判断当前系统中是否存在需要的第三方库。如果不存在则使用这里的第三方库(直接编译并使用第三方库的diamante),这也是为什么我们需要执行git submodule update --init --recursive来下载所依赖第三库源码的原因。

tools

tools这个文件夹中的内容到底是做什么的,简单看一下官方的介绍:

1
2
3
This folder contains a number of scripts which are used as
part of the PyTorch build process. This directory also doubles
as a Python module hierarchy (thus the `__init__.py`).

其中包含了一些脚本生成代码工具(利用python)、用于编译一些组件的脚本和代码,还有一些开发人员需要的工具、以及AMD显卡帮助编译代码和一些特殊情况需要使用的工具等。在我们编译Pytorch源码的过程中会使用到这个文件夹中的代码。

有一点需要说明,那就是Pytorch利用了很多的代码生成,例如操作层函数的头文件NativeFunction.h等,所以tools中的代码生成脚本还是比较重要的。

提一个可能会使用到的脚本build_pytorch_libs.sh,这个脚本是用来编译libtorch库的,libtorch就是不需要python包装的使用C++的Pytorch库,方便于部署阶段使用。

关于libtorch的具体介绍和简单使用可以看这里:利用Pytorch的C++前端(libtorch)读取预训练权重并进行预测

关于tools中的文件就不具体介绍了,大家可以看一下其中的readme

其他的文件夹就不多说了,相对上面的来说并不是很重要。

编译

编译重头戏来了,编译过程中大家可能会遇到各种各样的问题,但是其实只要我们将环境准备妥当,大部分都可以一次性编译好的:

  • 确保你的cuda和cudnn安装正确,环境变量都设置正确
  • 确保你的python环境纯净,最好使用anaconda创建一个环境,安装好pytorch需要的依赖包。
    https://github.com/pytorch/pytorch#from-source
  • 确保你的C++编译器的版本不要太低,最好4.9以及以上

若在原来的Python环境下编译,可能出现一些奇怪的错误,例如因为我之前有Pytorch基于cuda10.2的,现在我在cuda9.0下编译出来的pytorch,也去调用cuda10.2了,更改环境变量也没有用。

编译选项

python的安装方式并不是单独利用Cmake进行构建的,而是结合了python端的setuptools搭配cmake进行构建,pytorch的项目还是比较庞大的,所以编译代码也是老长,我们主要看看编译过程中的环境变量即可:

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
# Environment variables you are probably interested in:
#
# DEBUG
# build with -O0 and -g (debug symbols)
#
# REL_WITH_DEB_INFO
# build with optimizations and -g (debug symbols)
#
# MAX_JOBS
# maximum number of compile jobs we should use to compile your code
#
# NO_CUDA
# disables CUDA build
#
# ....
# ....
#
# Environment variables for feature toggles:
#
# NO_CUDNN
# disables the cuDNN build
#
# NO_FBGEMM
# disables the FBGEMM build
#
# NO_TEST
# disables the test build
#
# NO_MIOPEN
# disables the MIOpen build

这些编译变量根据我们的需要在执行python setup.py install使用,如果你不想编译CUDA,则NO_CUDA=1 python setup.py install。另外,在编译之前,我们需要安装一些Python库,如numpy,否则编译出来的Pytorch就没法使用numpy和Pytorch的接口。完整过程如下:

1
2
pip install numpy pyyaml mkl mkl-include setuptools cmake cffi typing
python setup.py install

执行以上语句我们就可以进行编译了。

编译出现的问题1

编译Pytorch1.0.0时,若编译过程中遇到RuntimeError: Source files: ['Type.h', 'Tensor.h', 'TensorMethods.h'] did not match generated files.。这个问题是编译中产生的,原因是PyTorch1.0.0以后的版本aten/src/ATen/gen.py中的第42行加入如下图的部分,也就是会创建一个core_tmp的文件夹,但是由于某种原因,Tensor.h, TensorMethods.h并没有被拷贝过去,所以我们显式的设置GEN_TO_SOURCE就可以避免这个问题。

1
2
3
4
export GEN_TO_SOURCE=/你的路径/pytorch/aten/src/ATen/core/ # 这个路径的设置也非常重要,如果不设置的话,系统会生成一个core_tmp/,会找不到文件需要链接的头文件导致编译失败~.

# 例如
export GEN_TO_SOURCE=/data2/zhaodali/tmp/pytorch/aten/src/ATen/core/

编译出现的问题2

对于Pytorch0.4.1或者Pytorch0.5.0的版本中,caffe2要单独编译的。也就是在执行python setup.py install之后(两者顺序不能调到),还需要执行一下python setup_caffe.py install。caffe2依赖的Python包为numpy,future,hypothesis,protobuf另外,对于Python3.7还需要更改vim pytorch/caffe2/python/pybind_state.h 的第二百行从str = PyUnicode_AsUTF8AndSize(input[i], &strSize);改为str = const_cast<char*>(PyUnicode_AsUTF8AndSize(input[i], &strSize));

在gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)编译成功了caffe2和pytorch0.5.0

编译出现的问题3

在下拉Pytorch0.4.1的第三方库的时候,出现了错误:fatal: repository 'https://github.com/NervanaSystems/nervanagpu.git/' not found,则执行下面语句:

1
2
3
4
5
从 .gitmodules中删除nervanagpu
git clean -xdf
git submodule deinit -f .
git submodule sync
git submodule update --init --recursive

ninja

ninja可以大大加快编译速度,而且在编译过程中提示的错误信息更加完整和详细,如果我们想使用ninja来编译,那么直接在当前的python环境中pip install ninja即可。

Pytorch的安装程序会自动查找当前环境中是否有ninja,如果有的话,则优先使用ninja进行编译。

不同的安装模式

只安装libtorch库:创建build文件夹,在里头执行python ../tools/build_libtorch.py

开发者模式:python setup.py build develop(对Python开发有帮助)

安装后的自我检验

默认我们安装Pytorch的时候会自带上caffe2(当然也可以选择不安装caffe2,但是运行某些特定卷积操作时会报错~),因此我们在安装成功后不仅要检测Pytorch是否安装成功,同时也要检查caffe2是否安装成功。

Pytorch

检查Pytorch安装是否成功:

1
2
3
4
5
6
7
8
9
10
11
import torch
print(torch.cuda.is_available())
print(torch.backends.cudnn.is_acceptable(torch.cuda.FloatTensor(1)))
print(torch.backends.cudnn.version())
print(torch.version.cuda)

# 输出
True # 出现Turn说明cuda正常
Ture # 出现Ture说明cudnn正常
7301 # 这是我的版本号
9.0.176

如果遇到报错ModuleNotFoundError: No module named 'torch._C',则大部分情况是因为当前在pytorch源码文件夹下编译的,考虑使用cd ..退出该文件夹,再进行验证。

caffe2

在安装caffe2的环境下运行python并导入caffe2.python,如果顺利加载则证明安装成功。如果不能成功import的话,可以先看看错误信息,很有可能是一些小错误,例如:

1
2
3
4
5
6
7
8
9
10
11
Python 3.6.6 |Anaconda, Inc.| (default, Oct  9 2018, 12:34:16) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from caffe2.python import core
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/prototype/anaconda3/envs/pytorch-dev/lib/python3.6/site-packages/caffe2/python/__init__.py", line 2, in <module>
from caffe2.proto import caffe2_pb2
File "/home/prototype/anaconda3/envs/pytorch-dev/lib/python3.6/site-packages/caffe2/proto/caffe2_pb2.py", line 6, in <module>
from google.protobuf.internal import enum_type_wrapper
ModuleNotFoundError: No module named 'google'

上方的导入出错是因为当期的虚拟环境没有安装protobuf,简单执行命令pip install protobuf即可,其他类似错误根据错误提示信息安装相应的库就可以了。

如果成功import则不会出现报错信息:

1
2
3
4
5
Python 3.6.6 |Anaconda, Inc.| (default, Oct  9 2018, 12:34:16) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from caffe2.python import core
>>>

如何卸载

如果是源码安装的Pytorch,卸载需要执行:

1
2
pip uninstall torch
python setup.py clean

参考

Pytorch源码编译简明指南
如何查看pytorch对应cuda的版本
源码编译pytorch 1.0 遇到的问题
PyTorch學習筆記(16)——編寫你自己的PyTorch kernel(基於PyTorch1.2.0)
Fix the build break for python3.7 PyUnicode_AsUTF8AndSize() prototype changing #9259
NervanaSystems/nervanagpu Repository not found for 0.4.0 #20845

------ 本文结束------
坚持原创技术分享,您的支持将鼓励我继续创作!

欢迎关注我的其它发布渠道