前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >灵活开发、高效训练、便捷部署不可兼得?这款国产框架表示都可以有

灵活开发、高效训练、便捷部署不可兼得?这款国产框架表示都可以有

作者头像
机器之心
发布2020-06-16 14:53:49
5160
发布2020-06-16 14:53:49
举报
文章被收录于专栏:机器之心

机器之心发布

机器之心编辑部

在近期举办的「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

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

本文分享自 机器之心 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档