TVM:通过Python接口(AutoTVM)来编译和优化模型

TVM:通过Python接口(AutoTVM)来编译和优化模型

上次我们已经介绍了如何从源码编译安装 tvm,本文我们将介绍在本机中使用 tvm Python 接口来编译优化模型的一个demo。

TVM 是一个深度学习编译器框架,有许多不同的模块可用于处理深度学习模型和运算符。 在本教程中,我们将学习如何使用 Python API 加载、编译和优化模型。

在本文中,我们将使用 Python 接口的 tvm 完成以下任务:

  • 为 tvm runtime 编译一个预训练好的 ResNet50-v2 模型
  • 在编译好的模型上运行一张真实的图像,并得到正确的结果
  • 使用 tvm 在 CPU 上 tune 模型
  • 使用 tvm 收集的数据重新编译并优化模型
  • 再次运行一张真实的图像,对比优化前后模型的输出和性能

导入必要的包

  • onnx:用于模型的加载和转换
  • PIL:用于处理图像数据的 Python 图像库
  • numpy:用于图像数据预处理和后处理的
  • 用于下载测试数据的辅助程序
  • TVM relay 框架和 TVM Graph Executor
import onnx
from tvm.contrib.download import download_testdata
from PIL import Image
import numpy as np
import tvm.relay as relay
import tvm
from tvm.contrib import graph_executor

下载并加载onnx模型

在本文中,我们将使用 ResNet-50 v2。

TVM 提供了一个帮助库来下载预先训练的模型。 通过模块提供模型 URL、文件名和模型类型,TVM 将下载模型并将其保存到磁盘。 对于 ONNX 模型的实例,我们可以使用 ONNX runtime 将其加载到内存中。另外提一下一个很方便的查看 onnx 模型的工具:netron。

model_url = "".join(["https://github.com/onnx/models/raw/","master/vision/classification/resnet/model/","resnet50-v2-7.onnx",]
)model_path = download_testdata(model_url, "resnet50-v2-7.onnx", module="onnx")
onnx_model = onnx.load(model_path)

下载、预处理并加载测试图像

我们从网络上下载一只小猫的图像作为测试图像。

img_url = "https://s3.amazonaws.com/model-server/inputs/kitten.jpg"
img_path = download_testdata(img_url, "imagenet_cat.png", module="data")# 将图像尺寸调整为 (224, 224)
resized_image = Image.open(img_path).resize((224, 224))
img_data = np.asarray(resized_image).astype("float32")# 此时我们图像的数据排布是 HWC,但是 onnx 需要的是 CHW,所以要转换以下
img_data = np.transpose(img_data, (2, 0, 1))# 根据 ImageNet 数据集的标准进行归一化
imagenet_mean = np.array([0.485, 0.456, 0.406]).reshape((3, 1, 1))
imagenet_stddev = np.array([0.229, 0.224, 0.225]).reshape((3, 1, 1))
norm_img_data = (img_data / 255 - imagenet_mean) / imagenet_stddev# 增加通道维,此时我们输入的数据排布为 NCHW
img_data = np.expand_dims(norm_img_data, axis=0)

注意,以上的模型和测试图像完全可以替换成自己的,只要按要求转换为指定的格式即可

通过relay编译模型

target = 'llvm'

注意:请定义正确的定义正确的target

指定正确的 target 会对编译模块的性能产生巨大影响,因为它可以利用 target 上可用的硬件功能。 有关更多信息,请参阅自动调整 x86 CPU 的卷积网络。 我们建议确定您正在运行的 CPU 以及可选功能,并适当设置 target 。 例如,对于某些处理器 target = “llvm -mcpu=skylake”,或 target = “llvm -mcpu=skylake-avx512” 用于具有 AVX-512 矢量指令集的处理器。

# 注意这里 'input_name' 可能会根据模型不同而不同,大家可以使用上面提到 netron 工具来查看输入名称
input_name = "data"
shape_dict = {input_name: img_data.shape}mod, params = relay.frontend.from_onnx(onnx_model, shape_dict)with tvm.transform.PassContext(opt_level=3):lib = relay.build(mod, target=target, params=params)dev = tvm.device(str(target), 0)
module = graph_executor.GraphModule(lib["default"](dev))

在 TVM Runtime 上执行

在模型编译完成之后,我们可以使用 TVM Runtime 来用模型来输出预测结果。要运行 TVM Runtime 来完成预测,我们需要:

  • 编译好的模型,就是我们刚刚做的
  • 有效地模型输入
dtype = "float32"
module.set_input(input_name, img_data)
module.run()
output_shape = (1, 1000)
tvm_output = module.get_output(0, tvm.nd.empty(output_shape)).numpy()

收集基本性能数据

我们这里要收集这个未优化模型相关的一些基本性能数据,然后将其与 tune 后的模型进行比较。 为了消除 CPU 噪声的影响,我们以多次重复的方式在多个批次中运行计算,然后收集一些关于平均值、中值和标准偏差的基础统计数据。

import timeittiming_number = 10
timing_repeat = 10
unoptimized = (np.array(timeit.Timer(lambda: module.run()).repeat(repeat=timing_repeat, number=timing_number))* 1000/ timing_number
)
unoptimized = {"mean": np.mean(unoptimized),"median": np.median(unoptimized),"std": np.std(unoptimized),
}print(unoptimized)

此处输出:

{'mean': 229.1864895541221, 'median': 228.7280524149537, 'std': 1.0664440211813757}

对结果进行后处理

如前所述,不同的模型输出张量的方式可能不同。

在我们的例子中,我们需要进行一些后处理,使用为模型提供的查找表将 ResNet-50-V2 的输出呈现为更易读的形式。

from scipy.special import softmax# 下载标签列表
labels_url = "https://s3.amazonaws.com/onnx-model-zoo/synset.txt"
labels_path = download_testdata(labels_url, "synset.txt", module="data")with open(labels_path, "r") as f:labels = [l.rstrip() for l in f]# 打开并读取输出张量
scores = softmax(tvm_output)
scores = np.squeeze(scores)
ranks = np.argsort(scores)[::-1]
for rank in ranks[0:5]:print("class='%s' with probability=%f" % (labels[rank], scores[rank]))

此处输出:

class='n02123045 tabby, tabby cat' with probability=0.610551
class='n02123159 tiger cat' with probability=0.367180
class='n02124075 Egyptian cat' with probability=0.019365
class='n02129604 tiger, Panthera tigris' with probability=0.001273
class='n04040759 radiator' with probability=0.000261

调整 (tune)模型

之前编译的模型工作在 TVM Runtime 上,但是并未提供任何针对特定硬件平台的优化。这里我们来演示如何构建一个针对特定硬件平台的优化模型。

在某些情况下,使用我们自己编译的模块运行推理时,性能可能无法达到预期。 在这种情况下,我们可以利用自动调谐器(Auto-tuner)为模型找到更好的配置并提高性能。 TVM 中的调优是指优化模型以在给定目标上运行得更快的过程。 这与训练(training)和微调(fine-tuning)的不同之处在于它不会影响模型的准确性,而只会影响运行时性能。 作为调优过程的一部分,TVM 将尝试运行许多不同的算子实现的可能,以查看哪个性能最佳。 并将这些运行的结果存储在调整记录文件中。

在最简单的形式下,tuning 需要我们指定三项:

  • 我们想要运行该模型的目标设备的规格
  • 存储调整记录输出文件的路径
  • 要调整的模型的路径

首先我们导入一些需要的库:

import tvm.auto_scheduler as auto_scheduler
from tvm.autotvm.tuner import XGBTuner
from tvm import autotvm

为运行器(runner)设置一些基本的参数,运行其会根据这组特定的参数来生成编译代码并测试其性能。

  • number 指定我们将要测试的不同配置的数目
  • repeat 指定我们对每种配置测试多少次
  • min_repeat_ms 执行运行每次配置测试的多长时间,如果重复次数低于此值,则会增加。该选项对于 GPU tuning 时必须的,对于 CPU tuning 则不需要。将其设为 0 即禁用它。
  • timeout 指定了每次配置测试的运行时间上限。
number = 10
repeat = 1
min_repeat_ms = 0  # 由于我们是 CPU tuning,故不需要该参数
timeout = 10  # 秒# 创建 TVM runner
runner = autotvm.LocalRunner(number=number,repeat=repeat,timeout=timeout,min_repeat_ms=min_repeat_ms,enable_cpu_cache_flush=True,
)

创建一个简单的结构来保存调整选项。

  • tunner:我们使用 XGBoost 算法来指导搜索。 在实际中可能需要根据模型复杂度、时间限制等因素选择其他算法。
  • tirals:对于实际项目,您需要将试验次数设置为大于此处使用的值 10。 CPU 推荐 1500,GPU 3000-4000。 所需的试验次数可能取决于特定模型和处理器,因此值得花一些时间评估一系列值的性能,以找到调整时间和模型优化之间的最佳平衡。
  • early_stopping 参数是在应用提前停止搜索的条件之前要运行的最小 trial 数。
  • measure_option 指定将在何处构建试用代码以及将在何处运行。 在本例中,我们使用我们刚刚创建的 LocalRunner 和一个 LocalBuilder。
  • tuning_records 选项指定一个文件来写入调整数据。
tuning_option = {"tuner": "xgb","trials": 10,"early_stopping": 100,"measure_option": autotvm.measure_option(builder=autotvm.LocalBuilder(build_func="default"), runner=runner),"tuning_records": "resnet-50-v2-autotuning.json",
}

注意:在此示例中,为了节省时间,我们将试验次数和提前停止次数设置为 10。如果将这些值设置得更高,我们可能会看到更多的性能改进,但这是以花费调优时间为代价的。 收敛所需的试验次数将根据模型和目标平台的具体情况而有所不同。

# 开始从 onnx 模型中提取 tasks
tasks = autotvm.task.extract_from_program(mod["main"], target=target, params=params)# 一次 tune 提取到的 tasks
for i, task in enumerate(tasks):prefix = "[Task %2d/%2d] " % (i + 1, len(tasks))tuner_obj = XGBTuner(task, loss_type="rank")tuner_obj.tune(n_trial=min(tuning_option["trials"], len(task.config_space)),early_stopping=tuning_option["early_stopping"],measure_option=tuning_option["measure_option"],callbacks=[autotvm.callback.progress_bar(tuning_option["trials"], prefix=prefix),autotvm.callback.log_to_file(tuning_option["tuning_records"]),],)

此处输出:

[Task  1/25]  Current/Best:    0.00/   0.00 GFLOPS | Progress: (0/10) | 0.00 s
[Task  1/25]  Current/Best:   33.79/  49.04 GFLOPS | Progress: (4/10) | 5.66 s...Done.[Task 25/25]  Current/Best:    3.13/   3.13 GFLOPS | Progress: (4/10) | 3.17 s
[Task 25/25]  Current/Best:    2.48/   3.13 GFLOPS | Progress: (8/10) | 15.43 s
[Task 25/25]  Current/Best:    0.00/   3.13 GFLOPS | Progress: (10/10) | 45.72 s

使用 tuning data 编译优化过的模型

作为上述调优过程的输出,我们获得了存储在 resnet-50-v2-autotuning.json 中的调优记录。 编译器将根据该结果为指定 target 上的模型生成高性能代码。

现在已经收集了模型的调整数据,我们可以使用优化的算子重新编译模型以加快计算速度。

with autotvm.apply_history_best(tuning_option["tuning_records"]):with tvm.transform.PassContext(opt_level=3, config={}):lib = relay.build(mod, target=target, params=params)dev = tvm.device(str(target), 0)
module = graph_executor.GraphModule(lib["default"](dev))

验证优化过后的模型的运行后的输出结果与之前的相同:

dtype = "float32"
module.set_input(input_name, img_data)
module.run()
output_shape = (1, 1000)
tvm_output = module.get_output(0, tvm.nd.empty(output_shape)).numpy()scores = softmax(tvm_output)
scores = np.squeeze(scores)
ranks = np.argsort(scores)[::-1]
for rank in ranks[0:5]:print("class='%s' with probability=%f" % (labels[rank], scores[rank]))

此处输出:

class='n02123045 tabby, tabby cat' with probability=0.610552
class='n02123159 tiger cat' with probability=0.367180
class='n02124075 Egyptian cat' with probability=0.019365
class='n02129604 tiger, Panthera tigris' with probability=0.001273
class='n04040759 radiator' with probability=0.000261

确是是相同的。

比较调整过的和未调整过的模型

这里我们同样收集与此优化模型相关的一些基本性能数据,以将其与未优化模型进行比较。 根据底层硬件、迭代次数和其他因素,在将优化模型与未优化模型进行比较时,我们能看到性能改进。

import timeittiming_number = 10
timing_repeat = 10
optimized = (np.array(timeit.Timer(lambda: module.run()).repeat(repeat=timing_repeat, number=timing_number))* 1000/ timing_number
)
optimized = {"mean": np.mean(optimized), "median": np.median(optimized), "std": np.std(optimized)}print("optimized: %s" % (optimized))
print("unoptimized: %s" % (unoptimized))

此处输出:

optimized: {'mean': 211.9480087934062, 'median': 211.2688914872706, 'std': 1.1843122740378864}
unoptimized: {'mean': 229.1864895541221, 'median': 228.7280524149537, 'std': 1.0664440211813757}

在本教程中,我们给出了一个简短示例,说明如何使用 TVM Python API 编译、运行和调整模型。 我们还讨论了对输入和输出进行预处理和后处理的必要性。 在调整过程之后,我们演示了如何比较未优化和优化模型的性能。

这里我们展示了一个在本地使用 ResNet 50 V2 的简单示例。 但是,TVM 支持更多功能,包括交叉编译、远程执行和分析/基准测试。这将会在以后的教程中介绍。

Ref:

https://tvm.apache.org/docs/tutorial/autotvm_relay_x86.html#sphx-glr-tutorial-autotvm-relay-x86-py

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/532679.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TVM:在树莓派上部署预训练的模型

TVM:在树莓派上部署预训练的模型 之前我们已经介绍如何通过Python接口(AutoTVM)来编译和优化模型。本文将介绍如何在远程(如本例中的树莓派)上部署预训练的模型。 在设备上构建 TVM Runtime 首先我们需要再远程设备…

2.2线性表的顺序表

2.2.1线性表的顺序表示和实现------顺序映像 【顺序存储】在【查找时】的时间复杂度为【O(1)】,因为它的地址是连续的,只要知道首元素的地址,根据下标可以很快找到指定位置的元素 【插入和删除】操作由于可能要在插入前或删除后对元素进行移…

TVM:交叉编译和RPC

TVM:交叉编译和RPC 之前我们介绍了 TVM 的安装、本机demo和树莓派远程demo。本文将介绍了在 TVM 中使用 RPC 进行交叉编译和远程设备执行。 通过交叉编译和 RPC,我们可以在本地机器上编译程序,然后在远程设备上运行它。 当远程设备资源有限…

2.3单链表的基本使用及其cpp示例

2.3线性表的链式表现与实现 2.3.1.1单链表 【特点: *用一组任意的存储单元存储线性表的数据元素 *利用指针实现用不同相邻的存储单元存放逻辑上相邻的元素 *每个元素ai,除存储本身信息外,还存储其直接后继的元素(后一个元素的地址…

TVM:简介

TVM:简介概述 Apache TVM 是一个用于 CPU、GPU 和机器学习加速器的开源机器学习编译器框架。它旨在使机器学习工程师能够在任何硬件后端上高效地优化和运行计算。本教程的目的是通过定义和演示关键概念,引导您了解 TVM 的所有主要功能。新用户应该能够从…

2.3.3单链表的双向链表

2.3.3双向链表 插入、删除 指在前驱和后驱方向都能游历(遍历)的线性链表 双向链表的每个结点有两个指针域 【结构】:prior data next 双链表通常采用带头结点的循环链表形式 可理解为首位相接的数据“圈”,每个结点都可以向前…

nvidia-smi 命令详解

nvidia-smi 命令详解 简介 nvidia-smi - NVIDIA System Management Interface program nvidia smi(也称为NVSMI)为来自 Fermi 和更高体系结构系列的 nvidia Tesla、Quadro、GRID 和 GeForce 设备提供监控和管理功能。GeForce Titan系列设备支持大多数…

2.4一元多项式的表示及相加,含cpp算法

2.4一元多项式的表示及相加 n阶多项式的表示: n阶多项式有n1项 指数按升幂排序 【 优点: 多项式的项数可以动态增长,不存在存储溢出的问题插入,删除方便,不移动元素 【表示: 有两个数据域,一…

TVM:使用Tensor Expression (TE)来处理算子

TVM:使用Tensor Expression (TE)来处理算子 在本教程中,我们将聚焦于在 TVM 中使用张量表达式(TE)来定义张量计算和实现循环优化。TE用纯函数语言描述张量计算(即每个表达式都没有副作用)。当在 TVM 的整体…

4-数据结构-串的学习

4.1串类型的定义 1.串:(或字符串) 串是由多个字符组成的有限序列,记作:S‘c1c2c3…cn’ (n>0) 其中S是串的名字,‘c1c2c3…cn’ 是串值 ci是串中字符 n是串的长度,表示字符的数目 空串&a…

Linux下rm误删恢复 extundelete

Linux下rm误删恢复 extundelete 误删之后要第一时间卸载(umount)该分区,或者以只读的方式来挂载(mount)该分区,否则覆写了谁也没办法恢复。如果误删除的是根分区,最好直接断电,进入…

5-数据结构-数组的学习

5.1数组的定义 定义: 由一组类型相同的数据元素构成的有序集合,每个数据元素称为一个数据元素(简称元素),每个元素受n(n>1)个线性关系的约束,每个元素在n个线性关系中的序号i1、…

timm 视觉库中的 create_model 函数详解

timm 视觉库中的 create_model 函数详解 最近一年 Vision Transformer 及其相关改进的工作层出不穷,在他们开源的代码中,大部分都用到了这样一个库:timm。各位炼丹师应该已经想必已经对其无比熟悉了,本文将介绍其中最关键的函数之…

C--数据结构--树的学习

6.2.1二叉树的性质 1.二叉树 性质: 1.若二叉树的层次从1开始,则在二叉树的第i层最多有2^(i-1)个结点 2.深度为k的二叉树最多有2^k -1个结点 (k>1) 3.对任何一颗二叉树,如果其叶结点个数为n0,度为2的非叶结点个数…

TVM:使用 Schedule 模板和 AutoTVM 来优化算子

TVM:使用 Schedule 模板和 AutoTVM 来优化算子 在本文中,我们将介绍如何使用 TVM 张量表达式(Tensor Expression,TE)语言编写 Schedule 模板,AutoTVM 可以搜索通过这些模板找到最佳 Schedule。这个过程称为…

TVM:使用 Auto-scheduling 来优化算子

TVM:使用 Auto-scheduling 来优化算子 在本教程中,我们将展示 TVM 的 Auto-scheduling 功能如何在无需编写自定义模板的情况下找到最佳 schedule。 与基于模板的 AutoTVM 依赖手动模板定义搜索空间不同,auto-scheduler 不需要任何模板。 用…

C语言—sort函数比较大小的快捷使用--algorithm头文件下

sort函数 一般情况下要将一组数从的大到小排序或从小到大排序&#xff0c;要定义一个新的函数排序。 而我们也可以直接使用在函数下的sort函数&#xff0c;只需加上头文件&#xff1a; #include<algorithm> using namespace std;sort格式&#xff1a;sort(首元素地址&…

散列的使用

散列 散列简单来说&#xff1a;给N个正整数和M个负整数&#xff0c;问这M个数中的每个数是否在N中出现过。 比如&#xff1a;N&#xff1a;{1,2,3,4}&#xff0c;M{2,5,7}&#xff0c;其中M的2在N中出现过 对这个问题最直观的思路是&#xff1a;对M中每个欲查的值x&#xff0…

关于C++中的unordered_map和unordered_set不能直接以pair作为键名的问题

关于C中的unordered_map和unordered_set不能直接以pair作为键名的问题 在 C STL 中&#xff0c;不同于有序的 std::map 和 std::set 是基于红黑树实现的&#xff0c;std::unordered_map 和 std::unordered_set 是基于哈希实现的&#xff0c;在不要求容器内的键有序&#xff0c…

AI编译器与传统编译器的联系与区别

AI编译器与传统编译器的区别与联系 总结整理自知乎问题 针对神经网络的编译器和传统编译器的区别和联系是什么&#xff1f;。 文中提到的答主的知乎主页&#xff1a;金雪锋、杨军、蓝色、SunnyCase、贝壳与知了、工藤福尔摩 笔者本人理解 为了不用直接手写机器码&#xff0…