ONNX导出后怎么用?Python推理代码示例奉上
1. ONNX模型导出的意义与优势
在深度学习工程实践中,模型训练完成后如何高效部署是关键环节。ONNX(Open Neural Network Exchange)作为一种开放的神经网络交换格式,正在成为跨平台模型部署的事实标准。
1.1 为什么选择ONNX格式
- 跨框架兼容性:支持PyTorch、TensorFlow、Keras等主流框架之间的模型转换
- 生产环境友好:可在服务器、边缘设备、移动端等多种环境下运行
- 优化潜力大:可通过ONNX Runtime实现硬件加速和性能优化
- 简化部署流程:避免在生产环境中安装完整的训练框架依赖
对于cv_resnet18_ocr-detection这类OCR文字检测模型而言,ONNX格式能够有效降低部署复杂度,同时保持较高的推理效率。
1.2 导出ONNX的关键参数配置
根据镜像文档说明,在导出ONNX模型时需要关注以下核心参数:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 输入高度 | 640/800/1024 | 影响精度与速度的平衡 |
| 输入宽度 | 640/800/1024 | 建议与高度保持一致 |
| 动态轴设置 | False | 固定尺寸可提升推理速度 |
不同输入尺寸的选择直接影响模型的内存占用和推理延迟,需根据实际应用场景权衡。
2. ONNX Runtime环境准备
要运行导出的ONNX模型,首先需要安装并配置ONNX Runtime推理引擎。
2.1 安装ONNX Runtime
# CPU版本(通用) pip install onnxruntime # GPU版本(推荐用于高性能场景) pip install onnxruntime-gpu # 或使用TensorRT加速(高级用户) pip install onnxruntime-gpu tensorrt注意:GPU版本要求系统已正确安装CUDA驱动和cuDNN库。
2.2 验证安装结果
import onnxruntime as ort # 查看可用的执行提供者 print("可用执行提供者:") print(ort.get_available_providers()) # 示例输出: # ['CPUExecutionProvider', 'CUDAExecutionProvider']如果能看到CUDAExecutionProvider,说明GPU加速已就绪。
3. Python推理代码完整实现
本节提供基于ONNX Runtime的完整推理代码示例,涵盖预处理、推理执行和后处理全流程。
3.1 模型加载与会话初始化
import onnxruntime as ort import cv2 import numpy as np from typing import Tuple, List, Dict import json class ONNXOCRDetector: """ 基于ONNX模型的文字检测器 """ def __init__(self, model_path: str, input_size: Tuple[int, int] = (800, 800)): self.model_path = model_path self.input_size = input_size self.session = None self.input_name = None # 初始化ONNX Runtime会话 self._initialize_session() def _initialize_session(self): """创建ONNX Runtime会话""" # 配置选项 options = ort.SessionOptions() options.intra_op_num_threads = 4 # 内部线程数 options.inter_op_num_threads = 4 # 间操作线程数 # 尝试优先使用GPU providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] try: self.session = ort.InferenceSession( self.model_path, sess_options=options, providers=providers ) print(f"成功使用 {self.session.get_providers()[0]} 进行推理") except Exception as e: print(f"GPU推理初始化失败,回退到CPU: {e}") self.session = ort.InferenceSession( self.model_path, sess_options=options, providers=['CPUExecutionProvider'] ) # 获取输入名称 self.input_name = self.session.get_inputs()[0].name def preprocess(self, image: np.ndarray) -> np.ndarray: """ 图像预处理:调整尺寸、归一化、通道变换 Args: image: BGR格式的numpy数组 Returns: 处理后的输入张量 """ # 转换为RGB rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 调整尺寸 resized = cv2.resize(rgb_image, self.input_size) # 转换为float32并归一化 normalized = resized.astype(np.float32) / 255.0 # HWC -> CHW 并增加batch维度 transposed = np.transpose(normalized, (2, 0, 1)) input_tensor = np.expand_dims(transposed, axis=0) return input_tensor3.2 推理执行与结果解析
def postprocess(self, outputs: List[np.ndarray], original_shape: Tuple[int, int], score_threshold: float = 0.2) -> Dict: """ 后处理:解析模型输出 Args: outputs: 模型原始输出 original_shape: 原始图像尺寸 score_threshold: 置信度阈值 Returns: 包含检测结果的字典 """ # 假设模型输出包含:boxes, scores, texts等 # 具体结构需根据实际模型确定 boxes = outputs[0] # 检测框坐标 scores = outputs[1] # 置信度分数 texts = outputs[2] # 识别文本(如有) # 过滤低置信度结果 valid_indices = np.where(scores >= score_threshold)[0] # 缩放回原始尺寸 orig_h, orig_w = original_shape[:2] input_h, input_w = self.input_size scale_x = orig_w / input_w scale_y = orig_h / input_h scaled_boxes = [] for box in boxes[valid_indices]: # 假设box格式为[x1,y1,x2,y2,x3,y3,x4,y4] scaled_box = [] for i in range(0, len(box), 2): x = float(box[i]) * scale_x y = float(box[i+1]) * scale_y scaled_box.extend([x, y]) scaled_boxes.append(scaled_box) result = { "success": True, "texts": [texts[i] for i in valid_indices] if len(texts) > 0 else [], "boxes": scaled_boxes, "scores": scores[valid_indices].tolist(), "inference_time": 0.0, # 实际推理时间将在infer中填充 "image_path": "unknown" } return result def infer(self, image: np.ndarray, score_threshold: float = 0.2) -> Dict: """ 执行完整推理流程 Args: image: 输入图像(BGR格式) score_threshold: 检测阈值 Returns: 检测结果字典 """ import time start_time = time.time() # 保存原始形状 original_shape = image.shape # 预处理 input_tensor = self.preprocess(image) # 执行推理 outputs = self.session.run(None, {self.input_name: input_tensor}) # 后处理 result = self.postprocess(outputs, original_shape, score_threshold) # 记录推理时间 inference_time = time.time() - start_time result["inference_time"] = round(inference_time, 3) return result3.3 完整使用示例
def main(): # 初始化检测器 detector = ONNXOCRDetector( model_path="model_800x800.onnx", input_size=(800, 800) ) # 读取测试图像 image = cv2.imread("test.jpg") if image is None: raise FileNotFoundError("无法加载测试图像") # 执行检测 result = detector.infer(image, score_threshold=0.2) # 打印结果 print("检测结果:") for i, (text_list, score) in enumerate(zip(result["texts"], result["scores"])): text = "".join(text_list) if isinstance(text_list, list) else str(text_list) print(f"{i+1}. {text} (置信度: {score:.2f})") # 可视化检测框 vis_image = image.copy() for box in result["boxes"]: # 将四点坐标转换为numpy数组 points = np.array(box).reshape(-1, 2).astype(int) cv2.polylines(vis_image, [points], True, (0, 255, 0), 2) # 保存可视化结果 cv2.imwrite("detection_result.png", vis_image) print(f"可视化结果已保存至 detection_result.png") # 保存JSON结果 with open("result.json", "w", encoding="utf-8") as f: json.dump(result, f, ensure_ascii=False, indent=2) print("JSON结果已保存至 result.json") if __name__ == "__main__": main()4. 性能优化建议
为了获得最佳的推理性能,建议从以下几个方面进行优化。
4.1 模型层面优化
| 优化策略 | 说明 |
|---|---|
| 量化压缩 | 将FP32模型转换为INT8,显著减小模型体积 |
| 图优化 | 使用ONNX的optimizer工具进行算子融合 |
| 剪枝 | 移除不重要的神经元连接 |
# 示例:启用ONNX Runtime的图优化 options = ort.SessionOptions() options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL4.2 运行时参数调优
# 创建优化的会话配置 def create_optimized_session(model_path: str): options = ort.SessionOptions() # 启用所有图优化 options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL # 设置线程数(建议设置为物理核心数) options.intra_op_num_threads = 8 options.inter_op_num_threads = 2 # 启用内存复用 options.add_session_config_entry("session.use_env_allocators", "1") providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] session = ort.InferenceSession(model_path, sess_options=options, providers=providers) return session4.3 批量推理实现
对于需要处理多张图片的场景,建议使用批量推理以提高吞吐量:
def batch_infer(self, images: List[np.ndarray], score_threshold: float = 0.2) -> List[Dict]: """ 批量推理函数 """ batch_tensor = [] for image in images: input_tensor = self.preprocess(image) batch_tensor.append(input_tensor[0]) # 去掉单样本的batch维度 # 组合成batch batch_input = np.stack(batch_tensor) # 执行批量推理 start_time = time.time() outputs = self.session.run(None, {self.input_name: batch_input}) batch_time = time.time() - start_time # 分别后处理每个结果 results = [] for i in range(len(images)): single_output = [output[i:i+1] for output in outputs] result = self.postprocess( single_output, images[i].shape, score_threshold ) result["inference_time"] = batch_time / len(images) results.append(result) return results5. 常见问题与解决方案
5.1 输入尺寸不匹配
问题现象:推理时报错"Input size mismatch"
解决方案: - 确保推理时使用的input_size与导出模型时一致 - 或在导出时启用动态轴:
# 导出时允许动态尺寸 dynamic_axes = { 'input': {0: 'batch_size', 2: 'height', 3: 'width'}, } torch.onnx.export(..., dynamic_axes=dynamic_axes)5.2 GPU内存不足
问题现象:CUDA out of memory
解决方法: - 减小输入图像尺寸 - 降低batch size - 使用CPU推理 - 升级显卡或增加显存
5.3 输出结果异常
调试建议: 1. 检查预处理是否与训练时一致 2. 验证归一化参数(均值、标准差) 3. 确认通道顺序(BGR vs RGB) 4. 使用简单测试图像验证基本功能
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。