第3章 ONNX Runtime推理引擎:安装、配置与基础使用

3.1 ONNX Runtime是什么?为什么需要它?

ONNX Runtime,说白了就是一个跨平台的推理加速引擎。它能把训练好的ONNX模型跑起来,而且跑得尽可能快。我刚开始接触的时候也觉得这玩意儿就是个"模型加载器",后来才发现,它内部做了大量的图优化、算子融合和内存复用。

你想想看,ONNX模型本身只是个中间表示。它描述了计算图,但不知道怎么跑最快。ONNX Runtime的任务就是——拿到这个图,分析它,优化它,然后调度到合适的硬件上执行。CPU、GPU、NPU,它都支持。

为什么我们需要它?因为不同框架(PyTorch、TensorFlow、MXNet)导出的模型,推理方式完全不同。你想部署一个模型,总不能让用户装一套PyTorch吧?ONNX就是那个"通用接口",而ONNX Runtime就是那个"高效执行器"。

核心定位:ONNX Runtime 不负责训练,只负责推理。它的价值在于"一次导出,到处运行",同时跑得比原生框架更快。

ONNX Runtime 全栈架构图 ONNX 模型 (.onnx) ONNX Runtime Core 执行后端 (CPU / CUDA / TensorRT / OpenVINO / DirectML) 硬件 (x86 / ARM / GPU / NPU) 内部优化策略 • 节点融合 (Fusion) • 常量折叠 • 内存复用 + 多线程调度

3.2 安装方式与版本选择

安装ONNX Runtime的方式很简单。我个人习惯用pip,但有些场景需要源码编译。下面我列出几种主流方式,你自己按需选择。

3.2.1 pip安装(推荐)

这是最快的方法。一行命令搞定。注意区分CPU版和GPU版:

# CPU版本(所有平台通用)
pip install onnxruntime

# GPU版本(需要NVIDIA显卡 + CUDA)
pip install onnxruntime-gpu

为什么推荐用pip?因为ONNX Runtime的PyPI包已经包含了预编译的二进制文件。你不需要装VS、CMake、CUDA Toolkit那些东西。我在项目里给客户做快速原型验证时,都是直接用pip装,十分钟就能跑起来。

小技巧:如果你不确定环境是否支持GPU版本,可以先装CPU版。跑通了再切GPU版。我见过不少同事一上来就装GPU版,结果CUDA版本不对,报错一大片。

3.2.2 conda安装

如果你用Anaconda管理环境,也可以用conda:

conda install -c conda-forge onnxruntime

不过说句实话,conda上的版本更新经常比PyPI慢一两周。我自己很少用。

3.2.3 源码编译(高级场景)

遇到特殊需求——比如你要集成自定义算子、或者要跑在ARM Linux嵌入式设备上——那就得源码编译了。步骤大概是:

# 克隆仓库
git clone --recursive https://github.com/microsoft/onnxruntime.git
cd onnxruntime

# 编译CPU版本(Linux / macOS)
./build.sh --config Release --build_shared_lib

# Windows用户用这个
# .\build.bat --config Release --build_shared_lib

编译时间……嗯,我的机器(i7-12700,32GB内存)大概需要30分钟。如果你要编译GPU版,记得加上 --use_cuda 参数。

注意:源码编译前,确认你的CMake版本 ≥ 3.13,Python ≥ 3.8。我曾经因为CMake版本太低,卡在编译环节两小时,后来才发现是版本问题。

3.3 配置验证:是否安装成功

装完了,怎么确认装好了?很简单,跑一小段Python:

import onnxruntime

print(onnxruntime.__version__)        # 打印版本号
print(onnxruntime.get_device())       # 打印设备:CPU 或 GPU

输出示例:

1.16.0
CPU

如果你装的是GPU版,get_device() 应该返回 GPU。如果返回了 CPU,别慌,先检查CUDA版本是否匹配。

快速检查CUDA兼容性:ONNX Runtime每个版本都有对应的CUDA要求。比如1.16.0支持CUDA 11.8和12.x。具体看官方Release Notes。

3.4 基础使用:加载模型并推理

安装验证通过后,来点实际的。下面我用一个简单的示例演示ONNX Runtime的完整推理流程。

3.4.1 创建Session

第一步,创建推理会话(Session)。这是所有操作的入口:

import onnxruntime
import numpy as np

# 创建Session
session = onnxruntime.InferenceSession("model.onnx")

# 获取模型信息
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name

print(f"输入节点: {input_name}, 形状: {session.get_inputs()[0].shape}")
print(f"输出节点: {output_name}, 形状: {session.get_outputs()[0].shape}")

这里有个关键点:InferenceSession 默认使用的是CPU执行提供者(Execution Provider)。如果你想用GPU,需要显式指定。

3.4.2 指定GPU执行提供者

我建议直接创建Session时把Providers参数传进去:

# 优先使用CUDA,回退到CPU
session = onnxruntime.InferenceSession(
    "model.onnx",
    providers=['CUDAExecutionProvider', 'CPUExecutionProvider']
)

# 查看当前可用的Provider
print(onnxruntime.get_available_providers())

为什么要传两个?因为如果CUDA不可用,Runtime会自动降级到CPU,不会直接崩。我在生产环境里就是这么做的,容错性更好。

3.4.3 执行推理

准备输入数据,调用 run 方法:

# 构造输入(假设模型输入是 [1, 3, 224, 224] 的float32张量)
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)

# 执行推理
outputs = session.run([output_name], {input_name: input_data})

# outputs 是一个列表,取出结果
result = outputs[0]
print(f"推理结果形状: {result.shape}")
参数 说明 示例
output_names 输出节点名称列表,可指定多个 ['output']
input_feed 字典,key为输入节点名称 {'input': input_data}

3.5 配置选项:调优推理性能

ONNX Runtime提供了一些配置参数,能直接影响推理速度和内存占用。我挑几个最常用的说说。

3.5.1 Session Options

import onnxruntime

options = onnxruntime.SessionOptions()

# 启用图优化(默认就是开启的,但可以调整级别)
options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL

# 设置线程数(默认是0,表示使用所有核心)
options.intra_op_num_threads = 4   # 算子内并行线程数
options.inter_op_num_threads = 2   # 算子间并行线程数

# 启用内存模式优化
options.enable_cpu_mem_arena = True

session = onnxruntime.InferenceSession(
    "model.onnx",
    session_options=options
)

这里有个经验:线程数不是越多越好。我测试过一个ResNet50模型,默认8线程反而比4线程慢,原因是线程切换开销超过了并行收益。建议你自己跑个benchmark找到最佳值。

3.5.2 设置日志等级

默认日志太多?可以关掉:

options.log_severity_level = 3  # 0=Verbose, 1=Info, 2=Warning, 3=Error
options.log_verbosity_level = 3 # 同上

生产环境我一般设成3,只打印错误。开发调试时可以设成0,看内部优化日志。

3.6 常见问题与避坑指南

我曾经遇到过一次:在Windows上装了onnxruntime-gpu,结果运行时报"CUDA driver version is insufficient"。折腾半天发现是显卡驱动太老,更新驱动到528.33后解决。所以记住——GPU版不仅依赖CUDA Toolkit,还依赖NVIDIA驱动。

3.6.1 模型版本不匹配

ONNX Runtime对ONNX opset版本有要求。如果你导出的模型用了太新的算子,而Runtime版本太低,就会报错。解决办法:

  • 升级ONNX Runtime到最新版
  • 或者在导出时指定较低的opset版本:torch.onnx.export(..., opset_version=14)

3.6.2 性能突然变差

检查一下是否不小心用了CPU Provider而不是GPU。可以用 session.get_providers() 确认。如果明明是GPU版,却走了CPU,多半是CUDA路径没配置对。

3.6.3 内存泄漏

持续性推理服务中,如果每次推理都创建新的Session,就会造成内存泄漏。正确做法是:复用Session。Session是线程安全的,可以在多线程中共享。

最佳实践:把 InferenceSession 做成单例,整个进程只初始化一次。我所有线上服务都是这么设计的。

3.7 本章小结

我们这一章讲了ONNX Runtime的安装、配置和基础使用。核心要点就三条:

  • 安装:CPU用 pip install onnxruntime,GPU用 pip install onnxruntime-gpu,特殊场景才源码编译。
  • 配置:Session Options可以控制线程数、优化级别和日志。别忘了指定Execution Provider。
  • 基础使用:创建Session → prepare input → run → get output,就这么简单。