TVM 是什么?A compiler stack,graph level / operator level optimization,目的是(不一样框架的)深度学习模型在不一样硬件平台上提升 performance (我要更快!)html
TVM, a compiler that takes a high-level specification of a deep learning program from existing frameworks and generates low-level optimized code for a diverse set of hardware back-ends.前端
compiler比较好理解。C编译器将C代码转换为汇编,再进一步处理成CPU能够理解的机器码。TVM的compiler是指将不一样前端深度学习框架训练的模型,转换为统一的中间语言表示。stack个人理解是,TVM还提供了后续处理方法,对IR进行优化(graph / operator level),并转换为目标硬件上的代码逻辑(可能会进行benchmark,反复进行上述优化),从而实现了端到端的深度学习模型部署。java
我刚刚接触TVM,这篇主要介绍了如何编译TVM,以及如何使用TVM加载mxnet模型,进行前向计算。Hello TVM!node
背景介绍
随着深度学习逐渐从研究所的“伊甸园”迅速在工业界的铺开,摆在你们面前的问题是如何将深度学习模型部署到目标硬件平台上,可以多快好省地完成前向计算,从而提供更好的用户体验,同时为老板省钱,还能减小碳排放来造福子孙。python
和单纯作研究相比,在工业界咱们主要遇到了两个问题:ios
- 深度学习框架实在是太TMTM多了。caffe / mxnet / tensorflow / pytorch训练出来的模型都彼此有不一样的分发格式。若是你和我同样,作过不一样框架的TensorRT的部署,我想你会懂的。。。
- GPU实在是太TMTM贵了。深度学习春风吹满地,老黄股票真争气。另外一方面,一些嵌入式平台没有使用GPU的条件。同时一些人也开始在作FPGA/ASIC的深度学习加速卡。如何将深度学习模型部署适配到多样的硬件平台上?
为了解决第一个问题,TVM内部实现了本身的IR,能够将上面这些主流深度学习框架的模型转换为统一的内部表示,以便后续处理。若想要详细了解,能够看下NNVM这篇博客:NNVM Compiler: Open Compiler for AI Frameworks。这张图应该可以说明NNVM在TVM中起到的做用。c++
为了解决第二个问题,TVM内部有多重机制来作优化。其中一个特色是,使用机器学习(结合专家知识)的方法,经过在目标硬件上跑大量trial,来得到该硬件上相关运算(例如卷积)的最优实现。这使得TVM可以作到快速为新型硬件或新的op作优化。咱们知道,在GPU上咱们站在Nvidia内部专家的肩膀上,使用CUDA / CUDNN / CUBLAS编程。但相比于Conv / Pooling等Nvidia已经优化的很好了的op,咱们本身写的op极可能效率不高。或者在新的硬件上,没有相似CUDA的生态,如何对网络进行调优?TVM这种基于机器学习的方法给出了一个可行的方案。咱们只需给定参数的搜索空间(少许的人类专家知识),就能够将剩下的工做交给TVM。若是对此感兴趣,能够阅读TVM中关于AutoTuner的介绍和tutorial:Auto-tuning a convolutional network for ARM CPU。git
编译
个人环境为Debian 8,CUDA 9。github
准备代码
config文件
编辑config文件,打开CUDA / BLAS / cuBLAS / CUDNN的开关。注意下LLVM的开关。LLVM能够从这个页面LLVM Download下载,我以前就已经下载好,版本为7.0。若是你像我同样是Debian8,能够使用for Ubuntu14.04的那个版本。因为是已经编译好的二进制包,下载以后解压便可。算法
找到这一行,改为
编译
这里有个坑,由于咱们使用了LLVM,最好使用LLVM中的clang。不然可能致使tvm生成的代码没法二次导入。见这个讨论帖:_cc.create_shared error while run tune_simple_template。
python包安装
demo
使用tvm为mxnet symbol计算图生成CUDA代码,并进行前向计算。
最后的话
我我的的观点,TVM是一个颇有意思的项目。在深度学习模型的优化和部署上作了不少探索,在官方放出的benchmark上表现仍是不错的。若是使用非GPU进行模型的部署,TVM值得一试。不过在GPU上,得益于Nvidia的CUDA生态,目前TensorRT仍然用起来更方便,综合性能更好。若是你和我同样,主要仍然在GPU上搞事情,能够密切关注TVM的发展,并尝试使用在本身的项目中,不过我以为仍是优先考虑TensorRT。另外一方面,TVM的代码实在是看不太懂啊。。。
想要更多
- TVM paper:TVM: An Automated End-to-End Optimizing Compiler for Deep Learning
- TVM 项目主页:TVM
后续TVM的介绍,不知道啥时候有时间再写。。。随缘吧。。。
如何评价陈天奇团队新开源的TVM?
12 个回答
从去年nnvm推出以后,很是感谢在zhihu和
上有一些讨论 如何评价陈天奇的模块化深度学习系统NNVM? ,关于nnvm剩下的瓶颈。这个讨论自己加上早期的nnvm编译尝试,让我意识到了能够支持快速调优底层op的重要性。在接下来的八个多月里面咱们不断迭代完成了TVM。
TVM尝试从更高的抽象层次上总结深度学习op的手工优化经验,用来使得用户能够快速地以自动或者半自动的方法探索高效的op实现空间。
TVM和已有的解决方案不一样,以XLA做为例子,TVM走了和目前的XLA比更加激进的技术路线,tvm能够用来使得实现XLA须要的功能更加容易 :已有的解决方案自己基于高级图表示的规则变换,能够产生一些图级别的组合op优化,如conv-bn fusion,可是依然要依赖于手写规则来达到从图的表示到代码这一步。图的op表示到代码自己能够选择的东西太多,如何作线程,如何利用shared memory,而大部分没有在图语言里面获得刻画,致使难以自动化。 这样下去深度学习系统的瓶颈必然从op实现的复杂度变成了实现graph compiler中模式生成规则的复杂度。走这个方向须要很是大的工程团队的支持,而咱们但愿采用更少的人力达到一样甚至更好的效果。
咱们采起了风险更大可是回报也更大的长远技术路线。简单地说,TVM经过把图到op生成规则这一步进一步抽象化,把生成规则自己分红各个操做原语,在须要的时候加以组合。基于tvm咱们能够快速地组合出不一样的schedule方案。
这个想法并不新颖,正如其它回答中提到的Halide,或者polyhedra method都是尝试去作这件事情。想法虽然美好,可是自动代码生成这条路线必需要生成代码效率到达手写的80%-90%效率以上,才会有实际使用的价值。一旦到达了80%到90%的效率以上,经过fusion,layout的一些高级联合优化就能够弥补这一个gap来获得比直接组合手写代码更好的效果。
可是这也正是这个问题最困难的地方,咱们须要能使得自动或者半自动生成的代码达到手写代码的效果。在TVM以前,已有的解决方案都尚未解决这个问题。我知道的最好的GPU自动生成代码大概能够到Cublas的50%的运行效率,而大部分的已有方案只是针对单线程cpu有比较好的效果。
固然已有的解决方案有很多值得参考的地方。好比polyhedra method自己很是精辟地把程序优化的大部分问题总结为针对整数集的分析。Halide里面的schedule和declaration分离的思想等。这些思想都很是强地影响了TVM的设计
这自己是一个颇有趣的科研问题,dmlc的的初衷就是去解决这样新的问题,发布新的解决方案。TVM在很大程度上解决了这个问题。要解决它,须要作到两点:设计足够大的schedule空间,使得它能够囊括包括cpu和gpu在内能够作到的手写优化,设计足够强大的搜索算法。以前的方法之因此没有图片,难点在于须要足够大的空间。
全部的抽象老是有缺陷的,因此死抱一个固定的抽象确定不能解决全部的问题。可是能够被写出来的手工优化基本上也是能够被抽象的。过去的几个月咱们就是沿着这样的思路,不断地去总结手工优化的经验加以抽象到TVM中。虽然咱们不敢保证TVM包含全部可能的手工优化,可是我基本上cover了我知识范围里面能够涉及到的东西(使得TVM至少比我知道的多)。随着TVM的演化,会有更多的这样的手工优化经验能够被加入进来。这也真是须要HPC机器学习和编译等各方面人才一块儿协力的结果。
到目前为止,咱们基本能够肯定TVM目前提供的schedule空间在cpu上能够作到90%,类似或者超过一些手写优化库效果的方案,在gpu上几本能够作到达到或者超过手写cuda的方案,可是和手写assembly在一些状况还有80%的差距(主要来源于gpu的寄存器分配比较困难)。TVM自己也意识到的手写优化的重要性,会在容许在各个级别混用手写优化的代码, 来弥补剩下这一平衡。
这是一个很是激动的前沿课题,基于这个项目自己还会有很多有趣的研究方向,咱们在不少地方已经能够看到很是好的效果。因此咱们很是但愿对于机器学习,hpc,编译原理,硬件加速 有兴趣的同窗一块儿加入进来,一块儿来推进这个项目。而由于咱们目前到达的效果自己,TVM已经能够被使用于实际的应用场景中了。
最后有一些细节上面的东西,TVM自己的设计中是很是注重开发效率和可扩展性。TVM直接提供了很是好用的python和真机调试框架,能够不依赖于上层框架直接基于python开发调试。这一点使得tvm在调试开发和效率迭代上面比起已有的方案有比较大的优点。将来咱们也会放出一些样例教程,让你们均可以开发高效的代码
将来会有自动图编译以及直接在python端定义customop
目前 TVM 放出的资料还较少,周日学习了下代码,和你们交流分享,有疏漏烦请回复指出。
TVM 的应用场景,是跟 TensorFlow XLA 对标,提供将模型输出到不一样设备 native code 的能力。这里面有几个能够对标的组件:
- TOPI (TVM Operator Inventory) 大约对应 XLA HLO, 描述在 DL 领域会用到的高层次 Operator 如 matmul, conv2d 等。这一层次能够作 CSE、Fusion 等优化。
- Schedule + HalideIR + TVM IR 无对应
- 代码输出 TVM 使用 LLVM IR 和 Source Code, 对应 XLA 使用 LLVM IR.
这里面,TVM 的切入点是在 High Level 到 Low Level 中间,插入了 Schedule 层,大概流程是 NNVM -> TVM/TOPI -> TVM/Schedule -> TVM/HalideIR -> TVM IR -> LLVM IR 或 Source Code。中间 TVM Schedule 的主要思想来自于 Halide. 这里要简单介绍一下 Halide 这个为图像处理设计的语言。Halide 其特色是计算描述(algorithm)和计算过程(schedule)分离(http://people.csail.mit.edu/jrk/jrkthesis.pdf)。这么作是由于计算机体系结构的设计(缓存,SIMD 等),直接裸写算法不能得到最高性能(一个例子是三重循环裸写矩阵乘会很慢)。所以不一样的体系结构,对一个算法的计算过程也就不一样。分离算法定义和计算过程,则方便为不一样的体系结构制定不一样的 schedule, 进一步能够探索 schedule 的自动生成(Automatically Scheduling Halide Image Processing Pipelines)。更详细的介绍建议去 Halide 官网 Halide 学习。
当初看到 Halide 的时候,就在想这个想法在 DL 领域必定会有用,现在终于被 DMLC 推进进入了人们的视线。我对这个事情的见解是:
- Halide 能够比较快的实现一个性能还不错的 kernel,开发效率很高,换不一样 schedule 测试方便。对比传统 kernel 实现通常是手写 C/C++ 或者汇编代码,开发效率较低。但任何抽象都不是完美的,有足够人力的状况下,传统写法必定能够得到不低于 Halide 的效率。
- Halide 提供了 auto-tune 的可能,但目前也只是在学术界研究,离工业级生产还远。所以可预见的将来,咱们仍是要为不一样的 target 手写 schedule 的。
根据
介绍,TVM 相对 Halide 作的比较多的工做,主要的是去解决 schedule 空间包含手写优化的问题。具体内容移步 crowowrk 的回答。
TVM 的另外一个目的是,但愿经过 TOPI 这个 Operator 库,为全部兼容 dlpack 的深度学习框架提供 kernel 库,这个目标是十分欢迎的,具体效果还有待观察。
反过来看 TensorFlow 的 XLA,目前 XLA 还在快速开发中,有几点能够注意:
- XLA 并不反对独立出来给其余框架用 XLA standalone
- XLA 欢迎尝试各类 idea,目前有人在 Incorporate Polyhedral Compilation
- XLA 目前不少 kernel 实现是基于 Eigen 的。某种程度上,Eigen 这种数学库也是 TOPI 的对标。
总的来讲,TVM 目标是很好的,很是支持。Soumith (PyTorch 主要做者)也在积极参与 TVM 项目并表示在接下来的几个月内会有更多关于 TVM/PyTorch 的消息 Twitter 。
edit: 跟做者交流更新了若干技术细节。
最近阵容有点强大。
这周四咱们请了天奇来将门作线上直播,给你们亲自讲讲TVM。
天奇的直播首秀,就在本周四(16号)下午1点,将门创投斗鱼直播间!
欢迎你们呼朋引伴来给天奇打call!!
最近在考虑将深度学习移植到移动端, 面对不少问题:
- ios 11有coreml, ios10用metal, 更低版本须要手写低版本metal代码
- neon指令集优化
- 安卓gpu
- caffe,tensorflow,darknet等一堆框架,移植不便,并且没法一一优化.
瞌睡送枕头, 感受tvm就是答案.
github上关注了tianqi, 一直纳闷最近半年为啥没有提交代码, 难道上课太忙?
直到昨天...

忽然都亮起来了.
吐槽一句, mxnet号称是轻量级框架, 各类宏,lambda看的怀疑人生,nnvm竟然还能把torch弄过来用Orz. 后来看了caffe, 不少功能都用第三方库, 主体代码很清晰简单啊, 这才是轻量啊...
利益相关: dmlc脑残粉, tianqi脑残粉
你们还记得找不到工做的bhuztez么
他在去年在一位七岁小朋友的指导下预言,2017年出现的下一代深度学习框架后端会利用Polyhedral model作fusion,减小GPU内存带宽压力,提高运行速度。被某人下结论说“真心不会.. 合并带来的内存节省只是一次elementwise op的代价,比起卷积开销真是一个毛毛”
https://www.zhihu.com/question/48615510/answer/115592046
而如今的广告
“如今咱们看到了 TVM 构建了由循环转换工具好比 loopy、多面体分析启发的图优化”
“咱们经过自动融合运算符并使 TVM 生成融合的内核,在图形节点之间和之中应用优化”
bhuztez不如赶忙改行,事实证实他那点破烂水平,连名校博士头衔都没有,真心不适合写程序 ,我建议他仍是去卖煎饼果子吧 :)
如何评价陈天奇的模块化深度学习系统NNVM?
7 个回答
北京时间 10 月 13 日 12:00 更新:
加个 Disclaimer: 本文仅表明我的观点,与雇主无关。
北京时间 10 月 4 日 1:40 更新:
看到做者
我以前的回答,主要是在把 NNVM 跟 TF 内部的 graph 这层比对,并非为了分出高下,而只是在作技术上的比对,并表达个人一些想法。但愿你们不要有门派之争,理性讨论,共同促使技术进步。
=== 10 月 3 日答案原文以下 ===
tl;dr NNVM 的出现不在于技术上有多大突破(该有的 TF 都有),而在于意欲打造一个公共接口(虽然我并不认同)。当下推出的 TinyFlow, 也有一点集合社区力量对抗 Google 的 TensorFlow 的意思。无论怎么说,DL Framework 社区活跃,终归是一件好事,做为从业者很是感谢!
首先建议想作技术分析的同窗,都先看看 TensorFlow 的代码,虽然量很大,但核心都在core/framework 和 common_runtimecore/distributed_runtime 几个目录下面,从 Session 一路分析进去,并不难懂。
nnvm 从 github 上看,是为了做为模块化 DL 系统中的计算图的一环。NNVM offers one such part, it provides a generic way to do computation graph optimization such as memory reduction, device allocation and more while being agnostic to the operator interface definition and how operators are executed. NNVM is inspired by LLVM, aiming to be a high level intermediate representation library for neural nets and computation graphs generation and optimizations.
这些部分,在 TensorFlow 里面都有相对成熟的实现。
先说图表示,在 TensorFlow 里面有两种图,一种是用于接口的,基于 protobuf 表示的图 tensorflow/graph.proto,称之为 GraphDef。另外一种是 C++ 内部运行时用的图表示 tensorflow/graph.h,称之为 Graph. 而 Operator 的定义,TF 是经过一个在 C++ 里面实现的 DSL 作的 tensorflow/op.h 使用方法例如 tensorflow/math_ops.cc,这个在 NNVM 里面也采用了相似的形式。
以后作图的优化,在 C++ 层面有 tensorflow/graph_optimizer.h. 基于这个接口,目前也作了若干实现 tensorflow/graph_optimizer.cc 如常数折叠,公共表达式消除等。除此以外,在 Python 层面也有 graph_editor 用来作图的编辑。好比 Sublinear Memory 理论上用 GraphEditor 是能够作到的。
TF 的 Operator 和 Kernel 也是分开的,相同的 Operator 能够有 CPU/CUDA 等多种实现,OpenCL 也在进行中。往 TF 里面加 Operator 并不复杂 https://www.tensorflow.org/versions/r0.11/how_tos/adding_an_op/index.html.
因此先泼一瓢冷水:我粗略的扫了一下 NNVM 的代码,能够说目前 NNVM 的目标,在 TF 内部都有实现而且都有比较好的抽象隔离。从新造轮子,政治意义大于技术意义。转载做者 陈天奇 的微博 关于今天深度学习系统争论。目前的壁垒并不是... 来自陈天奇怪:
关于今天深度学习系统争论。目前的壁垒并不是使用哪个,而是系统内部高度耦合,使得改进或者从头fork打造系统的代价变高。经过模块化,去中心化来解决这些问题,防止垄断。 当你们均可以经过组装组件几天从头打造MX, TF或者相似系统的时候。这些争论就不复存在了。
最近推出的 TinyFlow 号称是 2k 行的 TensorFlow,但其实看代码会发现,作到当面这个层面,即便彻底重写,代码成本也不算高。TinyFlow 目前(2016.10.3)的本质是一个 Python DSL 到 Lua 代码的转换器。而 TensorFlow 自己的结构并不复杂,难点在于无穷多 Operator 的实现,和当初分布式架构的设计。这两个实现出来,本质上都是工做量问题,这也是 TF 的 codebase 如此庞大的缘由。
NNVM 目前(2016.10.3)自身也不包括 Operator 的定义,这会致使使用 NNVM 的不一样框架本质上是没法互换的。而定义 Operator 这个工做量比较大,甚至不必定能完成(好比不一样 Framework 对 padding 的定义就不太同样),不知道 NNVM 是否有意愿往这个方向发展。
目前 DL 的领域还在高速发展,新的网络结构(好比 ResNet, GAN)、计算节点(各类神奇的 Operator)、计算方法(好比 lowbit, sublinear memory)、计算设备(好比 TPU,寒武纪)都在不断涌现。分布式架构也在不断演进。在这个时间点,我认为 monolithic 的框架重构相对方便,会有更加旺盛的生命力。而 NNVM 的理想,恐怕跟现实仍是有必定差距的。目前更有价值的,我以为并不在图表示层,而是各类 Operator 的 kernels. 每一个设备的 kernel 都须要专业人员定制,工做量大,难度高。cudnn 解决了 CUDA 设备上的大部分问题,但仍然有不少 Operator 须要本身实现。lowbit 目前也并无特别可用的实现。若是能有一个统一的库,定义每一个 Operator 在各类设备上的最优运行代码,应该对社区更有帮助。而上层的网络定义,和具体每一个图的运行调度方式(好比 MXNet 的 Dependency Engine,TensorFlow 的分布式框架和 rendezvous 设计),这些代码量不大,但更容易体现出差别化的部分,我想仍是留待每一个框架本身解决吧。
我的愚见,请各位参考。虽然我并不认同 NNVM 的目标,但依然对陈天奇先生对社区的贡献很是钦佩。各位如想评论,请至少大略了解 NNVM,TensorFlow 和 MXNet 的内部实现架构,以节省你们时间。以前讨论事后更加意识到了@王健飞 所说的更好地支持更多平台的op调优的重要性。昨天咱们发布了dmlc/tvm 来解决这部分问题。
-------
在几个月以后给了几个关于NNVM的报告,也思考了它和已有系统的差异。追加一下这一页slide,是我对于在抽象成面上面各个系统差异的理解。

原回答
-------
我是NNVM的做者。
总结一下,技术上自己的NNVM和现有的东西的差异是最小模块化和去中心化,下降深度学习系统优化门槛。除了为了解决现有问题,更可能是为了将来考虑。
关因而否重复造轮子的问题,图表示和优化自己在MXNet就已经存在,楼上也提到TF也有对应的抽象,为何咱们须要从新写一遍呢,基本上也就是以上两点缘由。
基本上现有的深度学习系统分红两块,1) 基本的operator的实现, 2)支撑其中的系统调度,优化,解释或者编译架构。
在工程难点上,operator须要堆代码,可是对于工程架构的难度上面而言相对较低(也就是说能够写的人比较多一些),可是须要堆比较大量的代码。而剩下的系统优化部分,内存,执行调度和分布式优化对于总体系统而言的难度相对高一些。Operator的集合问题虽然是一个问题,看已经有的成熟框架不管是Torch, Theano或者MXNet的operator完整程度基本上能够知足于大部分应用,也就是说这部分暂时属于已经解决或者能够经过堆积工程力量容易解决的问题。楼上说的最小化通用的 Op接口很重要,和NNVM咱们考虑的方向垂直。我以为相对干净的Op应该去推进成为一个独立的模块,而Op实现自己其实没有必要和框架耦合很深(虽然遗憾的是目前的设计暂时没有作到这一点)。
NNVM但愿解决的是垂直于operator实现的问题。有趣的是其实TF在这一暂时没有花特别多的力气,因此会让人以为operator是大头。其实这里有不少有趣的东西,在执行,调度和编译优化上面。编程模型和一个图自己的执行模式和硬件也会有更多的差别。
直接讨论一下设计,目前TF采起了单一的动态执行模式,使得自己执行特别依赖于动态内存分配以及threading。而这并不是是大部分场景下的最优方案。大部分场景下基于对于有限的图进行的静态分配,能够更大的缓解这个问题,实际状况如MX自己的内存损耗能够作的更好。为何目前TF不会出现多种执行模式呢,是由于TF自己Op的接口仍是过于通常地针对的动态,而若是要更好的优化须要更细化的Op接口(分开内存分配和计算的部分),这就考虑到一个Op甚至可能有多种接口的可能性。
NNVM自己的图的设计参考了TF,MX和caffe2的图部分。楼上的评论基本上提到了几个很是重要的概念,即系统优化和Op具体的性息相关。可是和PL不一样,咱们不能直接简单的抽象出有限个操做来表示整个程序。这个时候彷佛框架和Op会有比较强的关联性,致使比较大的耦合。可是也并不是如此,由于每个优化自己其实只依赖于Op的部分属性。好比有同窗提到的常数折叠,其实须要知道的是一个Op是不是常数,以及如何去展开常数两个函数。NNVM自己的作法是容许注册这两个额外属性来容许常数折叠优化。可是当不须要这个优化的时候,能够直接去掉这一部分。使得深度学习的优化能够插拔。如今看起来可能有些overkill,可是咱们相信将来深度学习系统在这方面会有很大的发展,容许不一样的优化来自于不一样群体和研究人员是咱们更加喜欢的方式。
基于以上缘由,NNVM容许给每一个op注册任意的信息。而且能够使得属性和注册和op的实现分开。这相对于TF的op接口而言是一个进步的地方。TF内部的全部op属性是须要提早数据结构指定的,也就是说,目前TF能够注册shape inference, op的输入参数的个数,可是没法注册好比咱们须要的新的细化Op接口,或者有些人关心的代码生成函数。若是须要加入这些特性,必需要修改Op的接口。这意味着全部的开发须要在一个中心,而且只能保留你们关心的东西。若是forkA有feature1, forkB有feature2的状况,forkB想要拿到 feature1就会比较不方便。由于NNVM容许不修改Op接口注册任意信息,相对解决了这个问题。
固然模块化和去中心化并不是对于全部人都重要,就是见仁见智把。
将来的深度学习系统会有更多系统的问题,使得优化和执行更加多样化。咱们不可以期待全部的优化都来自于一个团队,或者只应用于一个框架。更多的优化看起来会带来更多的耦合,可是也并不是如此。
发布TinyFlow缘由很简单。大部分人并无意识到其实目前深度学习的“系统”部分能够经过简单抽象获得。TinyFlow做为一个教程性质的项目,能够用比较短的代码展现目前有的大部分优化概念,而且把Op的部分代理给Torch(由于Op自己虽然重要,可是并不做为架构一部分)。若是能够有更多的同窗来关注深度学习系统优化,基本这个项目的目的就达到了。
值得认可的是,NNVM目前只是走出了第一步,只是包含了MXNet原有的一些优化,暂时内容很少,咱们会继续带来更多好玩的东西。 咱们也会继续坚持模块化和去中心化的思想,使得咱们新的成果能够更好的用在各个平台里面
NNVM Compiler: Open Compiler for AI Frameworks
盗一张文中图:

给我一种感受,nnvm牵起了pytorch,cntk,caffe2,caffe,keras(?)的小手,开始干xla。。(keras怎么哪都有你。。)
例如一个高速网络架构下,一个关键内存未对齐的操做可能损失可观的总体性能,一个忽视的锁的设计也可能损失可观的性能,一个tcp超时参数的不合理可能损失乐观的性能,甚至一个硬件插槽的不合理也可能损失可观总体的性能,一个不合理的通讯算法架构等等。
一般,系统领域fine-grained的优化实现相当重要,这须要对平台的长期耕耘,也须要决策层面长期的支持,绝逼几个单纯的架构能解决的问题。
不多见到从distributed level将系统设计,大都仍是仍是based single node层面讲灵活性,期待有更多关于总体system层面讨论架构。
最近有点勤奋。
在沐哥醉心与写书和带娃的日子里,为了让你们不忘了咱们。
这周四咱们请了天奇来将门作线上直播,给你们亲自讲讲TVM。
天奇的直播首秀,就在本周四(16号)下午1点,将门创投斗鱼直播间!
欢迎你们呼朋引伴来给天奇打call!!
NNVM Compiler是由mxnet的做者们提出的一个神经网络的一个中间表示。它包括两部分:NNVM和TVM。NNVM主要作的是graph层面的表示,它经过表示节点和节点之间的链接关系,把Operator链接成一个computation graph,再经过TVM来优化每个节点,来生成最终可执行的代码。
对于计算图级别优化,其实NNVM作了一些,可是他并无作到极致,还有不少咱们认为可优化的地方,它其实里面并无提供。固然NNVM可能只是为了创建一个这样的平台,让你们去贡献代码,作这样一个开源社区的概念。固然就是说若是要追求极致性能的话,有时候仍是须要关注这样的一个优化的手段。
原文有一些有效的优化手段介绍:https://zhuanlan.zhihu.com/p/33693725?group_id=944960178852945920
Momenta:Paper Reading | 让深度学习更高效运行的两个视角
我不清楚做者们是否想考虑过相反的情形,即对DL来讲,有没有可能Operator是核心,框架是辅助?
从做者的回复来看,是有些看不上Op开发的,都打算代理给Torch了
如何学习TVM的代码?
2 个回答
或许和不少人不一样,以个人经验来看,以为理解TVM,或者推理框架必定要从前端开始。即你从一个Tensorflow模型 / MXNet模型等,是如何转为NNVM的,而后再应该是后续的图优化,以及后续的TVM Tensor,LLVM代码生成等东西。
为何我会这么强调从前端开始呢?由于不理解前端模型,就很难理解后续TVM为何是这样,并且出了错之后很难知道究竟是什么缘由,好比不少时候找了半天,其实只是你忘记了模型输入图片的预处理,却误认为是后续卷积的调度优化作的有问题,因此我强烈建议先从一个模型前端开始,在tvm/nnvm/frontend里面选取一个前端。而选取前端开始不该该仅仅是看,Bug / 需求驱动永远是最好学习源代码的方式,建议从一个固化好的模型开始,而后补足NNVM算子,好比Mobilenet / Resnet50等,这里也是让你熟悉工具,熟悉NNVM的开始,可能会遇到不少问题,可是一个一个克服会收获不少,这里面推荐一个看模型的好工具: https://github.com/lutzroeder/Netron 我也是看苹果公司一我的用了之后发现的,确实是好东西。
接下来你应该首先理解TOPI,这是架设在NNVM与TVM之间的东西(首先忽略图优化,你后面再去看),由于你须要理解NNVM Symbol (其它模型在转为NNVM前端表示时会以Symbol形式的Api表示) 如何与TVM之间是如何链接起来的,在这里面你会有点迷糊,由于TVM是C++和Python混合的工程,这里面你须要在这二者跳来跳去,可是你这一步你最重要的是抓住两个核心: FTVMCompute (@reg.register_compute) / @reg.register_schedule,这一个你须要分别在nnvm/top里面的C++ / Python去找,top里面会告诉你是如何从NNVM进入topi的。
这一步完成之后,你则须要进入topi里面的任意一个后端Target去看,我暂时推荐x86后端,由于这一个后端尚未被AutoTVM改造。对于你来讲,理解起来更容易。在这里你会遇到topi/nn里面的@tvm.target.generic_func到相似具体@generic.schedule_conv2d_nchw.register(["cpu"])的改变,这是TVM的核心所在,对于卷积这样的数据负载处理,为了优化而沿用Halide的思想: 计算与调度分离。为了理解这个,你最好参考一下这个文档: https://docs.tvm.ai/tutorials/optimize/opt_gemm.html#sphx-glr-tutorials-optimize-opt-gemm-py
到这一步理解好之后,后续的TVM底层API大部分状况下你都不须要去动,包括后续的LLVM自动生成,优化等你也大部分不须要去动,由于相似CNN这样的网络,大部分你要作的工做就是在调度上,如何减小Cache Miss ,如何更好的让数据Locality是更关键的地方。
到这一步之后,你能够再回过头去理解图优化的部分,如Operator Fusion / FoldScaleAxis等,以及包括TVM目前最核心最不同凡响的地方: AutoTVM(https://docs.tvm.ai/tutorials/autotvm/tune_nnvm_arm.html#sphx-glr-tutorials-autotvm-tune-nnvm-arm-py),这是TVM去击败NCNN等用手写汇编的推理框架的关键一环,用机器学习去解决机器学习的问题,让你从调度参数的设置中解放出来,而专心写调度算法。这里面目前ARM CPU的调度算法并不是是最优的,可是从测试来看,至少在测试中使用硬件和环境来看,已经超过能找到的推理框架。后续我将撰写一篇文章到TVM社区,将我在ARM CPU的工做写出来,这将改善目前ARM CPU的官方调度版本,这将在Mobilenet等模型中有很好的提高,敬请关注!
TVM是很好的一个项目,这种基于编译优化思想的深度学习推理框架正是我赞同的,虽然还有不少工做须要作,可是我认为它已经走在一个很好的方向上了。
一步一步解读神经网络编译器TVM(一)——一个简单的例子
前言
这是一个TVM教程系列,计划从TVM的使用说明,再到TVM的内部源码,为你们大体解析一下TVM的基本工做原理。由于TVM的中文资料比较少,也但愿贡献一下本身的力量,若有描述方面的错误,请及时指出。
那啥是TVM?
简单来讲,TVM能够称为许多工具集的集合,其中这些工具能够组合起来使用,来实现咱们的一些神经网络的加速和部署功能。这也是为何叫作TVM Stack了。TVM的使用途径很广,几乎能够支持市面上大部分的神经网络权重框架(ONNX、TF、Caffe2等),也几乎能够部署在任何的平台,例如Windows、Linux、Mac、ARM等等。
如下面一张图来形容一下,这张图来源于(https://tvm.ai/about):
乍看这么多感受很是地复杂,但咱们只须要知道TVM的核心功能就能够:TVM能够优化的训练好的模型,并将你的模型打包好,而后你能够将这个优化好的模型放在任何平台去运行,能够说是与落地应用息息相关。
TVM包含的东西和知识概念都有不少,不只有神经网络优化量化op融合等一系列步骤,还有其余更多细节技术的支持(Halide、LLVM),从而使TVM拥有很强大的功能…好了废话不说了,再说就憋不出来了,若是想多了解TVM的能够在知乎上直接搜索TVM关键字,那些大佬有不少关于TVM的介绍文章,你们能够去看看。
其实作模型优化这一步骤的库已经出现不少了,不管是Nvidia自家的TensorRT仍是Pytorch自家的torch.jit
模块,都在作一些模型优化的工做,这里就很少说了,感兴趣的能够看看如下文章:
利用Pytorch的C++前端(libtorch)读取预训练权重并进行预测
利用TensorRT实现神经网络提速(读取ONNX模型并运行)
利用TensorRT对深度学习进行加速
开始使用
说到这里了,感受有必要说下:咱们为何要使用TVM?
若是你想将你的训练模型移植到Window端、ARM端(树莓派、其余一系列使用该内核的板卡)或者其余的一些平台,利用其中的CPU或者GPU来运行,而且但愿能够经过优化模型来使模型在该平台运算的速度更快(这里与模型自己的算法设计无关),实现落地应用研究,那么TVM就是你的不二之选。另外TVM源码是由C++和Pythoh共同搭建,阅读相关源码也有利于咱们程序编写方面的提高。
安装
安装其实没什么多说的,官方的例子说明的很详细。你们移步到那里按照官方的步骤一步一步来便可。
不过有两点须要注意下:
- 建议安装LLVM,虽然LLVM对于TVM是可选项,可是若是咱们想要部署到CPU端,那么llvm几乎是必须的
- 由于TVM是python和C++一块儿的工程,python能够说是C++的前端,安装官方教程编译好C++端后,这里建议选择官方中的Method 1来进行python端的设置,这样咱们就能够随意修改源代码,再从新编译,而Python端就不须要进行任何修改就能够直接使用了。
(官方建议使用Method 1)
利用Pytorch导出Onnx模型
说了这么多,演示一个例子才能更好地理解TVM究竟是作什么的,因此咱们这里以一个简单的例子来演示一下TVM是怎么使用的。
首先咱们要作的是,获得一个已经训练好的模型,这里我选择这个github仓库中的mobilenet-v2,model代码和在ImageNet上训练好的权重都已经提供。好,咱们将github中的模型代码移植到本地,而后调用并加载已经训练好的权重:
import torch
import time from models.MobileNetv2 import mobilenetv2 model = mobilenetv2(pretrained=True) example = torch.rand(1, 3, 224, 224) # 假想输入 with torch.no_grad(): model.eval() since = time.time() for i in range(10000): model(example) time_elapsed = time.time() - since print('Time elapsed is {:.0f}m {:.0f}s'. format(time_elapsed // 60, time_elapsed % 60)) # 打印出来时间
这里咱们加载训练好的模型权重,并设定了输入,在python端连续运行了10000次,这里咱们所花的时间为:6m2s。
而后咱们将Pytorch模型导出为ONNX模型:
import torch
from models.MobileNetv2 import mobilenetv2 model = mobilenetv2(pretrained=True) example = torch.rand(1, 3, 224, 224) # 假想输入 torch_out = torch.onnx.export(model, example, "mobilenetv2.onnx", verbose=True, export_params=True # 带参数输出 )
这样咱们就获得了mobilenetv2.onnx
这个onnx格式的模型权重。注意这里咱们要带参数输出,由于咱们以后要直接读取ONNX模型进行预测。
导出来以后,建议使用Netron来查看咱们模型的结构,能够看到这个模型由Pytorch-1.0.1导出,共有152个op,以及输入id和输入格式等等信息,咱们能够拖动鼠标查看到更详细的信息:
好了,至此咱们的mobilenet-v2模型已经顺利导出了。
利用TVM读取并预测ONNX模型
在咱们成功编译而且能够在Python端正常引用TVM后,咱们首先导入咱们的onnx格式的模型。这里咱们准备了一张飞机的图像:
这个图像在ImageNet分类中属于404: 'airliner'
,也就是航空客机。
下面咱们将利用TVM部署onnx模型并对这张图像进行预测。
import onnx
import time import tvm import numpy as np import tvm.relay as relay from PIL import Image onnx_model = onnx.load('mobilenetv2.onnx') # 导入模型 mean = [123., 117., 104.] # 在ImageNet上训练数据集的mean和std std = [58.395, 57.12, 57.375] def transform_image(image): # 定义转化函数,将PIL格式的图像转化为格式维度的numpy格式数组 image = image - np.array(mean) image /= np.array(std) image = np.array(image).transpose((2, 0, 1)) image = image[np.newaxis, :].astype('float32') return image img = Image.open('../datasets/images/plane.jpg').resize((224, 224)) # 这里咱们将图像resize为特定大小 x = transform_image(img)
这样咱们获得的x
为[1,3,224,224]
维度的ndarray
。这个符合NCHW格式标准,也是咱们通用的张量格式。
接下来咱们设置目标端口llvm
,也就是部署到CPU端,而这里咱们使用的是TVM中的Relay IR,这个IR简单来讲就是能够读取咱们的模型并按照模型的顺序搭建出一个能够执行的计算图出来,固然,咱们能够对这个计算图进行一系列优化。(如今TVM主推Relay而不是NNVM,Relay能够称为二代NNVM)。
target = 'llvm' input_name = '0' # 注意这里为以前导出onnx模型中的模型的输入id,这里为0 shape_dict = {input_name: x.shape} # 利用Relay中的onnx前端读取咱们导出的onnx模型 sym, params = relay.frontend.from_onnx(onnx_model, shape_dict)
上述代码中导出的sym
和params
是咱们接下来要使用的核心的东西,其中params就是导出模型中的权重信息,在python中用dic表示:
而sym
就是表示计算图结构的功能函数,这个函数中包含了计算图的流动过程,以及一些计算中须要的各类参数信息,Relay IR以后对网络进行优化就是主要对这个sym
进行优化的过程:
fn (%v0: Tensor[(1, 3, 224, 224), float32], %v1: Tensor[(32, 3, 3, 3), float32], %v2: Tensor[(32,), float32], %v3: Tensor[(32,), float32], %v4: Tensor[(32,), float32], %v5: Tensor[(32,), float32], ... %v307: Tensor[(1280, 320, 1, 1), float32], %v308: Tensor[(1280,), float32], %v309: Tensor[(1280,), float32], %v310: Tensor[(1280,), float32], %v311: Tensor[(1280,), float32], %v313: Tensor[(1000, 1280), float32], %v314: Tensor[(1000,), float32]) { %0 = nn.conv2d(%v0, %v1, strides=[2, 2], padding=[1, 1], kernel_size=[3, 3]) %1 = nn.batch_norm(%0, %v2, %v3, %v4, %v5, epsilon=1e-05) %2 = %1.0 %3 = clip(%2, a_min=0, a_max=6) %4 = nn.conv2d(%3, %v7, padding=[1, 1], groups=32, kernel_size=[3
15 条评论
一个在科研界混的人以为NNVM更像一个科研项目,设计追求各类灵活,这在很大程度上确实可以促进社区的发展。但重口难调啊,设计无比灵活的东西针对特定需求,性能却不必定牛逼,在工程应用上各家有各家的独特需求和硬件条件,我的以为更须要针对需求的定制。
我是作系统多媒体框架的,对DL的东西不了解,看了陈天奇的回答,以为蛮有感触的。
Android上的多媒体框架就是一个渣。stagefright的player连google本身的应用都不使用;java层从新封装的exoplayer也并无为广大第三方应用所接受。由于他的思路就是面向特定的应用来构建实现,没有留给第三方开发者真正的定制化开发空间。好比你想在视频播放过程当中加一个额外的后处理实现图像加强,这不可以经过添加一个新的组件来简单的解决。从陈天奇的回复来看,TF也是相似的解决方案 -- 你想添加一个额外的处理环节,很难。而这是在作框架设计的时候须要慎重考虑的东西。
而参考其余的多媒体框架,像Linux的GStreamer,Windows的DShow;都是能够方便地作到上面的事情。也就是他们是真的面向pipeline的graph设计:每一个模块(算子,plugin)具备统一的直观的接口(而不是具象的每一个特定属性);不一样的模块(算子)能够自动地完成链接和交互数据。内存的管理和数据的流动在框架的约束下交由模块自动完成。这样在搭建的新的场景的时候,就是垒积木,足够简单;在pipeline中添加和去除处理环节也能够经过几行代码来完成。
至于“相对干净的Op应该去推进成为一个独立的模块”,这个能够参考多媒体领域的ffmpeg。他就是focuse在具体的视频编解码和处理功能,而独立于不一样的多媒体框架(能够充分被Gstreamer,VLC,甚至stagefright所使用)
这种框架设计的思路,随着场景和工程的演进,能够表现出更多的优点。