已学完
💡 一句话解释: 告别先画塑料图再通电的老古董,写一行 Python 就立即在显卡上跑出一行数字,断点想停就停!
# 传统静态图:sess.run(loss) 犹如黑盒 | PyTorch:pdb.set_trace() 原生随手断点调试
烦恼:静态图像塑料模具,报错根本看不懂
死图盲调,见不到真实数值
办法:写一行跑一行,实时看清每一个数值
随手打印断点,符合直觉
解决了什么:消灭了繁琐的图构建与 Session 会话机制,让深度学习代码拥有跟原生 Python 一样的极佳可读性与调试体验。
代价是什么:每次迭代都要经过 Python 虚拟机重新派发解释,在算子很小时容易遭遇 CPU 开销瓶颈。
已学完
💡 一句话解释: 前向运算时每个算子都悄悄在张量屁股后系一根红绳(grad_fn),调用 backward() 时顺着绳子倒带就算出了梯度!
# 链式法则 DAG:y = x * 2 ➔ y.grad_fn 为 MulBackward0,倒带触发梯度的积累与反传
烦恼:手推几百层复合函数导数会疯掉
人工算梯度,稍有不慎全盘错
办法:算一步留一张底单,一键倒带链式相乘
底单倒查,链式法则全自动
解决了什么:在动态执行的同时,依靠 C++ 层的 grad_fn 链表在运行时按需构建反向计算图,免除任何人工求导。
代价是什么:必须在前向传播期间将需要的中间激活值(Activation)缓存在显存中,直到反向传播结束后才能释放。
已学完
💡 一句话解释: 转置(Transpose)或者切片变形状时,根本不用重新搬运内存!底层是一整条连续数组,改改“步长游标(Stride)”就能瞬间换个新视角!
# x.view() / x.transpose() 零开销共享同一底层 Storage | .contiguous() 才会真正触发物理内存重排
烦恼:每次换个排版都傻傻深拷贝一次
只是换个角度看,全村大搬家
办法:底层物理不动,只改眼镜度数(Stride)
物理条带不动,换副墨镜看
解决了什么:通过 Tensor 元数据(Shape, Stride, Offset)与底层 Storage 分离,让转置、切片、变形操作全部实现零拷贝。
代价是什么:非连续张量(Non-contiguous)在调用某些特定底核算子时必须先调用 `.contiguous()` 重新整理内存。
已学完
💡 一句话解释: 每算一个乘法都找英伟达驱动要显存会慢成龟速!PyTorch 先向显卡批发一大块仓库,用完了内部洗洗再给下个算子,绝不还给驱动!
# cudaMalloc 极其昂贵 | PyTorch 维护双大小块显存池:torch.cuda.memory_allocated() vs memory_reserved()
烦恼:每秒找英伟达借还显存上万次
频繁借还,排队全耗在窗口上
办法:自家设蓄水池,用完擦干净继续给下家用
自带蓄水池,内部极速循环
解决了什么:使用 Caching Allocator 避免昂贵的驱动级 cudaMalloc/cudaFree 同步系统调用,大幅降低显存分配开销。
代价是什么:`nvidia-smi` 看到的显存占用(Reserved)往往虚高,因为 PyTorch 会把用过的显存扣留在池中备用。
已学完
💡 一句话解释: 显存放不下 100 层的中间结果?那就只记住几个关键里程碑,反向传播算到某一段时,再当场花两秒重新算一遍!
# torch.utils.checkpoint.checkpoint(module, input) ➔ 显存暴省 70%,算力仅多消耗 20%~30%
烦恼:为了求导把所有中间值全存下,显存爆炸
全盘死记硬背,脑容量先憋爆
办法:只存几个存档点,用时就地重算
只留关键路标,随用随重温
解决了什么:前向时不保留所有中间激活值,在反向时按需局部重算(Recompute),突破大模型单卡显存墙。
代价是什么:增加了大约 20%~30% 的额外前向浮点计算时间。
已学完
💡 一句话解释: 多张显卡一起训练时,别让所有人把梯度都发给一个主卡(会被挤爆);围成一个圆圈像传绣球一样交替同步!
# 告别单进程多线程的旧 DataParallel (GIL 瓶颈) ➔ torch.distributed.launch 独立进程与 NCCL 环通信
烦恼:众星捧月集中同步,主卡网络先堵死
全挤一个窗口,排队水泄不通
办法:手拉手围成圆环,互通有无飞速传递
圆环接力互传,带宽打满无瓶颈
解决了什么:DDP 采用独立进程消除 Python GIL 限制,结合 NCCL Ring-AllReduce 使跨卡通信与反向计算并行重叠。
代价是什么:每张卡必须各自完整冗余保存一份模型权重和优化器状态(后续促成了 FSDP 零冗余切分)。
已学完
💡 一句话解释: C++ 生产服务器上可没有 Python 解释器;TorchScript 像给动态模型打了个“离线可执行安装包”,在没有 Python 的纯 C++ 环境飞速运行!
# torch.jit.trace(model, dummy_input) 录制算子流 / torch.jit.script(model) 解析 Python AST 语法树
烦恼:C++ 纯净服务端无法塞入 Python 运行时
带着大包袱,寸步难离开发机
办法:剥除 Python 糖衣,编译为纯 C++ 字节码
打包脱壳,独立翱翔生产线
解决了什么:将动态 Python 模型序列化保存为脱离 Python 依赖的独立 IR 计算图,供 C++ LibTorch 跨平台运行。
代价是什么:对 Python 动态特性的语法支持受限,遇到复杂外部第三方库时极易 trace 失败或静默截断。
已学完
💡 一句话解释: PyTorch 2.0 的黑科技:在 Python 虚拟机把字节码送去执行的千钧一发之际拦截下来,把能图优化的部分打包送走,不兼容的部分照常放行!
# 利用 PEP 523 Frame Evaluation API 拦截 Python 栈帧 ➔ 自动切割出纯张量 FX Graph 计算图
烦恼:以前的模型编译器一遇到非张量代码就直接罢工
遇到特殊语句,编译器直接躺平
办法:分流安检,合规的打包走快轨,不合规的走常规
自动安检分流,绝不报错卡死
解决了什么:在 Python 虚拟机层面无侵入式截获字节码,智能识别并抽取可编译的计算子图,实现代码 100% 零修改兼容。
代价是什么:频繁的代码分道(Graph Break)会导致计算图碎片化,减弱全局编译加速的效果。
已学完
💡 一句话解释: 不用再写一个乘法存一次显存、写一个加法存一次显存!Inductor 编译器会自动把十几个连续动作合并成一个定制 GPU 算子,在芯片片上缓存一口气完成!
# 编译后端将 FX Graph 编译为 OpenAI Triton 代码 ➔ 实现逐元素与归约算子的极致硬件级 Kernel Fusion
烦恼:每走一小步都要回显存歇一口气
走一步喝口水,全在路上折腾
办法:打包合成单一超级算子,寄存器一次通关
多合一冲压,流水线瞬间通关
解决了什么:自动将连续的内存密集型算子编译融合成单个 OpenAI Triton 硬件级内核,彻底打破 GPU 显存带宽墙。
代价是什么:第一次执行(首次启动或遇到全新张量形状)时会有几秒到几十秒的 JIT 编译等待时间。
下一站
💡 一句话解释: 一键给模型披上机甲!平时开发依然是熟悉的动态 Python,上线前只需加一句 torch.compile(model),即可自动享受工业级静态图极致加速!
# PyTorch 2.0 终极形态:opt_model = torch.compile(model, mode="reduce-overhead")
烦恼:鱼与熊掌不可兼得的世纪难题
要调试就慢,要性能就痛苦
办法:一行 compile,动态写、静态跑!
一行开启,天下武功唯快不破
解决了什么:彻底消弭动态图(易用)与静态图(性能)的历史鸿沟,提供一行代码加速现存所有深度学习模型的标准解。
代价是什么:依赖本地 C++/CUDA 编译链工具栈环境,且遇到频繁动态尺寸时需要开启 dynamic=True 进行专门调优。