第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 不负责训练,只负责推理。它的价值在于"一次导出,到处运行",同时跑得比原生框架更快。
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,就这么简单。