2、TVMC的使用

1、获取模型

在本教程中,我们将使用ResNet-50 v2模型。ResNet-50是一个用于图像分类的,50层的卷积神经网络。我们使用的模型是已经训练好的,训练的数据集有1000种分类100多万张图像。该网络的输入图像尺寸为224x224。如果你对ResNet-50模型的结构感兴趣,我们建议下载Netron,这是一个免费的ML模型查看器。

在本教程中,我们将使用ONNX格式的模型。

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

2、ONNX模型编译TVM运行时

下载ResNet-50模型后,我们使用tvmc编译它。模型编译得到的将是一个目标设备平台上的动态库。我们可以使用TVM运行时在目标设备上运行该模型。

tvmc compile \
--target "llvm" \
--output resnet50-v2-7-tvm.tar \
resnet50-v2-7.onnx

我们来看看tvmc compile生成了哪些文件:

mkdir model
tar -xvf resnet50-v2-7-tvm.tar -C model
ls model

看到的有三个文件:

  • mod.so是一个用c++库表示的模型,可以被TVM运行时加载。
  • mod.json是TVM Relay计算图的文本表示。
  • mod.params是一个包含预训练模型参数的文件。

模型可以通过TVM运行时API运行。

3、使用TVMC运行编译好的模型

现在我们已经将模型编译到模块中,然后就可以使用TVM运行时对其进行预测了。TVMC内置了TVM运行时,允许您运行编译后的TVM模型。为了使用TVMC运行模型并进行预测,我们需要具备两个条件:

  • 刚刚编译生成的模块
  • 对模型进行预测的有效输入

每个模型都有特定的张量形状、格式和数据类型。所以大多数模型需要一些预处理和后处理,以确保输入正确,并解释输出。TVMC的输入和输出数据都采用了NumPy的.npz格式。这是一种支持良好的NumPy格式,可以将多个数组序列化存入到一个文件中。

作为本教程的输入,我们将使用一只猫的图像,您也可以替换为其他任何图像。

输入预处理

我们使用的ResNet-50 v2模型预期输入为ImageNet格式。下面是一个对ResNet-50 v2输入图像做预处理的脚本示例。

这个脚本要求环境支持Python Image库。您可以使用pip3 install --user pillow安装Image

运行下面这个python文件生成imagenet_cat.npz输入数据

#!python ./preprocess.py
from tvm.contrib.download import download_testdata
from PIL import Image
import numpy as np

img_url = "https://s3.amazonaws.com/model-server/inputs/kitten.jpg"
img_path = download_testdata(img_url, "imagenet_cat.png", module="data")

# Resize it to 224x224
resized_image = Image.open(img_path).resize((224, 224))
img_data = np.asarray(resized_image).astype("float32")

# ONNX expects NCHW input, so convert the array
img_data = np.transpose(img_data, (2, 0, 1))

# Normalize according to 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]

# Add batch dimension
img_data = np.expand_dims(norm_img_data, axis=0)

# Save to .npz (outputs imagenet_cat.npz)
np.savez("imagenet_cat", data=img_data)

运行编译后的模型

有了模型(是否可以TVM运行时)和输入数据,我们现在可以运行TVMC进行预测:

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

回想一下,.tar模型文件包括一个C++库、一个Relay模型的描述,以及模型的参数。TVMC包括TVM运行时,运行时可以加载模型并根据输入进行预测。当运行上面的命令时,TVMC生成文件predictions. npz,它包含NumPy格式的模型输出张量。

在本例中模型编译和运行在同一台机器上。在某些情况下,我们可能希望通过RPC Tracker远程运行模型。要了解更多相关选项,请查看tvmc run --help。

输出后处理

正如前面提到的,每个模型都有自己特定的输出张量。

在我们的示例中,我们需要对输出做一些后处理,同时提供一个查找表,将ResNet-50 v2的输出翻译为便于人类阅读的形式。

下面的脚本是从输出中提取标签的后处理示例。

#!python ./postprocess.py
import os.path
import numpy as np
 
from scipy.special import softmax
 
from tvm.contrib.download import download_testdata
 
# Download a list of labels
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"
 
# Open the output and read the output tensor
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]))

运行脚本将产生下面的输出:

 4、ResNet模型自动调优

前面我们将模型编译为TVM运行时,不包括任何针对特定平台的优化。在本节中,我们将向您展示如何使用TVMC构建一个针对您的工作平台的优化模型。

在某些情况下,编译后的模型在推理时并没有获得预期的性能。此时我们可以使用自动调优器,为我们的模型找到更好的配置,从而提高性能。TVM中的调优是对模型进行优化,使其在给定目标上运行得更快。这与模型的训练或参数微调不同,因为调优不会影响模型的准确性,而只会影响运行性能。作为调优过程的一部分,TVM将尝试运行许多不同的算子实现变体,看哪一种性能最好。这些运行结果存储在调优记录文件中,该文件最终作为调优子命令的输出。

以最简单的形式来说,调优需要提供以下三件事:

  • 运行模型的设备规格
  • 存储调优记录输出文件的路径,
  • 要调优的模型的路径。

下面的例子演示了它是如何实际工作的:

# 默认搜索算法需要 xgboost,有关调优搜索算法的详细信息,参见下文
pip install xgboost

tvmc tune \
--target "llvm" \
--output resnet50-v2-7-autotuner_records.json \
resnet50-v2-7.onnx

 接下来,我遇到这种情况

RuntimeError( RuntimeError: FLOP estimator fails for this operator. Error msg: The length of axis is not constant. . Please use cfg.add_flop to manually set FLOP for this operator

解决办法在Error when trying to tune the ResNet Model - Troubleshooting - Apache TVM Discuss

在这个例子中,如果为--target选项指定一个更具体的目标,您将看到更好的结果。例如,在Intel i7处理器上,你可以使用--target llvm -mcpu=skylake。在这个调优示例中,我们使用LLVM作为指定架构的编译器在CPU上进行本地调优。

TVMC将对模型的参数空间进行搜索,尝试不同的算子配置,并选择在您的平台上运行最快的配置。虽然这是一个基于CPU和模型运算的引导搜索,但仍然需要几个小时才能完成搜索。此搜索的输出将保存到resnet50-v2-7-autotuner_records.json文件中,稍后将用于编译一个优化的模型。 

默认情况下,使用XGBoost Grid算法引导搜索。根据模型的复杂性和可用时间,您可能想要选择不同的算法。可以通过tvmc tune --help获得完整的列表。

 调优命令运行时间可能很长,所以tvmc调优提供了许多选项来定制调优过程,包括重复次数(例如--repeat和--number)、使用的调优算法等等。查看tvmc tune --help以获得更多信息。

5、使用调优数据编译优化模型

resnet50-v2-7-autotuner_records.json中存储了作为上面调优过程的输出的调优记录。这个文件有两种用法:

  • 作为进一步调优的输入(通过tvmc tune --tuning-records)
  • 作为编译器的输入
tvmc compile \
--target "llvm" \
--tuning-records resnet50-v2-7-autotuner_records.json  \
--output resnet50-v2-7-tvm_autotuned.tar \
resnet50-v2-7-frozen.onnx

 运行优化后的模型,并产生和优化前相同的输出:

tvmc run \
--inputs imagenet_cat.npz \
--output predictions_1.npz \
resnet50-v2-7-tvm_autotuned.tar
 
python postprocess.py

 运行的结果如下:

 

 6、比较调优和未调优模型 

TVMC为您提供了在模型之间进行基本性能基准测试的工具。您可以指定重复次数,并在模型运行时(独立于运行时启动)报告给TVMC。我们可以大致了解调优在多大程度上提高了模型性能。例如,在测试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.15
 
tvmc 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

 7、小结

在本教程中,我们介绍了TVM的命令行驱动程序TVMC。我们演示了如何编译、运行和调优模型。我们还讨论了对输入和输出进行预处理和后处理的必要性。在调优过程之后,我们演示了如何比较未优化模型和优化模型的性能。

这里我们给出了一个在本地运行ResNet-50 v2的简单示例。但是,TVMC支持更多的特性,包括交叉编译、远程执行和分析/基准测试。

要了解其他可用选项,请查看tvmc --help。

 

 

参考资料:

【TVM帮助文档学习】使用TVMC编译和优化模型_zxros10的博客-CSDN博客_tvmc

 使用 TVMC 编译和优化模型 | Apache TVM 中文站 (hyper.ai)

posted @ 2022-12-01 00:48  多一些不为什么的坚持  阅读(333)  评论(0编辑  收藏  举报