会话、输入与执行后端
跳转到“会话、输入与执行后端”ONNX Runtime(ORT)加载 ONNX 模型,为图分配可执行实现,然后运行推理。InferenceSession 通常复用多次;不要把每次创建会话的成本都混进“纯推理延迟”。本页先使用 ONNX 基础 生成的 affine.onnx,其接口为 x: float32[N,3] → y: float32[N,3]。
执行提供者(Execution Provider,EP)负责受支持的子图或节点。常用的 Python 标识符如下;是否可用取决于所安装的 ORT 构建及硬件依赖。
| 后端 | providers 中的准确名称 |
|---|---|
| CPU | CPUExecutionProvider |
| NVIDIA CUDA | CUDAExecutionProvider |
| NVIDIA TensorRT | TensorrtExecutionProvider |
| Windows DirectML | DmlExecutionProvider |
| Intel OpenVINO | OpenVINOExecutionProvider |
CPU、CUDA 是口头简称,不能代替上表中的 API 名称。get_available_providers() 反映当前安装可提供哪些后端;会话的 get_providers() 反映该会话注册了哪些后端。两者都不能证明每个节点实际跑在 GPU 上。Python API
一个可直接验证的 CPU 推理程序
跳转到“一个可直接验证的 CPU 推理程序”先生成 affine.onnx,再在同一目录运行下面的脚本:
import numpy as npimport 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) == 1assert 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 符号维本身没有自动添加这些约束。多输入模型必须为每个必需输入提供正确名称和数据。
如何显式选择 CUDA
跳转到“如何显式选择 CUDA”在已安装匹配的 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 name | session.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 上执行,不作为硬件性能或兼容性的实测结论。