第4章:onnxruntime入门:安装与配置、Session创建与运行、CPU与GPU后端选择
好,咱们进入实战环节。这一章我带你从零搭建onnxruntime环境,然后跑通第一个推理程序。说实话,很多同学卡在第一步——装好了库,但不知道怎么创建Session,或者不知道什么时候该用GPU。别急,咱们一步步来。
4.1 安装与配置:别让环境成为拦路虎
安装onnxruntime其实很简单,但有几个坑我得提前说。我个人习惯用pip直接装,省事。
# CPU版本(最稳定)
pip install onnxruntime
# GPU版本(需要CUDA和cuDNN)
pip install onnxruntime-gpu
嗯,这里要注意:GPU版本对CUDA版本有要求。我曾在项目里踩过坑——装了最新版onnxruntime-gpu,结果CUDA 11.8不兼容,跑起来直接报错。后来查文档才发现,onnxruntime 1.15对应CUDA 11.8,1.16对应CUDA 12.x。所以建议你先查一下版本对应关系。
如果你用conda,也可以这样装:
conda install -c conda-forge onnxruntime
我个人更推荐pip,因为conda的包更新有时会慢半拍。不过看个人习惯,哪个顺手用哪个。
4.2 Session创建与运行:核心操作就这么几步
装好了,咱们来写第一个推理程序。说白了,onnxruntime的核心就是三个步骤:加载模型、创建Session、运行推理。
import onnxruntime as ort
import numpy as np
# 1. 加载模型
model_path = "model.onnx"
# 2. 创建Session
session = ort.InferenceSession(model_path)
# 3. 准备输入数据(假设模型输入是1x3x224x224)
input_name = session.get_inputs()[0].name
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)
# 4. 运行推理
outputs = session.run(None, {input_name: input_data})
print("推理完成,输出形状:", outputs[0].shape)
你看,代码就这么几行。但这里有个细节:session.run()的第一个参数是输出名称,传None表示返回所有输出。如果你只想拿某个特定输出,可以传输出名称的列表。
session.get_inputs()和session.get_outputs()就能看到。这样能避免很多低级错误。
# 查看模型输入输出信息
for input_meta in session.get_inputs():
print(f"输入: {input_meta.name}, 形状: {input_meta.shape}, 类型: {input_meta.type}")
for output_meta in session.get_outputs():
print(f"输出: {output_meta.name}, 形状: {output_meta.shape}, 类型: {output_meta.type}")
4.3 CPU与GPU后端选择:什么时候该用GPU?
这个问题其实挺常见的。你想想看,GPU不是万能的。我见过有人拿GPU跑一个单张图片的推理,结果因为数据传输开销,比CPU还慢。为什么会这样?因为GPU的优势在于并行计算,但数据从内存拷贝到显存需要时间。
简单来说,选择原则是这样的:
| 场景 | 推荐后端 | 原因 |
|---|---|---|
| 单张图片推理(延迟敏感) | CPU | 避免数据传输开销 |
| 批量推理(吞吐量优先) | GPU | 并行计算优势明显 |
| 模型很小(如MobileNet) | CPU | GPU加速不明显 |
| 模型很大(如ResNet-152) | GPU | CPU推理太慢 |
| 生产环境部署 | 按需选择 | 综合考虑成本和性能 |
那代码里怎么切换呢?其实很简单:
# CPU后端(默认)
session_cpu = ort.InferenceSession("model.onnx")
# GPU后端
session_gpu = ort.InferenceSession("model.onnx", providers=['CUDAExecutionProvider'])
# 也可以指定多个provider,按优先级排列
session_mixed = ort.InferenceSession(
"model.onnx",
providers=['CUDAExecutionProvider', 'CPUExecutionProvider']
)
嗯,这里要注意:如果你装了onnxruntime-gpu,但没装CUDA,或者CUDA版本不对,创建Session时会报错。我建议你写个简单的检查函数:
def check_available_providers():
"""检查可用的执行后端"""
available = ort.get_available_providers()
print("可用的providers:", available)
if 'CUDAExecutionProvider' in available:
print("GPU可用,可以加速推理")
else:
print("GPU不可用,将使用CPU")
check_available_providers()
4.4 实战:跑一个完整的推理流程
好,咱们把前面学的串起来,写一个完整的示例。假设你有一个训练好的ResNet-50模型,导出成了ONNX格式。
import onnxruntime as ort
import numpy as np
from PIL import Image
import time
def preprocess_image(image_path):
"""预处理图片:resize、归一化、转CHW"""
img = Image.open(image_path).resize((224, 224))
img_array = np.array(img).astype(np.float32) / 255.0
# 标准化(ImageNet的均值和标准差)
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
img_array = (img_array - mean) / std
# 转成CHW格式并增加batch维度
img_array = np.transpose(img_array, (2, 0, 1))
img_array = np.expand_dims(img_array, axis=0)
return img_array
def run_inference(model_path, image_path, use_gpu=False):
"""运行推理"""
# 选择后端
if use_gpu:
session = ort.InferenceSession(
model_path,
providers=['CUDAExecutionProvider', 'CPUExecutionProvider']
)
else:
session = ort.InferenceSession(model_path)
# 预处理
input_data = preprocess_image(image_path)
input_name = session.get_inputs()[0].name
# 推理并计时
start = time.time()
outputs = session.run(None, {input_name: input_data})
elapsed = time.time() - start
# 后处理(假设是分类任务)
probabilities = np.exp(outputs[0]) / np.sum(np.exp(outputs[0]), axis=1, keepdims=True)
predicted_class = np.argmax(probabilities)
print(f"推理耗时: {elapsed*1000:.2f} ms")
print(f"预测类别: {predicted_class}, 置信度: {probabilities[0][predicted_class]:.4f}")
return predicted_class, probabilities
# 运行
model_path = "resnet50.onnx"
image_path = "cat.jpg"
print("=== CPU推理 ===")
run_inference(model_path, image_path, use_gpu=False)
print("\n=== GPU推理 ===")
run_inference(model_path, image_path, use_gpu=True)
4.5 常见问题与排查
最后,我总结几个新手容易遇到的问题:
- Session创建失败:检查模型路径是否正确,ONNX文件是否损坏。我遇到过模型导出时版本不兼容的问题,用
onnx.checker.check_model()可以验证。 - GPU不可用:确认安装了onnxruntime-gpu,且CUDA版本匹配。用
ort.get_available_providers()检查。 - 推理结果不对:多半是预处理和后处理的问题。检查输入数据的形状、数据类型、归一化方式是否和训练时一致。
- 性能不如预期:用
session.run()的options参数可以设置优化级别,或者尝试开启enable_profiling=True来分析瓶颈。
好了,这一章的内容就到这里。你学会了安装、创建Session、选择后端,还跑了一个完整的推理流程。下一章咱们会深入Session的配置选项,看看怎么进一步榨干性能。