前置背景
在深度学习框架中,动态图和静态图是构建和执行模型的两种核心方式。随着Pytorch、TensorFlow、PaddlePaddle等框架的发展,动态图与静态图在功能、性能、可用性上的差异逐渐明显,并在训练和推理的不同阶段承担着不同的角色。本文会从基本概念、实现方式、代码差异、动转静技术、实际应用场景等角度,系统剖析两种计算图机制。
一、什么是计算图
计算图是一种表示神经网络中计算操作与数据依赖关系的有向图。每个节点表示一个操作(比如加减乘除)边表示张量的流动。在深度学习中,训练或者推理的每一步都可以抽象为图中的一次前向或反向传播计算。
二、动态图与静态图的定义与区别
个人总结:动态图像Python脚本,灵活但是执行效率不高,方便调试。静态图像C++,需要预编译,复杂但性能高。
动态图(Dynamic Graph) | 静态图(Static Graph) | |
---|---|---|
图的构建方式 | 运行时动态创建,边定义边执行 | 执行前一次性构建完整图,之后执行 |
控制流支持 | 使用 Python 原生控制流(if /for ) |
需使用特殊图操作(如 tf.cond() ) |
调试与开发体验 | 简洁、直观、便于调试 | 不易调试,需借助可视化和日志工具 |
性能与部署 | 难以优化,运行时构图存在开销 | 可做全图优化(图融合、内存复用等),更快 |
代表框架 | PyTorch、TF Eager、Paddle动态图 | TensorFlow 1.x、Paddle静态图、ONNX、XLA |
三、代码层次对比:动态图 vs 静态图
- 动态图(Torch):每行代码立即执行,是Python解释器原生行为的一部分。方便调试,灵活修改
ini
import torch
x = torch.tensor([2.0])
y = 2 * x + 3
print(y) # 输出 tensor([7.])
- 静态图(TensorFlow 1.x):先定义完整的图,再在Session中喂数据执行,图结构无法在运行中改变
ini
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
x = tf.placeholder(tf.float32)
y = 2 * x + 3
with tf.Session() as sess:
result = sess.run(y, feed_dict={x: [2.0]})
print(result) # 输出 [7.]
四、动转静--实现高效性能部署
- 为什么要做动转静?
虽然动态图开发便捷,但在推理部署阶段需要静态图带来性能优势,比如: - 内存优化(buffer重用)
- 图算子融合
- 并行执行、跨平台部署(TensorRT/ONNX)
Pytorch动转静
ini
import torch
import torch.nn as nn
# 定义模型
class SimpleModel(nn.Module):
def forward(self, x):
return 2 * x + 3
model = SimpleModel()
example_input = torch.tensor([2.0])
# 动转静:跟踪方式
scripted_model = torch.jit.trace(model, example_input)
# 保存静态模型
scripted_model.save("model.pt")
# 加载执行
loaded = torch.jit.load("model.pt")
print(loaded(torch.tensor([3.0]))) # 输出 tensor([9.])
小问题:训练用动态图 vs 推理用静态图,Why
-
训练用动态图的原因
- 实验需求变多: 频繁调参、换结构、添加新模块
- 便于Debug:每一步执行结果可即时打印,断点调试
- 梯度自动追踪:动态图天然支持autograd,反向传播更加简洁
-
推理用静态图的原因
- 执行效率优先:模型结构已经固定,关注的是延迟、吞吐、内存占用
- 易于部署:静态图可序列化为.onnx,.pb,.pt部署到C++、移动端、嵌入式
- 图级优化更彻底:比如常量折叠、算子融合、内存复用、batch并发等