从我开发的深度学习框架看深度学习这几年:TensorFlow, PaddlePaddle(飞桨), 无穷
起源:https://zhuanlan.zhihu.com/p/363271864l
和深度学习框架打交道已有多年工夫。从Google的TensorFlow, 到百度的PaddlePaddle,再到当初腾讯的无穷。很庆幸在AI技术暴发的这些年横跨中美几家公司,站在一个比拟好的视角看着世界产生微小的变动。在这些经验中,视角在一直切换,从最早的算法钻研,到起初的框架开发,到机器学习平台和更多基础架构,每一段都有不同的感触和更深的领悟。
清明节这几天有些工夫写了这篇文章,从我的视角,用几个深度学习框架串起来这些年历史上的一些乏味的插曲,和技术背地的一些故事,省得贵重的记忆随着工夫在脑中淡去。
TensorFlow
入门
故事开始在2015年底,我完结了在Google Core Storage和Knowledge Engine的工作,退出了Google Brain,在Samy Bengio下负责一名Research Software Engineer,简称RSWE。RSWE角色的产生次要是因为Google Brain和DeepMind发现Research Scientist很难在钻研中解决简单的工程问题,并最终技术落地。因而须要卷入一些工程能力比拟强的Engineer和Scientist一起工作。而我比拟“侥幸”的成为Google Brain第一个RSWE。
退出新组的前一个周末,十分兴奋的提前探访了Google Brain的办公地点。想到能近距离在Jeff Dean旁边工作还是有些小冲动,毕竟是读着MapReduce, BigTable, Spanner那些论文一路成长起来的。办公场合没有特地,Jeff和大家一样坐在一起,比拟意外的是发现我工位旁几米的办公室门牌上写着谷歌创始人Larry Page\&Sergey Brin,办公室被许多奖杯,证书,太空服之类的杂物突围着。看来公司对于AI技术的器重水平实在十分的高。
言归正传,晚期的TensorFlow比拟缺模型示例,相干API文档还不太标准,于是先开始给TensorFlow搭建模型库。我花了一年工夫把Speech Recognition, Language Model, Text Summarization, Image Classification, Object Detection, Segmentation, Differential Privacy, Frame Prediction等模型写了一遍,起初成为TensorFlow github上model zoo的雏形。那年还是个到处都是高扬果实的时候,没有GPT3这种极其烧钱的大模型,只有对模型做一些小的调整,扩充模型的规模,就能刷新State-Of-The-Art。Bengio大佬常常在世界各地云游,偶然回来后的1v1还是能给我不少的指引。印象粗浅的第一次面聊,这位写过几百篇论文的一字眉大神给刚刚入门的我在白板上手推了gradient descent的一些公式。另外一次1v1,他发给我一本Ian Goodfellow写的书(过后还是草稿pdf),而后我每天晚上就躺在茶水间的沙发上一边做试验一边读书。
那年还产生了件有意思的插曲,AlphaGo大战人类棋手。DeepMind和Brain有十分严密的单干关系,组里组织了一轮paper reading,认真研读了相干的paper,而后大家带上啤酒和零食组织了观战流动,感觉就有点像是在看球赛。那次学会了两件事,强化学习算法,还有围棋的英文是Go。
16年是TensorFlow高速倒退的一年。Jeff的演讲里常常有TensorFlow代码被援用次数指数级暴涨的图。然而16年也是TensorFlow被喷的比拟惨的一年。TF的Operator粒度是十分细的,据说这是从外部上一代框架DistBelif上汲取的教训。细粒度的Operator能够通过组合造成各种高层的Layer,具备更好的灵活性和扩展性。然而,对于性能和易用性来说却是比较严重的问题,一个模型轻易就有几千个甚至跟多的Operator。举几个例子:
过后我要实现第一个基于TensorFlow的ResNet,光为了写一个BatchNormalization(查了好几个外部版本居然都有些问题),须要通过5~10个细粒度的算子通过加减乘除的形式组装起,1001层的ResNet有十分多的BatchNorm,整个ResNet有成千上万个Operator,可想而知,性能也不怎么样。不久后我敌人Yao搞了个Fused BatchNormalization,据说能让整个ResNet提速好几成。
BatchNormalization只是高级难度,做Speech Recognition时为了在Python层用TensorFlow实现BeamSearch也花了不少功夫。过后写了个End-to-End的模型,用的是Seq2Seq with Attention,可能一个模型间接把声音转成文字。为了把搜寻生产线上语音辨认的数据训到最初收敛,用128个GPU整整花了2个月的工夫。每天早上下班第一件事就是关上TensorBoard,放大后能力看到Loss又降落了那么一点点。
16年时TensorFlow训练模式次要是基于Jeff等几位的Paper,基于参数服务器的异步训练是支流。训练速度线性扩展性不错,然而明天基于ring的同步训练在NLP,CV这些畛域的声音更响一些。记得第一次和Jeff独自交换是对于Speech Recognition分布式训练的试验状况,加到128个GPU做异步训练根本能保障线性扩大,然而基于SyncOptimizer的同步训练速度会慢很多。过后Jeff问了下收敛成果有没有收到影响,我懵了一下,说没有仔细分析过,连忙回去查一下。顺便八卦一下,Jeff真是十分瘦,握手的时候感觉简直就剩皮包骨了。
开发过一些模型后发现算法研究员其实还有不少痛点。1. 不晓得怎么Profile模型。2. 不晓得怎么优化性能。为了解决这两个问题,我抽空写了个tf.profiler。tf.profiler的原理比较简单,就是把Graph, RunMeta和一些其余的产物做一些剖析,而后用户能够通过CLI,UI或者python API疾速的去分析模型的构造,Parameter, FLOPs, Device Placement, Runtime等属性。另外还做了个外部数据的抓取工作,去抓算法研究员的训练任务的metrics,如果发现GPU利用率异样,网络通行量过大,数据IO慢时会主动发邮件揭示,并给出一些批改的倡议。
让一个分心搞算法钻研的人写一个白板的数学公式不难,然而让他去搞明确简单的工作配置,分布式系统里的性能、资源、带宽问题确是件很艰难的事。无论如许牛的研究员都会问为什么工作没能跑起来,是资源不够还是配置不对。记得有天黄昏,人不多,Geoffrey Hinton大神忽然走过去问到Can you do me a favor?My job cannot start...(正当我筹备许可时,Quoc Lee曾经领先接单了,真是个精力的越南小哥。。。)
Moonshots
Google Brain每年会组织一次Moonshots提案,许多起初比拟胜利的我的项目都是这样孵化进去的,比方AutoML,Neural Machine Translation等等。团队成员会提出一些过后技术比拟难达到的我的项目,大家组成相似兴趣小组的模式投入到这些我的项目中。
当初火的一塌糊涂的AutoML有点因为商业化或者其余起因,感觉曾经对原始的定义做了极大的拓展。过后Brain孵化这个我的项目的时候有两队人在做LearningToLearn的我的项目,一个小队心愿通过遗传算法来搜寻更优的模型构造,另一个小队则决定应用强化学习算法搜寻。遗传算法小队在应用资源时比拟审慎,通常只应用几百个GPU。而另一个小队则应用了几千个GPU。最初强化学习小队更早的做出了成绩,也就是Neural Architecuture Search。而另一个小队尽管起初应用更多的GPU也达到了相似的成果,然而要晚了不少。
一个比拟乏味的插曲是Brain尽管很早就有几万张GPU,然而每当论文截稿的前一段时间总是不够用,其中搞NAS的同学经常在邮件中被暗示。为了解决资源的调配问题,领导们被卷入了一个十分长的email,起初大略解决方案是每个人会被调配大量的高优先级GPU和适量的竞争级GPU资源。而NAS的同学因为曾经实现了资本的原始积累成为了一个很火的我的项目,失去了特批的独立资源池。为了反对这个策略,我又开发了个小工具,当初回头想想还挺吃力不讨好的。
动态图
疾速成长的工夫总是过得很快,Megan退出Brain后,我被安顿向她汇报,过后的RSWE团队曾经有十几人,而Google Brain也从几十人变成了几百人。
2017年初,经Megan介绍,TensorFlow团队一位资深专家Yuan Yu找到我,问有没有关注Pytorch,约我调研后一块聊聊。于是我就去网上收集了一下Pytorch的材料,又试用了一下。作为一个TensorFlow的深度用户,我的第一反馈就是Pytorch解决了TensorFlow很大的痛点,用起来十分的“天然”。
和Yuan聊完后,咱们疾速的决定在TensorFlow上也尝试反对相似Pytorch的imperative programming用法。Demo的开发过程还算比较顺利,我大略花了一个多月的工夫。记得过后我把我的项目命名为iTensorFlow, short for imperative TensorFlow。(起初被改名成eager,感觉好奇怪)。
Demo的设计思路其实也不简单:1. TensorFlow graph能够被切分成任意粒度的Subgraph,能够通过函数调用的语法间接执行,2. TensorFlow对用户通明的记录执行过程以用于反向梯度计算。给用户的感觉就就相似Python native的运行。
进而产生几个推导:1. 当Subgraph的粒度是operator时,根本等价于Pytorch。2. 当Subgraph粒度由多个operator组成时,保留了graph-level optimization的能力,能够编译优化。
最初再埋个伏笔:1. tf.Estimator能够主动的去交融Subgraph,造成更大的Subgraph。用户在开发阶段基于imperative operator-level Subgraph能够简略的调试。用户在部署阶段,能够主动交融大的Subgraph,造成更大的optimization space。
做完之后,我十分兴奋的和Yuan演示成绩。Yuan也说要帮我在TensorFlow外面推这个计划。过后Pytorch的成长速度十分的快,TensorFlow的Director也招集了多名专家级的工程师同时进行计划的摸索。 过后我还没能进入TensorFlow的决策层,最终失去的论断是1. 让咱们成立一个虚构组专门做这个我的项目。2. 之前的Demo全副推倒从新做,TensorFlow 2.0作为最重要Feature 公布,默认应用Imperative Mode (后改名叫Eager Mode,中文经常叫动态图)。我则作为团队的一员在我的项目中奉献来一些代码。
起初Brain来了位新的大神,Chris Lattner,在编程语言和编译畛域钻研的同学预计很多意识他。他提出来心愿用Swift来实现Deep Learning Model的Progamming,也就是起初的Swift for TensorFlow。理由大略是Python是个动静的语言,很难动态编译优化。起初我和他深刻探讨来几次,从技术上十分同意他的观点,然而也明确的示意Swift for TensorFlow是一条很难走的路。用Python并不是因为Python语言如许好,而是因为很多人用Python。和Chris的一些交换中我对编译过程中的IR和Pass有了更深的理解,对起初在PaddlePaddle中的一些工作产生了不少的影响。
一个插曲是某位TensorFlow团队的资深专家有次轻轻和我说:Python is such a bad language。这句话我品尝了良久,不过和他一样没有勇气大声说进去。。。
过后动态图的我的项目还延展出两个比拟乏味的我的项目。有两个其余团队的哥们想对Python做语法分析,进而编译control flow。我很婉转的示意这个计划做成通用解决方案的可能性不太大,然而这个我的项目仍然被很执着的做了很长一段时间,并且进行了开源,然而这个我的项目也就缓缓死于非命了。另一个很酷的我的项目是齐全用numpy来结构一个deep learning model。通过隐式的tape来实现主动的求导,起初我的项目如同逐步演化成来JAX我的项目。
API
前面我逐步转到了TensorFlow做开发。记得2017年还产生了一件印象粗浅的事件,当TensorFlow播种海量用户时,网上一篇“TensorFlow Sucks"火了。尽管那篇文章很多观点我不能苟同,许多想法比拟浮浅。然而,有一点不能否定,TensorFlow API是比拟让人蛋疼的。1. 同一个性能往往几套反复的API反对。2. API常常变动,而且常常产生不向后兼容的问题。3. API的易用性不高。
为什么会产生这个问题呢?可能要从Google这个公司的工程师文化说起。Google是十分激励自在翻新和跨团队奉献的。常常会有人给另一个团队奉献代码,并以此作为有影响力的论据参加降职。所以在晚期TensorFlow还不是特地欠缺的时候,常常有内部的团队给TensorFlow奉献代码,其中就蕴含了API。另外,在Google外部的对立代码仓库下,放出去的API是能够很容易的降级批改的,很多时候只须要grep和replace一下就行。然而github上放出去的API齐全不一样,Google的员工不能去批改百度,阿里,腾讯外部的TensorFlow应用代码。对此TensorFlow团队晚期确实没有十分无效的计划,起初才呈现了API Committee对public API做对立的把关和布局。
在我做视觉的时候,和Google外部一个视觉团队有过很多单干,其中一个是slim API。这个视觉团队十分的强,当年还拿了CoCo的冠军。随着他们模型的推广风行,他们的tf.slim API也被广为流传。slim API的arg\_scope应用了python context manager的个性。相熟晚期TensorFlow的人晓得还有tf.variable\_scope, tf.name\_scope, tf.op\_name\_scope等等。with xxx\_scope一层套一层,简单的时候代码简直没有什么可读性。另外就是各种global collection,什么global variable, trainable variable, local variable。这在传统的编程语言课里,全局变量这种货色可能是拿来当反面教材的。然而,算法人员的视角是不一样的,with xxx\_scope和global collection能缩小他们的代码量。尽管咱们晓得正当的程序设计办法也能够做到,然而算法专家预计须要把工夫用来读paper,不太违心钻研这些程序设计的问题。
记得在晚期外部还有两个流派的争执:面向对象和面向过程的API设计。
基于我教育历史的洗脑,感觉这个是不须要争执的问题。Keras的Layer class和Pytorch的Module class这些面向对象的接口设计无疑是十分优雅的。然而,其实过后确实产生了十分强烈的争执。一些functional API的作者认为functional的调用十分节俭代码量:一个函数就能够解决的问题,为什么须要先结构一个对象,而后再call一下?
在TensorFlow动态图能力开发的晚期,咱们也重复探讨了2.0外面接口的设计方案。作为炮灰的我又接下了写Demo的工作。
闭关两周后,我给出了一个计划:1. 复用Keras的Layer接口。2. 然而不复用Keras的Network,Topology等其余更高层的简单接口。
起因次要又两点:1. Layer是十分简洁优雅的,Layer能够套Layer,整个网络就是一个大Layer。Layer形象成construction和execution两阶段也十分天然。2. Keras有很多历史上为了极简设计的高层接口。我集体教训感觉很难满足用户灵便的需要,并不需要官网提供。而且这样可能会导致TensorFlow API层适度简单。
起初计划被驳回了一半,大佬们心愿可能更多的复用Keras接口。其实没有完满的API,只有最适宜某类人群的API。有个小插曲,过后Keras的作者François也在Google Brain。为了在TensorFlow 2.0的动态图和动态图同时应用Keras的接口,不得不在Keras API内做很多革新。通常François在Review代码时都十分的不愿意,然而最初又往往斗争。很多时候,特地是技术方面,假相可能在多数不被大多数人了解的人手上,须要工夫来发现。
TPU
感觉互联网公司那几年,真正把AI芯片做得成熟且宽泛可用的,只有Google一家。TPU始终都是Google Brain和TensorFlow团队关注的重点。起因可能是Jeff老是提起这件事,甚至一度在TensorFlow搞GPU优化是件很没前途的事件。
TPU有个比拟特地的中央,在于bfloat16的类型。现在bfloat16,还有英伟达最新GPU上的TF32都曾经被广为理解了。在过后还是个不小的翻新。
bfloat16的原理非常简单,就是把float32的后16bit全副截掉。和IEEE的float16相比,bfloat16的mantissa bits会少一些,然而exponential bits会多一些。保留更多的exponential bits有利于gradients很靠近0时不会隐没,保障bfloat16训练时可能更好的保留模型的成果。而传统基于float16训练时,往往须要做loss scaling等调试能力达到相似的成果。因而bfloat16能让AI芯片更快的运算,同时又确保收敛成果通常不会有损失。
为了在TensorFlow上全面的反对bfloat16,我过后花了不少的功夫。尽管之前有基于bfloat16通信的计划,然而要在所有中央都无缝买通bfloat16,还有十分多的工作要做。比方eigen和numpy都不反对bfloat16这种非凡的货色。幸好他们都能够扩大数据类型(就是文档太少了)。而后还要修复成千盈百个fail掉的unit tests来证实bfloat16能够在python层齐备的应用。
TPU是一个十分高难度,跨团队,跨技术栈的简单工程。据说Google有位十分优良的工程师,为了在TPU上反对depthwise convolution一个TPU kernel,花掉了半年的工夫。
其实这一点也不夸大,除了底层的硬件设计,单是将tensorflow graph编译成硬件binary的XLA我的项目晚期就至多卷入几十人。从HLO到底层的target-specific code generation,简直又重写了一遍TensorFlow C++层,远比之前的解释型执行器简单。
TPU的训练在底层跑通后,我基于底层接口的根底上实现Python层的撑持API,而后去实现几个模型。过后碰到了好几个难题,有些在几周工夫内解决了,有些继续到我不再团队后好些年。这里举几个例子。
- 过后一个TPU Pod(如同是512个chips)算得太快了,即便是很简单模型的计算也会卡在数据的IO和预处理上。起初搞了个分布式的data processing,通过多个CPU机器来同时去解决数据,能力喂饱TPU。
- 早器的TPU API易用性比拟弱。通常一个model须要在TPU上train几百步而后再返回python层,否则TPU的性能会飞快的进化。这对于算法人员是很不敌对的,这意味着debug能力的缺失,以及大量简单模型无奈实现。记得当年OKR被迫升高为反对常见的CV模型。
- TPU如何反对动态图。记得我过后迫于TPU的束缚,做了个所谓的JIT的能力。就是Estimator先在CPU或者GPU上迭代N步,实现模型的初步调试,而后再主动的deploy到TPU上。从算法人员角度,既满足单步调试的能力,又能在次要training过程用上TPU。
团队
Google Brain是个很神奇的团队,比拟不客气的说,在2015年后的几年间包揽了全世界在深度学习畛域一半以上的关键技术冲破,比方TPU,TensorFlow, Transformer, BERT, Neural Machine Translation, Inception, Neural Architecture Search, GAN,Adverserial Training, Bidrectional RNN等等。这里不只有深度学习畛域的图灵奖获得者,还有编程语言、编译器、计算机体系结构、分布式系统的顶级专家,甚至还有生物,物理学专家。Jeff将这些人放在一起后,产生了神奇的化学反应,放慢了技术扭转世界的步调。
PaddlePaddle 飞桨
Paddle其实诞生工夫比拟早,据说是大概13~14年的时候徐伟老师的作品。起初据说Andrew Ng感觉Paddle叫一次不过瘾,就改名成了PaddlePaddle。Paddle和那个年代的框架Caffe有相似的问题,灵活性不够。很多中央用C++写成比拟粗粒度的Layer,无奈通过Python等简略的编程语言实现模型的疾速结构。
起初17年下半年,团队开始齐全从新写一个框架,然而继承了Paddle的名字。2017年底的时候,Paddle国内的团队找到了我,邀请我负责Paddle国内研发团队的负责人。抱着打造国产第一框架的现实,我承受了邀请,一个月后就在北京入职了。
晚期设计
退出团队的时候,新的Paddle还是一个比拟晚期的原型零碎,外面有一些设计曾经被开发了进去。我发现其中有些设计理念和TensorFlow有显著的差别,然而实现的时候却又模拟了TensorFlow。
仿编程语言
设计者心愿设计一种编程语言来实现深度学习模型的构建(有点相似Julia等把深度学习模型的个性嵌入到了编程语言中)。然而在实现上,我发现其实和TensorFlow比拟相似。都是通过Python去申明一个动态模型构造,而后把模型构造交给执行器进行解释执行。并没有创造一种新的深度学习编程语言。
这块我根本没有对设计进行调整。实质上和TensorFlow晚期动态图的没有区别。然而在细节上,TF基于Graph的模型能够通过feed/fetch选择性的执行任意一部分子图,更加灵便。Paddle中与Graph对应的是Program。Program就像失常程序一样,只能从头到尾残缺的执行,无奈选择性的执行。因而Paddle在这块绝对简化了一些,然而能够通过在Python层结构多个Program的形式补全这部分灵活性的缺失,总体来说表达能力是足够的。
Transpiler
Transpiler是对Program进行间接改写,进而能够让模型可能被分布式运行,或者进行优化。初衷是比拟好的,能够升高算法人员的应用难度。然而在实现上,最开始是在Python层间接对Program构造进行改写。起初我从新设计了IR+Pass的Compiler体系,通过一种更系统性的形式做了实现。
LoDTensor
可能是因为团队的NLP和搜寻背景比拟强,对于变长序列的器重水平很高。Paddle的底层数据是LoDTensor,而不是相似其余框架Tensor。LoDTensor相当于把变长序列信息耦合进了Tensor外面。这可能导致比拟多的问题,比方很多Operator是齐全序列无关的,根本无法解决序列信息在输出Tensor和输入Tensor的关系,进而比拟随机的解决,给框架的健壮性埋下隐患。尽管我始终想推动序列信息和Tensor的解耦合,然而因为种种原因,没有彻底的实现这个重构的指标,心愿前面能改掉。
性能
18年初的时候,Paddle还是个原型零碎。因为OKR指标,团队曾经开始初步接入一些业务场景。其实一个比拟大的痛点就是性能太差。单机单卡速度十分慢,单机4卡减速比只有1.x。然而性能问题的定位却十分艰难。我花了些工夫写了些profile的工具,比方timeline。一些显著的性能问题能够被疾速的定位进去并修复。
然而单机多卡的速度还是十分慢,timeline剖析后发现其中有个ParallelOp,存在大量的Barrier。最初改写成了ParallelExecutor,把Program复制了N份部署在多张卡上,在其中插入AllReduce通信算子,而后这N倍的算子基于图依赖关系,一直把ready的算子扔进线程池执行。即便这样,咱们也发现在多卡的性能上,不同模型须要应用不同的线程调度策略来达到最优。很难有一种完满的one-fits-all的计划。前面咱们再聊如何通过IR+Pass的办法插件化的反对不同的算子调度策略。
分布式的训练也碰到不少的问题。一开始应用grpc,花了挺大的功夫做并行申请,而后又切成了brpc,在RDMA等方面做了不少的优化。分布式训练的性能逐渐失去了晋升。另外为了做到自动化分布式部署,后面提到的Transpiler随着场景的减少,Python代码也变得越来越简单。
模型推理在公司内碰到来十分强劲的对手。Anakin的GPU推理速度确实很快,让我吃惊的是他们居然是用SASS汇编实现大量根底算子的开发,针对Pascal架构做了异样极致的优化,甚至在某些场景远超TensorRT。我始终主张训练和推理要尽量用一样的框架,并不需要一个独自的推理框架来解决性能问题。应用不同的框架做推理会造成很多意外的精度问题和人工开销。
因为推理性能的问题,咱们和兄弟团队产生来旷日持久比赛,作为狗头军事,我充分发挥来团队在CPU这块的技术积攒、以及和Intel外援的良好关系,在CPU推理场景经常稍逊一筹。在GPU方面苦于对手无底线应用汇编,和我方阵线太多、人员不够,只能战略性放弃了局部头部模型,通过反对子图扩大TensorRT引擎的形式,利用Nvidia的技术劣势在许多个通用场景下开展防御。当初回想起来实在一段乏味的经验。
Imtermediate Representation\&Pass
Imtermediate Representation+Pass的模式次要是从LLVM的架构上借鉴来的。在编译器上次要是用来解决把M个编程语言中任意一个编译到N个硬件设施中任意一个执行的问题。简略的解决方案是为每个编程语言和硬件独自写一个编译器。这须要M*N个编译器。显然这对于简单的编译器开发来说,是十分高老本的。
Intermediate Representation是架构设计中形象能力的典型体现。不同编程语言的档次不一样,或者仅仅是单纯的反对的性能有些差别。然而,这些编程语言终归须要在某种硬件指令集上执行。所以在编译的过程中,他们会在某个抽象层次上造成共性的表白。而IR+Pass的办法很好的利用了这一点。其根本思维是通过多层Pass (编译改写过程),逐步的把不同语言的表达方式在某个档次上改写成对立的IR的表达方式。在这个过程中,表达方式逐步靠近底层的硬件。而IR和Pass能够很好的被复用,极大的升高了研发的老本。
深度学习框架也有着十分相似的需要。
- 用户心愿通过高层语言形容模型的执行逻辑,甚至是仅仅申明模型的构造,而不去关怀模型如何在硬件上实现训练或者推理。
- 深度学习框架须要解决模型在多种硬件上高效执行的问题,其中包含协同多个CPU、GPU、甚至大规模分布式集群进行工作的问题。也包含优化内存、显存开销、进步执行速度的问题。
更具体的。前文说到须要可能主动的将用户申明的模型Program主动的在多张显卡上并行计算、须要将Program拆分到多个机器上进行分布式计算、还须要批改执行图来进行算子交融和显存优化。
Paddle在一开始零散的发展了下面形容的工作,在分布式、多卡并行、推理减速、甚至是模型的压缩量化上各自进行模型的改写。这个过程非常容易产生重复性的工作,也很难对立设计模式,让团队不同的研发疾速了解这些代码。
意思到这些问题后,我写了一个Single Static Assignment(SSA)的Graph,而后把Program通过第一个根底Pass改写成了SSA Graph。而后又写了第二个Pass把SSA Graph改写成了能够多卡并行的SSA Graph。
前面的事件就应该能够以此类推了。比方推理减速能够在这个根底上实现OpFusionPass, InferenceMemoryOptimizationPass, PruningPass等等,进而达到执行时推理减速的目标。分布式训练时则能够有DistributedTransPass。量化压缩则能够有ConvertToInt8Pass等等。这一套货色根本解决了下层Program申明到底层执行器的Compiler问题。
这个过程中确实碰到了不少的阻力。比方分布式晚期通过Python实现了这个逻辑,须要迁徙到C++层。压缩量化的研发更喜爱写Python,而IR\&Pass是基于C++的。不同Pass间程序依赖和Debug等。
全套深度学习框架工具
TensorFlow Everywhere本来是TensorFlow团队时的一个口号,意思是TensorFlow须要反对深度学习模型在任意的场景下运行,进而达到AI Everywhere的指标。能够说深度学习框架心愿成为AI的“操作系统”,就像鱼离不开水、App离不开iOS/Android一样。
Paddle作为全面对标TensorFlow的国产深度学习框架,天然也心愿提供全套的解决方案。在晚期的时候,Paddle和公司其余团队单干了PaddleMobile,提供了挪动端的推理能力。起初又发展了Paddle.js,反对在H5、Web等场景的推理能力。为了在toB,在Linux的根底上又新增了Windows的反对。为了反对无人车等设施、又反对了在更多不同设施上运行。
举个PaddleMobile的例子。深度学习框架想再挪动设施上部署面临这比拟多的挑战。手机的空间和算力都比服务器小很多,而模型最开始在服务器训练好后体积绝对较大,须要从很多角度下手。1. 应用较小的模型构造。2. 通过量化,压缩等伎俩削减模型体积。
另外挪动段深度学习框架是通常基于ARM CPU,GPU则有Mali GPU, adreno GPU等等。为了最求比拟极致性能,经常须要应用汇编语言。有个同学写到前面简直狐疑人生,感觉本人大学学的货色不太对。为了不显著减少APP的体积,框架编译后的体积须要在KB~几MB的级别,因而须要基于部署的模型构造自身用到的算子进行选择性编译。极其的时候甚至须要是通过C++ Code Gen的办法间接生成前向计算必须的代码,而不是通过一个通用的解释器。
回顾
随着我的项目的复杂化,很多辣手的问题逐步从深度学习的畛域技术问题转变成了软件工程开发和团队治理分工的问题。随着团队的一直变动,本人有时候是作为一个leader的角色在解决问题,有的时候又是以一个independent contributor的角色在参加探讨。很庆幸本人经验过这么一段,有些问题在亲身经历后能力想得明确,想得开。时代有时候会把你推向风口浪尖,让你带船队扬帆起航,在更多的时候是在一直的斗争与摸索中寻找后退的方向。
无穷
无穷是腾讯PCG建设的一个深度学习框架,次要心愿解决大规模举荐场景下的训练和推理问题。深度学习在举荐场景的利用和CV、NLP、语音有些不一样。
- 业务会继续的产生用户的行为数据。当用户规模达到数千万或者上亿时就会产生海量的训练数据,比方用户的画像,用户的点击,点赞,转发行为,还有Context等等。
- 这些数据是高度稠密的,通常会编码成ID类的特色进而通过Embedding的形式进入模型训练。随着业务规模的晋升和特色工程日渐简单,比方累计用户数,商品,内容减少,以及特色穿插的应用,Embedding参数的体积能够达到GB,甚至TB级。
- 举荐场景是实时动态变化的,新用户,内容,热点一直产生。用户的趣味,用意逐步变动,因而模型须要继续一直的适应这些变动,时刻放弃最好的状态。
调整
19年中这个我的项目时大略有2~3人。团队心愿开发一个新的版本,基于TensorFlow进行扩大增强,使得无穷能够复用TensorFlow已有的能力,并且可能反对举荐场景下的非凡需要。无穷一开始采纳的是基于参数服务器的架构。TensorFlow被复用来提供Python API以及实现根底算子的执行。而参数服务器,分布式通信等方面则是本人开发,没有复用TensorFlow。
这个抉择在团队过后的状况下是比拟正当的。如果抉择另一种方向,基于TensorFlow底层进行革新,研发难度会比拟大,而且很可能与社区版TensorFlow走向不同的方向,进而导致TensorFlow版本难以降级。而把TensorFlow作为一个本地执行的lib则能够在外围开发,不须要理解TensorFlow外部的简单逻辑,也能够复用一些其余开源组件,比方pslib。
晚期在软件开发的流程上绝对比拟欠缺。为了保障工程的推动,我先帮忙做了些根底工作,比方加上了第一个自动化测试和继续集成,对一些适度封装和奇怪的代码做了重构和简化。
另外,在接口层也做了一些调整。原来框架开始执行后就进入C++执行器,无奈从python层提供或者返回任何执行后果,也无奈在python层执行逻辑进行插件化的扩大。为了满足预期用户未来须要进行调试的需要,我模仿tf.Session和tf.Estimator对执行层的接口做了重构。这样用户能够通过feed/fetch的形式单步调试执行的过程。也能够通过Hook的形式在执行前后扩大任意的逻辑,进步框架的实用场景。
另外一个问题是python层根本齐全是全局变量,很难进行多模型的封装。像TensorFlow有Graph实例或者Paddle有Program实例。因为python层须要重构的量比拟大,我临时先退出了Context的封装,勉强将各种状态和配置封装在了Context下。思考到短期可能不会有更简单的需要,临时没有把这件事做完。
reader那块也做了一些重构。最开始那块的线程模型异样简单,一部分是因为分布式文件系统等基础设施无奈提供比拟好的SDK,导致许多逻辑不得不在深度学习框架外面,比方文件的本地缓存。思考到特色加工的逻辑比较复杂,以及一些老的TensorFlow用户可能习惯于tf.Example和tf.feature\_column等根底算子库,我在reader层引入了基于TensorFlow的tf.dataset。不过起初发现用户仿佛更关怀性能问题,喜爱自定义C++ lib的形式来解决特色解决的问题。
API设计是个老大难的问题。TensorFlow,Paddle,无穷都没能幸免。在一个多人协同的团队里,每个研发更多还是关注每个独立性能是否实现开发,而性能的接口往往须要思考到整体的API设计格调,易用性,兼容性等许多因素,经常在高速迭代的过程中被疏忽掉。可怜的是API经常不能像外部实现一样前期优化。当API被放给用户应用后,后续的批改往往会毁坏用户代码的正确性。很多时候只能本人评审一下。
降级
无穷通过一年根底能力的打磨,逐步的成为来整个事业群对立的大规模举荐模型训练和推理框架,撑持数十个业务场景,每天都能生产数千个增量和全量的模型。简略的实现性能曾经不能满足业务和团队倒退的需要,须要在技术上更加前沿。
数据处理
数据格式上要从原来的明文转到更高效的二进制。另外基于CSR编码的稠密数据能够进一步的缩小数据处理时的拷贝等额定开销。
流水线
尽量开掘训练中能够并行的中央,通过流水线的形式进步并发度,进而进步训练的速度。比方在数据读取的过程中,就能够提前依照参数服务器的规模对数据进行预切分,并告知参数服务器须要提前准备哪些参数。这样当pull/push的时候可能更快的实现计算,进而进步每个minibatch的速度。
同样,当应用GPU训练时,也能够在数据IO的并行过程中,预计算将来须要用的的Embedding参数。这样GPU训练下一轮的数据时,须要用到的Embedding曾经提前被计算好,能够间接开始训练,缩小来期待的工夫。
定制化参数服务器
因为无穷解决的一个关键问题是举荐模型的海量参数问题,因而参数服务器必须是高度优化过的。并且应该正当的将举荐模型的畛域常识引入到设计中,通过非凡的策略进一步产生差异化的劣势。
定制化的线程模型,内存治理和HashMap。因为参数是被切分归属到不同线程上,所以能够通过无锁化的把每次pull/push的参数解决好。另外因为海量参数耗费较大硬件老本,内存空间都须要通过定制化的内存池来治理。否则很可能有大量的空间碎片在默认内存库中无奈及时归还给操作系统。另外也有无奈精细化管制内存清理机制,导致内存OOM或者节约。定制化的内存治理能够解决这些问题,甚至通过非凡的内存淘汰策略,在不侵害模型成果的根底上进一步升高内存的开销。高性能HashMap则是须要解决Embedding疾速的增删改查的问题。
Embedding向量的治理也是有十分多能够改良的中央。1. 动静的扭转Embeding向量的长度来反对模型的压缩,进步模型成果。2. 扩大Embedding的元数据来记录热度,点击展示等统计值,有助于进步训练推理时高级分布式架构的Cache命中率,曾经模型的训练成果。3. 模型的复原和导出机制在大规模Embedding场景对于Serving时可能实时加载模型更新重要。另外还须要思考到工作失败重启后资源伸缩等问题。
GPU训练
传统PS架构的训练模式下,因为单台机器的计算能力无限,须要几十甚至上百个实例进行分布式训练。然而这样会导致大量的计算被用在来有效的开销上。比方稠密特色在网络通信两边的解决。这种额定开销甚至常常超过无效计算。
GPU和相应的高速网络链接能够解决这一问题。单台8卡机器通过NVLink连接起来,速度甚至能够超过几十台物理机,有更高的性价比。然而因为几百GB,甚至TB级的参数问题,还有Embedding的GPU计算问题,导致GPU始终都没有被宽泛的用起来。
然而试验发现其实稠密特色存在显著的Power-law散布,少部分Hot特色应用远多于其余大量不Hot的特色。因而,通过在数据处理时统计特色,而后批量将未来新须要的Embedding换入GPU,就能够让GPU长时间的进行间断训练,而不须要频繁的和CPU内存替换参数。
GPU预测
随着举荐模型复杂度进步,引入传统CV,NLP的一些构造须要耗费更多的计算。CPU往往很难在无效的时间延迟下(几十毫秒)实现大量候(几百上千)全集在简单举荐模型的推理。而GPU则成为了一个潜在的解决方案。
同样,GPU推理也须要解决显存远小于Embedding参数的问题。通过在训练时事后计算Hot Embedding,而后加载如推理GPU,能够肯定水平的缓解这个问题。在推理时仅有少部分的Embedding没有在GPU显存中缓存,须要通过CPU内存拷贝进入GPU。
而通过模型的量化和压缩能进一步缩小Embedding参数的规模。试验表明当大部分Embedding参数的值管制为0时,模型仍然可能体现出原来的成果,甚至略优。
总结
深度学习算法的倒退和深度学习框架的倒退是相辅相成,互相促进的。从2002年时Torch论文发表后,框架的技术倒退绝对迟缓,性能无奈显著晋升导致无奈摸索更加简单的算法模型,或者利用更加大规模的数据集。
在2010年后逐步呈现了Caffe, Theano等框架,通过将更高性能的GPU引入,能够训练更加简单的CNN和RNN模型,深度学习算法的倒退呈现来显著的减速。
到了2014\~2017年几年间,TensorFlow的呈现让用户能够通过简略的Python语言将细粒度的算子组装各种模型构造。并且模型能够简略的被分布式训练,而后自动化部署在服务器,手机,摄像头等各种设施上。而Pytorch的动态图用法满足了钻研人员对易用性和灵活性更高的要求,进一步推动算法钻研。
国内的深度学习框架技术在这股浪潮中也紧跟这世界的步调。Paddle在14年左右产生,在国内积攒了肯定的用户,在过后根本能比肩其余的框架。尽管在TensorFlow和Pytorch等更先进的框架呈现后,国内错过了贵重的几年技术升级的窗口以及社区生态培养机会,然而咱们看到从18年到20年间,新版的PaddlePaddle,OneFlow, MindSpore等深度学习框架陆续开源,技术上逐步赶了上来。
举荐场景在电商,视频,资讯等泛滥头部互联网公司的火爆导致举荐系统对AI硬件的耗费在互联网公司超过了传统NLP,CV,语音等利用的总和。许多公司开始针对举荐场景(以及广告,搜寻)的非凡需要对深度学习框架进行定制优化。百度的abacus是比拟晚期的框架,和其余晚期框架一样,在易用性和灵活性上较弱。无穷,XDL等框架则进行了改良,兼顾了社区兼容性,算法易用性和零碎的性能等纬度。
深度学习的框架的触角其实远不止咱们常见到的。随着AI技术的推广,Web、H5、嵌入式设施、手机等场景下都有许多优良的深度学习框架产生,如PaddleMobile, TFLite,tensorflow.js等等。
深度学习框架的技术也逐步从更多纬度开始拓展。ONNX被提出来作为对立的模型格局,尽管离指标有很长的间隔和问题须要解决。然而从它的风行咱们能看到社区对于框架间互通的渴望。随着摩尔定律难以维持,框架开始更多的从新的硬件和异构计算畛域寻求冲破。为了反对海量的算子在CPU、FPGA、GPU、TPU、NPU、Cerebras等泛滥AI芯片上运行,TVM、XLA等借鉴编译技术几十年来的积攒,在更加艰巨的路线上进行来继续的摸索,常常能传来新进展的好消息。深度学习框架也不再仅利用于深度学习,还在科学计算,物理化学等畛域发光发热。
【我的项目举荐】
面向小白的顶会论文外围代码库:https://github.com/xmu-xiaoma666/External-Attention-pytorch
面向小白的YOLO指标检测库:https://github.com/iscyy/yoloair
面向小白的顶刊顶会的论文解析:https://github.com/xmu-xiaoma666/FightingCV-Paper-Reading
“点个在看,月薪十万!”
“学会点赞,身价千万!”
【技术交换】
已建设深度学习公众号——FightingCV,关注于最新论文解读、基础知识坚固、学术科研交换,欢送大家关注!!!
请关注FightingCV公众号,并后盾回复ECCV2022即可取得ECCV中稿论文汇总列表。
举荐退出FightingCV交换群,每日会发送论文解析、算法和代码的干货分享,进行学术交流,加群请增加小助手wx:FightngCV666,备注:地区-学校(公司)-名称
【赠书流动】
为感激各位老粉和新粉的反对,FightingCV公众号将在10月1日包邮送出4本《智能数据分析:入门、实战与平台构建》来帮忙大家学习,赠书对象为当日浏览榜和分享榜前两名。想要参加赠书流动的敌人,请增加小助手微信FightngCV666(备注“城市-方向-ID”),不便分割取得邮寄地址。
本文由mdnice多平台公布