深度学习框架演进简史

以古为鉴,可以知兴替。-《新唐书 魏征传》

本文主要从设计思路上分析,不考虑生态、分布式等其他其实也很重要角度。

第一代 - 要有光

一言以蔽之,以Caffe为主要代表的第一代框架把神经网络建模a sequence of layers。我妄加揣测一下,这是由当时的历史条件和发起者的背景所共同决定的:

  1. 当时Deep Learning发迹于计算机视觉领域,其主要算法为CNN,而对CNN的模块是单入单出(Single Input Single Output),这种情况下用pipeline来描述就足够了。
  2. 即便后续出现了GoogleNet这种宽拓扑以及ResNet这种含short cut的拓扑,因为CNN的每层都比较heavy使得layer间的并行从计算平台层面来讲尚不能获得什么好处,所以把layer flat成pipeline也成为一种不错的选择。
  3. 而Caffe的发起者贾扬清来自Berkeley视觉实验室,对做计算机视觉的而言,pipeline是一个再自然不过的选择。

随着Deep Learning从计算机视觉滥觞,发扬到NLP和Speech领域,以及在CNN上出现了越来越多的非pipeline型拓扑,第一代架构暴露出越来越多的问题,其中两个最大的槽点是:

  1. pipeline型架构显然不是建模deep learning拓扑的最好表达,越来越多的、各种各样的拓扑结构(如RNN,attention mechanism)呼唤一个更好的表达:graph。
  2. layer粒度太粗,使得每想出一个新的拓扑,几乎都需要自己写一个或几个layer。算法科学家需要下载、编译、加代码、再编译、debug......太折腾人了也太考验算法科学家的编程能力了。时代呼唤一个粒度更细的算子的框架,使得算法科学家可以用提供的那些算子搭出一个新的层。

于是,第二代框架应运而生。

第二代 - 要有图

如上所述,以TensorFlow/MxNet/Caffe2为代表的第二代深度学习框架的核心设计思路就是a graph of operators, 如果要加一个形容词的话, 那就是"a static graph of operators"。其主要特点是:

  1. 把深度神经网络拓扑建模成一个DAG(Directed Acyclic Graph),解决RNN类拓扑的layer间并行问题。
  2. 包含很多各个粒度的算子,既有max这种底层算子(兼顾灵活度)也有conv这种高层算子(兼顾效率),这种实现是一种费力的讨好,算是一种practice而不是design。同时,也造成了用户对TensorFlow API凌乱的印象和使用上的困惑。

为了解决第二个问题,即如何对外暴露最细的operator但同时保持efficiency,再往大里进一步,即如何让用户只关心逻辑而框架来解决优化问题,大家的探索方向集中于设计一个domain specific compiler上。典型的有TensorFlow的XLA(Accelerated Linear Algebra), DMLC的TVM(Tensor VM)。下面借用TensorFlow XLA的一张图来描述该想法,图中HLO指High Level Optimizer。


image.png

这个想法的基本要义我曾在Neural Network Virtual Machine一文中做过阐述,下面从流程上阐述,具体地说XLA会接受用户的图定义作为输入,该图定义会首先经过一个target independent 的optimizer,在这里做一下与底层设备无关的普适的分析和优化工作(如Common Subexpression Elimination, operator fusion, buffer analysis for allocating runtime memory for computation等),然后生成一个中间表示(IR,Intermediate Representation),该中间表示然后针对特定硬件再做target dependent的optimizer和目标代码生成。可见,这是一个典型LLVM架构。既然是VM,我们就要考虑第二步的工作采用JIT(Just-In-Time)式(runtime编译)还是AOT(Ahead-Of-Time)式(编译时编译好,runtime只使用)。TensorFlow XLA两者都支持。我倾向于把它称为2.5代框架。

第三代 - 要自由

历史的车轮滚滚向前, 转眼到了人人高喊AI民主化的大跃进时代。Bayes之父的NIPS演讲都没人听了,所有的人都在为AI疯狂打call。民主化的本质是傻瓜化。也就是,下至黄口小儿上至黄髫老翁,只要想玩AI,分分钟搞定,是民主化的最高目标。


image.png

前两代框架总体上来讲还是为效率而生、为工程师而设计,因此采用了声明式的编程范式(Declarative Program Paradigm or Symbolic Program Paradigm), 这种范式的典型编程模式如下:

1. define a graph(definition graph)
2. init a session with the definition graph
3. compile the graph in session to find best runtime configuration
4. run the session

这种编程模式可以称为“专家模式”(典型的follow专家模式设计的东西有:单反相机、Latex),它的优点是效率高、速度快;缺点就是“所见非所得”,debug比较困难,需要你对你做的事情比较了解不然容易抓瞎。民主化呼唤“所见即所得”的”傻瓜模式“系统(典型的傻瓜模式的东西有:傻瓜相机、Word),为普罗大众而设计,于是第三代框架诞生了,它以“所见即所得”为设计目标,以imperative program paradigm为主要设计手段,以pyTorch, TensorFlow Eager,MxNet Gluon为主要代表。它的主要优势为:

  1. Fast debugging with immediate run-time errors。不需要tf.Print了,直接print就可以啦。
  2. Support for dynamic models。可以根据上一个operator的runtime输出决定下一个operator用哪个。

我们把第三代框架总结为:a dynamic graph of operators

故事没有结束......

如我们所知,世上没有免费的午餐,flexibility is often at odds with performance。第三代框架是以牺牲性能为代价来换取灵活性的,怎么在提供灵活性的同时获得更多性能,MxNet Gluon提供了Hybridize接口,如名所示,我们正走在微妙地平衡2.0和3.0的道路上。

你可能感兴趣的:(深度学习框架演进简史)