如何在TVM上集成Codegen(上)
许多常用的深度学习内核,或者提供DNNL或TensorRT等框架和图形引擎,让用户以某种方式描述模型,从而获得高性能。此外,新兴的深度学习加速器也有自己的编译器、内核库或runtime框架。
当用户试图在一个新的内核库或设备上工作时,必须学习一个新的编程接口。对于统一编程接口的需求变得越来越重要,让所有用户和硬件后端提供商站在同一个页面上。
广泛使用的深度学习框架共享编程接口,许多硬件设备提供商,尝试将设备后端集成到TensorFlow。由于TensorFlow没有为新的后端提供正式的后端接口,必须对TensorFlow进行注册,这涉及到许多源文件的更改,维护变得困难。
本节将展示作为一个硬件后端提供商,如何轻松地利用自带的Codegen(BYOC)框架,将硬件设备的内核库/编译器/框架,集成到TVM。利用BYOC框架最重要的优势,设备的所有相关源文件都是自包含的,设备的codegen/runtime,可以嵌入到TVM代码库。
1)带有codegen的TVM代码库,将与上游兼容
2)TVM用户可以根据需要,选择启用codegen/runtime。
首先说明一个场景,可能需要使用BYOC实现TVM,然后概述BYOC编译和runtime流。最后,以Intel DNNL(又称MKL-DNN,OneDNN)为例,逐步说明如何将供应商库或执行引擎,集成到TVM与BYOC。
Bring an ASIC Accelerator to TVM
先做一个场景,说明为什么要将加速器引入TVM,可以从BYOC框架中,获得哪些特性。如果不确定案例是否适合BYOC,欢迎在tvm.ai里讨论。
假如制作了一个边缘设备平台,有一个ARM CPU和一个很棒的加速器,在常见的图像分类模型中,取得了惊人的性能。加速器在Conv2D、ReLU、GEMM和其它广泛使用的CNN算子上表现良好。
但是,目标检测模型也越来越流行,客户需要在平台上,同时运行图像分类和目标检测模型。虽然加速器能够执行目标检测模型中的几乎所有算子,但缺少一个算子(例如,非最大抑制,NMS)。
Let TVM execute unsupported operators
由于TVM为不同的后端,提供了多个代码源,开源社区很容易在短时间内,在CPU或GPU上实现新的操作程序。如果将加速器的编译流与BYOC集成到TVM,TVM将执行Relay图分区,将图的一部分 load 到加速器上,将其它部分保留在TVM上。因此,可以宣称平台能够运行所有模型,而不必担心新的算子。
Customize graph-level optimization
ASIC加速器必须有自己的编译流。通常,可能是以下情况之一:
生成一个图形表示,将其输入图形引擎:
可能有自己的图形引擎,能够在加速器上执行图形(或神经网络模型)。例如,Intel DNNL和NVIDIA TensorRT,都使用引擎运行整个图形或模型,因此能够
1)减少算子之间的内存事务;
2)使用算子融合优化图形执行。
为了实现上述两个优化,可能需要在编译期间处理该图。例如,Conv2D和bias addition,在TVM中是两个独立的算子,但可能是加速器上的一个算子(具有bias addition功能的Conv2D)。可能希望通过将conv2d-add graph模式,替换为带有“bias”节点的“uconv2d”,优化图形。
如果编译流程属于这种情况,建议阅读本文的其余部分,但跳过将DNNL带到TVM:C源代码生成。
生成汇编代码,编译为可执行的二进制文件:
如果平台不像前面的例子,有一个端到端的执行框架,可能有一个编译器,用ISA的汇编代码编译程序。为了向编译器提供汇编代码,需要一个codegen,从Relay图生成和优化汇编代码。
如果编译流程属于这种情况,建议阅读本文的所有其余部分,但跳过将DNNL到TVM:JSON Codegen/Runtime。
How BYOC Works
简单地解释一下BYOC框架,如何工作的。有关底层框架组件及实现的详细说明,参阅开发人员文档。给定图1中的Relay图,BYOC框架执行以下步骤:

Figure 1: The Original Relay Graph.

  1. Graph Annotation
    以用户提供的Relay图为例,第一步是在图中注释,可能 load 到加速器的节点。需要遵循Bring DNNL to TVM:
    来实现受支持算子的白名单,或者自定义复合算子的图形模式列表。图2显示了一个示例注释结果。

Figure 2: The Graph with Annotations.

  1. Graph Transformation
    第二步是基于注释,对图形进行变换和优化。具体来说,BYOC执行以下转换。
    2.1:合并编译器区域:
    如图2所示,图中现在有许多“区域”,可以 load 到加速器上,可以合并其中一些区域,减少数据传输和内核启动开销。步骤2.1使用贪婪算法,合并尽可能多的这些区域,保证功能的正确性。结果如图3所示。

Figure 3: After Merging Compiler Regions.
2.2: Partition Graph:
对于上一步中的每个区域,创建一个带有属性编译器的Relay函数,指示该Relay函数,应该完全 load 到加速器上,如图4所示。

Figure 4: After Graph Partitioning.
3. Code Generation
现在我们知道应该 load Relay图的哪个部分。在这一步中,按顺序将每个带有Compiler=your_accelerator加速器的Relay函数,发送到codegen。
codegen应该将Relay函数,编译成与编译流相匹配的形式。可以是C源代码,或任何文本格式。
最后,所有编译的函数,将与其它未 load 的Relay函数一起,通过TVM export_library Python API,序列化到一个single .so文件中。换句话说,用户在运行此flow后,将只获得一个one .so文件。
4. Runtime
可能还需要实现一个runtime,初始化图形引擎(如果适用),执行编译后的函数。在推理过程中,当TVM runtime遇到图4中相应的函数调用时,TVM runtime(即图runtime或VM)将利用runtime,调用 load 的函数。runtime负责使用给定的输入张量数组,启动编译函数,将结果填充到输出张量数组中。
以DNNL为例,演示如何使用BYOC框架,实现上述工作流。本文引用的所有代码和行号,都基于TVM存储库的主分支提交8a0249c。
Bring DNNL to TVM: Annotation Rules
BYOC框架提供了两种方法,描述支持的算子和模式,以DNNL为例来说明如何使用。这里提供了完整的实现。将codegen的注释规则放在
python/tvm/relay/op/contrib/your_codegen_name.py.
Rules for single operators
可以使用BYOC API,直观地指定加速器,支持哪些Relay算子。例如,使用下面的代码片段,构建一个规则,说明DNNL codegen支持Conv2D:
@tvm.ir.register_op_attr(“nn.conv2d”, “target.dnnl”)
def _dnnl_conv2d_wrapper(attrs, args):
return True
这将注册一个新属性target.dnnl接力nn.conv2d算子。通过这种方式,BYOC注释可以调用target.dnnl(),检查DNNL codegen中是否支持。
另一方面,每个算子编写上面的代码片段,可能很乏味。对于DNNL实现,实现了一个helper函数,即_register_external_op_helper,更方便:
def _register_external_op_helper(op_name, supported=True):
@tvm.ir.register_op_attr(op_name, “target.dnnl”)
def _func_wrapper(attrs, args):
return supported
return _func_wrapper

_register_external_op_helper(“nn.batch_norm”)
_register_external_op_helper(“nn.conv2d”)
_register_external_op_helper(“nn.dense”)
_register_external_op_helper(“nn.relu”)
_register_external_op_helper(“add”)
_register_external_op_helper(“subtract”)
_register_external_op_helper(“multiply”)
在上面的示例中,指定了DNNL codegen支持的算子列表。
Rules for graph patterns
加速器或编译器,可能已将某些模式(例如Conv2D+add+ReLU),优化为单个指令或API。可以指定从图形模式,到指令/API的映射。对于DNNL来说,Conv2D API已经包含了bias addition,允许附加下一个ReLU,可以将DNNL以下代码片段:
DNNLConv2d(const bool has_bias = false, const bool has_relu = false) {
// … skip …
auto conv_desc = dnnl::convolution_forward::desc(
dnnl::prop_kind::forward_inference,
dnnl::algorithm::convolution_direct,
conv_src_md, conv_weights_md, conv_bias_md, conv_dst_md,
strides_dims, padding_dims_l, padding_dims_r);

// Attach ReLU
dnnl::primitive_attr attr;
if (has_relu) {
dnnl::post_ops ops;
ops.append_eltwise(1.f, dnnl::algorithm::eltwise_relu, 0.f, 0.f);
attr.set_post_ops(ops);
}

auto conv2d_prim_desc = dnnl::convolution_forward::primitive_desc(
conv_desc, attr, engine_);
// … skip …
在本例中,除了单个conv2d,希望将图形模式conv2d+relu映射到DNNLConv2d(false,true),将conv2d+add+relu映射到DNNLConv2d(true,true)。用下面的代码片段实现:
def make_pattern(with_bias=True):
data = wildcard()
weight = wildcard()
bias = wildcard()
conv = is_op(‘nn.conv2d’)(data, weight)
if with_bias:
conv_out = is_op(‘add’)(conv, bias)
else:
conv_out = conv
return is_op(‘nn.relu’)(conv_out)

@register_pattern_table(“dnnl”)
def pattern_table():
conv2d_bias_relu_pat = (“dnnl.conv2d_bias_relu”, make_pattern(with_bias=True))
conv2d_relu_pat = (“dnnl.conv2d_relu”, make_pattern(with_bias=False))
dnnl_patterns = [conv2d_bias_relu_pat, conv2d_relu_pat]
return dnnl_patterns
在DNNL示例中,实现了两个具有不同名称的模式,可以在codegen中轻松地识别。这些模式是用Relay模式语言实现的。可以学习如何编写模式。
通过模式表,可以使用一个Relay pass执行
%1 = nn.conv2d(%data, %weight, …)
%2 = add(%1, %bias)
%3 = nn.relu(%2)
to
%1 = fn(%input1, %input2, %input3,
Composite=“dnnl.conv2d_bias_relu”,
PartitionedFromPattern=“nn.conv2d_add_nn.relu_”) {
%1 = nn.conv2d(%input1, %input2, …)
%2 = add(%1, %input3)
nn.relu(%2)
}
%2 = %1(%data, %weight, %bias)
hus,DNNL codegen可以获得模式名conv2d_bias_relu,将%1映射到DNNLConv2d(true,true)。
在复合函数中,还有一个名为“PartitionedFromPattern”的属性。如果模式包含通配符算子,这可能会很有帮助。例如,可能有一个模式表(“conv2d_with_something”, conv2d -> *):
def make_pattern(with_bias=True):
data = wildcard()
weight = wildcard()
conv = is_op(‘nn.conv2d’)(data, weight)
return wildcard()(conv)
In this case, you will get a composite function with Composite=conv2d_with_something, but you have no idea about what graph it actually matched. That’s where PartitionedFromPattern comes into play. You can know that if the matched graph is conv2d -> add or conv2d -> relu by looking at PartitionedFromPattern to see if it is nn.conv2d_add_ or nn.conv2d_nn.relu_.
Bring DNNL to TVM: Relay Graph Transformation
使用上一步中的注释规则,可以应用BYOC Relay pass列表,将Relay图从图1转换为图4:
mod = create_relay_module_from_model() # Output: Figure 1
mod = transform.MergeComposite(pattern_table)(mod)
mod = transform.AnnotateTarget([“dnnl”])(mod) # Output: Figure 2
mod = transform.MergeCompilerRegions()(mod) # Output: Figure 3
mod = transform.PartitionGraph()(mod) # Output: Figure 4
As can be seen, each Relay pass can be mapped to a step we have introduced in How BYOC Works.

如何在TVM上集成Codegen(上)相关推荐

  1. 如何在TVM上集成Codegen(下)

    如何在TVM上集成Codegen(下) Bring DNNL to TVM: JSON Codegen/Runtime 现在实现将Relay,序列化为JSON表示的DNNL codegen,然后实现D ...

  2. iis php mysql 集成_如何在IIS上集成php(iis+mysql+php+zend)

    下面介绍下如何在IIS上集成php. 这里我就不说cgi了,因为cgi需要系统权限过高,不建议虚拟主机使用,而且cgi程序也很少有人用到,楼主说的要iis6.0结合php 安装需要:windows20 ...

  3. TensorRT宏碁自建云(BYOC, BuildYourOwnCloud)上集成

    TensorRT宏碁自建云(BYOC, BuildYourOwnCloud)上集成 这个PR增加了对分区.编译和运行TensorRT BYOC目标的支持. Building 有两个新的cmake标志: ...

  4. TVM在ARM GPU上优化移动深度学习

    TVM在ARM GPU上优化移动深度学习 随着深度学习的巨大成功,将深度神经网络部署到移动设备的需求正在迅速增长.与在台式机平台上所做的类似,在移动设备中使用GPU可以提高推理速度和能源效率.但是,大 ...

  5. dropzonejs vue 使用_如何在Dropzone上手动触发上传文件事件

    我将Dropzonejs很好地集成到了我的前端(VueJS)中.如何在Dropzone上手动触发上传文件事件 我有验收测试Dropzone使用Webdriver/Codeception的问题.底线是W ...

  6. 在Vs2017上集成osgearth3.2和qt5.9,并加载shp文件。

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.QT5.9在vs上部署 1.1 在Vs中下载插件 1.2 配置QT 二.OsgEarth3.2环境配置. 三.在Q ...

  7. linux mint能用安卓,如何在Ubuntu和Linux Mint上安装Android Studio

    Android Studio 是一个全新的 Android 开发环境,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工 ...

  8. 如何在Red Hat Enterprise Linux上安装Python 3

    如何在Red Hat Enterprise Linux上安装Python 3. 本文介绍如何安装Python 3, ,pip,venv,virtualenv和pipenv在企业Linux 7.按照本文 ...

  9. 怎么用显卡计算_卷积神经网络在移动端集成显卡上的加速

    作者: 王乐园 王贻达 AWS AI 本文谈的是在移动端加速卷积神经网络.虽然AWS是个云服务公司,我们同样重视edge上的计算.现代终端设备一般都跟云端服务器相连,但只要可能,我们都希望计算可以在本 ...

最新文章

  1. 语言模型自然语言处理[置顶] 哥伦比亚大学 自然语言处理 公开课 授课讲稿 翻译(四)...
  2. 在北京做Java开发如何月薪达到两万,需要技术水平达到什么程度?
  3. 引用与指针的异同-基础篇
  4. linux-shell命令之file【辨识文件类型】
  5. Hotspot hotswap, who and who are best freinds
  6. php while循环次数,php while循环得到循环次数
  7. MFC中打开文件对话框:CFileDlg
  8. antd From 中 Form.Item里含有自己封装的组件,获取不到值的解决方法
  9. Java 项目开发及管理常用工具收集
  10. 剑指offer(C++)-JZ34:二叉树中和为某一值的路径(二)(数据结构-树)
  11. db2 jdbc驱动参数_db2的jdbc驱动安装及例子
  12. 阿铭Linux_网站维护学习笔记20190304
  13. php 获取pdf中的图片,使用PHP从PDF中提取图像
  14. linux压缩到最小命令,Linux压缩打包命令
  15. 微信支付“举刀”挥向谁?
  16. 深度学习技术在股票交易上的应用研究调查
  17. matlab编写扫雷,MATLAB版本的扫雷小游戏
  18. mysql典型安装和完全安装的区别_MySQL安装详解(安装版本mysql-5.5.25)
  19. lisp注册注册机源码_[求助]请教在lisp中加入注册码
  20. 七二法则-“莫道君行早,更有早行人”

热门文章

  1. Go 学习笔记(55)— Go 标准库 sql (初始化数据库、插入、更新、删除数据库表、单行查询、多行查询、事务处理)
  2. Google Colab——用谷歌免费GPU跑你的深度学习代码
  3. Relay IR表示
  4. TensorRT 数据和表格示例
  5. RGB Color Codes Chart
  6. 将深度学习低延迟推理性能提高一倍
  7. 用NVIDIA-TensorRT构造深度神经网络
  8. 深度学习与传统图像识别
  9. Centos7 下安装VIM编辑器
  10. Swoole入门介绍