**内容一览:**本节讲解运用 TVMC 编译和优化模型。TVMC 是 TVM 的指令驱动程序,经过指令行履行 TVM 功用。本节是了解 TVM 作业原理的根底。

**关键词:**TVMC TVM 机器学习

本节将介绍 TVMC(TVM 的指令行驱动程序)。TVMC 经过指令行界面履行 TVM 功用 (包括对模型的主动调优、编译、分析和履行)。

学完本节后,可用 TVMC 完结下面的使命:

  • 为 TVM runtime 编译预练习的 ResNet-50 v2 模型。

  • 用编译好的模型猜测真实图画,并解说输出和模型功用。

  • 运用 TVM 在 CPU上调优模型。

  • 用 TVM 搜集的调优数据,重新编译优化过的模型。

  • 经过优化的模型猜测图画,并比较输出和模型功用。

本节对 TVM 及 TVMC 的功用进行了概述,并为了解 TVM 的作业原理奠定根底。

运用 TVMC

TVMC 是 Python 应用程序,也是 TVM Python 软件包的一部分。 用 Python 包装置 TVM 时,会得到一个叫 tvmc 的指令行应用程序。渠道和装置办法不同,此指令的方位也会发生变化。

另外,假如 $PYTHONPATH 上有 TVM 这个 Python 模块,则可经过可履行 Python 模块(用 python -m tvm.driver.tvmc 指令)来拜访指令行驱动功用。

本教程用 tvmcpython -m tvm.driver.tvmc 来翻开 TVMC 指令行。

运用如下指令检查协助页:

tvmc --help

tvmc 可用的 TVM 的主要功用来自子指令 compileruntune 。运用 tvmc–help 检查给定子指令的特定选项。

本教程将介绍这些指令,开端前请先下载一个预练习的模型。

获取模型

在本教程中,咱们将运用 ResNet-50 v2。ResNet-50 是一个用来对图画进行分类的 50 层深的卷积神经网络。 接下来要用的模型,已经在超过100万张具有1000种不同分类的图画上,进行了预练习。该网络的输入图画的巨细为224×224。

引荐下载 Netron(免费的 ML 模型检查器)来更深入地探究 ResNet-50 模型的组织结构。

下载 Netron:netron.app/

本教程运用 ONNX 格局的模型:

wget https://github.com/onnx/models/raw/b9a54e89508f101a1611cd64f4ef56b9cb62c7cf/vision/classification/resnet/model/resnet50-v2-7.onnx

Tips 1 支撑的模型格局:

TVMC 支撑用 Keras、ONNX、TensorFlow、TFLite 和 Torch 创立的模型。可用 –model-format 选项指明正在运用的模型格局。履行 tvmc compile –help 来获取更多信息。

Tips 2 向 TVM 添加对 ONNX 的支撑

TVM 依靠系统中可用的 ONNX Python 库。用指令 pip3 install –user onnx onnxoptimizer 来装置 ONNX。假如具有 root 拜访权限并且希望大局装置 ONNX,则能够删除 –user 选项。onnxoptimizer 依靠是可选的,仅用于 onnx>=1.9 。

将 ONNX 模型编译到 TVM Runtime

下载 ResNet-50 模型后,用 tvmc compile 对其进行编译。编译的输出成果是模型(被编译为方针渠道的动态库)的 TAR 包。用 TVM runtime 可在方针设备上运转该模型:

# 大概需求几分钟,取决于设备tvmc compile \--target "llvm" \--input-shapes "data:[1,3,224,224]" \--output resnet50-v2-7-tvm.tar \resnet50-v2-7.onnx

检查 tvmc compile 在模块中创立的文件:

mkdir modeltar -xvf resnet50-v2-7-tvm.tar -C modells model

解压后有三个文件:

* mod.so 是可被 TVM runtime 加载的模型,表示为 C++ 库。

* mod.json 是 TVM Relay 核算图的文本表示。

* mod.params 是包括预练习模型参数的文件。

模块可由应用程序直接加载,而模型可经过 TVM runtime API 运转。

Tips 3 定义正确的 TARGET

指定正确的 target(选项 **–target** )可大大提升编译模块的功用,由于可利用 target 上可用的硬件功用。参看 针对 x86 CPU 主动调优卷积网络 获取更多信息。主张确定好运用的 CPU 类型以及可选功用,然后适当地设置 target。

运用 TVMC 运转来自编译模块的模型

将模型编译到模块后,可用 TVM runtime 对其进行猜测。 TVMC 具有内置的 TVM runtime,答应运转已编译的 TVM 模型。

要用 TVMC 运转模型并猜测,需求:

  • 刚生成的编译模块。

  • 用来猜测的模型的有用输入。

模型的张量 shape、格局和数据类型各不相同。 因而,大多数模型都需求预处理和后处理,确保输入有用,并能够解说输出。TVMC 采用了 NumPy 的 .npz 格局的输入和输出,可很好地支撑将多个数组序列化到一个文件中。

本教程中的图画输入运用的是一张猫的图画,你也能够依据喜好挑选其他图画。

使用 TVMC 编译和优化模型

输入预处理

ResNet-50 v2 模型的输入应该是 ImageNet 格局。下面是 ResNet-50 v2 预处理图画的脚本示例。

首先用 pip3 install –user pillow 下载 Python 图画库,以满意脚本运转对图画库的依靠。

#!python ./preprocess.pyfrom tvm.contrib.download import download_testdatafrom PIL import Imageimport numpy as npimg_url = "https://s3.amazonaws.com/model-server/inputs/kitten.jpg"img_path = download_testdata(img_url, "imagenet_cat.png", module="data")# 重设巨细为 224x224resized_image = Image.open(img_path).resize((224, 224))img_data = np.asarray(resized_image).astype("float32")# ONNX 需求 NCHW 输入, 因而对数组进行转化img_data = np.transpose(img_data, (2, 0, 1))# 依据 ImageNet 进行标准化imagenet_mean = np.array([0.485, 0.456, 0.406])imagenet_stddev = np.array([0.229, 0.224, 0.225])norm_img_data = np.zeros(img_data.shape).astype("float32")for i in range(img_data.shape[0]):      norm_img_data[i, :, :] = (img_data[i, :, :] / 255 - imagenet_mean[i]) / imagenet_stddev[i]# 添加 batch 维度img_data = np.expand_dims(norm_img_data, axis=0)# 保存为 .npz(输出 imagenet_cat.npz)np.savez("imagenet_cat", data=img_data)

运转编译模块

有了模型和输入数据,接下来运转 TVMC 进行猜测:

tvmc run \--inputs imagenet_cat.npz \--output predictions.npz \resnet50-v2-7-tvm.tar

.tar 模型文件中包括一个 C++ 库、对 Relay 模型的描述文件,以及模型的参数文件。TVMC 包括 TVM runtime(可加载模型,并对输入进行猜测)。运转以上指令,TVMC 会输出一个新文件 predictions.npz, 其中包括 NumPy 格局的模型输出张量。

在此示例中,用于编译模型的和运转模型的是同一台机器。某些情况下,或许会用 RPC Tracker 来长途运转它。检查 tvmc run –help 来了解有关这些选项的更多信息。

输出后处理

如前所述,每个模型供给输出张量的方式都不一样。

本示例中,咱们需求用专为该模型供给的查找表,运转一些后处理(post-processing),然后使得 ResNet-50 v2 的输出方式更具有可读性。

下面的脚本是一个后处理示例,它从编译模块的输出中提取标签:

#!python ./postprocess.pyimport os.pathimport numpy as npfrom scipy.special import softmaxfrom tvm.contrib.download import download_testdata# 下载标签列表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]output_file = "predictions.npz"# 翻开并读入输出张量if os.path.exists(output_file):    with np.load(output_file) as data:        scores = softmax(data["output_0"])        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]))

这个脚本的运转输出如下:

python postprocess.py# class='n02123045 tabby, tabby cat' with probability=0.610553# class='n02123159 tiger cat' with probability=0.367179# class='n02124075 Egyptian cat' with probability=0.019365# class='n02129604 tiger, Panthera tigris' with probability=0.001273# class='n04040759 radiator' with probability=0.000261

用其他图画替换上述猫的图画,看看 ResNet 模型做了什么样的猜测。

主动调优 ResNet 模型

以前的模型被编译到 TVM runtime 上运转,因而不包括特定于渠道的优化。本节将介绍怎么用 TVMC,针对作业渠道构建优化模型。

用编译的模块推理,有时或许无法取得预期的功用。在这种情况下,可用主动调优器更好地装备模型,然后进步功用。 TVM 中的调优是指,在给定 target 上优化模型,使其运转得更快。与练习或微调不同,它不会影响模型的准确性,而只会影响 runtime 功用。

作为调优进程的一部分,TVM 完结并运转许多不同算子的变体,以检查哪个功用最佳。 这些运转的成果存储在调优记载文件(tune 指令的终究输出)中。

调优最少要包括:

  • 运转此模型的方针设备的渠道要求

  • 存储调优记载的输出文件的途径

  • 要调优的模型的途径。

下面的示例演示了其作业流程:

# 默认查找算法需求 xgboost,有关调优查找算法的详细信息,拜见下文pip install xgboosttvmc tune \--target "llvm" \--output resnet50-v2-7-autotuner_records.json \resnet50-v2-7.onnx

此例中,为 –target 标志指定更详细的 target 时,会得到更好的成果。例如,在 Intel i7 处理器上,可用 –target llvm -mcpu=skylake 。 这个调优示例把 LLVM 作为指定架构的编译器,在 CPU 上进行本地调优。

TVMC 针对模型的参数空间进行查找,为算子尝试不同的装备,然后挑选渠道上运转最快的装备。虽然这是基于 CPU 和模型操作的引导式查找,但仍需求几个小时才能完结查找。查找的输出将保存到 resnet50-v2-7-autotuner_records.json 文件中,该文件之后会用于编译优化模型。

Tips 4 定义调优查找算法:

这个查找算法默认用 XGBoost Grid 算法进行引导。依据模型复杂度和可用时间,可挑选不同的算法。完好列表可检查 tvmc tune –help

对于消费级的 Skylake CPU,输出如下:

使用 TVMC 编译和优化模型

运用调优数据编译优化模型

从上述调优进程的输出文件 `resnet50-v2-7-autotuner_records.json 可获取调优记载。

该文件可用来:

  • 作为进一步骤优的输入(经过 tvmc tune –tuning-records

  • 作为编译器的输入

履行 tvmc compile –tuning-records 指令让编译器利用这个成果为指定 target 上的模型生成高功用代码。检查 tvmc compile –help 来获取更多信息。

模型的调优数据搜集到后,可用优化的算子重新编译模型来加快核算速度。

tvmc compile \--target "llvm" \--tuning-records resnet50-v2-7-autotuner_records.json  \--output resnet50-v2-7-tvm_autotuned.tar \resnet50-v2-7.onnx

验证优化模型是否运转并发生相同成果:

tvmc run \--inputs imagenet_cat.npz \--output predictions.npz \resnet50-v2-7-tvm_autotuned.tarpython postprocess.py

验证猜测值是否相同:

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

比较调优和未调优的模型

TVMC 供给了模型之间的基本功用评估工具。 可指定重复次数,也可指定 TVMC 报告模型的运转时间(独立于 runtime 启动)。可大致了解调优对模型功用的提升程度。

例如,对 Intel i7 系统进行测验时,调优后的模型比未调优的模型运转速度快 47%:

tvmc run \--inputs imagenet_cat.npz \--output predictions.npz  \--print-time \--repeat 100 \resnet50-v2-7-tvm_autotuned.tar# Execution time summary:# mean (ms)   max (ms)    min (ms)    std (ms)#     92.19     115.73       89.85        3.15tvmc run \--inputs imagenet_cat.npz \--output predictions.npz  \--print-time \--repeat 100 \resnet50-v2-7-tvm.tar# Execution time summary:# mean (ms)   max (ms)    min (ms)    std (ms)#    193.32     219.97      185.04        7.11

写在最终

本教程介绍了 TVMC( TVM 的指令行驱动程序),演示了怎么编译、运转和调优模型,还讨论了对输入和输出进行预处理和后处理的必要性。 调优后,演示怎么比较未优化和优化模型的功用。

本文档展现了一个在本地运用 ResNet-50 v2 的简单示例。然而,TVMC 支撑更多功用,包括交叉编译、长途履行和分析/基准测验。

tvmc –help 指令检查其他可用选项。

下个教程 Compiling and Optimizing a Model with the Python Interface 将介绍用 Python 接口的相同编译和优化步骤。

​继续重视,不要错过~