【tvm官网教程】AutoTVM:基于模板的自动调优
目的
理解华为的autotvm,先理解原生的。
1. autotvm模块
TVM中的自动调优模块,auto-tuning包括:
- 调优空间定义API
- 高效自动调优器
- 调优结果和数据集支持
- 分布式度量以扩大调优范围
1.1 API get_config()
tvm.autotvm.task.task.get_config()
获得当前的autotvm配置对象,返回值类型ConfigSpace或者ConfigEntity。
1.2 class ConfigSpace
class tvm.autotvm.task.space.ConfigSpace
1.2.1 define_split()
define_split(name, axis, policy=‘factors’, **kwargs)
定义一个可调节旋钮,用于将指定轴split。
policy–调优器策略:
- factors 调优器将尝试所有可分解因子
- power2 调优器将尝试2的幂次方的因子
- verbose 调优器将尝试上述两个策略的并集
- candidate 调试器根据指定的候选集进行尝试
policy额外的参数还有:
- max_factor
- filter
- num_outputs: split之后轴的最大个数
- no_tail:bool值,是否仅将可除数作为因子
1.2.2 class SplitEntity
classtvm.autotvm.task.space.SplitEntity(size)
成员函数只有一个apply。
apply(sch, op, axis)
sch-调度, op-stage, axis-将被切分的轴。
1.2.3 define_knob()
define_knob(name, candidate)
用候选集定义一个可调节旋钮。
1.3 task
tvm.autotvm.task
任务是模板函数的可调组成。
调优器执行可调任务,并优化任务中所有模板功能的联合配置空间。
1.3.1 create
Task = tvm.autotvm.task.task.create(task_name, args, target, target_host=None)
创建一个调优任务,并初始化搜索空间。
## 前面定义搜索空间,这里创建搜索任务
N, H, W, CO, CI, KH, KW, strides, padding = 1, 7, 7, 512, 512, 3, 3, (1, 1), (1, 1)
task = autotvm.task.create("tutorial/conv2d_no_batching", args=(N, H, W, CO, CI, KH, KW, strides, padding), target="cuda")
print(task.config_space)
ConfigSpace (len=10454400, space_map=
0 tile_f: Split(policy=factors, product=512, num_outputs=4) len=220
1 tile_y: Split(policy=factors, product=7, num_outputs=4) len=4
2 tile_x: Split(policy=factors, product=7, num_outputs=4) len=4
3 tile_rc: Split(policy=factors, product=512, num_outputs=3) len=55
4 tile_ry: Split(policy=factors, product=3, num_outputs=3) len=3
5 tile_rx: Split(policy=factors, product=3, num_outputs=3) len=3
6 auto_unroll_max_step: OtherOption([0, 512, 1500]) len=3
7 unroll_explicit: OtherOption([0, 1]) len=2
)
1.4 measure
tvm.autotvm.measure
面向用户的API,用于指定如何衡量生成的代码。
1.4.1 measure_option
tvm.autotvm.measure.measure_option(builder, runner)
设置测量选项。为了测量配置,TVM将build并运行它。因此,我们必须为这两个步骤设置选项。builder和runner在超时、并行等方面有自己的选项。
1.5 tuner
tvm.autotvm.tuner
调优器以task作为输入。它从ConfigSpace中挑出了一些有可能是最优解的ConfigEntity,并在实际硬件上进行了评估。然后根据测量结果挑出下一批ConfigEntity。所谓调优就是此过程的循环。
常用的调优器有:
- RandomTuner() 以随机顺序枚举搜索空间
- GridSearchTuner() 以grid顺序枚举搜索空间
- GATuner() 以基因算法搜索
- XGBTuner() 以xgboost算法搜索
常用成员函数有:
- tune() 开始调优
tune(n_trial, measure_option, early_stopping=None, callbacks=(), si_prefix=‘G’)
1.6 apply_history_best
tvm.autotvm.apply_history_best(records)
调优的过程可以记录在log中,在将日志文件传递给这个方法得到最佳解的上下文。
@autotvm.template("tutorial/conv2d_no_batching")
def conv2d_no_batching(N, H, W, CO, CI, KH, KW, stride, padding):
### 一堆计算描述和可调调度,成为一个模板
return s, [raw_data, kernel, conv]
with autotvm.apply_history_best("conv2d.log"):
with tvm.target.Target("cuda"):
s, arg_bufs = conv2d_no_batching(N, H, W, CO, CI, KH, KW, strides, padding)
func = tvm.build(s, arg_bufs)
# func现在已经是可以执行的、经过优化的、与target耦合的函数实现了
a_np = np.random.uniform(size=(N, CI, H, W)).astype(np.float32)
w_np = np.random.uniform(size=(CO, CI, KH, KW)).astype(np.float32)
c_np = conv2d_nchw_python(a_np, w_np, strides, padding) #作为精度标杆,作为性能基数
dev = tvm.gpu()
a_tvm = tvm.nd.array(a_np, device=dev)
w_tvm = tvm.nd.array(w_np, device=dev)
c_tvm = tvm.nd.empty(c_np.shape, device=dev)
func(a_tvm, w_tvm, c_tvm)
2. class Schedule
tvm.te中的类Schedule,有四个成员函数:cache_read()、cache_write()、create_group()、normalize()、rfactor()。
normalize()
从当前调度中build一个归一化的调度,插入必要的rebase,以保证迭代向量从0开始。
stage = create_group(outputs, inputs, include_inputs=False)
用给定的输入输出绑定,创建一个stage。
tensor = cache_read(tensor, scope, readers)
为读取器创建原始张量的缓存读取。如果调用这个接口,必须在所有split、fuse原语之前。
tensor = cache_write(tensor, scope)
在保存到tensor之前,创建原始张量的缓存写入。
tensor = rfactor(tensor, axis, factor_axis=0)
1. 调优专用于GPU的高性能Conv
你大可针对特定的输入shape,手写conv、depthwiseconv的GPU实现,但是难写,而且难以移植到新device上。
TVM支持定义调度模板,然后用搜索算法自动搜索最佳设计,并将搜索过程、最佳设计记录,也有方便的工具进行分析和导入导出(入门教程)。
step1. 定义搜索空间
tensor = tvm.topi.nn.conv2d_nchw(Input, Filter, stride, padding, dilation, out_dtype=None)
接收NCHW格式的输入,进行conv2d计算。step2. 搜索空间
2. 调优专用于GPU的高性能CNN
可以从tvm.relay.testing中加载一下预定义的网络,也可以从Pytorch、Tensorflow、MXNet、ONNX、Keras、TFLite、CoreML、DarkNet和Caffe2等框架导出并加载模型。
tvm.relay
The Relay IR namespace containing the IR definition and compiler.
relay.op.get(“nn.conv2d”) 没找到doc
autotvm.task.extract_from_program() 没找到doc
tvm.relay.build(ir_mod, target=None, target_host=None, params=None, mod_name=‘default’)
build能在TVM图执行器(TVM graph executor)运行的Relay函数。
辅助函数,返回:
- graph_json 能被图执行器接收的json字符串;
- mod tvm.Module 包含必要库的模型
- params dict 最终图的权重
class tvm.contrib.graph_executor.GraphModule(module)
运行时模型包装器。
这是底层TVM模块的精简包装。您还可以直接调用底层模块的set_input(),run()和get_output()函数。
3. 调优专用于x86 CPU的高性能CNN
除了对kernel的实现调优之外,TVM的Relay模块还支持图级别的优化。
优化器有:use_DP、PBQPTuner。
没有找到对应的doc,但是教程中可以看到几个成员函数:
benchmark_layout_transform()
run()
write_opt_sch2record_file()
class tvm.transform.PassContext(opt_level=2, required_pass=None, disabled_pass=None, trace=None, config=None)
进行Relay优化/分析的基础上下文。每个pass上下文都包含许多辅助信息,这些信息用于帮助优化过程。这些信息包括记录优化过程中的错误的错误报告器等。
4. 调优专用于ARM CPU的高性能CNN
TVM使用RPC会话与ARM板进行通信。在调优期间,调优器会将生成的代码发送到ARM板上,并测量ARM板上的计算速度。
为了扩大调优范围,TVM使用RPC Tracker来管理分布式设备。RPC跟踪器是一个集中式控制器节点。我们可以将所有设备注册到跟踪器。
在host侧启动RPC Tracker:
python -m tvm.exec.rpc_tracker --host=0.0.0.0 --port=9190
在device上build TVM运行时;
然后向跟踪器注册device:
python -m tvm.exec.rpc_server --tracker=[HOST_IP]:9190 --key=rk3399