编写可调模板并使用自动调谐器
这是TVM中自动调整模块的入门资料。
自动调整有两个步骤。第一步是定义搜索空间。第二步是运行搜索算法来探索这个空间。在本文中,可以学习如何在TVM中执行这两个步骤。整个工作流程由矩阵乘法示例说明。
注意,本文无法在Windows或最新版本的macOS上运行。要使其运行,需要将本文的内容包装在一个if name == “main”:块中。
安装依赖项
要在TVM中使用autotvm软件包,需要安装一些额外的依赖项。可以跳过此步骤(安装xgboost),不需要XGBoost(如果使用python2,将“ 3”更改为“ 2”):
pip3 install --user psutil xgboost cloudpickle
为了使TVM在调整中更快地运行,建议使用cython作为TVM的FFI。在TVM的根目录中,执行(如果使用python2,则将“ 3”更改为“ 2”):
pip3 install --user cython
sudo make cython3
现在返回python代码。导入包。
import logging
import sys

import numpy as np
import tvm
from tvm import te
import tvm.testing

the module is called autotvm

from tvm import autotvm
步骤1:定义搜索空间
在本节中,将确定性TVM调度代码重写为可调调度模板。可以将搜索空间定义的过程视为现有调度代码的参数化。
首先,如何在TVM中实现分块矩阵乘法。

Matmul V0: Constant tiling factor

def matmul_v0(N, L, M, dtype):
A = te.placeholder((N, L), name=“A”, dtype=dtype)
B = te.placeholder((L, M), name=“B”, dtype=dtype)

k = te.reduce_axis((0, L), name="k")
C = te.compute((N, M), lambda i, j: te.sum(A[i, k] * B[k, j], axis=k), name="C")
s = te.create_schedule(C.op)# schedule
y, x = s[C].op.axis
k = s[C].op.reduce_axis[0]yo, yi = s[C].split(y, 8)
xo, xi = s[C].split(x, 8)s[C].reorder(yo, xo, k, yi, xi)return s, [A, B, C]

参数化调度
在先前的调度代码中,使用常数“ 8”作为平铺因子。可能不是最佳选择,最佳拼接因子取决于实际的硬件环境和输入形状。
如果希望调度代码可在更广泛的输入形状和目标硬件中移植,最好定义一组候选值,根据目标硬件上的测量结果选择最佳值。
在autotvm中,可以为此类值定义可调参数或“旋钮”。

Matmul V1: List candidate values

@autotvm.template(“tutorial/matmul_v1”) # 1. use a decorator
def matmul_v1(N, L, M, dtype):
A = te.placeholder((N, L), name=“A”, dtype=dtype)
B = te.placeholder((L, M), name=“B”, dtype=dtype)

k = te.reduce_axis((0, L), name="k")
C = te.compute((N, M), lambda i, j: te.sum(A[i, k] * B[k, j], axis=k), name="C")
s = te.create_schedule(C.op)# schedule
y, x = s[C].op.axis
k = s[C].op.reduce_axis[0]# 2. get the config object
cfg = autotvm.get_config()# 3. define search space
cfg.define_knob("tile_y", [1, 2, 4, 8, 16])
cfg.define_knob("tile_x", [1, 2, 4, 8, 16])# 4. schedule according to config
yo, yi = s[C].split(y, cfg["tile_y"].val)
xo, xi = s[C].split(x, cfg["tile_x"].val)s[C].reorder(yo, xo, k, yi, xi)return s, [A, B, C]

对先前的调度代码进行了四处修改,并获得了一个可调的“模板”。可以一一解释这些修改。

  1. 使用装饰器,标记为简单模板。

  2. 获取配置对象:可以将其cfg视为此函数的参数,通过其它方式获得。使用此参数,此函数不再是确定性的调度代码。将不同的配置传递给此功能并获得不同的调度,此功能是一个“模板”。
    为了使模板函数更紧凑,在单个函数中做了两件事。(1)定义搜索空间,(2)根据该空间中的实体进行调度。为了达到这个目的,将cfg其设为aConfigSpace或ConfigEntityobject。
    当ConfigSpace时,收集此功能中的所有可调旋钮并建立搜索空间。如果是ConfigEntity,忽略所有空间定义API(即cfg.define_XXXXX(…))。相反,存储所有可调旋钮的确定性值,并根据这些值进行调度。
    在自动调整期间,首先使用一个ConfigSpace 对象,调用此模板以构建搜索空间。然后,ConfigEntity 在构建空间中将此模板称为不同的模板,以获取不同的调度。最后,测量由不同调度生成的代码,选择最佳调度。

  3. 定义两个可调旋钮。第一个tile_y具有5个可能的值。第二个tile_x具有相同的可能值列表。这两个旋钮是独立的,跨越大小= 5x5 = 25的搜索空间

  4. 根据中的确定性值进行调度 cfg
    使用更好的空间定义API
    在上一个模板中,手动列出了旋钮的所有可能值。这是定义空间的最低级别的API。还提供了另一组API,以使空间定义更轻松,更智能。建议使用这套高级API。
    在下面的示例中,用于ConfigSpace.define_split定义拆分旋钮。列举所有可能的方式,分割轴并构造空间。
    还提供了ConfigSpace.define_reorder用于重新排序的旋钮和 ConfigSpace.define_annotate用于展开,向量化,线程绑定的注释。当高级API不能满足要求时,始终可以退而使用低级API。
    @autotvm.template(“tutorial/matmul”)
    def matmul(N, L, M, dtype):
    A = te.placeholder((N, L), name=“A”, dtype=dtype)
    B = te.placeholder((L, M), name=“B”, dtype=dtype)

    k = te.reduce_axis((0, L), name=“k”)
    C = te.compute((N, M), lambda i, j: te.sum(A[i, k] * B[k, j], axis=k), name=“C”)
    s = te.create_schedule(C.op)

    schedule

    y, x = s[C].op.axis
    k = s[C].op.reduce_axis[0]

    define space begin

    cfg = autotvm.get_config()
    cfg.define_split(“tile_y”, y, num_outputs=2)
    cfg.define_split(“tile_x”, x, num_outputs=2)

    define space end

    schedule according to config

    yo, yi = cfg[“tile_y”].apply(s, C, y)
    xo, xi = cfg[“tile_x”].apply(s, C, x)

    s[C].reorder(yo, xo, k, yi, xi)

    return s, [A, B, C]
    笔记
    有关更多说明 cfg.defile_split
    在此模板中,将枚举所有可以将y轴分解为长度为y的两个轴的可能组合。例如,如果y的长度是32,使用32的因子将其分成两个轴,则(外轴的长度,内轴的长度)对有6个可能的值,即(32,1) ,(16、2),(8、4),(4、8),(2、16)或(1、32)。只是tile_y的6个可能值。cfg.define_split(“tile_y”, y, num_outputs=2)
    在调度期间,cfg[“tile_y”]是一个SplitEntity对象。将外轴和内轴的长度存储在cfg[‘tile_y’].size (具有两个元素的元组中)中。在此模板中,使用来应用它。实际上,这等于yo, yi = cfg[‘tile_y’].apply(s, C, y)yo, yi = s[C].split(y, cfg[“tile_y”].size[1]) 或 yo, yi = s[C].split(y, nparts=cfg['tile_y"].size[0])
    使用cfg.apply API的优点是,使多层拆分(当num_outputs> = 3时)更加容易。
    第2步:搜索空间
    在第1步中,通过将旧的调度代码扩展到模板中来构建搜索空间。下一步是选择一个调谐器,并在这个空间中进行探索。
    TVM中的自动调谐器
    调谐器的工作可以通过以下伪代码来描述
    ct = 0
    while ct < max_number_of_trials:
    propose a batch of configs
    measure this batch of configs on real hardware and get results
    ct += batch_size
    当提议下一批配置时,调谐器可以采取不同的策略。在autotvm中为四个调谐器提供了不同的策略。
    • RandomTuner:以随机顺序枚举空间
    • GridSearchTuner:按网格搜索顺序枚举空间
    • GATuner:使用遗传算法搜索空间
    • XGBTuner:使用基于模型的方法。训练XGBoost模型,预测降低的IR的速度,并根据预测选择下一批。
    可以根据空间大小,时间预算和其它因素选择调谐器。例如,如果空间很小(小于1000),那么使用gridsearch调谐器或随机调谐器就足够了。如果空间为10 ^ 9的水平(这是CUDA GPU上conv2d运算符的空间大小),则XGBoostTuner可以更有效地进行探索并找到更好的配置。
    开始调整
    继续矩阵乘法示例。首先,应该创建一个调整任务。还可以检查初始化的搜索空间。在这种情况下,对于512x512方阵乘法,空间大小为10x10 = 100
    N, L, M = 512, 512, 512
    task = autotvm.task.create(“tutorial/matmul”, args=(N, L, M, “float32”), target=“llvm”)
    print(task.config_space)
    输出:
    ConfigSpace (len=100, space_map=
    0 tile_y: Split(policy=factors, product=512, num_outputs=2) len=10
    1 tile_x: Split(policy=factors, product=512, num_outputs=2) len=10
    )
    然后,需要定义如何测量生成的代码并选择一个调谐器。由于空间很小,可以使用随机调谐器。
    仅进行10次试用以进行演示。可以根据自己的时间预算进行更多试验。会将调整结果记录到日志文件中。以后可以使用此文件来获得最佳配置。

logging config (for printing tuning log to the screen)

logging.getLogger(“autotvm”).setLevel(logging.DEBUG)
logging.getLogger(“autotvm”).addHandler(logging.StreamHandler(sys.stdout))

There are two steps for measuring a config: build and run.

By default, we use all CPU cores to compile program. Then measure them sequentially.

We measure 5 times and take average to reduce variance.

measure_option = autotvm.measure_option(builder=“local”, runner=autotvm.LocalRunner(number=5))

Begin tuning with RandomTuner, log records to file matmul.log

You can use alternatives like XGBTuner.

tuner = autotvm.tuner.RandomTuner(task)
tuner.tune(
n_trial=10,
measure_option=measure_option,
callbacks=[autotvm.callback.log_to_file(“matmul.log”)],
)
输出:
Get devices for measurement successfully!
No: 1 GFLOPS: 0.00/0.00 result: MeasureResult(costs=(‘request_remote() argument after ** must be a mapping, not tuple’,), error_no=7, all_cost=10, timestamp=1614595264.4104242) [(‘tile_y’, [-1, 64]), (‘tile_x’, [-1, 1])],None,6
No: 2 GFLOPS: 0.00/0.00 result: MeasureResult(costs=(‘request_remote() argument after ** must be a mapping, not tuple’,), error_no=7, all_cost=10, timestamp=1614595264.714398) [(‘tile_y’, [-1, 512]), (‘tile_x’, [-1, 8])],None,39
No: 3 GFLOPS: 0.00/0.00 result: MeasureResult(costs=(‘request_remote() argument after ** must be a mapping, not tuple’,), error_no=7, all_cost=10, timestamp=1614595265.0153906) [(‘tile_y’, [-1, 2]), (‘tile_x’, [-1, 8])],None,31
No: 4 GFLOPS: 0.00/0.00 result: MeasureResult(costs=(‘request_remote() argument after ** must be a mapping, not tuple’,), error_no=7, all_cost=10, timestamp=1614595265.3186316) [(‘tile_y’, [-1, 1]), (‘tile_x’, [-1, 32])],None,50
No: 5 GFLOPS: 0.00/0.00 result: MeasureResult(costs=(‘request_remote() argument after ** must be a mapping, not tuple’,), error_no=7, all_cost=10, timestamp=1614595265.6324012) [(‘tile_y’, [-1, 256]), (‘tile_x’, [-1, 64])],None,68
No: 6 GFLOPS: 0.00/0.00 result: MeasureResult(costs=(‘request_remote() argument after ** must be a mapping, not tuple’,), error_no=7, all_cost=10, timestamp=1614595265.934279) [(‘tile_y’, [-1, 256]), (‘tile_x’, [-1, 512])],None,98
No: 7 GFLOPS: 0.00/0.00 result: MeasureResult(costs=(‘request_remote() argument after ** must be a mapping, not tuple’,), error_no=7, all_cost=10, timestamp=1614595266.2497342) [(‘tile_y’, [-1, 128]), (‘tile_x’, [-1, 2])],None,17
No: 8 GFLOPS: 0.00/0.00 result: MeasureResult(costs=(‘request_remote() argument after ** must be a mapping, not tuple’,), error_no=7, all_cost=10, timestamp=1614595266.5602283) [(‘tile_y’, [-1, 8]), (‘tile_x’, [-1, 4])],None,23
No: 9 GFLOPS: 0.00/0.00 result: MeasureResult(costs=(‘request_remote() argument after ** must be a mapping, not tuple’,), error_no=7, all_cost=10, timestamp=1614595267.6910803) [(‘tile_y’, [-1, 256]), (‘tile_x’, [-1, 32])],None,58
No: 10 GFLOPS: 0.00/0.00 result: MeasureResult(costs=(‘request_remote() argument after ** must be a mapping, not tuple’,), error_no=7, all_cost=10, timestamp=1614595267.9915197) [(‘tile_y’, [-1, 64]), (‘tile_x’, [-1, 128])],None,76
最后,最好从缓存文件中应用历史记录,并检查其正确性。可以matmul直接在 autotvm.apply_history_best上下文中调用该函数。当调用此函数时,使用其参数查询调度上下文,并使用相同的参数获得最佳配置。

apply history best from log file

with autotvm.apply_history_best(“matmul.log”):
with tvm.target.Target(“llvm”):
s, arg_bufs = matmul(N, L, M, “float32”)
func = tvm.build(s, arg_bufs)

check correctness

a_np = np.random.uniform(size=(N, L)).astype(np.float32)
b_np = np.random.uniform(size=(L, M)).astype(np.float32)
c_np = a_np.dot(b_np)

c_tvm = tvm.nd.empty(c_np.shape)
func(tvm.nd.array(a_np), tvm.nd.array(b_np), c_tvm)

tvm.testing.assert_allclose(c_np, c_tvm.asnumpy(), rtol=1e-2)

编写可调模板并使用自动调谐器相关推荐

  1. 编写可调模板并使用Auto-tuner自动调谐器

    编写可调模板并使用Auto-tuner自动调谐器 本文介绍在TVM自动调谐模块. 自动调谐有两个步骤.第一步是定义搜索空间.第二步是运行一个搜索算法来探索这个空间.可以学习如何在TVM中执行这两个步骤 ...

  2. 性能测试脚本的编写和调试_编写自动调试器以在测试执行期间捕获异常

    性能测试脚本的编写和调试 以前,我曾说过, 您总是想保留一些调试器断点作为例外 . 这有助于防止代码在不注意的情况下腐烂掉,有时掩盖了另一个问题. 如果您认真对待这一点,那么最好将此想法扩展到自动化测 ...

  3. 编写自动调试器以在测试执行期间捕获异常

    以前,我曾说过, 您总是想保留一些调试器断点作为例外 . 此帮助可防止代码在不引起注意的情况下腐烂掉-有时掩盖了另一个问题. 如果您认真对待这一点,则最好将此概念扩展到自动化测试中. 但是想出一个全面 ...

  4. ARM-CPU卷积网络的自动调谐

    ARM-CPU卷积网络的自动调谐 为特定的ARM设备自动调谐对于获得最佳性能至关重要.这是一个关于如何调整整个卷积网络的资料. 以模板的形式编写了TVM中ARM CPU的操作实现.模板有许多可调旋钮( ...

  5. NVIDIA GPU卷积网络的自动调谐

    NVIDIA GPU卷积网络的自动调谐 针对特定设备和工作负载的自动调整对于获得最佳性能至关重要.这是关于如何为NVIDIA GPU调整整个卷积网络. NVIDIA GPU在TVM中的操作实现是以模板 ...

  6. CAN调谐器与SILICON调谐器(又称为铁壳调谐器和硅片调谐器)

    在美国数字电视的转换已经开始,并继续逐渐的过渡.归功于数字机顶盒(STBs),绝大多数有线电视和卫星用户不受影响.即便是没有STB的大多数基本有线电视用户,也有足够的时间进行调整,因为在2009年二月 ...

  7. x86 cpu卷积网络的自动调谐

    x86 cpu卷积网络的自动调谐 这是一个关于如何为x86cpu调整卷积神经网络的文档. 本文不会在Windows或最新版本的macOS上运行.要让它运行,需要将主体包装在 if name == &q ...

  8. python爬虫图片实例-【图文详解】python爬虫实战——5分钟做个图片自动下载器...

    我想要(下)的,我现在就要 python爬虫实战--图片自动下载器 之前介绍了那么多基本知识[Python爬虫]入门知识(没看的赶紧去看)大家也估计手痒了.想要实际做个小东西来看看,毕竟: talk ...

  9. ROS入门笔记(十):编写与测试简单的消息发布器和订阅器(C++)

    ROS入门笔记(十):编写与测试简单的消息发布器和订阅器(C++) 文章目录 01 导读 02 功能包的创建 03 功能包的源代码编写 3.1 编写发布器节点(talker.cpp) 3.2 编写订阅 ...

最新文章

  1. [原]ImportError: No module named thrift.Thrift问题解决
  2. java如何将数据保存为xml6_用Java实现可保存状态的数据库生成XML树,源码来了(9)...
  3. linux安装docker部署java项目
  4. linux mysql5.7启动失败/tmp/mysql.sock ?
  5. Spring Cloud Sleuth 原理简介和使用
  6. 本周两场直播丨PostgreSQL中的锁;数据库对象命名设计规范手册
  7. math api matrix
  8. Lua笔记2 变量、循环和流程控制
  9. 苹果Mac开启root用户及切换到root用户的方法
  10. 4412 学习目录总结
  11. Linux 内核编程总结
  12. 历代易学经典名著(简介及电子书下载)V3.0版
  13. MapReduce优缺点
  14. 如何在windows电脑网页上创建苹果ID?
  15. 关于win10系统中谷歌浏览器崩溃的问题
  16. SpringBoot系列之(三):启动过程
  17. winscp开启ftp_Linux 之WinSCP连接FTP
  18. Oracle 10g 32位 下载地址
  19. js将字符串按照逗号分割
  20. 【网络安全】知名网络安全企业有哪些?

热门文章

  1. zip压缩多个文件,解压时不包含目录层级
  2. Solr 使用Facet分组过程中与分词的矛盾解决办法
  3. Go 学习笔记(65)— Go 中函数参数是传值还是传引用
  4. 阿里巴巴图学习框架 euler 安装运行记录
  5. 正确“假期休息模式”
  6. 梯度下降优化算法概述
  7. 自定义算子高性能开发
  8. 2021年大数据Kafka(七):Kafka的分片和副本机制
  9. 2021年大数据Hadoop(七):HDFS分布式文件系统简介
  10. Python:Downloader Middlewares