跳转到内容
新建笔记

ONNX Runtime:输入检查、执行后端与推理验证

会话、输入与执行后端

跳转到“会话、输入与执行后端”

ONNX Runtime(ORT)加载 ONNX 模型,为图分配可执行实现,然后运行推理。InferenceSession 通常复用多次;不要把每次创建会话的成本都混进“纯推理延迟”。本页先使用 ONNX 基础 生成的 affine.onnx,其接口为 x: float32[N,3] → y: float32[N,3]。

执行提供者(Execution Provider,EP)负责受支持的子图或节点。常用的 Python 标识符如下;是否可用取决于所安装的 ORT 构建及硬件依赖。

后端providers 中的准确名称
CPUCPUExecutionProvider
NVIDIA CUDACUDAExecutionProvider
NVIDIA TensorRTTensorrtExecutionProvider
Windows DirectMLDmlExecutionProvider
Intel OpenVINOOpenVINOExecutionProvider

CPU、CUDA 是口头简称,不能代替上表中的 API 名称。get_available_providers() 反映当前安装可提供哪些后端;会话的 get_providers() 反映该会话注册了哪些后端。两者都不能证明每个节点实际跑在 GPU 上。Python API

一个可直接验证的 CPU 推理程序

跳转到“一个可直接验证的 CPU 推理程序”

先生成 affine.onnx,再在同一目录运行下面的脚本:

import numpy as np
import onnxruntime as ort
session = ort.InferenceSession(
"affine.onnx", providers=["CPUExecutionProvider"]
)
input_meta = session.get_inputs()
output_meta = session.get_outputs()
assert len(input_meta) == len(output_meta) == 1
assert input_meta[0].type == "tensor(float)"
assert input_meta[0].shape == ["N", 3]
print("providers:", session.get_providers())
print("input:", input_meta[0].name, input_meta[0].type, input_meta[0].shape)
def infer(x):
x = np.asarray(x)
if x.dtype != np.float32:
raise TypeError("x must be float32")
if x.ndim != 2 or x.shape[0] < 1 or x.shape[1] != 3:
raise ValueError("expected a nonempty [N, 3] input")
if not np.isfinite(x).all():
raise ValueError("this demo accepts finite input values only")
feed = {input_meta[0].name: np.ascontiguousarray(x)}
return session.run([output_meta[0].name], feed)[0]
for batch in (1, 2, 8):
x = np.arange(batch * 3, dtype=np.float32).reshape(batch, 3) / 4
y = infer(x)
np.testing.assert_allclose(y, 2 * x + 1, rtol=1e-6, atol=1e-6)
print("three batch sizes passed")

这里查询输入名称后建立字典,不把示例文字 input_name 当作真实张量名。检查空批量和有限值是本例的应用约定;ONNX 符号维本身没有自动添加这些约束。多输入模型必须为每个必需输入提供正确名称和数据。

在已安装匹配的 GPU 版 ORT、驱动及所需 CUDA/cuDNN 库的环境中,按优先顺序注册后端:

import onnxruntime as ort
if "CUDAExecutionProvider" not in ort.get_available_providers():
raise RuntimeError("this ORT installation does not offer CUDAExecutionProvider")
session = ort.InferenceSession(
"affine.onnx",
providers=[("CUDAExecutionProvider", {"device_id": 0}),
"CPUExecutionProvider"],
)
if "CUDAExecutionProvider" not in session.get_providers():
raise RuntimeError("CUDA provider failed to initialize")
print(session.get_providers())

优先级表示先尝试 CUDA 支持的运算,允许 CPU 处理其余部分;它不是“整个图一定在 GPU 上”的开关。若性能与预期不符,应查看日志或 profiling 中的节点分配与拷贝,而不是只看设备名称。这个小算子例子也不保证 GPU 比 CPU 快。

原笔记中的 SessionOptions.use_cuda = True、SessionOptions.add_provider(...) 不是这里使用的 Python API。GPU 依赖版本应查询对应 ORT 版本的兼容表,不能仅以“电脑装了 CUDA”判断准备完成。CUDA EP 文档

主机内存、设备内存与共享流

跳转到“主机内存、设备内存与共享流”

普通 session.run 接收 NumPy 输入并返回主机端输出;使用 GPU 时,这条路径可能涉及主机与设备之间的传输。I/O Binding 能指定设备缓冲区,减少不必要的来回复制,但必须同时管理形状、dtype、地址和生命周期。

原笔记还引用了 user_compute_stream。它用于把 ORT 工作提交到用户提供的 CUDA 流,并非“输入一个地址就自动共享 PyTorch 张量”。使用时至少要满足:

  • 流属于正确设备和兼容的 CUDA 上下文,且在 ORT 使用期间保持有效。
  • 生产输入、执行推理、消费输出之间有正确的流顺序或事件同步。
  • 绑定缓冲区持续存活,不会在异步操作结束前被释放或复用。
  • 按官方限制组合 provider 选项;该选项不能与外部 allocator 任意混用。

只有确实需要设备端互操作时再引入这一层。官方示例和 I/O Binding 说明见 user_compute_stream。

现象先核对
Unknown provider / CUDA 初始化失败名称、ORT 安装包、依赖库和日志
Invalid input namesession.get_inputs() 的真实名称
Unexpected input data type模型元数据与数组 dtype
Invalid dimensions固定维度、动态范围、NCHW/NHWC 是否一致
能运行但结果不对预处理、输出顺序、模型权重、后处理与数值基线
GPU 比 CPU 慢数据量、拷贝、节点回退、预热、计时边界

本页 CPU 程序已在 ORT 1.30.0、NumPy 2.4.6 上执行,并检查了错误 dtype、错误形状和非有限输入的拒绝路径。CUDA 与共享流部分未在 GPU 上执行,不作为硬件性能或兼容性的实测结论。