前言
最近在浅尝Pytorch的源码,利用业余时间去品读品读,看着看着,第一次对Pytorch有了重新的认识。
原来现在Pytorch的版图是如此之大,Pytorch已经不是一年前的Pytorch了。
在集成了Caffe2之后,Pytorch已经开始变成了庞然大物,涉及到的东西也远远不是之前的caffe可以比较了,总之,Pytorch有很多很多值得我们学习的地方,这篇文章简单讲讲编译Pytorch的一些额外知识,让我们自己亲手编译Pytorch的时候不是盲目去编译,而是有目的有选择的来。
之前所写的相关文章,也可以作为参考:
- Pytorch-0.4.1-cuda9.1-linux源码安装指南
- 深度学习-在ubuntu16.04安装CUDA9.1-总结(问题完全解决方案)
- pytorch-0.2成功调用GPU:ubuntu16.04,Nvidia驱动安装以及最新cuda9.0与cudnnV7.0配置
Pytorch的生态:
其中有Pytorch自家的库也有一块合作的,可以看出FaceBook的野心挺大,但对于我们来说究竟是好是坏呢,总之希望FB抽出更多人力好好打磨Pytorch吧。
获取源代码
编译首先是要获取源代码。
从官方获取源代码是最好的方式,从Pytorch的github官网可以下载最新的代码。
记住,从官方克隆最新的代码的时候要加入recursive
这个参数,因为Pytorch本身需要很多的第三方库参与编译:
git clone --recursive https://github.com/pytorch/pytorch
当然也可以从release界面却下载之前的稳定版本源代码。如果我们是直接下载官方提供的源码包或者正式的release包,那么我们需要在下载之后执行
代码语言:javascript复制git submodule update --init --recursive
上面这条命令是下载并更新第三方库,我们要保证需要的第三方库都下载完毕,不然在编译过程中会中断。
目录结构
以下是Pytorch源码包展开的目录结构(只展示了主要的一些文件夹),其中主要的源码都在以下展示的文件夹中:
其中使用红箭头标注的就是几个比较重要的核心库。下面简单介绍一下:
核心文件夹
核心文件夹主要是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这个文件夹中的内容到底是做什么的,简单看一下官方的介绍:
代码语言:javascript复制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的安装方式并不是单独利用Cmake进行构建的,而是结合了python端的setuptools搭配cmake进行构建,pytorch的项目还是比较庞大的,所以编译代码也是老长,我们主要看看编译过程中的环境变量即可:
代码语言:javascript复制# 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
.
执行以上语句我们就可以进行编译了。
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安装是否成功:
代码语言:javascript复制>>> import torch
>>> print(torch.cuda.is_available())
>>> print(torch.backends.cudnn.is_acceptable(torch.cuda.FloatTensor(1)))
... print(torch.backends.cudnn.version())```
True # 出现Turn说明cuda正常
Ture # 出现Ture说明cudnn正常
7401 # 这是我的版本号
caffe2
在安装caffe2的环境下运行python并导入caffe2.python
,如果顺利加载则证明安装成功。如果不能成功import的话,可以先看看错误信息,很有可能是一些小错误,例如:
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
则不会出现报错信息:
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,卸载需要执行:
代码语言:javascript复制pip uninstall torch
python setup.py clean
常见问题
ninja
如果遇到找不到build.ninja的问题,可以尝试重新安装ninja,或者不使用ninja即可。
没有相应的第三方库
如果你的cmake报告以下的错误:
代码语言:javascript复制CMake Error at aten/src/ATen/CMakeLists.txt:225 (add_subdirectory):
The source directory
/home/prototype/Downloads/pytorch/third_party/sleef
does not contain a CMakeLists.txt file.
CMake Error at aten/src/ATen/CMakeLists.txt:226 (set_property):
set_property could not find TARGET sleef. Perhaps it has not yet been
created.
说明编译源码所需要的第三方库没有下载好,这时候需要执行git submodule update --init --recursive
来更新第三方库源,下载好我们的第三方库即可。
授人以鱼不如授人以渔,最好的查询编译问题的地址还是GITHUB的issue界面,将你遇到的问题粘贴过去查询即可,基本你遇到其他人也遇到的问题都在这里了。
参考链接
https://pytorch.org/blog/a-tour-of-pytorch-internals-1/
https://github.com/pytorch/pytorch/blob/master/CONTRIBUTING.md