文档章节

TVM:

o
 osc_wws45aot
发布于 2019/08/20 15:29
字数 19901
阅读 121
收藏 0

「深度学习福利」大神带你进阶工程师,立即查看>>>

Hello TVM

TVM 是什么?A compiler stack,graph level / operator level optimization,目的是(不同框架的)深度学习模型在不同硬件平台上提高 performance (我要更快!)

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,反复进行上述优化),从而实现了端到端的深度学习模型部署。

我刚刚接触TVM,这篇主要介绍了如何编译TVM,以及如何使用TVM加载mxnet模型,进行前向计算。Hello TVM!

TVM概念图

背景介绍

随着深度学习逐渐从研究所的“伊甸园”迅速在工业界的铺开,摆在大家面前的问题是如何将深度学习模型部署到目标硬件平台上,能够多快好省地完成前向计算,从而提供更好的用户体验,同时为老板省钱,还能减少碳排放来造福子孙。

和单纯做研究相比,在工业界我们主要遇到了两个问题:

  • 深度学习框架实在是太TMTM多了。caffe / mxnet / tensorflow / pytorch训练出来的模型都彼此有不同的分发格式。如果你和我一样,做过不同框架的TensorRT的部署,我想你会懂的。。。
  • GPU实在是太TMTM贵了。深度学习春风吹满地,老黄股票真争气。另一方面,一些嵌入式平台没有使用GPU的条件。同时一些人也开始在做FPGA/ASIC的深度学习加速卡。如何将深度学习模型部署适配到多样的硬件平台上?

为了解决第一个问题,TVM内部实现了自己的IR,可以将上面这些主流深度学习框架的模型转换为统一的内部表示,以便后续处理。若想要详细了解,可以看下NNVM这篇博客:NNVM Compiler: Open Compiler for AI Frameworks。这张图应该能够说明NNVM在TVM中起到的作用。

NNVM在TVM中的作用

为了解决第二个问题,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

编译

我的环境为Debian 8,CUDA 9。

准备代码

   

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的介绍,不知道啥时候有时间再写。。。随缘吧。。。

 

 


 

 

 

关注者
1,824
被浏览
101,159
他们也关注了该问题蓝色Mr.张立党陈天奇圆角骑士魔理沙Naiyan WanglambdaJi
 
 

12 个回答

默认排序​
等 285 人赞同了该回答

从去年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,感觉跟 xla,ngraph,DLVM比,tvm在张量计算的实现和优化这一块做的更好,之前看论文感觉TVM重点是支持多后端,但是今天你的回答感觉又像是TVM重点在自动代码生成这一块,而且并没有提出中间表示IR这一说,别的编译器xla/dlvm都有IR的设计理念,那tvm有一套 graph或者底层张量计算的IR的设计么?
​赞同 285​​14 条评论
​分享
​收藏​感谢
收起​
 
等 115 人赞同了该回答

目前 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 推动进入了人们的视线。我对这个事情的看法是:

  1. Halide 可以比较快的实现一个性能还不错的 kernel,开发效率很高,换不同 schedule 测试方便。对比传统 kernel 实现一般是手写 C/C++ 或者汇编代码,开发效率较低。但任何抽象都不是完美的,有足够人力的情况下,传统写法一定可以获得不低于 Halide 的效率。
  2. Halide 提供了 auto-tune 的可能,但目前也只是在学术界研究,离工业级生产还远。因此可预见的未来,我们还是要为不同的 target 手写 schedule 的。

根据 

 介绍,TVM 相对 Halide 做的比较多的工作,主要的是去解决 schedule 空间包含手写优化的问题。具体内容移步 crowowrk 的回答

 

 

TVM 的另一个目的是,希望通过 TOPI 这个 Operator 库,为所有兼容 dlpack 的深度学习框架提供 kernel 库,这个目标是十分欢迎的,具体效果还有待观察。

反过来看 TensorFlow 的 XLA,目前 XLA 还在快速开发中,有几点可以注意:

  1. XLA 并不反对独立出来给其他框架用 XLA standalone
  2. XLA 欢迎尝试各种 idea,目前有人在 Incorporate Polyhedral Compilation
  3. XLA 目前很多 kernel 实现是基于 Eigen 的。某种程度上,Eigen 这种数学库也是 TOPI 的对标。

 

总的来说,TVM 目标是很好的,非常支持。Soumith (PyTorch 主要作者)也在积极参与 TVM 项目并表示在接下来的几个月内会有更多关于 TVM/PyTorch 的消息 Twitter 。

edit: 跟作者交流更新了若干技术细节。

​赞同 115​​5 条评论
​分享
​收藏​感谢
收起​
 等 23 人赞同了该回答

最近阵容有点强大。

 

这周四我们请了天奇来将门做线上直播,给大家亲自讲讲TVM。

 

天奇的直播首秀,就在本周四(16号)下午1点,将门创投斗鱼直播间!

 

欢迎大家呼朋引伴来给天奇打call!!

 

详情>>线上 | TVM发明人陈天奇: TVM-深度学习全栈自动优化和软硬件协同设计

​赞同 23​​添加评论
​分享
​收藏​感谢
40 人赞同了该回答

最近在考虑将深度学习移植到移动端, 面对很多问题: 

  • ios 11有coreml, ios10用metal, 更低版本需要手写低版本metal代码
  • neon指令集优化
  • 安卓gpu
  • caffe,tensorflow,darknet等一堆框架,移植不便,而且无法一一优化.

瞌睡送枕头, 感觉tvm就是答案. 

github上关注了tianqi, 一直纳闷最近半年为啥没有提交代码, 难道上课太忙?

直到昨天...

突然都亮起来了.

吐槽一句, mxnet号称是轻量级框架, 各种宏,lambda看的怀疑人生,nnvm居然还能把torch弄过来用Orz. 后来看了caffe, 很多功能都用第三方库, 主体代码很清晰简单啊, 这才是轻量啊...

利益相关: dmlc脑残粉, tianqi脑残粉

​赞同 40​​1 条评论
​分享
​收藏​感谢
5 人赞同了该回答
估计Google最近应该也会发一个东西,MXNet和TensorFlow竞争好激烈。。。
​赞同 5​​添加评论
​分享
​收藏​感谢
20 人赞同了该回答

大家还记得找不到工作的bhuztez么

他在去年在一位七岁小朋友的指导下预言,2017年出现的下一代深度学习框架后端会利用Polyhedral model做fusion,减少GPU内存带宽压力,提升运行速度。被某人下结论说“真心不会.. 合并带来的内存节省只是一次elementwise op的代价,比起卷积开销真是一个毛毛”

https://www.zhihu.com/question/48615510/answer/115592046

而现在的广告

“现在我们看到了 TVM 构建了由循环转换工具比如 loopy、多面体分析启发的图优化”

“我们通过自动融合运算符并使 TVM 生成融合的内核,在图形节点之间和之中应用优化”

bhuztez不如赶紧改行,事实证明他那点破烂水平,连名校博士头衔都没有,真心不适合写程序 ,我建议他还是去卖煎饼果子吧 :)

 

 

 


 

 

 

如何评价陈天奇的模块化深度学习系统NNVM?

介绍文章:MXNet专栏 | 陈天奇:NNVM打造模块化深度学习系统
项目地址:GitHub - dmlc/nnvm: Intermediate Computational Graph Representation for Deep Learning Systems

NNVM是否会成为深度学习时代的汇编语言,成为沟通底层计算设备(GPU、FPGA)与高层的计算图描述语言(Tensorflow、MXNet)的通用桥梁?
关注者
1,443
被浏览
51,424
他们也关注了该问题蓝色SpaceAtlasYubin Wang陈天奇Naiyan Wang王峰圆角骑士魔理沙
 
 

7 个回答

默认排序​
等 200 人赞同了该回答

北京时间 10 月 13 日 12:00 更新:
加个 Disclaimer: 本文仅代表个人观点,与雇主无关。

北京时间 10 月 4 日 1:40 更新:
看到作者 

 的 回答,并且在评论中进行了简单交流。我的看法和作者的想法,都是认同 graph 定义和优化这一层的,区别是我认为这一层放在框架内部更方便开发,而 NNVM 想把这一层拿出来大家一起做大做强避免重复劳动。这个想法我是非常支持的,但最终是否好用还是要看工程的发展和理论总结,尤其是在当前 NNVM 的实现还非常单薄的情况下。
我之前的回答,主要是在把 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 的内部实现架构,以节省大家时间。
​赞同 200​​收起评论
​分享
​收藏​感谢
收起​
 

15 条评论

​切换为时间排序
  • KaiJ
    KaiJ2 年前
    同意。另外不知道“从头fork打造系统”的需求是哪儿来的,而且有了nnvm也并不容易从头打造系统,因为最麻烦的还是opr。。而且跟llvm的情形不同,PL的前端非常丰富而且差异巨大,而中间表示用上三地址码可以用很多现成的优化理论。 但deep learning的前端基本就是graph的描述,并没有太多接口以外的差异。而在不考虑opr具体特性情况下的中间表示能带来的优化有限,可能最有用的就是内存分配;像const folding目前看来在nnvm的框架里并不好做,因为需要知道opr的具体操作,需要定义好tensor结构,管理一些内存分配,这样nnvm就要膨胀成大半个dl框架了。如答主所述,真正加速还得看后面的kernel和硬件,这块门槛最高,价值最大,但现在社区里能看到的成果也是最少的。
  • 王佳明
    王佳明回复KaiJ2 年前
    同意,dnn的优化需要各种kernal代码,如你所说,门槛最高,价值最大,目前都是cudnn包办,社区就一个nervana,成立两年就卖了几亿刀。在图结构上跳舞是舍本逐末。
  • 刘知远
    刘知远2 年前
    文笔很好呀!
  • 渡河
    渡河2 年前
    膜拜大大!
  • 某一个吃货
    某一个吃货2 年前
    其实看最新mxnet的issue,整个mxnet将会在nnvm上进行重构。我觉得良好的系统结构抽象和完全的性能不可兼得。但并不是说选择优秀的抽象就不如优秀的性能。整个mxnet在几位大牛(感觉就五个人左右)的贡献下(和TF几十个人的团队相比),开发效率非常高,这也和优秀的抽象和思考是分不开的。
  • 某一个吃货
    某一个吃货2 年前
    其实我觉得做这个的大家都明白框架之间的差异其实都并不大,核心就是分布式和Operator的支持程度。Google有着最优秀的研究和工程人才自然有优势。但我觉得nnvm志不在此,并不是为了和TF一较高下,而是一个开源社区降低入门contribution门槛的阶梯。其实当有一天nnvm(dmlc)社区成长的和keras一样之后,优秀架构的潜力就会慢慢显现,之前的那些问题也都不会是问题。
  • 王健飞
    王健飞 (作者) 回复某一个吃货2 年前
    在 TF 已经开源近一年的时间点上,正如我回答所说,NNVM 在架构上目前并没有优势,更多的是政治意义。如果您对这方面技术有兴趣,欢迎您去阅读 TF,Caffe,MXNet 的代码,再做评论。
  • 某一个吃货
    某一个吃货2 年前
    我觉得就像当年推出Java的时候,只是为了解决一个问题(跨平台)而不是为了解决所有的问题(性能啊语言特性啊),肯定不会想到现在的用途。毕竟有一个重要的人生经验【一个人的命运啊,当然要靠自我奋斗,但是也要考虑到历史的行程】
  • 谢流远
    谢流远2 年前
    tf分布式效率那么差,架构设计并不能算优点吧
  • 谢流远
    谢流远2 年前
    kernel其实常用的性能瓶颈10个都不到,剩下的长尾优化意义不大而且永远不可能写完。如果你做的是novel research,那你用的op必然不可能有别人给你写好。
  • dzhwinter
    dzhwinter2 年前
    反对一下,TF的抽象做的太多了,分布式设计易用性并不好,小operator组合导致效率差现在还没有解决。认同NNVM讲的三个story。
  • 刘弈
    刘弈2 年前
    飞机好厉害~虽然我看不懂 0.0
  • 王雷
    王雷2 年前
    赞同楼上的反对,TF的抽象过多,设计质量和代码质量并不理想
  • 时间的朋友
    时间的朋友1 年前

    一个在科研界混的人觉得NNVM更像一个科研项目,设计追求各种灵活,这在很大程度上确实能够促进社区的发展。但重口难调啊,设计无比灵活的东西针对特定需求,性能却不一定牛逼,在工程应用上各家有各家的独特需求和硬件条件,个人觉得更需要针对需求的定制。

  • zhuao
    zhuao8 个月前

    我是做系统多媒体框架的,对DL的东西不了解,看了陈天奇的回答,觉得蛮有感触的。

    Android上的多媒体框架就是一个渣。stagefright的player连google自己的应用都不使用;java层重新封装的exoplayer也并没有为广大第三方应用所接受。因为他的思路就是面向特定的应用来构建实现,没有留给第三方开发者真正的定制化开发空间。比如你想在视频播放过程中加一个额外的后处理实现图像增强,这不能够通过添加一个新的组件来简单的解决。从陈天奇的回复来看,TF也是类似的解决方案 -- 你想添加一个额外的处理环节,很难。而这是在做框架设计的时候需要慎重考虑的东西。

     

    而参考其他的多媒体框架,像Linux的GStreamer,Windows的DShow;都是可以方便地做到上面的事情。也就是他们是真的面向pipeline的graph设计:每个模块(算子,plugin)具有统一的直观的接口(而不是具象的每个特定属性);不同的模块(算子)可以自动地完成连接和交互数据。内存的管理和数据的流动在框架的约束下交由模块自动完成。这样在搭建的新的场景的时候,就是垒积木,足够简单;在pipeline中添加和去除处理环节也可以通过几行代码来完成。

    至于“相对干净的Op应该去推动成为一个独立的模块”,这个可以参考多媒体领域的ffmpeg。他就是focuse在具体的视频编解码和处理功能,而独立于不同的多媒体框架(可以充分被Gstreamer,VLC,甚至stagefright所使用)

     

    这种框架设计的思路,随着场景和工程的演进,可以表现出更多的优势。

 等 278 人赞同了该回答

之前讨论过后更加意识到了@王健飞 所说的更好地支持更多平台的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原有的一些优化,暂时内容不多,我们会继续带来更多好玩的东西。 我们也会继续坚持模块化和去中心化的思想,使得我们新的成果可以更好的用在各个平台里面

​赞同 278​​收起评论
​分享
​收藏​感谢
收起​

12 条评论

​切换为时间排序
  • 当然模块化和去中心化并非对于所有人都重要,就是见仁见智吧
  • 强黄
    强黄2 年前
    为你们感到骄傲
  • 王健飞
    王健飞2 年前
    自定义 attributes 的部分不太理解。为了使用 nnvm 的不同框架能够交互,必然还是要规定一组 common attributes 的,这些 common attributes 可能跟其他框架内置固定的 attributes 并无二致。
    如果要某个框架要增加新的 attributes, 在不求通用性的前提下,可以不改 NNVM 只在自己前后端实现,但这跟在一个 monolithic 框架中增加一个新的 attributes, 似乎代码量不相上下?

    以及,支持自定义 attributes 只要支持一个 map 即可,我没有确认目前各个框架的实现,但即使目前不是 map, 在 Operator 定义中增加一个 map 支持任意 attributes 也不是难事吧?
  • 陈天奇
    陈天奇 (作者) 回复王健飞2 年前
    没错,任意attr实现代码量不多。通用性,可拆除和去中心化,是我们关心的东西。这里的意义在于基于NNVM的attr优化可以复用于各个前后端。如果所有的东西都是一个团队在一个框架里面实现,同样代码量没有多大差别。

    只是在考虑通用性的时候会多一个心眼,有时候代码会比hack的干净一些。这是在从MXNet转化到NNVM的一个体会。

    虽然复制总是可能的,大家都会做,但是还是希望可以有共同的抽象。就好象op和模型,也是一样的道理。

    最后,其实我们就是希望用最小的代码完成最多的事情,而不是overdesign一个东西。虽然不一定形成技术壁垒,但是有利于增加新的好玩的东西进行高效开发
  • 王健飞
    王健飞回复陈天奇 (作者) 2 年前
    了解了。我们并没有本质的分歧,都认同 NNVM 这一层的意义。只是我觉得这一层目前放到框架内部做开发起来比较快,而 NNVM 觉得这一层拿出来复用会更有好处。本质上是做工程的口味问题,而不是技术的高下。另外一点是,我个人觉得目前 DL 系统开发的难点并不在 Graph 层,不过这个见仁见智,就不多讨论了。

    在不打算定义通用 Op 的情况下,Attr 的设计应该是 NNVM 的关键,这个设计是对各种 Op 的本质的概括,不知道我理解是否正确。这一点上,希望有更加理论的成果出现。加油!
  • 陈天奇
    陈天奇 (作者) 2 年前
    恩,我的理解是DL本身已经很难用枚举instruction set的方式来统一中间表示了。所以通过通用的Attr来统一。

    如何根据各个优化的需求抽象出合适的Attr以及对应的优化的确是未来的关键。使用NNVM的设计基本上就是强迫我们在未来系统演化中有更多这样的思考。

    现在DL系统开发还有很多不确定因素,相信不论大家角度如何,总会有更多好玩的东西出来的
  • lv-yafei
    lv-yafei2 年前
     在MxNet中,对于变长lstm和attention等网络,图的构建和销毁开销还是比较大的,虽然nnvm优化了建图的时间,但是还是无法做到可以被忽略不计,nnvm以后是否会提供类似于tensorflow的动态流图的构建。对于NLP等任务,动态流图可能无法做到显存最优,但是却可以避免反复构建图的开销。
  • 陈天奇
    陈天奇 (作者) 回复lv-yafei2 年前
    未来考虑子图结构组合吧。这样子图可以避免反复拷贝构建
  • 齐显东
    齐显东2 年前
    现在出来了 Weld,可以对比一下吗?是类似的框架吗?
  • 余风云
    余风云1 年前

    还以为是寒武纪

  • ForAnyThing
    ForAnyThing1 年前
    人和人差距真大,现在留言还这么少,等你真的全民皆知的时候,至少我早点膜拜了大神。
15 人赞同了该回答

NNVM Compiler: Open Compiler for AI Frameworks

盗一张文中图:

给我一种感觉,nnvm牵起了pytorch,cntk,caffe2,caffe,keras(?)的小手,开始干xla。。(keras怎么哪都有你。。)

​赞同 15​​收起评论
​分享
​收藏​感谢

7 条评论

​切换为时间排序
    • charging
      charging1 年前
      keras蛮好用的
    • 罗若天
      罗若天 (作者) 回复charging1 年前

      我相信的。

    • SLee
      SLee回复罗若天 (作者) 1 年前
      我是你的忠实粉丝。关注你很久了!看你的博客论文代码!life is shot us pytorch
展开其他 1 条回复
  • bingo
    bingo1 年前
    不是中文图吗
  • 罗若天
    罗若天 (作者) 回复bingo1 年前
    上文中的图
  • sean zhuh
    sean zhuh1 年前
    感觉tf好高傲,自成一家不和我们玩😂
6 人赞同了该回答
就就像xen理论性能等比kvm优秀,但是redhat准备全力推kvm的时候,趋势开始转移到kvm,所以,长期的运营支持,精确高质量的实现都是一个平台的核心竞争力,而且DL平台对交叉专业领域的需求,加速硬件,系统架构,通信架构,互联基础网络,分布式并行架构,等等都也非常重要。
例如一个高速网络架构下,一个关键内存未对齐的操作可能损失可观的整体性能,一个忽视的锁的设计也可能损失可观的性能,一个tcp超时参数的不合理可能损失乐观的性能,甚至一个硬件插槽的不合理也可能损失可观整体的性能,一个不合理的通信算法架构等等。
通常,系统领域fine-grained的优化实现至关重要,这需要对平台的长期耕耘,也需要决策层面长期的支持,绝逼几个单纯的架构能解决的问题。
很少见到从distributed level将系统设计,大都还是还是based single node层面讲灵活性,期待有更多关于整体system层面讨论架构。
​赞同 6​​添加评论
​分享
​收藏​感谢
1 人赞同了该回答

最近有点勤奋。

 

在沐哥醉心与写书和带娃的日子里,为了让大家不忘了我们。

 

这周四我们请了天奇来将门做线上直播,给大家亲自讲讲TVM。

 

天奇的直播首秀,就在本周四(16号)下午1点,将门创投斗鱼直播间!

 

欢迎大家呼朋引伴来给天奇打call!!

 

详情>>线上 | TVM发明人陈天奇: TVM-深度学习全栈自动优化和软硬件协同设计

​赞同 1​​添加评论
​分享
​收藏​感谢
1 人赞同了该回答

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 | 让深度学习更高效运行的两个视角​zhuanlan.zhihu.com图标

 

​赞同 1​​添加评论
​分享
​收藏​感谢
2 人赞同了该回答
NNVM的想法是,我来做DL框架的优化,别人来丰富具体的Operator
我不清楚作者们是否想考虑过相反的情形,即对DL来说,有没有可能Operator是核心,框架是辅助?
从作者的回复来看,是有些看不上Op开发的,都打算代理给Torch了

 

 

 


 

 

 

如何学习TVM的代码?

对陈天奇团队的开源深度学习编译器TVM很感兴趣,特别是看到18年发的论文中提到的在FPGA上的部署。对于基础知识薄弱(如体系架构编译等方面)的学生,应…显示全部
关注者
282
被浏览
18,089
他们也关注了该问题立党蓝色
 
 

你可以邀请下面用户,快速获得回答

 

委托刘看山邀请

开启后为你智能邀请潜在的回答者
开启邀请
[object Object]
回答过相似问题
邀请回答
[object Object]
最近回答过该领域问题
邀请回答
[object Object]
在「机器学习」话题下有 7 个回答
邀请回答
[object Object]
在「编译器」话题下获得过 101 个赞
邀请回答
更多推荐结果

2 个回答

默认排序​
等 224 人赞同了该回答

或许和很多人不同,以我的经验来看,觉得理解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是很好的一个项目,这种基于编译优化思想的深度学习推理框架正是我赞同的,虽然还有很多工作需要做,但是我认为它已经走在一个很好的方向上了。

​赞同 224​​收起评论
​分享
​收藏​取消感谢
收起​

19 条评论

​切换为时间排序
  • Rand Xie
    Rand Xie10 个月前
    感谢蓝色大大.
    • 乔枫惜
      乔枫惜10 个月前
      之前也看到了阿里发的使用TVM优化batch matmul的blog,请问后面您那边是会有大规模的部署优化计划吗?
    • 蓝色
      蓝色 (作者) 回复乔枫惜10 个月前
      他们是另外一个团队,我们目前还是更强调在嵌入式设备上做端上推理的性能
    • 乔枫惜
      乔枫惜回复蓝色 (作者) 10 个月前
      非常感谢,很期待您的文章,希望发布的时候能在知乎上也宣传一下让我们知晓。
展开其他 1 条回复
    • WhySmalltalk
      WhySmalltalk10 个月前

      至少在测试中使用硬件和环境来看,已经超过能找到的推理框架。。谦虚点。。。

    • 蓝色
      蓝色 (作者) 回复WhySmalltalk10 个月前
      很谦虚了,至少在我们的测试环境是如此。
    • 蓝色
      蓝色 (作者) 回复WhySmalltalk10 个月前
      看你公司是ARM吧,实话,ARM-NN很弱。
展开其他 2 条回复
  • cyoung
    cyoung9 个月前
    蓝大,这种混合工程用什么工具看源代码比较方便?比如用clion,除了c++代码,其他都是文本形式展示,不好看
  • 蓝色
    蓝色 (作者) 回复cyoung9 个月前

    CLion可以的,装一个Python插件就可以了。我用的是QtCreator + PyCharm

     

  • 轻舟
    轻舟6 个月前

    请教蓝大,在看x86后端,遇到一个警告:
    WARNING:autotvm:Cannot find config for target=llvm, workload=('dense', (1, 512, 'float32'), (1000, 512, 'float32'), (1000, 'float32')). A fallback configuration is used, which may bring great performance regression.
    问题出在哪里?应该怎么去调优?谢谢指教!

  • 蓝色
    蓝色 (作者) 回复轻舟6 个月前
    这是Auto Tuning的警告,你可以自己Tuning, 你这里是dense op,Tuning可以参考卷积的做法 https://docs.tvm.ai/tutorials/autotvm/tune_nnvm_x86.html#sphx-glr-tutorials-autotvm-tune-nnvm-x86-py来Tuning dense.
  • Ahuier
    Ahuier3 个月前
    有一个问题今天没看明白,像batchnorn算子,我在源码看不到它的具体实现,卷积就可以看到,这是为什么呢?是被优化掉了,还是怎么回事,我想添加自己的算子
  • 蓝色
    蓝色 (作者) 回复Ahuier3 个月前
    SimplifyInference优化掉了,变为了scale * data + shift
  • 3038
    30382 个月前

    您好! 有个问题请教一下。 我这边打算使用TVM部署到手机,auto tuning以后发现计算速度反而变慢。 如果不经过auto Tuning的话,性能和ncnn此类相比,会差很大吗? 

  • 蓝色
    蓝色 (作者) 回复30382 个月前
    Auto Tuning不应该变慢,你可以再确认一下是不是哪里出了问题。不经过Auto Tuning,在arm cpu上会跑fallback,性能不好说
  • 3038
    3038回复蓝色 (作者) 2 个月前

    谢谢,我按照您的意见确认一下

 

 

 


 

 

 

 

一步一步解读神经网络编译器TVM(一)——一个简单的例子

《一步一步解读神经网络编译器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可以优化的训练好的模型,并将你的模型打包好,然后你可以将这个优化好的模型放在任何平台去运行,可以说是与落地应用息息相关。

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端就不需要进行任何修改就可以直接使用了。

《一步一步解读神经网络编译器TVM(一)——一个简单的例子》
(官方建议使用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和输入格式等等信息,我们可以拖动鼠标查看到更详细的信息:

《一步一步解读神经网络编译器TVM(一)——一个简单的例子》

好了,至此我们的mobilenet-v2模型已经顺利导出了。

利用TVM读取并预测ONNX模型

在我们成功编译并且可以在Python端正常引用TVM后,我们首先导入我们的onnx格式的模型。这里我们准备了一张飞机的图像:

《一步一步解读神经网络编译器TVM(一)——一个简单的例子》

这个图像在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) 

上述代码中导出的symparams是我们接下来要使用的核心的东西,其中params就是导出模型中的权重信息,在python中用dic表示:

《一步一步解读神经网络编译器TVM(一)——一个简单的例子》

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, 3]) ... %200 = clip(%199, a_min=0, a_max=6) %201 = mean(%200, axis=[3]) %202 = mean(%201, axis=[2]) %203 = nn.batch_flatten(%202) %204 = multiply(1f, %203) %205 = nn.dense(%204, %v313, units=1000) %206 = multiply(1f, %v314) %207 = nn.bias_add(%205, %206) %207 } 

好了,接下来我们需要对这个计算图模型进行优化,这里我们选择优化的等级为3:

with relay.build_config(opt_level=3): intrp = relay.build_module.create_executor('graph', sym, tvm.cpu(0), target) dtype = 'float32' func = intrp.evaluate(sym) 

最后我们得到可以直接运行的func

其中优化的等级分这几种:

OPT_PASS_LEVEL = {
    "SimplifyInference": 0, "OpFusion": 1, "FoldConstant": 2, "CombineParallelConv2D": 3, "FoldScaleAxis": 3, "AlterOpLayout": 3, "CanonicalizeOps": 3, } 

最后,我们将之前已经转化格式后的图像x数组和模型的参数输入到这个func中,并且返回这个输出数组中的最大值

output = func(tvm.nd.array(x.astype(dtype)), **params).asnumpy() print(output.argmax()) 

这里我们得到的输出为404,与前文描述图像在ImageNet中的分类标记一致,说明我们的TVM正确读取onnx模型并将其应用于预测阶段。

我们另外单独测试一下模型优化后运行的速度和之前直接利用pytorch运行速度之间比较一下,可以发现最后的运行时间为:3m20s,相较之前的6m2s快了将近一倍。

since = time.time()
for i in range(10000): output = func(tvm.nd.array(x.astype(dtype)), **params).asnumpy() time_elapsed = time.time() - since print('Time elapsed is {:.0f}m {:.0f}s'. format(time_elapsed // 60, time_elapsed % 60)) # 打印出来时间 

当然,这个比较并不是很规范,不过我们可以大概分析出TVM的一些可用之处了。

后记

这一篇仅仅是带大家了解一下什么是TVM以及一个简单例子的使用,在接下来的文章中会涉及到部分TVM设计结构和源码的解析。可能涉及到的知识点有:

  • 简单编译器原理
  • C++特殊语法以及模板元编程
  • 神经网络模型优化过程
  • 代码部署

等等,随时可能会进行变化。

人工智能已经开始进入嵌入式时代,各式各样的AI芯片即将初始,将复杂的网络模型运行在廉价低功耗的板子上可能也不再是遥不可及的幻想,不知道未来会是怎么样,但TVM这个框架已经开始走了一小步。

 

 


 

 

 

陈天奇等人提出TVM:深度学习自动优化代码生成器

机器之心  · 2018-02-18
此处@各种工程师

编者按:本文来自公众号机器之心(ID:AI_era),作者陈天奇等,机器之心编译,参与者李泽南、路雪。

TVM 是由华盛顿大学在读博士陈天奇等人提出的深度学习自动代码生成方法,去年 8 月机器之心曾对其进行过简要介绍。该技术能自动为大多数计算硬件生成可部署优化代码,其性能可与当前最优的供应商提供的优化计算库相比,且可以适应新型专用加速器后端。近日,这项研究的论文《TVM: End-to-End Optimization Stack for Deep Learning》终于完成,内容包含新方法的介绍与讨论,以及 TVM 在英伟达、AMD 的 GPU、树莓派及一些 FPGA 上的性能评估。

(项目链接:https://github.com/dmlc/tvm)

深度学习模型可以识别图像、处理自然语言,以及在部分具有挑战性的策略游戏中击败人类。在其技术发展的过程中,现代硬件稳步推进的计算能力扮演了不可或缺的作用。很多目前最为流行的深度学习框架,如 TensorFlow、MXNet、Caffe 和 PyTorch,支持在有限类型的服务器级 GPU 设备上获得加速,这种支持依赖于高度特化、供应商特定的 GPU 库。然而,专用深度学习加速器的种类越来越多,这意味着现代编译器与框架越来越难以覆盖所有的硬件。

显而易见,以现有的点到点方式实现不同深度学习框架对所有种类的硬件进行后端支持是不现实的。我们的最终目标是让深度学习负载可以轻松部署到所有硬件种类中,其中不仅包括 GPU、FPGA 和 ASIC(如谷歌 TPU),也包括嵌入式设备,这些硬件的内存组织与计算能力存在着显著的差异(如图 1 所示)。考虑到这种需求的复杂性,开发一种能够将深度学习高级程序降低为适应任何硬件后端的低级优化代码的优化框架是最好的方法。

目前的深度学习框架依赖于计算图的中间表示来实现优化,如自动微分和动态内存管理 [3,7,4]。然而,图级别的优化通常过于高级,无法有效处理硬件后端算子级别的转换。另一方面,目前深度学习框架的算子级别库通常过于僵化,难以轻松移植到不同硬件设备上。为了解决这些问题,我们需要一个可实现从计算图到算子级别的优化,为各种硬件后端带来强大性能的编译器框架。

陈天奇等人提出TVM:深度学习自动优化代码生成器

图 1:CPU、GPU 与类 TPU 加速器需要不同的片上存储架构和计算基元。在生成优化代码时我们必须考虑这一问题。

陈天奇等人提出TVM:深度学习自动优化代码生成器

图 2:TVM 堆栈图。目前的堆栈支持多种深度学习框架以及主流 CPU、GPU 以及专用深度学习加速器。

优化的四大基本挑战

深度学习的优化编译器需要同时展示高级别与低级别的优化,在论文中,研究人员总结了在计算图级别与张量算子级别上的四大基本挑战:

  • 高级数据流复写:不同的硬件设备可能具有截然不同的内存层次结构,因此,融合算子与优化数据布局的策略对于优化内存访问至关重要。

  • 跨线程内存复用:现代 GPU 与专用加速器的内存可被多个计算核心共享,传统的无共享嵌套并行模式已不再是最优方法。为优化内核,在共享内存负载上的线程合作很有必要。

  • 张量计算内部函数:最新的硬件带来了超越向量运算的新指令集,如 TPU 中的 GEMM 算子和英伟达 Volta 架构中的 Tensor Core。因此在调度过程中,我们必须将计算分解为张量算术内部函数,而非标量或向量代码。

  • 延迟隐藏(Latency Hiding):尽管在现代 CPU 与 GPU 上,同时拥有多线程和自动缓存管理的传统架构隐藏了延迟问题,但专用的加速器设计通常使用精简控制与分流,这为编译器堆栈的调度带来了复杂性。所以,调度仍需仔细,以隐藏内存访问延迟。

TVM:一个端到端优化堆栈(见图 2),该端到端优化编译器堆栈可降低和调整深度学习工作负载,以适应多种硬件后端。TVM 的设计目的是分离算法描述、调度和硬件接口。该原则受到 Halide [22] 的计算/调度分离思想的启发,而且通过将调度与目标硬件内部函数分开而进行了扩展。这一额外分离使支持新型专用加速器及其对应新型内部函数成为可能。TVM 具备两个优化层:计算图优化层,用于解决第一个调度挑战;具备新型调度基元的张量优化层,以解决剩余的三个挑战。通过结合这两种优化层,TVM 从大部分深度学习框架中获取模型描述,执行高级和低级优化,生成特定硬件的后端优化代码,如树莓派、GPU 和基于 FPGA 的专用加速器。该论文做出了以下贡献:

  • 我们构建了一个端到端的编译优化堆栈,允许将高级框架(如 Caffe、MXNet、PyTorch、Caffe2、CNTK)专用的深度学习工作负载部署到多种硬件后端上(包括 CPU、GPU 和基于 FPGA 的加速器)。

  • 我们发现了提供深度学习工作负载在不同硬件后端中的性能可移植性的主要优化挑战,并引入新型调度基元(schedule primitive)以利用跨线程内存重用、新型硬件内部函数和延迟隐藏。

  • 我们在基于 FPGA 的通用加速器上对 TVM 进行评估,以提供关于如何最优适应专用加速器的具体案例。

我们的编译器可生成可部署代码,其性能可与当前最优的特定供应商库相比,且可适应新型专用加速器后端。

陈天奇等人提出TVM:深度学习自动优化代码生成器

图 3:两层卷积神经网络的计算图示例。图中每个节点表示一次运算,它消耗一或多个张量,并生成一或多个张量。张量运算可以通过属性进行参数化,以配置其行为(如 padding 或 stride)。

 

陈天奇等人提出TVM:深度学习自动优化代码生成器

论文:TVM: End-to-End Optimization Stack for Deep Learning

论文链接:https://arxiv.org/abs/1802.04799

论文摘要:可扩展框架,如 TensorFlow、MXNet、Caffe 和 PyTorch 是目前深度学习领域中最流行和易用的框架。但是,这些框架只对窄范围的服务器级 GPU 进行优化,要把工作负载部署到其他平台,如手机、嵌入式设备和专用加速器(如 FPGA、ASIC),则需要大量手动工作。我们提出了 TVM,一个端到端的优化堆栈,具备图形级和算子级的优化,以为多种硬件后端提供深度学习工作负载的性能可移植性。我们讨论了 TVM 所解决的深度学习优化挑战:高级算子融合(operator fusion)、多线程低级内存重用、任意硬件基元的映射,以及内存延迟隐藏。实验结果证明 TVM 在多个硬件后端中的性能可与适应低功耗 CPU 和服务器级 GPU 的当前最优库相比。我们还通过针对基于 FPGA 的通用深度学习加速器的实验,展示了 TVM 对新型硬件加速器的适应能力。该编译器基础架构已开源。

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

TensorRT是什么,TensorRT是英伟达公司出品的高性能的推断C++库,专门应用于边缘设备的推断,TensorRT可以将我们训练好的模型分解再进行融合,融合后的模型具有高度的集合度。例如卷积层和激活层进行融合后,计算速度可以就进行提升。当然,TensorRT远远不止这个:

我们平时所见到了深度学习落地技术:模型量化、动态内存优化以及其他的一些优化技术TensorRT都已经有实现,更主要的,其推断代码是直接利用cuda语言在显卡上运行的,所有的代码库仅仅包括C++和cuda,当然也有python的包装,我们在利用这个优化库运行我们训练好的代码后,运行速度和所占内存的大小都会大大缩减。

 

 

其实类似TensorRT具体工作的有很多,例如TVM、TC(Tensor Comprehensions),都做了一些类似于TensorRT的工作,将训练好的模型转化为运行在特定端(例如GPU)的进行模型优化等一系列操作后的代码,从而达到快速预测的效果。

那么为什么要选择TensorRT呢?很简单,因为我们目前主要使用的还是Nvidia的计算设备,在Nvidia端的话肯定要用Nvidia亲儿子了

 

 

在我这里的实验结论表明,在FP32的精度下,使用TensorRT和不使用TensorRT在GPU上运行的速度比大概为3:1,也就是在我这个模型为前提条件下,TensorRT在GPU端使我的模型速度提升了3倍(不同模型不同显卡不同构架提升速度不同)。

TensorRT具备的功能

目前TensorRT的最新版本是5.0,TensorRT的发展其实已经有一段时间了,支持转化的模型也有caffe、tensorflow和ONNX了,我们要知道,TensorRT是有自己的模型框架的,我们首先先其他训练好的框架通过转化代码转化为TensorRT的代码才可以使用。TensorRT对Caffe模型的支持度最高,同时也支持将Caffe模型转化为int8精度。

而ONNX模型的转化则是近半年来的实现成果,目前支持了大部分的运算(经过测试,我们平常使用的90%的模型都可以使用ONNX-TensorRT来进行转化)。唯一遗憾的是ONNX模型目前还不支持int8类型的转化。


 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 



 

 
 
 
 
o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
Rust SGX SDK 1.0.5 发布,支持代码加密保护和离线密文分发

Rust SGX SDK v1.0.5 发布了,支持代码加密保护和离线密文分发 Rust SGX SDK 是百度安全实验室开发的一个 Intel SGX 可信计算平台的 Rust 语言开发工具包。基于 Rust SGX SDK,程序员可以快速...

dingelish
2019/02/06
584
1
QEMU 4.0.0 发布,几乎可以模拟任何硬件设备的模拟器

QEMU 4.0.0 发布了,此版本更新亮点包括: ARM:实现了一批 ARMv8.X 的扩展,包括 SB、PredInv、HPD、LOR、FHM、AA32HPD、PAuth、JSConv、CondM、FRINT 与 BTI ARM:virt:支持 >255 GB 的 ...

h4cd
2019/04/25
1.4W
15
贾扬清:我对人工智能方向的一点浅见

阿里妹导读:作为 AI 大神,贾扬清让人印象深刻的可能是他写的AI框架Caffe ,那已经是六年前的事了。经过多年的沉淀,成为“阿里新人”的他,对人工智能又有何看法?最近,贾扬清在阿里内部分...

阿里云云栖社区
2019/04/15
98
0
使用C++调用pytorch模型(Linux)

前言 模型转换思路通常为: Pytorch -> ONNX -> TensorRT Pytorch -> ONNX -> TVM Pytorch -> 转换工具 -> caffe Pytorch -> torchscript(C++版本Torch) 我的模型是使用Pytorch1.0训练的,第三......

osc_28hs2gg6
2019/05/07
19
0
nginx location正则

nginx location正则写法 一个示例: 已开头表示精确匹配 如 A 中只匹配根目录结尾的请求,后面不能带任何字符串。 开头表示uri以某个常规字符串开头,不是正则匹配 ~ 开头表示区分大小写的正...

osc_0w39fdhx
2019/09/25
3
0

没有更多内容

加载失败,请刷新页面

加载更多

如何在IntelliJ中永久启用行号? - How can I permanently enable line numbers in IntelliJ?

问题: 如何在IntelliJ IDEA中永久启用行号? 解决方案: 参考一: https://stackoom.com/question/3Zn/如何在IntelliJ中永久启用行号 参考二: https://oldbug.net/q/3Zn/How-can-I-permane...

法国红酒甜
22分钟前
3
0
Docker镜像加速

vim /etc/docker/daemon.json # 将"registry-mirrors": ["https://......com"] (对应自己的加速地址)复制到文件中 # 重新加载文件和重启dockersudo systemctl daemon-reloadsudo syst......

codeccb
33分钟前
12
0
Android Studio使用lombok

参考:https://github.com/mplushnikov/lombok-intellij-plugin 使用@Setter/@Getter时,刚开始在Structure的函数列表里没有生成响应的函数,且调用set/get的地方也报红,但编译OK。 按网上的...

大熊猫
34分钟前
7
0
Stream数据流

Stream类基础操作 从JDK1.8开始提供了java.yitl.stream数据流的开发板,而Stream就是这个包中提供的一个接口,这个接口主要是通过函数式编程结构实现集合数据的分析,所以在Collection接口张...

哼着我的小调调
39分钟前
13
0
在视图控制器之间传递数据 - Passing Data between View Controllers

问题: I'm new to iOS and Objective-C and the whole MVC paradigm and I'm stuck with the following: 我是iOS和Objective-C以及整个MVC范例的新手,但我坚持以下几点: I have a view th......

fyin1314
52分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部