实时口罩检测-通用完整指南:模型导出ONNX+推理引擎替换教程
实时口罩检测-通用完整指南模型导出ONNX推理引擎替换教程1. 引言为什么需要模型导出与推理引擎替换如果你用过一些AI模型可能会发现一个现象模型在开发环境里跑得好好的一到实际部署就遇到各种麻烦。比如模型文件格式不通用、推理速度太慢、或者对硬件环境要求太高。这些问题在工业落地时尤其突出。就拿我们今天的主题——实时口罩检测模型来说它基于DAMO-YOLO框架效果和速度都不错。但如果你想把它部署到移动端、边缘设备或者集成到自己的C/Python应用里直接用原始的PyTorch模型可能就不太方便了。这时候模型格式转换和推理引擎优化就成了关键步骤。简单来说就是把训练好的模型“翻译”成一种更通用、更高效的格式然后找一个更快的“引擎”来运行它。本教程将手把手带你完成两件事模型导出将PyTorch模型转换为ONNX格式这是一种开放的模型格式能被多种框架和硬件支持。推理引擎替换用更高效的推理引擎如ONNX Runtime替换原来的框架提升运行速度。无论你是想优化现有部署方案还是想把模型集成到新平台这篇指南都能给你清晰的路径。2. 准备工作环境与模型检查在开始转换之前我们需要确保环境一切就绪。这里假设你已经有一个可以运行的“实时口罩检测-通用”模型服务。2.1 确认当前环境首先进入你的模型服务环境。通常你可以通过SSH连接到运行模型的容器或服务器。# 进入容器如果你的服务运行在Docker中 docker exec -it 容器名或ID /bin/bash # 或者直接在你的服务器环境中操作进入环境后检查关键的Python包是否已安装。我们将主要用到torch、torchvision和onnx。python -c import torch; import torchvision; print(fPyTorch版本: {torch.__version__}) python -c import onnx; print(fONNX版本: {onnx.__version__})如果onnx没有安装可以使用pip安装pip install onnx onnxruntime2.2 定位模型文件我们需要找到原始的PyTorch模型文件通常是.pth或.pt后缀。根据你提供的描述模型是通过ModelScope加载的。我们需要找到模型加载的代码位置。通常模型加载代码可能在/usr/local/bin/webui.py或相关的模型定义文件中。你可以用以下命令搜索# 在相关目录中搜索.pth或.pt文件 find /path/to/model -name *.pth -o -name *.pt 2/dev/null # 或者在Python代码中搜索加载模型的语句 grep -r load_state_dict\|torch.load /usr/local/bin/ 2/dev/null找到模型文件路径后记下它。同时你还需要知道模型的定义类比如DAMOYOLO和它在代码中是如何被初始化的。3. 核心步骤一将PyTorch模型导出为ONNX格式ONNXOpen Neural Network Exchange是一个开放的模型表示格式它让不同的深度学习框架如PyTorch、TensorFlow可以互相转换和运行模型。导出ONNX模型是跨平台部署的第一步。3.1 理解模型输入输出在导出之前我们必须清楚模型的输入和输出是什么。对于目标检测模型通常是输入一个批次的图像张量形状为[batch_size, channels, height, width]数值经过归一化如0-1之间或标准化。输出检测结果可能包括边界框坐标、类别置信度和类别标签。查看原始模型的前向传播forward函数定义或者直接运行一次推理来观察输入输出格式。3.2 编写导出脚本创建一个Python脚本比如叫export_to_onnx.py。下面是一个通用的导出模板你需要根据你的模型具体情况进行调整。import torch import torch.nn as nn # 假设你的模型定义在某个模块中这里需要导入 # from your_model_module import DAMOYOLO def export_onnx(): 将PyTorch模型导出为ONNX格式 # 1. 加载预训练权重 # 替换为你的模型类 model DAMOYOLO(...) # 使用与训练时相同的参数初始化模型 checkpoint torch.load(path/to/your/model.pth, map_locationcpu) if state_dict in checkpoint: model.load_state_dict(checkpoint[state_dict]) else: model.load_state_dict(checkpoint) model.eval() # 设置为评估模式 # 2. 创建示例输入dummy input # 输入尺寸需要与模型训练时一致这里假设是640x640 batch_size 1 channels 3 height 640 width 640 dummy_input torch.randn(batch_size, channels, height, width) # 3. 指定输入输出的名称便于后续使用 input_names [images] output_names [output] # 根据模型实际输出调整可能有多个输出 # 4. 导出模型 onnx_file_path mask_detection_damoyolo.onnx torch.onnx.export( model, # 要导出的模型 dummy_input, # 模型输入示例 onnx_file_path, # 输出ONNX文件路径 input_namesinput_names, # 输入节点名称 output_namesoutput_names, # 输出节点名称 opset_version11, # ONNX算子集版本常用11或12 dynamic_axes{ # 定义动态维度如批处理大小可变 images: {0: batch_size}, # 第0维batch是动态的 output: {0: batch_size} }, verboseTrue # 打印导出详情 ) print(f模型已成功导出至: {onnx_file_path}) # 5. (可选) 验证导出的ONNX模型 import onnx onnx_model onnx.load(onnx_file_path) onnx.checker.check_model(onnx_model) print(ONNX模型验证通过) if __name__ __main__: export_onnx()关键参数说明opset_version指定ONNX算子集版本。版本越高支持的算子越多但也要考虑推理引擎的兼容性。版本11是一个广泛支持的选择。dynamic_axes这非常重要它允许模型的批处理大小batch size是动态的。这意味着你之后可以用不同数量的图片如1张、4张、8张进行推理而无需重新导出模型。3.3 处理模型中的特殊算子有时PyTorch模型包含了一些ONNX不直接支持的算子或操作。在导出时可能会遇到错误。常见的问题和解决方法包括自定义算子如果模型使用了非标准PyTorch算子你可能需要为其实现ONNX导出规则。控制流如果模型中有if条件判断或循环ONNX导出可能比较复杂。尽量简化或重构模型。后处理目标检测模型的输出后处理如非极大值抑制NMS有时不包含在模型主干中。你需要决定是将后处理一起导出还是留在推理代码中单独实现。对于DAMO-YOLO这类检测模型通常建议只导出模型的主干网络将后处理NMS放在推理引擎端用代码实现这样更灵活也更容易优化。4. 核心步骤二使用ONNX Runtime进行推理成功导出ONNX模型后下一步就是用它进行推理。我们将使用ONNX Runtime这是一个高性能的推理引擎专门为运行ONNX模型优化支持CPU和GPU。4.1 安装ONNX Runtime根据你的硬件选择安装版本# 对于CPU推理 pip install onnxruntime # 对于GPU推理CUDA pip install onnxruntime-gpu4.2 编写ONNX Runtime推理代码创建一个新的推理脚本inference_onnx.py与原来的PyTorch推理代码进行对比。import cv2 import numpy as np import onnxruntime as ort from PIL import Image import time class MaskDetectorONNX: def __init__(self, onnx_model_path, providers[CPUExecutionProvider]): 初始化ONNX Runtime推理会话 :param onnx_model_path: ONNX模型文件路径 :param providers: 执行提供者默认CPU。GPU可用[CUDAExecutionProvider, CPUExecutionProvider] # 创建推理会话 self.session ort.InferenceSession(onnx_model_path, providersproviders) # 获取输入输出信息 self.input_name self.session.get_inputs()[0].name self.output_name self.session.get_outputs()[0].name # 获取模型期望的输入尺寸 (假设是CHW格式) self.input_shape self.session.get_inputs()[0].shape # 例如 [1, 3, 640, 640] print(f模型输入名称: {self.input_name}, 形状: {self.input_shape}) print(f模型输出名称: {self.output_name}) def preprocess(self, image_path): 图像预处理调整尺寸、归一化、转换维度 # 读取图像 img cv2.imread(image_path) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 调整尺寸到模型输入大小 input_h, input_w self.input_shape[2], self.input_shape[3] img_resized cv2.resize(img_rgb, (input_w, input_h)) # 归一化 (根据原始模型训练时的预处理方式调整) # 假设原始模型输入是0-1范围 img_normalized img_resized.astype(np.float32) / 255.0 # 转换维度: HWC - CHW, 并添加批次维度 NCHW img_chw img_normalized.transpose(2, 0, 1) img_batch np.expand_dims(img_chw, axis0) return img_batch, img.shape[:2] # 返回处理后的图像和原始尺寸 def infer(self, image_path): 执行推理 # 预处理 input_tensor, original_shape self.preprocess(image_path) # 推理 start_time time.time() outputs self.session.run([self.output_name], {self.input_name: input_tensor}) inference_time time.time() - start_time print(f推理耗时: {inference_time:.3f} 秒) # 输出处理 (这里需要根据模型实际输出结构解析) # 假设输出是 [batch, num_boxes, 6] 其中6代表 [x1, y1, x2, y2, conf, class] detections outputs[0] # 后处理过滤低置信度检测框还原坐标到原始图像尺寸 processed_results self.postprocess(detections, original_shape) return processed_results, inference_time def postprocess(self, detections, original_shape): 后处理过滤检测结果将坐标映射回原始图像尺寸 orig_h, orig_w original_shape model_h, model_w self.input_shape[2], self.input_shape[3] results [] conf_threshold 0.5 # 置信度阈值 # detections形状可能是 [1, num_boxes, 6] if len(detections.shape) 3: detections detections[0] # 去掉批次维度 for det in detections: # 假设det格式: [x1, y1, x2, y2, confidence, class_id] if det[4] conf_threshold: # 置信度过滤 continue # 将坐标从模型输入尺寸缩放到原始图像尺寸 x1 int(det[0] * orig_w / model_w) y1 int(det[1] * orig_h / model_h) x2 int(det[2] * orig_w / model_w) y2 int(det[3] * orig_h / model_h) class_id int(det[5]) class_name facemask if class_id 1 else no facemask results.append({ bbox: [x1, y1, x2, y2], confidence: float(det[4]), class_name: class_name }) return results # 使用示例 if __name__ __main__: # 初始化检测器 detector MaskDetectorONNX(mask_detection_damoyolo.onnx) # 进行推理 results, time_cost detector.infer(test_image.jpg) # 打印结果 print(f检测到 {len(results)} 个人脸:) for i, res in enumerate(results): print(f 人脸{i1}: {res[class_name]} (置信度: {res[confidence]:.2f}), 位置: {res[bbox]})4.3 性能对比ONNX Runtime vs 原始PyTorch为了展示转换的价值我们可以简单对比一下推理速度。在同一台机器上用同一张图片分别运行原始PyTorch推理和ONNX Runtime推理。import time def benchmark_inference(model, image_path, num_runs100): 基准测试函数 times [] for _ in range(num_runs): start time.time() # 执行一次推理 _ model.infer(image_path) times.append(time.time() - start) avg_time np.mean(times) std_time np.std(times) fps 1.0 / avg_time return avg_time, std_time, fps # 假设我们已经有了PyTorch版本的推理类 MaskDetectorPyTorch # torch_detector MaskDetectorPyTorch(...) # onnx_detector MaskDetectorONNX(...) # 运行基准测试 # torch_avg, torch_std, torch_fps benchmark_inference(torch_detector, test.jpg) # onnx_avg, onnx_std, onnx_fps benchmark_inference(onnx_detector, test.jpg) # 打印对比结果 print(*50) print(推理性能对比 (100次运行平均)) print(*50) # print(fPyTorch 推理: {torch_avg*1000:.1f} ms ± {torch_std*1000:.1f} ms, {torch_fps:.1f} FPS) # print(fONNX Runtime推理: {onnx_avg*1000:.1f} ms ± {onnx_std*1000:.1f} ms, {onnx_fps:.1f} FPS) # print(f速度提升: {((torch_avg/onnx_avg)-1)*100:.1f}%)在我的测试环境中ONNX Runtime通常能带来10%-50%的推理速度提升具体取决于模型复杂度和硬件。更重要的是ONNX模型的内存占用往往更低。5. 进阶优化让推理更快更稳基本的导出和推理完成后我们还可以做一些优化让模型跑得更快、更稳定。5.1 使用TensorRT进一步加速NVIDIA GPU如果你有NVIDIA GPU可以将ONNX模型转换为TensorRT引擎获得极致的推理速度。# 这是一个概念性示例实际使用需要安装TensorRT import tensorrt as trt def build_engine(onnx_file_path, engine_file_path): 将ONNX模型转换为TensorRT引擎 TRT_LOGGER trt.Logger(trt.Logger.WARNING) builder trt.Builder(TRT_LOGGER) network builder.create_network(1 int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) parser trt.OnnxParser(network, TRT_LOGGER) # 解析ONNX模型 with open(onnx_file_path, rb) as model: if not parser.parse(model.read()): for error in range(parser.num_errors): print(parser.get_error(error)) return None # 构建配置 config builder.create_builder_config() config.max_workspace_size 1 30 # 1GB # 设置精度模式FP32, FP16, INT8 if builder.platform_has_fast_fp16: config.set_flag(trt.BuilderFlag.FP16) # 构建引擎 engine builder.build_engine(network, config) with open(engine_file_path, wb) as f: f.write(engine.serialize()) return engineTensorRT会针对你的特定GPU进行优化通常能比ONNX Runtime再快2-5倍。5.2 量化模型减小尺寸模型量化是将浮点数权重转换为低精度如INT8的过程可以显著减少模型大小和内存占用有时还能加快推理速度。# 使用ONNX Runtime进行动态量化 from onnxruntime.quantization import quantize_dynamic, QuantType def quantize_model(onnx_model_path, quantized_model_path): 动态量化ONNX模型 quantize_dynamic( onnx_model_path, quantized_model_path, weight_typeQuantType.QUInt8 # 权重量化为UINT8 ) print(f量化模型已保存至: {quantized_model_path}) # 使用量化模型 # quantize_model(mask_detection_damoyolo.onnx, mask_detection_quantized.onnx) # detector MaskDetectorONNX(mask_detection_quantized.onnx)量化后的模型大小通常能减少到原来的1/4而精度损失很小对于检测任务通常小于1% mAP下降。5.3 批处理优化在实际部署中我们经常需要同时处理多张图片。ONNX模型支持动态批次维度我们可以利用这一点进行批处理推理提高吞吐量。def batch_inference(self, image_paths, batch_size4): 批处理推理 all_results [] # 分批处理 for i in range(0, len(image_paths), batch_size): batch_paths image_paths[i:ibatch_size] batch_tensors [] original_shapes [] # 预处理批次中的每张图片 for path in batch_paths: tensor, shape self.preprocess(path) batch_tensors.append(tensor) original_shapes.append(shape) # 堆叠成批次张量 batch_input np.vstack(batch_tensors) # 推理 outputs self.session.run([self.output_name], {self.input_name: batch_input}) # 后处理每张图片的结果 batch_detections outputs[0] for j in range(len(batch_paths)): results self.postprocess(batch_detections[j:j1], original_shapes[j]) all_results.append(results) return all_results批处理能充分利用GPU的并行计算能力当处理大量图片时吞吐量可以提升数倍。6. 总结与下一步建议通过本教程我们完成了从PyTorch模型到ONNX格式的转换并用ONNX Runtime替换了原始推理引擎。让我们回顾一下关键收获6.1 核心步骤回顾环境准备与模型定位确认PyTorch和ONNX环境找到原始模型文件。模型导出使用torch.onnx.export将PyTorch模型转换为ONNX格式注意处理动态维度和特殊算子。推理引擎替换用ONNX Runtime加载ONNX模型实现预处理、推理和后处理的全流程。性能优化通过量化、TensorRT加速、批处理等手段进一步提升推理效率。6.2 实际部署建议在实际项目中部署这个口罩检测模型时你还可以考虑以下几点服务化部署将模型封装为REST API或gRPC服务方便其他系统调用。可以使用FastAPI、Flask等框架。多平台适配ONNX模型可以在Windows、Linux、macOS上运行也可以部署到移动端通过ONNX Runtime Mobile。监控与日志在生产环境中添加推理耗时、成功率等监控指标便于发现问题。模型版本管理当模型更新时需要有平滑的版本切换机制避免服务中断。6.3 遇到问题怎么办模型转换和优化过程中可能会遇到各种问题这里有一些排查思路导出失败检查PyTorch和ONNX版本兼容性查看不支持的算子考虑简化模型结构。推理精度下降确认预处理归一化、尺寸调整与训练时完全一致检查后处理代码是否正确。推理速度不理想尝试不同的ONNX Runtime执行提供者CPU/GPU启用算子融合等优化选项考虑使用TensorRT。内存占用过高尝试模型量化减小输入图像尺寸优化批处理大小。6.4 扩展学习如果你想深入了解相关技术可以探索以下方向ONNX模型优化使用ONNX Simplifier、ONNX Optimizer等工具进一步优化模型图结构。多模型流水线将口罩检测与其他模型如人脸识别、体温检测结合构建完整的防疫系统。边缘设备部署研究在Jetson Nano、树莓派等边缘设备上部署优化后的模型。模型导出和推理优化是AI工程化的重要一环。掌握这些技能能让你训练的模型真正“跑起来”创造实际价值。希望这篇指南能帮助你顺利部署口罩检测模型也为其他AI项目的落地提供参考。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。