TVM:开源深度学习编译器与运行时

FreeGuideOnline 最新 2026-06-21

bash pip install apache-tvm


此命令会安装 TVM 核心编译器及 Python 前端,并默认启用 LLVM 后端。如需 GPU 支持,需根据系统配置从源码构建。

### 源码安装(启用 CUDA 支持,Ubuntu 为例)

1. 从 GitHub 下载源码:

```bash
git clone --recursive https://github.com/apache/tvm.git
cd tvm
  1. 创建构建目录并配置:
mkdir build && cp cmake/config.cmake build

编辑 build/config.cmake,设置 USE_CUDA ONUSE_LLVM ON 等选项。

  1. 构建(确保已安装 CUDA 工具包和 LLVM):
cd build
cmake ..
make -j$(nproc)
  1. 设置环境变量,将 TVM 加入 Python 路径:
export TVM_HOME=/path/to/tvm
export PYTHONPATH=$TVM_HOME/python:${PYTHONPATH}

验证安装成功:

import tvm
print(tvm.__version__)

TVM 命令行工具 TVMC

tvmc 是 TVM 提供的全流程命令行前端,可一行命令实现模型编译、调优和运行,非常适合快速验证。

基本工作流程

  1. 导入模型并编译
tvmc compile --target "llvm" my_model.onnx

输出 module.so(共享库)和 model.tar(模型参数与元信息)。

  1. 运行推理
tvmc run --inputs input.npz --output predictions.npz model.tar
  1. 自动调优加速
tvmc tune --target "llvm" my_model.onnx

调优后的日志会保存,再次编译时可应用最佳配置:

tvmc compile --tuning-records tuning.log --target "llvm" my_model.onnx

tvmc 支持多种目标:llvm(CPU)、cuda(NVIDIA GPU)、opencl 等。通过 --target 轻松切换。


使用 Python API 逐步编译与部署

对于需要更多控制的生产部署,直接使用 Python API 是更好的选择。下面以 ResNet-18 从 ONNX 导入并部署到 CPU 为例。

1. 导入模型并转换为 Relay

import onnx
import tvm
from tvm import relay

# 加载 ONNX 模型
onnx_model = onnx.load("resnet18.onnx")

# 转换到 Relay IR
input_name = "data"
shape_dict = {input_name: (1, 3, 224, 224)}
mod, params = relay.frontend.from_onnx(onnx_model, shape_dict)

2. 编译目标代码

# 指定 CPU 目标,可加入调优日志
target = tvm.target.Target("llvm")
with tvm.transform.PassContext(opt_level=3):
    lib = relay.build(mod, target=target, params=params)

lib 包含编译好的模块,可直接部署。

3. 创建运行时并执行

import numpy as np
from tvm.contrib import graph_executor

# 使用图执行器(适用于本地测试)
dev = tvm.cpu(0)
module = graph_executor.GraphModule(lib["default"](dev))

# 设置输入数据
input_data = np.random.rand(1, 3, 224, 224).astype("float32")
module.set_input(input_name, tvm.nd.array(input_data))

# 运行
module.run()

# 获取输出
output = module.get_output(0).numpy()

对于 GPU,只需将 dev = tvm.cuda() 并使用 target="cuda"


自动调优实战(AutoScheduler)

手工优化算子不现实,AutoScheduler(Ansor)可自动探索调度空间。以下展示对 ResNet-18 进行 CPU 调优的完整流程。

准备搜索任务

from tvm import auto_scheduler

tasks, task_weights = auto_scheduler.extract_tasks(mod["main"], params, target)

# 检查提取的任务数量(通常每个卷积、矩阵乘为一个任务)
for idx, task in enumerate(tasks):
    print(f"Task {idx}: {task.desc}")

执行搜索

tune_option = auto_scheduler.TuningOptions(
    num_measure_trials=200,   # 测量次数越多结果越好
    measure_callbacks=[auto_scheduler.RecordToFile("resnet18_rice.json")],
    verbose=2,
)

# 运行搜索(可能需要较长时间)
auto_scheduler.auto_scheduler(tasks, task_weights, tune_option)

搜索过程中,每个“trial”会在真实硬件上测量候选代码的性能,迭代优化。

应用最优调度重新编译

with auto_scheduler.ApplyHistoryBest("resnet18_rice.json"):
    with tvm.transform.PassContext(opt_level=3, config={"relay.backend.use_auto_scheduler": True}):
        lib = relay.build(mod, target=target, params=params)

此时生成的代码性能通常接近手工优化水平。


部署到边缘设备(C++ 运行时示例)

TVM 的分发模式允许将编译好的模型和极简运行时打包,嵌入式设备可轻松加载。

步骤概览

  1. Python 端编译模型并导出为部署所需文件:
libpath = "deploy_lib.tar"
lib.export_library(libpath)

deploy_lib.tar 包含动态库、图 JSON 和参数。

  1. 在 C++ 环境中加载调用(简化示例):
#include <tvm/runtime/module.h>
#include <tvm/runtime/packed_func.h>
#include <tvm/runtime/registry.h>

int main() {
    tvm::runtime::Module mod_factory = tvm::runtime::Module::LoadFromFile("deploy_lib.so");
    tvm::runtime::Module mod = mod_factory.GetFunction("default")(tvm::Device{kDLCPU, 0});

    // 设置输入、运行、获取输出... 与 Python GraphModule 逻辑一致
    return 0;
}