当前位置: 代码迷 >> 综合 >> paddlepaddle(七)动态图转静态图
  详细解决方案

paddlepaddle(七)动态图转静态图

热度:76   发布时间:2023-11-30 12:16:34.0

官方文档:动态图转静态图 

1.动态图和静态图

在深度学习模型构建上,飞桨框架支持动态图编程和静态图编程两种方式,其代码编写和执行方式均存在差异。

  • 动态图编程: 采用 Python 的编程风格,解析式地执行每一行网络代码,并同时返回计算结果。

  • 静态图编程: 采用先编译后执行的方式。需先在代码中预定义完整的神经网络结构,飞桨框架会将神经网络描述为 Program 的数据结构,并对 Program 进行编译优化,再调用执行器获得计算结果。

1.1优劣 

  • 动态图编程体验更佳、更易调试,但是因为采用 Python 实时执行的方式,开销较大,在性能方面与 C++ 有一定差距;
  • 静态图调试难度大,但是将前端 Python 编写的神经网络预定义为 Program描述,转到 C++ 端重新解析执行,脱离了 Python 依赖,往往执行性能更佳,并且预先拥有完整网络结构也更利于全局优化。

1.2动态图转静态图

  • 在模型开发时,推荐采用动态图编程。 可获得更好的编程体验、更易用的接口、更友好的调试交互机制。

  • 在模型训练或者推理部署时,只需添加一行装饰器 @to_static,即可将动态图代码转写为静态图代码,并在底层自动使用静态图执行器运行。 可获得更好的模型运行性能。

使用场景:使用动态图编程调试完成,转为静态图训练;使用动态图训练结束,转为静态图推理。 

1.3使用静态图的方式

飞桨框架 2.0 及以上版本默认的编程模式是动态图模式,包括使用高层 API 编程和基础的 API 编程。

方式一:如果想切换到静态图模式编程,可以在程序的开始执行 enable_static() 函数。

方式二:如果程序已经使用动态图的模式编写了,想转成静态图模式训练或者保存模型用于部署,可以使用装饰器 @to_static。 

 2.动态图转静态图使用样例

2.1使用方式

2.1.1方式一 使用 @to_static 进行动静转换

 使用 @to_static 装饰器装饰 SimpleNet (继承了 nn.Layer) 的 forward 函数。这种方式适合使用动态图编程调试完成,转为静态图训练。

import paddle
from paddle.jit import to_staticclass SimpleNet(paddle.nn.Layer):def __init__(self):super(SimpleNet, self).__init__()self.linear = paddle.nn.Linear(10, 3)@to_static # 动静转换def forward(self, x, y):out = self.linear(x)out = out + yreturn outnet = SimpleNet()
net.eval()
x = paddle.rand([2, 10])
y = paddle.rand([2, 3])
out = net(x, y)
paddle.jit.save(net, './net')

2.1.2方式二 调用 paddle.jit.to_static() 函数

调用 paddle.jit.to_static() 函数,仅做预测模型导出时推荐此种用法。

import paddle
from paddle.jit import to_staticclass SimpleNet(paddle.nn.Layer):def __init__(self):super(SimpleNet, self).__init__()self.linear = paddle.nn.Linear(10, 3)def forward(self, x, y):out = self.linear(x)out = out + yreturn outnet = SimpleNet()
net.eval()
net = paddle.jit.to_static(net)  # 动静转换
x = paddle.rand([2, 10])
y = paddle.rand([2, 3])
out = net(x, y)
paddle.jit.save(net, './net')

方式一和方式二的主要区别是,使用 @to_static 除了支持预测模型导出外,在模型训练时,还会转为静态图子图训练,而方式二仅支持预测模型导出。即方式一支持训练,方式二仅支持推理。

 2.2动转静模型导出

这里的导出与普通的保存不同,是指导出为静态图推理,将不再改动模型。

通过 forward 导出预测模型导出一般包括三个步骤:

  • 切换 eval() 模式:类似 Dropout 、LayerNorm 等接口在 train() 和 eval() 的行为存在较大的差异,在模型导出前,请务必确认模型已切换到正确的模式,否则导出的模型在预测阶段可能出现输出结果不符合预期的情况。

  • 构造 InputSpec 信息:InputSpec 用于表示输入的shape、dtype、name信息,且支持用 None 表示动态shape(如输入的 batch_size 维度),是辅助动静转换的必要描述信息。

  • 调用 save 接口:调用 paddle.jit.save接口,若传入的参数是类实例,则默认对 forward 函数进行 @to_static 装饰,并导出其对应的模型文件和参数文件。

net = SimpleNet()
# train(net)  模型训练 (略)# step 1: 切换到 eval() 模式
net.eval()# step 2: 定义 InputSpec 信息
x_spec = InputSpec(shape=[None, 3], dtype='float32', name='x')
y_spec = InputSpec(shape=[3], dtype='float32', name='y')# step 3: 调用 jit.save 接口
net = paddle.jit.save(net, path='simple_net', input_spec=[x_spec, y_spec])  # 动静转换

 3.动、静态图部署区别

3.1动态图

动态图下,模型指的是 Python 前端代码;参数指的是 model.state_dict() 中存放的权重数据。

上图展示了动态图下模型训练——>参数导出——>预测部署的流程。如图中所示,动态图预测部署时,除了已经序列化的参数文件,还须提供最初的模型组网代码。在动态图下,模型代码是 逐行被解释执行 的。 

3.2静态图

静态图部署时,模型指的是 Program ;参数指的是所有的 Persistable=True 的 Variable 。二者都可以序列化导出为磁盘文件,与前端代码完全解耦

 上图展示了静态图下模型训练——>模型导出——>预测部署的流程。如图所示,静态图模型导出时将Program和模型参数都导出为磁盘文件,Program 中包含了模型所有的计算描述( OpDesc ),不存在计算逻辑有遗漏的地方。

  相关解决方案