在实际场景中,深度学习模型运行的机器不总是有 GPU,这时候基于 tensorrt 的部署方案就无法满足要求,本文基于 openvino 压缩量化模型,然后将其部署到 CPU 上,实验证明:推理效果相当、推理耗时接近
1. 模型优化与性能提升在深度学习模型应用中,模型的大小和复杂度往往与其推理速度和内存占用密切相关。为了在资源有限的环境中部署大型网络,通常需要对模型进行优化。这一优化过程主要包括以下几方面:
量化(Quantization) :通过将浮点数向量的精度降低,将模型权重和激活值从 32 位转换为更高效的表示形式(如 8 位或 4 位),从而减少内存占用并加快推理速度。模型压缩(Model Compression) :通过去除冗余的网络结构、迁移学习等技术,削弱不必要的计算步骤,进一步降低模型复杂度。优化后端(Post-Tuning) :针对特定硬件架构(如 CPU)的性能特点,对模型进行微调,使其在资源利用上达到最佳状态。OpenVINO 框架通过集成了这些技术,为模型优化提供了通用的解决方案,支持多种深度学习模型的部署和推理任务。
2. 基于 ncff 量化模型NCFF(NNAfter Calibration and Folding)是一种用于对 ONNX 模型进行量化的工具。其核心原理是通过对模型中的神经网络层参数和激活值进行量化,减少数据类型的精度,从而降低计算复杂度和内存占用
NCFF 量化模型的原理主要包括以下几个步骤:首先,选择适合的量化位数;然后,使用训练数据对量化后的模型进行校准,以找到最佳的量化参数;最后,将原始模型中的神经网络层、权重和激活值转换为量化形式,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 def quantiz_by_onnx (onnx_path,save_dir='' ): train_dataset = DatasetBuilder() train_loader = DataLoader(train_dataset, batch_size=1 , shuffle=True ) def transform_fn (data_item ): img, label, weight, is_ng = data_item return {'input' : img.numpy()} calibration_dataset = nncf.Dataset(train_loader, transform_fn) print ('calibration....' ) onnx_model = onnx.load(onnx_path) quantized_model = nncf.quantize( model=onnx_model, calibration_dataset=calibration_dataset ) quantized_onnx_path=os.path.join(save_dir,os.path.basename(onnx_path)) onnx.save(quantized_model, quantized_onnx_path) print ('quantized_onnx_path:' ,quantized_onnx_path) return quantized_onnx_path
3. 基于 openvino 部署量化模型按照以下步骤基于 openvino 部署量化模型
模型转换:把 ONNX 文件转换成 OpenVINO 的 XML 格式。这一步应该涉及到 OpenVINO 的 API,比如 ov.convert_model 和 ov.save_model 编译模型:导入了一些性能优化的设置,比如启用 Hyper-threading 和 CPU 固定,这可能是在准备模型进行推理时的设置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 quantized_openvino_path=quantized_onnx_path.replace('.onnx' ,'.xml' ) ov_quantized_model = ov.convert_model(quantized_onnx_path) ov.save_model(ov_quantized_model, quantized_openvino_path) config = { hints.performance_mode: hints.PerformanceMode.LATENCY, hints.enable_hyper_threading(): False , hints.enable_cpu_pinning(): True } core = ov.Core() core.set_property({props.cache_dir: r"E:\MyCode\python-openvino\cache" }) ov_model = core.read_model(model=quantized_openvino_path) compiled_model = core.compile_model(model=ov_quantized_model, device_name="CPU" ,config=config)
基于编译好的模型,我们使用代码去测试其推理效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 img_path=r'1.png' img = cv2.imdecode(np.fromfile(img_path, dtype=np.uint8), 1 ) img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img = cv2.resize(img, (512 ,512 ), interpolation=cv2.INTER_NEAREST) img_data=img.astype(np.float32) img_data/=255.0 print (img_data.shape)img_data-=0.5 img_data/=0.5 h,w=img_data.shape[:2 ] img_data=img_data.reshape(1 ,1 ,h,w) print ('infer....' )output_layer = compiled_model.output(0 ) iters=100 start_time = time.time() for i in range (iters): output=compiled_model([img_data])[output_layer] end_time = time.time() print (f"运行时间:{(end_time - start_time)*1.0 /iters} 秒" )pred = np.zeros_like(output) pred[output>0.5 ]=255 pred=pred[0 ,0 ].astype(np.uint8) result=cv2.addWeighted(img,1.0 ,pred,0.3 ,0 ) cv2.imwrite('ai.png' ,result)
4. 在 C++ 部署 openvino 推理过程基于 openvino 的 cpp 版,在 CPU 上部署模型,关键代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 int OpenvinoRuntime::sync_infer (std ::vector <cv::Mat>& frames) { for (int si = 0 ; si < frames.size(); si++) { int imgWidth = frames[si].size().width; int imgHeight = frames[si].size().height; int retn = preprocess(frames[si]); if (retn < 0 ) { return -1 ; } try { const ov::Tensor input_tensor = ov::Tensor(compiled_model.input().get_element_type(), ov::Shape(modelInputShape), (float *)(frames[si].data)); curr_request.set_input_tensor(input_tensor); curr_request.infer(); } catch (ov::Exception& e) { std ::cout << e.what() << endl ; return -2 ; } float * detections = curr_request.get_output_tensor().data<float >(); cv::Mat predMat (cv::Size(modelInputWidth, modelInputHeight), CV_32F, detections) ; retn = postprocess(frames[si],predMat, imgWidth, imgHeight,si); if (retn < 0 ) { return -3 ; } } return 0 ; }
针对 openvino 提供的批处理及异步推理,相应还编写以下推理方法
1 2 3 4 5 int sync_infer (std ::vector <cv::Mat>& frames) ;int sync_infer_batch (std ::vector <cv::Mat>& frames) ;int async_infer (std ::vector <cv::Mat>& frames) ;int sync_infer_preprocess (std ::vector <cv::Mat>& frames) ;int async_infer_preprocess (std ::vector <cv::Mat>& frames) ;
除此之外,将以上的 C 代码编译为 dll,并使用 CLR 封装其接口,以便供 C 或者 C# 使用
5. 效果分析以下统计在不同软硬件平台部署模型的耗时,可以看出在 CPU 部署模型时,openvino 部署方式更快,已经接近在 GPU 部署
软件 硬件 耗时 备注 onnxruntime CPU 1000ms openvino CPU 30ms 输入图片 (512x512) openvino CPU 12ms 输入图片 (320x512) tensorrt GPU 8ms 输入图片 (320x512)
总结本文基于 openvino 构建了一个 onnx 模型的部署流程,其耗时和 GPU 部署接近,并最终编译为 dll,提供到不同的软件开发平台使用。
使用 openvino 丰富了模型的应用场景,从传统的要求使用 GPU,推广到 CPU,降低了模型应用的成本。