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

为特定的ARM设备自动调谐对于获得最佳性能至关重要。这是一个关于如何调整整个卷积网络的资料。

以模板的形式编写了TVM中ARM CPU的操作实现。模板有许多可调旋钮(平铺系数、矢量化、展开等)。将调整神经网络中的所有卷积和深度卷积算子。在调优之后,生成一个日志文件,其中存储了所有所需操作符的最佳旋钮值。当TVM编译器编译这些运算符时,它将查询此日志文件以获得最佳的旋钮值。

还发布了一些arm设备的预调参数。可以转到arm cpu基准测试来查看结果。

本文不会在Windows或最新版本的macOS上运行。要让它运行,需要将本教程的主体包装在if __name__ == "__main__": 块中。

安装依赖项

要在tvm中使用autotvm包,需要安装一些额外的依赖项。(如果使用python2,请将“3”更改为“2”):

pip3 install --user psutil xgboost tornado

为了使TVM在调谐过程中运行更快,建议使用cython作为TVM的FFI。在TVM的根目录中,执行(如果使用python2,将“3”更改为“2”):

pip3 install --user cython

sudo make cython3

现在回到python代码。导入包。

import os

import numpy as np

import tvm

from tvm import relay, autotvm

import tvm.relay.testing

from tvm.autotvm.tuner import XGBTuner, GATuner, RandomTuner, GridSearchTuner

from tvm.contrib.utils import tempdir

import tvm.contrib.graph_runtime as runtime

Define network

首先需要在转换前端API中定义网络。可以从转换测试. 也可以从MXNet、ONNX和TensorFlow加载模型。

def get_network(name, batch_size):

"""Get the symbol definition and random weight of a network"""

input_shape = (batch_size, 3, 224, 224)

output_shape = (batch_size, 1000)

if "resnet" in name:

n_layer = int(name.split("-")[1])

mod, params = relay.testing.resnet.get_workload(

num_layers=n_layer, batch_size=batch_size, dtype=dtype

)

elif "vgg" in name:

n_layer = int(name.split("-")[1])

mod, params = relay.testing.vgg.get_workload(

num_layers=n_layer, batch_size=batch_size, dtype=dtype

)

elif name == "mobilenet":

mod, params = relay.testing.mobilenet.get_workload(batch_size=batch_size)

elif name == "squeezenet_v1.1":

mod, params = relay.testing.squeezenet.get_workload(

batch_size=batch_size, version="1.1", dtype=dtype

)

elif name == "inception_v3":

input_shape = (batch_size, 3, 299, 299)

mod, params = relay.testing.inception_v3.get_workload(batch_size=batch_size, dtype=dtype)

elif name == "mxnet":

# an example for mxnet model

from mxnet.gluon.model_zoo.vision import get_model

block = get_model("resnet18_v1", pretrained=True)

mod, params = relay.frontend.from_mxnet(block, shape={"data": input_shape}, dtype=dtype)

net = mod["main"]

net = relay.Function(

net.params, relay.nn.softmax(net.body), None, net.type_params, net.attrs

)

mod = tvm.IRModule.from_expr(net)

else:

raise ValueError("Unsupported network: " + name)

return mod, params, input_shape, output_shape

Start RPC Tracker

TVM使用RPC会话与ARM板通信。在调谐过程中,调谐器将生成的代码发送到板上,并测量板上代码的速度。

为了扩大调整范围,TVM使用RPC跟踪器来管理分布式设备。RPC跟踪器是一个集中的控制器节点。可以把所有设备注册到跟踪器上。例如,如果有10部电话,可以将它们全部注册到跟踪器中,并行运行10次测量,从而加快调谐过程。

要启动RPC跟踪器,在主机上运行此命令。在整个整定过程中需要跟踪器,所以需要为这个命令打开一个新的终端:

python -m tvm.exec.rpc_tracker --host=0.0.0.0 --port=9190

The expected output is

INFO:RPCTracker:bind to 0.0.0.0:9190

Register devices to RPC Tracker

现在可以在跟踪器上注册设备。第一步是为ARM设备构建TVM运行时。

对于Linux:按照本节“在设备上构建TVM runtime”在设备上构建TVM runtime。然后通过

python -m tvm.exec.rpc_server --tracker=[HOST_IP]:9190 --key=rk3399

(将[HOST_IP]替换为主机的IP地址)

对于Android:按照这个readme页面在Android设备上安装TVM RPC APK。确保能通过android rpc测试。那么已经注册了设备。在调整过程中,必须进入开发者选项,并启用“在改变屏幕时保持屏幕唤醒”并给手机充电以使其稳定。

注册设备后,可以通过查询rpc_tracker来确认。

python -m tvm.exec.query_rpc_tracker --host=0.0.0.0 --port=9190

例如,如果有2个Huawei mate10 pro、11个Raspberry Pi 3B和2个rk3399,则输出可以是

Queue Status

----------------------------------

key          total  free  pending

----------------------------------

mate10pro    2      2     0

rk3399       2      2     0

rpi3b        11     11    0

可以将多个设备注册到跟踪器中,以加速调谐中的测量。

设置调谐选项

在调整之前,应该应用一些配置。这里以RK3399板为例。在设置中,应该相应地修改target和device_key键。如果您使用安卓手机,请将use_android设置为True。

#### DEVICE CONFIG ####

# Replace "aarch64-linux-gnu" with the correct target of your board.

# This target is used for cross compilation. You can query it by :code:`gcc -v` on your device.

target = tvm.target.Target("llvm -device=arm_cpu -mtriple=aarch64-linux-gnu")

# Also replace this with the device key in your tracker

device_key = "rk3399"

# Set this to True if you use android phone

use_android = False

#### TUNING OPTION ####

network = "resnet-18"

log_file = "%s.%s.log" % (device_key, network)

dtype = "float32"

tuning_option = {

"log_filename": log_file,

"tuner": "xgb",

"n_trial": 1500,

"early_stopping": 800,

"measure_option": autotvm.measure_option(

builder=autotvm.LocalBuilder(build_func="ndk" if use_android else "default"),

runner=autotvm.RPCRunner(

device_key,

host="0.0.0.0",

port=9190,

number=5,

timeout=10,

),

),

}

注意

如何设置调整选项

一般来说,这里提供的默认值工作正常。如果有足够长的时间来进行调试,可以提前停止调试。如果设备运行非常慢,或者conv2d操作员有许多gflop,考虑将超时设置得更大。

如果模型有深度卷积,可以考虑将try_spatial_pack_depthwise设置为True,这通常比默认优化性能更好。例如,在ARM CPU A53 2.0GHz上,发现它可以使Mobilenet V1型号上的深度卷积性能提高1.6倍。

Begin Tuning

现在可以从网络中提取调优任务并开始调优。这里,提供了一个简单的实用函数来优化任务列表。这个函数只是一个按顺序调整初始实现。将在将来引入更复杂的调优调度程序。

# You can skip the implementation of this function for this tutorial.

def tune_tasks(

tasks,

measure_option,

tuner="xgb",

n_trial=1000,

early_stopping=None,

log_filename="tuning.log",

use_transfer_learning=True,

):

# create tmp log file

tmp_log_file = log_filename + ".tmp"

if os.path.exists(tmp_log_file):

os.remove(tmp_log_file)

for i, tsk in enumerate(reversed(tasks)):

prefix = "[Task %2d/%2d] " % (i + 1, len(tasks))

# create tuner

if tuner == "xgb" or tuner == "xgb-rank":

tuner_obj = XGBTuner(tsk, loss_type="rank")

elif tuner == "xgb_knob":

tuner_obj = XGBTuner(tsk, loss_type="rank", feature_type="knob")

elif tuner == "ga":

tuner_obj = GATuner(tsk, pop_size=50)

elif tuner == "random":

tuner_obj = RandomTuner(tsk)

elif tuner == "gridsearch":

tuner_obj = GridSearchTuner(tsk)

else:

raise ValueError("Invalid tuner: " + tuner)

if use_transfer_learning:

if os.path.isfile(tmp_log_file):

tuner_obj.load_history(autotvm.record.load_from_file(tmp_log_file))

# do tuning

tsk_trial = min(n_trial, len(tsk.config_space))

tuner_obj.tune(

n_trial=tsk_trial,

early_stopping=early_stopping,

measure_option=measure_option,

callbacks=[

autotvm.callback.progress_bar(tsk_trial, prefix=prefix),

autotvm.callback.log_to_file(tmp_log_file),

],

)

# pick best records to a cache file

autotvm.record.pick_best(tmp_log_file, log_filename)

os.remove(tmp_log_file)

最后,启动优化作业并评估端到端性能。

def tune_and_evaluate(tuning_opt):

# extract workloads from relay program

print("Extract tasks...")

mod, params, input_shape, _ = get_network(network, batch_size=1)

tasks = autotvm.task.extract_from_program(

mod["main"], target=target, params=params, ops=(relay.op.get("nn.conv2d"),)

)

# run tuning tasks

print("Tuning...")

tune_tasks(tasks, **tuning_opt)

# compile kernels with history best records

with autotvm.apply_history_best(log_file):

print("Compile...")

with tvm.transform.PassContext(opt_level=3):

lib = relay.build_module.build(mod, target=target, params=params)

# export library

tmp = tempdir()

if use_android:

from tvm.contrib import ndk

filename = "net.so"

lib.export_library(tmp.relpath(filename), ndk.create_shared)

else:

filename = "net.tar"

lib.export_library(tmp.relpath(filename))

# upload module to device

print("Upload...")

remote = autotvm.measure.request_remote(device_key, "0.0.0.0", 9190, timeout=10000)

remote.upload(tmp.relpath(filename))

rlib = remote.load_module(filename)

# upload parameters to device

ctx = remote.context(str(target), 0)

module = runtime.GraphModule(rlib["default"](ctx))

data_tvm = tvm.nd.array((np.random.uniform(size=input_shape)).astype(dtype))

module.set_input("data", data_tvm)

# evaluate

print("Evaluate inference time cost...")

ftimer = module.module.time_evaluator("run", ctx, number=1, repeat=10)

prof_res = np.array(ftimer().results) * 1000  # convert to millisecond

print(

"Mean inference time (std dev): %.2f ms (%.2f ms)"

% (np.mean(prof_res), np.std(prof_res))

)

# We do not run the tuning in our webpage server since it takes too long.

# Uncomment the following line to run it by yourself.

# tune_and_evaluate(tuning_option)

Sample Output

调整需要编译许多程序并从中提取特性。因此建议使用高性能CPU。下面列出了一个示例输出。在一台32T的AMD Ryzen Threadripper上大约需要2个小时。

Extract tasks...

Tuning...

[Task  1/12]  Current/Best:   22.37/  52.19 GFLOPS | Progress: (544/1000) | 406.59 s Done.

[Task  2/12]  Current/Best:    6.51/  18.77 GFLOPS | Progress: (608/1000) | 325.05 s Done.

[Task  3/12]  Current/Best:    4.67/  24.87 GFLOPS | Progress: (480/1000) | 372.31 s Done.

[Task  4/12]  Current/Best:   11.35/  46.83 GFLOPS | Progress: (736/1000) | 602.39 s Done.

[Task  5/12]  Current/Best:    1.01/  19.80 GFLOPS | Progress: (448/1000) | 262.16 s Done.

[Task  6/12]  Current/Best:    2.47/  23.76 GFLOPS | Progress: (672/1000) | 563.85 s Done.

[Task  7/12]  Current/Best:   14.57/  33.97 GFLOPS | Progress: (544/1000) | 465.15 s Done.

[Task  8/12]  Current/Best:    1.13/  17.65 GFLOPS | Progress: (576/1000) | 365.08 s Done.

[Task  9/12]  Current/Best:   14.45/  22.66 GFLOPS | Progress: (928/1000) | 724.25 s Done.

[Task 10/12]  Current/Best:    3.22/  15.36 GFLOPS | Progress: (864/1000) | 564.27 s Done.

[Task 11/12]  Current/Best:   11.03/  32.23 GFLOPS | Progress: (736/1000) | 635.15 s Done.

[Task 12/12]  Current/Best:    8.00/  21.65 GFLOPS | Progress: (1000/1000) | 1111.81 s Done.

Compile...

Upload...

Evaluate inference time cost...

Mean inference time (std dev): 162.59 ms (0.06 ms)

注意

遇到困难?

自动调谐模块容易出错。如果总是看到“0.00/0.00 GFLOPS”,那么一定是出了什么问题。

首先,确保设置了正确的设备配置。然后,可以通过在脚本开头添加这些行来打印调试信息。它将打印每个测量结果,可以在其中找到有用的错误消息。

import logging

logging.getLogger('autotvm').setLevel(logging.DEBUG)

https://tvm.apache.org/docs/tutorials/autotvm/tune_relay_arm.html

最后,请随时寻求帮助https://discus.tvm.apache.org

下载Python源代码:tune_relay_arm.py

下载Jupyter笔记:tune_relay_arm.ipynb

ARM-CPU卷积网络的自动调谐的更多相关文章

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

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

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

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

  3. ARM CPU自动调度神经网络

    ARM CPU自动调度神经网络 对特定设备和工作负载进行自动调度,对于获得最佳性能至关重要.通过RPC使用自动调度器为ARM CPU调度整个神经网络. 为了自动调度神经网络,将网络划分为小的子图,进行 ...

  4. 自动调试用于移动GPU的卷积网络

    自动调试用于移动GPU的卷积网络 对特定设备进行自动调试对于获得最佳性能至关重要.这是有关如何调试整个卷积网络的说明文档. TVM中Mobile GPU的算子实现以模板形式编写.模板具有许多可调旋钮( ...

  5. arm cpu的架构及分类说明

    今天在编译mplayer for mx27ads的时候, 碰到了armv5te与armv6优化的问题. 默认的交叉编译器支持armv5te也支持armv6,就默认使用了mplayer中mpeg4的ar ...

  6. EdgeFormer: 向视觉 Transformer 学习,构建一个比 MobileViT 更好更快的卷积网络

    ​  前言 本文主要探究了轻量模型的设计.通过使用 Vision Transformer 的优势来改进卷积网络,从而获得更好的性能. 欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结.最新技术跟 ...

  7. 基于孪生卷积网络(Siamese CNN)和短时约束度量联合学习的tracklet association方法

    基于孪生卷积网络(Siamese CNN)和短时约束度量联合学习的tracklet association方法 Siamese CNN Temporally Constrained Metrics T ...

  8. 全卷积网络 FCN 详解

    背景 CNN能够对图片进行分类,可是怎么样才能识别图片中特定部分的物体,在2015年之前还是一个世界难题.神经网络大神Jonathan Long发表了<Fully Convolutional N ...

  9. 学习笔记CB009:人工神经网络模型、手写数字识别、多层卷积网络、词向量、word2vec

    人工神经网络,借鉴生物神经网络工作原理数学模型. 由n个输入特征得出与输入特征几乎相同的n个结果,训练隐藏层得到意想不到信息.信息检索领域,模型训练合理排序模型,输入特征,文档质量.文档点击历史.文档 ...

随机推荐

  1. windows-CODE注入(远程线程注入)

    远程线程注入(先简单说,下面会详细说)今天整理下代码注入(远程线程注入),所谓代码注入,可以简单的理解为是在指定内进程里申请一块内存,然后把我们自己的执行代码和一些变量拷贝进去(通常是以启线程的方式) ...

  2. <JVM下篇:性能监控与调优篇>补充:使用OQL语言查询对象信息

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  3. 【pytest系列】- pytest测试框架介绍与运行

    如果想从头学起pytest,可以去看看这个系列的文章! https://www.cnblogs.com/miki-peng/category/1960108.html 前言​ ​ 目前有两种纯测试的测 ...

  4. pandas(10):数据增删改

    目录 一.对索引进行操作 1 操作索引值df.rename() 二.指定数据替换.修改df.replace() 三.特殊值--缺失值处理 四.新增行列 1 直接赋值添加新列 2 df.assign() ...

  5. 第三部分 IDEA创建并运行项目

    可以创建一个maven,几行代码就解决了导入依赖,但是我的电脑不知道哪里出现了问题,IDEA重装,jdk重装,maven重装,都无法解决问题,找了3天,还是没有解决问题.最后只能采用手动导入包方法.看 ...

  6. [面向对象之继承应用(在子类派生重用父类功能(super),继承实现原理(继承顺序、菱形问题、继承原理、Mixins机制),组合]

    [面向对象之继承应用(在子类派生重用父类功能(super),继承实现原理(继承顺序.菱形问题.继承原理.Mixins机制),组合] 继承应用 类与类之间的继承指的是什么'是'什么的关系(比如人类,猪类 ...

  7. pip安装模块或者更新出现问题Error:Could not install packages due to an EnvironmentError

    问题分析 出现此问题大致的原因: 就是包安装的位置没有读写的权限,这个多半是因为安装python的时候安装在了C盘,或者其他programs这类的文件夹里 或者就是环境变量的设置的安装位置的问题,导致 ...

  8. Servlet-授课

    1 Servlet 1.1 Servlet概述 Servlet是SUN公司提供的一套规范,名称就叫Servlet规范,它也是JavaEE规范之一.我们可以像学习Java基础一样,通过API来学习Ser ...

  9. QTableWidget - 基础讲解(2) 样式、右键菜单、表头塌陷、多选等

    转载:https://www.cnblogs.com/zhoug2020/p/3789076.html 在Qt的开发过程中,时常会用到表单(QTableWidget)这个控件,网上的资料不少,但是都是 ...

  10. 【转载】复制文件到已存在的Jar

    复制文件到已存在的Jar 技术标签: Ant  OSGI  Eclipse  脚本  配置管理   问题 这两天在写一个小东西.这个小东西是一个大东西的一部分.其实也就是其中的一两个类.而这个大东西需 ...