PyTorch 入门
1 核心概念:Symbolic(符号式) vs. Imperative(命令式)
| 特性 | Symbolic(符号式/声明式) | Imperative(命令式) |
|---|---|---|
| 核心思想 | 先定义,后执行。像画蓝图,先声明计算流程(计算图),然后再喂入数据执行。 | 边定义,边执行。像捏粘土,每个操作指令会立即执行并返回结果。 |
| 类比 | 编译器语言(如C++):先写好所有代码,编译生成可执行文件,然后运行。 | 解释型语言(如Python):写一行代码,解释器就立即执行一行,并给出结果。 |
| 工作流程 | 1. 定义计算图(Placeholder,Variable,Operation) 2. 创建会话(Session) 3. 在会话中喂数据,运行图 |
1. 定义张量(Tensor) 2. 直接执行操作(如 c = a + b),立即得到结果 |
| 优点 | - 高性能优化:框架可以看到整个计算图,可以进行大幅度的优化(如操作融合、内存复用)。 - 易于部署:整个计算图可以很容易地序列化、导出并在不同平台部署(移动端、服务器)。 - 语言无关性:计算图是一种中间表示,可以用不同语言实现后端。 |
- 直观灵活:符合Python编程习惯,易于理解和调试。 - 动态控制流:可以使用Python原生的 if...else,for,while循环,非常适合动态网络结构(如RNN)。- 易于调试:可以随时打印中间变量的值,像调试普通Python代码一样。 |
| 缺点 | - 不直观:像“隔墙操作”,定义计算流程和实际执行分离,学习曲线较陡。 - 调试困难:无法在定义图的时候直接打印中间结果,需要使用 Session.run()。- 动态结构支持差:处理可变长度输入或复杂控制流的模型时非常笨拙。 |
- 优化受限:由于是逐行执行,框架无法预知后续操作,难以进行全局优化。 - 开销可能更大:每个操作都可能启动一个GPU内核,带来额外开销。 |
2 TensorFlow和PyTorch各版本的归属
下面的图表直观地展示了TensorFlow和PyTorch在不同版本中对于符号式和命令式编程模式的侧重与演变历程:
flowchart TD
A[深度学习框架范式] --> B{Symbolic 符号式}
A --> C{Imperative 命令式}
B --> B1[TensorFlow 1.x<br>经典符号式模式]
B1 --> B2[TF 2.x + tf.function<br>符号式(默认开启)]
C --> C1[PyTorch 1.x<br>经典命令式模式]
C1 --> C2[PyTorch 2.x + torch.compile<br>命令式(可编译为图)]
B2 -.->|趋势:融合| C2
C2 -.->|趋势:融合| B2
各个版本的具体情况:
| 框架版本 | 主要范式 | 设计哲学 |
|---|---|---|
| TensorFlow 1.x | 符号式 | 性能优先,为生产环境设计,但牺牲了灵活性。 |
| PyTorch 1.x | 命令式 | 灵活性优先,为研究和实验设计,但早期性能有优化空间。 |
| TensorFlow 2.x | 命令式(默认) + 符号式(tf.function) |
默认采用命令式(即时执行 Eager Execution),但无缝集成符号式(@tf.function),兼顾了性能和灵活性。 |
| PyTorch 2.x | 命令式(默认) + 符号式(torch.compile) |
保持易用性,通过即时编译(JIT)技术追赶性能。命令式为主,大幅增强符号式能力,通过torch.compile等技术引入强大的符号式(图)优化。PyTorch 2.0 的核心是 TorchDynamo 技术,它可以智能地捕获Python程序的计算图,并将其交给TorchInductor 等编译器进行深度优化(如内核融合、图级优化),从而在不改变用户命令式编程习惯的前提下,获得接近静态图的性能。 compiled_model = torch.compile(my_model) # 一行代码获得性能提升;out = compiled_model(x, y) # 第一次调用会编译图,后续调用极快 |
3 PyTorch的JIT编译技术
Torch的 JIT(Just-In-Time)技术是PyTorch中一种将模型或部分代码转换为中间表示(IR)然后进行优化和执行的编译技术,将动态的命令式的PyTorch代码转换为静态的可优化的计算图。它的主要目标是将动态图(命令式执行)的灵活性与静态图(符号式执行)的性能优势结合起来。
graph TB
A[Python代码] --> B{TorchScript转换}
B --> C[Tracing<br>跟踪执行]
B --> D[Scripting<br>源码解析]
C --> E[跟踪图]
D --> F[脚本图]
E --> G[TorchScript IR]
F --> G
G --> H[图优化]
H --> I[序列化]
I --> J[跨平台执行]
J --> K[CPU后端]
J --> L[GPU后端]
J --> M[移动端]
J --> N[C++接口]
JIT的核心组件:
- TorchScript:这是JIT的核心,它是一种PyTorch代码的
中间表示(IR),可以独立于Python运行时运行。TorchScript代码可以通过以下两种方式创建,但trace和script本身是图获取技术,而不是图优化技术:- Trace:需要实际运行模型再记录操作来捕获模型结构。它使用示例输入来执行模型,并记录执行的操作。这种方式对于没有分支的模型很有效,但只能记录特定输入下的执行路径,
无法捕获依赖于数据的控制流(如循环、条件判断)。 - Script:通过直接解析Python代码来生成TorchScript。它可以捕获所有的控制流,但要求代码
必须是TorchScript支持的子集(不能使用部分Python特性)。
- Trace:需要实际运行模型再记录操作来捕获模型结构。它使用示例输入来执行模型,并记录执行的操作。这种方式对于没有分支的模型很有效,但只能记录特定输入下的执行路径,
- 优化器:一旦模型被转换为TorchScript,JIT会进行一系列优化,如操作融合、常量传播、死代码消除等,以提高运行效率。
- 运行时:优化后的TorchScript可以在高性能的C++运行时中执行,而无需Python解释器,这使得模型可以部署在生产环境中(如移动端、服务器端),并享受性能提升。
JIT的优势:
- 性能提升:通过图优化和操作融合,减少内核启动次数和内存访问,提高执行速度。
- 部署友好:可以将模型序列化为一个文件,并在没有Python环境的C++程序中加载运行。
- 跨平台:支持在GPU、CPU等多种设备上运行,并支持移动端部署。
- 并行性:图优化可以更好地利用设备并行性。
JIT的局限:
- Python子集:TorchScript支持Python语法的一个子集,因此不是所有的Python代码都可以被转换。
- 调试困难:由于图优化和脱离Python环境,调试转换后的模型可能更困难。
- 动态性限制:虽然脚本模式可以捕获控制流,但一些动态特性(如动态数据结构)可能无法完全支持。
在PyTorch 2.0及以后的版本中,JIT技术仍然被使用,但同时PyTorch也引入了新的编译技术(如TorchDynamo和TorchInductor)来进一步优化性能。这些新技术旨在更好地支持动态图,并提供更简单的使用方式。

浙公网安备 33010602011771号