深入浅出 tvm – (1) 简介

深度学习分为2个过程:
  1. 训练:从数据得到模型
  2. 推理:从模型得到答案
为解决训练而设计的系统叫训练框架:比如 paddlepaddle,tensorflow,pytorch
为解决推理而设计的系统叫推理引擎:比如 paddle inference,tensorflow,pytorch-trt 等等
训练得到的模型,其实就是一个计算图,这个计算图接收输入,通过一系列的运算,得到一个结果,后面这个过程就叫推理

1. 编译器在什么位置?

如果只从系统的输入和输出看的话:编译器 ≈ 推理引擎
因为现在几乎所有的AI编译器,它的输入都是模型(计算图)+数据,输出就是一个推理结果,这个和推理引擎所干的事情几乎一模一样
以 tvm 为例,如下:
0
其中 nnvm + graph optimizations + tvm + tvm primitives 就是 TVM 干的事情
tvm 的输入就是 CoreML 或者 ONNX 模型(一种计算图的格式)

2. 为什么需要编译器(或者推理引擎)?

我们平时在用pytorch写神经网络训练代码和测试的时候,经常训练完了把模型save到磁盘,然后推理的时候load到内存再执行推理
既然 python 训练其实本身就支持模型推理,为什么还需要一个推理引擎?
答案是3个原因:
  1. 性能
  2. 异构硬件

2.1. 性能

编译器或者推理引擎推理效率更高效
一方面,pytorch 执行推理的时候,计算图其实是有点类似解析执行的,而编译器或者推理引擎要干的事情,是把计算图直接编译成目标硬件上可高效执行的代码(或者程序)
另一方面,pytorch 训练框架自带的推理,是不做任何优化的,但是编译器加入了很多优化:
  1. 面向图结构的:算符融合、死代码删除、常量折叠
  2. 面向体系结构的:prefetch、读写缓存、并行计算

2.2. 异构硬件

虽然同样都是输入:模型+数据,输出:推理结果
但是传统的推理引擎,为了能够在不同的目标硬件上执行,需要适配各种各样的目标硬件,甚至大部分推理引擎只支持在有限的硬件上运行,比如nvidia GPU,amd GPU,可能不支持寒武纪,华为升腾,百度昆仑等
而且更要命的是:这个适配完全是手写的,会产生非常多重复的轮子。比如框架为了支持一个新硬件,所有优化代码都要重新实现一遍,更别说框架茫茫多了
0
编译器怎么解决这个问题?
编译器把图转换成一系列的分层抽象的IR,在不同的层次完成不同的优化工作,在最底层完成到目标硬件的代码生成,这就极大的提升了整个生态的生产力,比如:
  1. 编译器前端 -> relay IR:将不同的训练框架产生的图,转换成统一的高层次IR表示
  2. 在 relay IR 上完成各种图结构的优化:比如死代码删除,算符融合
  3. relay IR -> TE:将 relay IR 转换成 Tensor Expression,并执行TE相关的优化
  4. TE -> TIR:一种低层次的 IR 表示,在这里完成体系结构相关的优化,比如schedule优化(这个schedule和我们平时所了解的集群调度不是一个概念,差异很大,不要搞混了)
  5. 完成目标硬件代码的转化:厂商在这里,一次对接,随处运行!

3. 主流编译器介绍

抛开那些传统的推理引擎不说,我们这里介绍一下当前主流的AI编译器
    1. TensortRT:一个nvidia出品的高性能的深度学习推理引擎,这个其实还是传统的一个推理引擎,算不上编译器,但是由于是nvidia专门为自家硬件而深度优化的引擎。所以现在不管是传统的其他推理引擎,还是编译器,底层的算子实现都会高度依赖TensorRT(难道还有其他人比厂家更了解自家硬件的体系结构吗?)
    2. onnx:onnx试图通过统一计算图的定义,来达到统一推理引擎的目标。由于推理引擎的输入就是计算图(模型),因此图统一了之后,后端的技术栈基本也可以统一了,所以onnx有一个专门的onnx-runtime
    3. nnvm:xx
    4. llvm-mlir(偏中间语言)
    5. tvm:xx
发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注