

aten: A Tensor Library的缩写。与Tensor相关的内容都放在这个目录下。如Tensor的定义、存储、Tensor间的操作(即算子/OP)等

可以看到在aten/src/Aten目录下,算子实现都在native/目录中。其中有CPU的算子实现,以及CUDA的算子实现(cuda/)等
torch: 即PyTorch的前端代码。我们用户在import torch时实际引入的是这个目录。
其中包括前端的Python文件,也包括高性能的c++底层实现(csrc/)。为实现Python和c++模块的打通,这里使用了pybind作为胶水。在python中使用torch._C.[name]实际调用的就是libtorch.so中的c++实现,而PyTorch在前端将其进一步封装为python函数供用户调用
c10、caffe2:移植caffe后端,c10指的是caffe tensor library,相当于caffe的aten。
PyTorch1.0完整移植了caffe2的源码,将两个项目进行了合并。引入caffe的原因是Pytorch本身拥有良好的前端,caffe2拥有良好的后端,二者在开发过程中拥有大量共享代码和库。简而言之,caffe2是一个c++代码,实现了各种设备后端逻辑
tools:用于代码自动生成(codegen),例如autograd根据配置文件实现反向求导OP的映射。
scripts:一些脚本,用于不同平台项目构建或其他功能性脚本
PyTorch 中,前端指的是 PyTorch 的 Python 接口,
后端指的是 PyTorch 的底层 C++ 引擎,它负责执行前端指定的计算
后端引擎也负责与底层平台(如 GPU 和 CPU)进行交互,并将计算转换为底层平台能够执行的指令


编译后的torch前端接口没有csrc后端接口,该部分c++内容(csrc目录)并没有被复制过来,而是以编译好的动态库文件(_C.cpython-*.so)
import torch
torch.Tensor


结论是对应实现在 torch/csrc/autograd/python_variable.cpp中,而这个是通过编译后的so包实现_C调用,因为pyi是一个python存根文件,只有定义没有实现,实现都在python_variable.cpp中



一个主流的训练框架需要有两大特征:
实现类似numpy的张量计算,可以使用GPU进行加速;
实现带自动微分系统的深度神经网络
import tensorflow as tf
a = tf.ones(5)
b = tf.ones(5)
c = a + b
sess = tf.Session()
print(sess.run(c))
import tensorflow as tf
a = tf.ones(5)
b = tf.ones(5)
c = a + b
print(c) # tf.Tensor([2. 2. 2. 2. 2.], shape=(5,), dtype=float32)
import torch
a = torch.ones(5)
b = torch.ones(5)
c = a + b
print(c) # tensor([2., 2., 2., 2., 2.])

小结训练框架最重要的特点是:
针对以上问题,提出3个问题:
import torch
a = torch.randn(5, 5)
b = torch.randn(5, 5)
c = a.add(b)
print(c.device) # cpu
a = a.to("cuda")
b = b.to("cuda")
c = a.add(b)
print(c.device) # cuda
https://pytorch.org/tutorials/advanced/dispatcher.html

aten/src/ATen/core/NamedRegistrations.cpp 算子注册机制
例如m.impl()中就是对dispatch key为CPU时neg算子的实现绑定,其绑定了neg_cpu()这个函数
大多数情况我们只需要实现m.impl,并绑定一个实现函数即可
除了m.def以及m.impl之外,还有m.fallback作为回退
在没有m.impl实现的情况下,默认回退的实现(例如fallback回cpu实现)。这样我们将不需要对cuda实现100%的算子实现,而是优先实现高优先级的算子,减少新设备情况下的开发量,而未被实现的算子则默认被fallback实现
实现一个定义算子add覆盖原始add算子(todo)
算子配置文件native_functions.yaml
PyTorch中采用了算子配置文件aten/src/ATen/native/native_functions.yaml,配合codegen模块自动完成整个流程
也就是多有的自动注册流程会基于当前这个yaml配置文件自动生成算子注册方法与python bind实现

以dot算子为例


pytorch/build/aten/src/ATen/RegisterCPU.cpp torch/csrc/autograd/generated/python_variable_methods.cppdispatch实际是前向算子
类似的也有反向算子,配置文件derivatives.yaml,其位于tools/autograd/derivatives.yaml

可以看到,每一个算子以“- name:”开头。
然后还包含一个result字段,这个字段其实就是这个算子的求导公式
前向算子会利用codegen自动生成注册部分的代码。同理,反向算子也可以根据算子微分注册表自动生成dispatch注册,然后被绑定到Python的函数中
有关梯度计算请参考

import torch
a = torch.ones(5, 5)
b = torch.ones(5, 5)
c = a + b
print(c)
实际上在每执行一条python代码时,前向传播的算子都会被实时调用执行
在用户调用某算子(例如dot时),其实调用的是Tensor下的dot()函数实现。其具体实现在c++中,经过pybind和dispatch(选择设备)机制后定位带at::native::dot()函数。而后对于CPU来说,可以调用intel MKL库的mkldnn_matmul()实现
import torch
a = torch.ones(5, 5, requires_grad=True)
b = torch.ones(5, 5, requires_grad=True)
c = (a + b).sum()
c.backward()
print(c)
print(c.grad_fn)
# tensor(50., grad_fn=)
#
在执行loss.backward()时,实际调用执行的是各中间tensor的grad_fn,由于反向计算时会组成一个由grad_fn为节点,next_functions为边的反向图,因此如何高效执行这个图成为一个问题
为了解决这个问题,引入了根据设备数建立的线程池调度引擎

# 拉取依赖
git clone --recursive https://github.com/pytorch/pytorch
cd pytorch
# if you are updating an existing checkout
git submodule sync
git submodule update --init --recursive
# 编包
export CMAKE_PREFIX_PATH=${CONDA_PREFIX:-"$(dirname $(which conda))/../"}
python setup.py build --cmake-only
ccmake build # or cmake-gui build