前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >飞桨核心框架最新升级:灵活高效兼顾,动静自然统一

飞桨核心框架最新升级:灵活高效兼顾,动静自然统一

作者头像
用户1386409
发布2020-06-16 15:36:58
5440
发布2020-06-16 15:36:58
举报
文章被收录于专栏:PaddlePaddle

在近期举办的“WAVE SUMMIT 2020”深度学习开发者峰会上,百度飞桨总架构师于佃海提到:

“飞桨的迭代前进,离不开两个重要驱动轮:一个是产业实践的打磨,一个是用户体验的持续优化。两个驱动轮互相配合,给飞桨提供了持久、广泛的发展动力,驱动飞桨拥有一个最灵活易用产业级深度学习框架。”

这两个驱动,对应到框架的设计,就是在确保高效的同时,做到灵活易用。基于这一指导思想,飞桨在编程界面上同时支持了命令式编程和声明式编程,即通常说的动态图和静态图。

两种编程方式各有优势,动态图类采用“define-by-run”的执行方式,写一行代码即可即时获得结果,在编程体验、调试便捷性等方面有绝佳的优势;而静态图采用“define-and-run”的方式,事先定义好整体网络结构再执行,能够对全局编译优化,更有利于性能的提升,也有利于模型的保存和部署,但在编程调试灵活性上有所欠缺。

如何兼顾这两种模式的优势,做到灵活编程、高效训练和部署,同时具备更统一的编程体验,是一个很大的挑战。

最新发布的飞桨核心框架1.8版本,带来了重磅更新,总结下来包括两点:

1、动态图性能更卓越,经过多个版本的持续深度优化,飞桨动态图的训练性能已经媲美静态图。

2、动静更加统一,完备实现了一键式动转静、动静混合编程,使动态图开发可以无缝衔接部署,并能通过静态图执行模式对部分模型实现进一步的训练加速。

这次重大更新意味着什么呢?

这意味着开发者可以采用动态图模式编写和调试AI模型,享受便捷灵活的开发体验;并可实现和静态图相当的高性能训练,并无缝衔接模型存储和部署应用。

通俗地讲,就是以后开发AI模型再也不用做选择题啦!经过持续地融合统一,使用飞桨动态图、静态图的优势均可得到充分发挥,自然灵活兼顾卓越性能,开发者可以更容易地写出优雅的代码,实现更高效的开发和训练、部署。

飞桨是如何做到动静统一,性能卓越的呢?接下来将为你揭晓。

动静统一,带来极致用户体验

飞桨框架的设计思想,是期望将深度学习计算的编程和内在表示保持一致,所以从用户界面上没有引入Graph等概念,直接以程序化的“Program”形式描述神经网络模型的计算过程,对应的用户开发和通用的编程体验更加接近。

从更好全局编译优化以及上线部署的角度考虑,飞桨提供了一种完备的内在描述ProgramDesc,可以表达任意复杂的模型,并实现编译期和运行时的分离——这个意义上可与声明式编程范式即“静态图”相对应。

从Python普及度、以及更方便用户灵活调试的角度出发,飞桨从2018年底开始扩展支持使用Python原生控制流、即时执行的命令式编程范式,也就是大家通常讲的“动态图”。

其实对飞桨而言,基于编程一致的计算描述,向动态图的扩展,以及动静转换都是非常自然的。

在之前的版本,飞桨已经提供了TracedLayer,可以将不含数据相关的控制流的动态图模型转成静态图模型。在1.8版本中飞桨提供了功能更强大、更通用的ProgramTranslator,可以完备地将Python语法下的计算定义转译为Program,从而和全局优化延时执行模式打通,并可实现模型结构存储和上线部署。

操作上非常简单,只需要在定义神经网络时添加一个装饰器,就可以将对应函数内部的所有定义,包括依赖数据的控制流实现,递归地转换为静态program执行。并且在这种模式下,可以灵活控制,实现动静混合编程。

下面通过一个示例讲述如何在一个Layer中添加装饰器。

代码语言:javascript
复制
class MNIST(fluid.dygraph.Layer):
    def __init__(self):
        super(MNIST, self).__init__()

        self.conv = Conv2D( 1, 20, 5)
        self.pool2d = Pool2D( pool_size=2, pool_stride=2 )
        self.pool_2_dim = 5 * 4 * 4
        self.proj = Linear(self.pool_2_shape, 10,
                      act="softmax")

    @declarative  # 添加装饰器,将forward函数内Layers递归地转为静态图的Program执行
    def forward(self, inputs):
        x = self.conv(inputs)
        x = self.pool2d( x )
        x = fluid.layers.reshape( x, [-1, self.pool_2_dim])
        x = self.proj(x)
        return x

可以看到,仅仅需要在最外层的Layer加上一个declarative装饰器,即可把动态图转成静态图进行执行,不需要在其他任何地方做修改。

如果需要部署模型,通过ProgramTranslator将动态图的模型转成静态图的program即可,示例如下:

代码语言:javascript
复制
import paddle.fluid as fluid
from paddle.fluid.dygraph import  to_variable

fluid.enable_imperative()
prog_trans = fluid.dygraph.ProgramTranslator()
model = MNIST()
in_np = np.random.random( [20, 3, 28, 28]).astype("float32")
in_var = to_vairbale( in_np )
out = model(in_var)
prog_trans.save_inference_model("./dy2stat_infer_model", fetch=[0])

借助ProgramTranslator,通过执行一次forward运算得到一个静态图的program。之后就可以复用之前静态图一键部署的逻辑进行部署上线。ProgramTranslator功能新推,还将持续完善,欢迎持续关注飞桨更新动态。

总结一下,基于动静更加统一的飞桨框架,开发者可采用更灵活的动态图模式编程。

  • 训练加速,在最外层的Layer加上一个declarative装饰器,即可把动态图转成静态图进行训练,通过全局优化可对部分任务(例如RNN类模型)明显提升训练性能。
  • 部署便捷,通过ProgramTranslator将动态图的模型转成静态图的program,即可实现模型的静态存储和部署应用。

这将大大提升开发者的开发体验。

性能卓越,

动态图媲美静态图的训练性能

其实,对于大部分任务而言,无需通过动静转换,飞桨的动态图训练已经具备非常高的性能。飞桨自1.3版本版增加动态图功能以来,持续数个版本,一直致力于提升训练的整体性能。目前在主流的任务上,飞桨动态图执行模式已经能够达到与静态图媲美的水平。

以上数据对比了几个主流模型在单张NVIDIA V100 GPU配置上的训练数据,测试环境如下:

  • Ernie:基于Wikipedia数据集,配置batch_size 50, seqlen256;
  • Resnet50:基于imagenet数据集,配置 batch_size 128;
  • Transformer base:基于iwslt14 de-en数据集,配置max_token = 4096;
  • Mobilenetv2:基于imagenet数据集,配置batch_size 256。

可以看出同模型在动态图模式下的训练速度与静态图相当。

这主要得益于以下层面的优化。

1. 降低框架开销(overhead)

框架的开销可以认为是任务训练的总时间减去op kernel计算的总时间,框架开销越小越好。对动态图的即时执行模式而言,由于频繁交互而又没有全局优化,框架开销对训练效率影响很大,特别是对于RNN这类任务。飞桨通过执行流程和数据结构优化来降低框架的overhead:

a) 减少Python与C++交互复杂数据结构开销。由于Python和C++端使用的数据结构不一致,会引入一次开销比较大的转换,通过优化流程,避免这类数据结构转换。

b) OP执行优化。每个OP执行时,均需要做一次数据构造,OP运行结束之后进行析构。通过优化C++端的执行流程,简化数据结构,降低整体的overhead。

c) 移除非必需的属性。在静态图时,每个OP需要引入一类属性对OP进行标记,这些属性的构造和析构耗费比较多的时间,但对于动态图不是必须的,通过移除属性的构造和析构,减低框架overhead。

2. 引入多流异步的数据加载方案

数据加载的性能会严重影响整个训练的吞吐。动态图模式下每个OP执行,都会申请Python的全局锁(Global Interpreter Lock),这将导致异步DataLoader中数据处理的线程效率受到很大影响。如果训练时每个batch的数据量比较大,DataLoader的性能就不如静态图下那么高效。为此,飞桨动态图引入了多线程数据处理流程,多线程不会受到全局锁的影响,进而提升执行效率。

3. 更优的cache机制

在卷积(Convolution)运算中,可根据输入的shape、dtype、data_format等信息选择最优的cudnn algorithm,由此来提升执行效率,但是搜索最优的cudnn algorithm会耗费比较多的时间。

通过引入algorithm的cache机制,可以降低搜索的耗时,提升任务的整体训练效率。用户仅需要设置 FLAGS_cudnn_exhaustive_search = 1 即可开启这种cache机制。通过开启该机制,语音的waveflow任务性能可提升200%。

除了以上优化,最新的1.8版本在动态图功能上也有进一步增强,可以支持更多的任务,满足开发者多样化的需求,目前主流的模型都可以使用动态图进行实现。

1) 支持double grad运算

在一些任务(如GAN相关的)中,强依赖梯度惩罚功能,在1.8版本提供了double grad的支持,方便任务的实现。double grad简单的使用示例如下:

代码语言:javascript
复制
      with fluid.enable_imperative()    # 激活动态图模式
        x = fluid.layers.ones(shape=[1], dtype='float32')
        x.stop_gradient = False
        y = x * x

        # Since y = x * x, dx = 2 * x
        # 调用grad方法                
        dx = fluid.dygraph.grad(
                outputs=[y],
                inputs=[x],
                create_graph=create_graph,
                retain_graph=True)[0]

        z = y + dx    
        z.backward()      # double grad计算

2) Layer支持hook功能

1.8版本提供了forward pre-hook和 forward post-hook的支持,方便组网的时候对input进行修改。

forward pre-hook的使用如下所示,通过forward_pre_hook,可以将输入整体扩大两倍,同时也可以支持更复杂的一些pre_hook运算( 比如参数的normalization 运算 ):

代码语言:javascript
复制
import paddle.fluid as fluid
import numpy as np

# forward_pre_hook函数修改了layer的输入:input = input * 2
def forward_pre_hook(layer, input):
    # 改变输入值
    input_return = (input * 2)
    return input_return

fluid.enable_imperative()
    linear = fluid.Linear(13, 5, dtype="float32")

    # 注册hook
    forward_pre_hook_handle = linear.register_forward_pre_hook(forward_pre_hook)

    value0 = np.arange(26).reshape(2, 13).astype("float32")
    in0 = fluid.dygraph.to_variable(value0)
    out0 = linear(in0)

    # 移除hook
    forward_pre_hook_handle.remove()

    value1 = value0 * 2
    in1 = fluid.dygraph.to_variable(value1)
    out1 = linear(in1)

同时飞桨已经开源了主流模型的动态图实现,不要错过这个丰富的资源库:

https://github.com/PaddlePaddle/models/tree/develop/dygraph

Ernie也开放了动态图的实现,赶快来试试吧:

https://github.com/PaddlePaddle/ERNIE

更多飞桨动态图的应用方法,欢迎访问:

https://www.paddlepaddle.org.cn/tutorials/projectdetail/386189

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PaddlePaddle 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 飞桨框架的设计思想,是期望将深度学习计算的编程和内在表示保持一致,所以从用户界面上没有引入Graph等概念,直接以程序化的“Program”形式描述神经网络模型的计算过程,对应的用户开发和通用的编程体验更加接近。
  • 其实,对于大部分任务而言,无需通过动静转换,飞桨的动态图训练已经具备非常高的性能。飞桨自1.3版本版增加动态图功能以来,持续数个版本,一直致力于提升训练的整体性能。目前在主流的任务上,飞桨动态图执行模式已经能够达到与静态图媲美的水平。
相关产品与服务
GPU 云服务器
GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于生成式AI,自动驾驶,深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档