根据MxNet深度学习框架官方文档 (https://mxnet.incubator.apache.org/versions/master/architecture/program_model.html#symbolic-vs-imperative-programs ), 从命令式编程和符号式编程的角度分析动态图和静态图深度学习框架的实现方式和特点。

  • 动态图框架对应的是 命令式编程
  • 静态图框架对应的是 符号式编程
什么是symblic/imperative style编程

使用过python或C++对imperative programs比较了解。imperative-stype programs在运行时计算,大部分python代码都是imperative。比如下面的例子:

  import numpy as npa = np.ones(10)b = np.ones(10) * 2c = b * ad = c + 1

当程序执行到 c=b∗ac=b∗ac=b∗a 时,代码开始做对应的数值计算。

symbolic programs与此不同。symbolic-stype program中,需要先给出一个函数的定义(可能十分复杂)。当我们定义这个函数时,并不会做真正的数值计算。这类函数的定义中使用数值占位符,当给定真正的输入后,才会对这个函数进行编译计算。上面的例子用symbolic-stype program重新写:

    A = Variable('A')B = Variable('B')C = B * AD = C + Constant(1)# compiles the functionf = compile(D)d = f(A=np.ones(10), B=np.ones(10)*2)

上述代码中,语句 C=B∗AC=B∗AC=B∗A 并不会触发真正的数值计算,但会生成一个计算图(也称symbolic graph)描述这个计算。下图时计算D的计算图


大部分symbolic-style program都显性或隐性的包含一个编译的步骤,把计算转换成可以调用的函数。上面的例子中,数值计算仅仅在代码最后一行进行,symbolic program一个重要特点是其明确有构建计算图和生成可执行代码两个步骤。对于神经网络,一般会用一个就算图描述整个模型。

其他流行深度学习框架中,PyTorch/Chainer/Minerva使用imperative style。symbolic-styple的框架包括Theano/CGT/TensorFlow。 CXXNet/Caffe这一类依赖配置文件的框架也看作symbolic-style库。此时配置文件被当作计算图的定义。下面我们对比一下二者的优劣:

命令式编程更加灵活

用python调用imperative-style库十分简单,编写方式和普通的python代码一样,在合适的位置调用库的代码实现加速。如果用python调用symbolic-style库,代码结构将出现一些变化,比如iteration可能无法使用。尝试把下面的例子转换成symbolic-style

    a = 2b = a + 1d = np.zeros(10)for i in range(d):d += np.zeros(10)

如果symblic-style API不支持for循环,转换就没那个直接。不能用python的编码思路调用symblic-style库。需要利用symblic API定义的domain-specific-language(DSL)。深度学习框架会提供功能强大的DSL,把神经网络转化成可被调用的计算图。

感觉上imperative program更加符合习惯,使用更加简单。例如,可以在任何位置打印出变量的值,轻松使用符合习惯的流程控制语句和循环语句。

符号式编程更加高效

既然imperative pragrams更加灵活,和计算机原生语言更加贴合,那么为什么很多深度学习框架使用symbolic风格? 最主要的原因式效率,内存效率和计算效率都很高。比如下面的例子:

    import numpy as npa = np.ones(10)b = np.ones(10) * 2c = b * ad = c + 1...


如果数组每个元素内存中占据8字节,在python中需要多少内存?
对于imperative programs中,需要在每一行上都分配必要的内存。一共4个数组,每个数组10个元素,一共4∗10∗8=320字节。 如果事先知道只有d是需要的结果,构造计算图时可以重复利用一些中间变量的空间。比如利用原址计算,我们可以把b的内存借给c使用,同样c的内存可以给d用,如此可以节省一半内存,仅仅需要2∗10∗8=160字节。
symbolic programs限制更多,因为只需要d,构建计算图后,一些中间量,比如c,的值将无法看到。

通过symbolic program,使用原址计算可以安全的重用内存,但牺牲了对c的访问可能。 imperative program可以处理各种访问可能,如果在python执行上述例子,任何中间量都可以方便访问。

symbolic program还可以通过operation folding优化计算。在上述的例子中,乘法和加法可以展成一个操作,如下图所示:

如果在GPU上运算,计算图只需要一个kernel,节省了一个kernel。在很多优化库,比如caffe/CXXNet,人工编码进行此类优化操作。 operation folding可以提高计算效率。

imperative program中不能自动operation folding,因为不知道中间变量是否会被访问到。symbolic program中可以做operation folding,因为获得了完整的计算图,而且明确哪些量以后会被访问,哪些量以后都不会被访问。

Backprop和AutoDiff的案例分析

在这一节,我们将基于自动微分或是反向传播的问题对比两种编程模式。梯度计算几乎是所有深度学习库所要解决的问题。使用命令式程序和符号式程序都能实现梯度计算。

我们先看命令式程序。下面这段代码实现自动微分运算,我们之前讨论过这个例子。

    class array(object) :"""Simple Array object that support autodiff."""def __init__(self, value, name=None):self.value = valueif name:self.grad = lambda g : {name : g}def __add__(self, other):assert isinstance(other, int)ret = array(self.value + other)ret.grad = lambda g : self.grad(g)return retdef __mul__(self, other):assert isinstance(other, array)ret = array(self.value * other.value)def grad(g):x = self.grad(g * other.value)x.update(other.grad(g * self.value))return xret.grad = gradreturn ret# some examplesa = array(1, 'a')b = array(2, 'b')c = b * ad = c + 1print d.valueprint d.grad(1)# Results# 3# {'a': 2, 'b': 1}

在上述程序里,每个数组对象都含有grad函数(事实上是闭包-closure)。当我们执行d.grad时,它递归地调用grad函数,把梯度值反向传播回来,返回每个输入值的梯度值。看起来似乎有些复杂。让我们思考一下符号式程序的梯度计算过程。下面这段代码是符号式的梯度计算过程。

    A = Variable('A')B = Variable('B')C = B * AD = C + Constant(1)# get gradient node.gA, gB = D.grad(wrt=[A, B])# compiles the gradient function.f = compile([gA, gB])grad_a, grad_b = f(A=np.ones(10), B=np.ones(10)*2)

D的grad函数生成一幅反向计算图,并且返回梯度节点gA和gB。它们对应于下图的红点。


命令式程序做的事和符号式的完全一致。它隐式地在grad闭包里存储了一张反向计算图。当执行d.grad时,我们从d(D)开始计算,按照图回溯计算梯度并存储结果。

因此我们发现无论符号式还是命令式程序,它们计算梯度的模式都一致。那么两者的差异又在何处?再回忆一下命令式程序“未雨绸缪”的要求。如果我们准备一个支持自动微分的数组库,需要保存计算过程中的grad闭包。这就意味着所有历史变量不能被垃圾回收,因为它们通过函数闭包被变量d所引用。那么,若我们只想计算d的值,而不想要梯度值该怎么办呢?

在符号式程序中,我们声明f=compiled([D>)来替换。它也声明了计算的边界,告诉系统我只想计算正向通路的结果。那么,系统就能释放之前结果的存储空间,并且共享输入和输出的内存。

假设现在我们运行的不是简单的示例,而是一个n层的深度神经网络。如果我们只计算正向通路,而不用反向(梯度)通路,我们只需分配两份临时空间存放中间层的结果,而不是n份。由于命令式程序需要为今后可能用到的梯度值做准备,中间结果不得不保存,就需要用到n份临时空间。

正如我们所见,优化的程度取决于对用户行为的约束。符号式程序的思路就是让用户通过编译明确地指定计算的边界。而命令式程序为之后所有情况做准备。符号式程序更充分地了解用户需要什么和不想要什么,这是它的天然优势。

当然,我们也能对命令式程序施加约束条件。例如,上述问题的解决方案之一是引入一个上下文变量。我们可以引入一个没有梯度的上下文变量,来避免梯度值的计算。这给命令式程序带来了更多的约束条件,以换取性能上的改善。

    with context.NoGradient():a = array(1, 'a')b = array(2, 'b')c = b * ad = c + 1

然而,上述的例子还是有许多可能的未来,也就是说不能在正向通路中做同址计算来重复利用内存(一种减少GPU内存的普遍方法)。这一节介绍的技术产生了显式的反向通路。在Caffe和cxxnet等工具包里,反向传播是在同一幅计算图内隐式完成的。这一节的讨论同样也适用于这些例子。
大多数基于函数库(如cxxnet和caffe)的配置文件,都是为了一两个通用需求而设计的。计算每一层的激活函数,或是计算所有权重的梯度。这些库也面临同样的问题,若一个库能支持的通用计算操作越多,我们能做的优化(内存共享)就越少,假设都是基于相同的数据结构。

因此经常能看到一些例子在约束性和灵活性之间取舍。

模型检查点

支持对配置文件设置检查点是符号式程序的加分项。因为符号式的模型构建阶段并不包含计算步骤,我们可以直接序列化计算图,之后再重新加载它,无需引入附加层就解决了保存配置文件的问题。

    A = Variable('A')B = Variable('B')C = B * AD = C + Constant(1)D.save('mygraph')...D2 = load('mygraph')f = compile([D2])# more operations...

因为命令式程序逐行执行计算。我们不得不把整块代码当做配置文件来存储,或是在命令式语言的顶部再添加额外的配置层。

参数更新

大多数符号式编程属于数据流(计算)图。数据流图能方便地描述计算过程。然而,它对参数更新的描述并不方便,因为参数的更新会引起变异(mutation),这不属于数据流的概念。大多数符号式编程的做法是引入一个特殊的更新语句来更新程序的某些持续状态。

用命令式风格写参数更新往往容易的多,尤其是当需要相互关联地更新时。对于符号式编程,更新语句也是被我们调用并执行。在某种意义上来讲,目前大部分符号式深度学习库也是退回命令式方法进行更新操作,用符号式方法计算梯度。

没有严格的边界

我们已经比较了两种编程风格。之前的一些说法未必完全准确,两种编程风格之间也没有明显的边界。例如,我们可以用Python的(JIT)编译器来编译命令式程序,使我们获得一些符号式编程对全局信息掌握的优势。但是,之前讨论中大部分说法还是正确的,并且当我们开发深度学习库时这些约束同样适用。

参考
  1. https://blog.csdn.net/z0n1l2/article/details/80873608
  2. https://mxnet.incubator.apache.org/versions/master/architecture/program_model.html#symbolic-vs-imperative-programs
  3. https://blog.csdn.net/daslab/article/details/50434145
  4. https://www.jianshu.com/p/1c7ef1ce5540 (MXNet的动态图接口Gluon

深度学习框架:动态图 vs 静态图相关推荐

  1. 深度学习框架TensorFlow(2.创建图,启动图)

    1.下载好Anaconda,打开Jupyter,新建一个文件 2.直接看代码:里面有些的解释在上一节的图可以看出,本篇就不做解释 import tensorflow as tf#导入模块并且命名 #创 ...

  2. PyTorch 的 Autograd、计算图、叶子张量、inplace 操作、动态图,静态图(来自知乎)

    本博文来自:https://zhuanlan.zhihu.com/p/69294347 非常感谢此博主! PyTorch 作为一个深度学习平台,在深度学习任务中比 NumPy 这个科学计算库强在哪里呢 ...

  3. 2017深度学习最新报告及8大主流深度学习框架超详细对比(内含PPT)

    2017深度学习最新报告(PPT) ​ 深度学习领军人物 Yoshua Bengio 主导的蒙特利尔大学深度学习暑期学校目前"深度学习"部分的报告已经全部结束. 本年度作报告的学术 ...

  4. 清华「计图」、旷视「天元」纷纷重磅开源,国产深度学习框架迎来高光时刻...

    来源:CSDN 本文约3141字,建议阅读7分钟. 本文介绍清华开源的深度学习框架 Jittor(计图) 引起了广泛关注,支撑旷视诸多业务和战绩的深度学习框架 MegEngine(天元) 也将在近日开 ...

  5. 深度学习静态图和静态图的区别以及优缺点是什么?

    深度学习静态图和静态图的区别以及优缺点是什么? 不论是动态图还是静态图,它们都属于计算图.计算图是用来描述运算的有向无环图,它有两个主要元素:结点(Node)和边(Edge).结点表示数据,如向量.矩 ...

  6. 清华开源深度学习框架计图,开源超级玩家再进阶

    2020-03-22 09:24 导语:清华开源计图,背后是三代人的共同努力. 雷锋网AI源创评论报道,据官方消息,清华大学计算机系图形实验室宣布开源一个全新的深度学习框架:Jittor,中文名计图. ...

  7. python学习框架图-从零搭建深度学习框架(二)用Python实现计算图和自动微分

    我们在上一篇文章<从零搭建深度学习框架(一)用NumPy实现GAN>中用Python+NumPy实现了一个简单的GAN模型,并大致设想了一下深度学习框架需要实现的主要功能.其中,不确定性最 ...

  8. Paddle框架理解:模型状态、动态图与静态图、paddle.nn与paddle.nn.functional异同

    文章目录 前言 1. 模型状态 2. 动态图与静态图 3. paddle.nn 与paddle.nn.functional异同 参考 前言 最近在学习Paddle深度学习框架,这里对个人觉得比较重要的 ...

  9. 清华自研深度学习框架「计图」开源!多项任务性能超过PyTorch

    2020-03-20 11:42:07 乾明 发自 凹非寺  量子位 报道 | 公众号 QbitAI AI框架,又来重磅中国玩家. 刚刚,清华自研的深度学习框架,正式对外开源."贵系&quo ...

  10. 盘点国内那些深度学习框架:清华计图Jittor、腾讯优图NCNN、百度飞桨PaddlePaddle、阿里X-DeepLearning

    1.清华计图Jittor 清华大学开发了一个名为计图(Jittor)的深度学习框架. 计图(Jittor:Just in Time)是一个采用元算子表达神经网络计算单元.完全基于动态编译(Just-i ...

最新文章

  1. 看一名 KDE 开发者如何使用 C++17 为项目提升巨大速度
  2. 如何设计好词袋模型BoW模型的类类型
  3. Online DDL
  4. 基于Flask实现后台权限管理系统 - 导言
  5. 在linux下实现mysql自动备份数据
  6. 关于tomcat和sessionCookieName和SESSION_PARAMETER_NAME以及disableURLRewriting参数原理和使用...
  7. nginx上配置phpmyadmin
  8. 瑞幸之后,又一支中概股自曝:虚增收入,股价盘后大跌
  9. mysql 5.5 外键_MySQL 5.5添加外键失败,错误[HY000] [150]和[HY000] [1005]
  10. 解决”不允许一个用户使用一个以上用户名与一个服务器或共享资源的多重连接“问题
  11. Newtonsoft.Json 序列化和反序列化 以及时间格式 2
  12. ASP.NET MVC 随想录——探索ASP.NET Identity 身份验证和基于角色的授权,中级篇
  13. access统计班级人数_使用ACCESS查询统计分数段人数
  14. 关于MATLAB未定义函数或变量 ‘wavread‘的很简单的解决办法
  15. 破解tumblr背景音乐
  16. ArcGisPro脚本工具【7】——通过高德地图API获取公交线路
  17. intel神经网络压缩库distiller使用时遇到的问题
  18. wps英文文档排版小技巧
  19. 上线文件服务器域名怎么建站,怎么做网站?建站详细流程
  20. C#读写二进制存档文件,可以修改部分单机游戏存档

热门文章

  1. 企业为什么要做微信公众号?
  2. Python:统计字符个数
  3. 各地大厂名单(一二线城市知名公司)
  4. pygame画圆练习赤橙黄绿青蓝紫
  5. 解决edge可以访问github,谷歌却无法访问的问题
  6. 怎么用软件测试相似相似度,文档相似性检测工具
  7. 5. ATF(ARM Trusted firmware)启动---bl31
  8. win7激活码失效 报错0xC004F057解决方法
  9. 【数据结构--二叉树】--附超详细图解
  10. C语言编程基础,手机购物程序的设计