乐于分享
好东西不私藏

近期人肉学PyTorch源码的人肉总结

近期人肉学PyTorch源码的人肉总结

PyTorch是什么?

PyTorch 是一种用于构建深度学习模型的功能完备框架,是一种通常用于图像识别和语言处理等应用程序的机器学习。使用 Python 编写,因此对于大多数机器学习开发者而言,学习和使用起来相对简单。PyTorch 的独特之处在于,它完全支持 GPU,并且使用反向模式自动微分技术,因此可以动态修改计算图形。这使其成为快速实验和原型设计的常用选择。

https://www.nvidia.cn/glossary/pytorch/
Matt White在PyTorch Day India 2026的Keynote Speech上说,PyTorch已经成为人工智能领域训练以及推理生态的核心[1]。
还是从小例程出发
      1 import torch      2 import torch.nn as nn      3 import torch.optim as optim      4       5 # ----------------------------      6 # 1. Create training data      7 # ----------------------------      8 # Input: [x, y]      9 # Target: z = x^2 + y^3     10 torch.manual_seed(0)     11      12 X = torch.randn(100002)              # 1000 samples, (x, y)     13 y = X[:, 0]**2 + X[:, 1]**3           # true function

在以上的PyTorch例程中,第 10 行通过 torch.manual_seed(0) 设置随机种子为 0,以确保生成的随机数可复现;

第 12 行用 torch.randn(10000, 2) 生成一个 10000×2 的张量 X,每行是一个样本的输入特征 (x, y),元素服从标准正态分布;

第 13 行通过 y = X[:, 0]**2 + X[:, 1]**3 计算每个样本的目标值 z,即第一个特征平方加上第二个特征立方,从而得到训练数据的输入 X 和输出 y,为后续模型训练提供基础。

第13行张量乘法的C++实现
我首先启动gdb对python的调试(因为PyTorch在python上运行)

gdb Python-3.13.2/build/python 

然后设置断点

(gdb)break PowKernel.cpp:67

然后运行这个脚本:

从上图可以看出,张量乘法的具体实现是在(如果用CPU计算)PowKernel.cpp中计算的,如果幂是3,那么就把张量的每个分量base,进行base*base*base运算(base ^ 3)

同时从上图的第51个frame,可以看出,python解释器运行的字节码的地址是func = 0x7fffd4847050

而通过gdb运行的第55个frame,我们可以看出python进行名字查找的过程:
也就是第2620行从python的字典里查找”__pow__”对应的字节码。
“__pow__”是什么时候加到Python的字典里的
继续通过gdb调试python程序,跟”__pow__”对应的字节码是什么时候加到Python的字典里的:
在gdb中设置断点:
1       breakpoint     keep y   0x000055555580c927 in _PyEval_EvalFrameDefault                                                    at ../Python/generated_cases.c.h:5710stop only if name==0x555555b9b0e8

从上图可见,是python程序执行到字节码STORE_NAME时,将”__pow__”对应的字节码存入字典的。

STORE_NAME这个字节码自身是什么产生的
通过对字节码所在内存进行反复跟踪,我通过硬件断点
hw watchpoint  keep y                      *(int *) 0x5555593aedf8stop only if *(int *) 0x5555593aedf8==114 

这个硬件断点每个机器,每个实例都可能不一样,读者不要深究,只可意会,不可言传。

我得到了以下用于调试的断点:
 breakpoint     keep y   0x000055555581e9ed in compiler_body at ../Python/compile.c:1661stop only if stmts==0x555559484138

这个软件断点每个机器,每个实例都可能不一样,读者不要深究,只可意会,不可言传。

这实际上是Python解释器对PyTorch的pytorch/torch/_tensor.py的编译过程。
以上是Python程序在语法分析过程中,对待编译的Python脚本的类的逐行分析处理过程(特别是1677行)。
而在我们的例子中,待编译的Python脚本是:pytorch/torch/_tensor.py
pytorch/torch/_tensor.py是 PyTorch 中实现 Tensor 类的核心 Python 文件,它定义了张量对象的属性和方法,包括创建张量、索引、数学运算、形状变换以及支持自动求导的接口,同时通过调用底层 C++/CUDA 实现完成高效的数值计算,是 PyTorch 张量操作的 Python 层基础。
_tensor.py对我们的这个“__pow__‘算符定义如下:
所以,本节中,这个STORE_NAME的产生,是因为1107行的这个赋值语句(”=”号)。而赋值语句的右边是cast函数调用,函数调用的参数里,有包括另外一个函数调用:
1112         _handle_torch_function_and_wrap_type_error_to_not_implemented(1113             _C.TensorBase.pow1114         )
即把cast函数调用的结果(__pow__的具体实现字节码序列)"STORE_NAME"到"__pow__"这个名字中去。

当Python在执行_tensor.py中的类的初始化时,会执行这个STORE_NAME,将__pow__的具体实现字节码序列作为value,"__pow__"作为key,存在python的字典里。

当Python在解释执行
y = X[:, 0]**2 + X[:, 1]**3
遇到了

X[:, 1]**3
就会到字典里找”__pow__”,进而执行__pow__的具体实现字节码序列。

近期,因为大语言模型的出现,我的内心对是否坚持学习开源基础软件,曾经有过短期的波动,然而下面的名言激励着我继续坚持:

“Twenty years from now you will be more disappointed by the things that you didn’t do than by the ones you did do. So throw off the bowlines. Sail away from the safe harbor. Catch the trade winds in your sails. Explore. Dream. Discover. ” Mark Twain

二十年后,你会因为没做过的事情而感到更多的遗憾,而不是因为你做过的事情。

所以解开缆绳,驶离安全的港湾。

让帆迎风起航,捕捉顺风。

去探索,去梦想,去发现!    马可-吐温

[1] Matt White PyTorch Day India 2026 Keynote Speech https://www.youtube.com/watch?v=ou_CYZrfIVA&t=324s

[2] https://www.passiton.com/inspirational-quotes/7114-twenty-years-from-now-you-will-be-more