当前位置:   article > 正文

Yolov3 CPU推理性能比较-Onnx、OpenCV、Darknet

darknet和onnx

为实时目标检测应用程序选择正确的推理框架变得非常具有挑战性,尤其是当模型应该在低功耗设备上运行时。在本文中,你将了解如何根据你的需要选择最佳的推理检测器,并发现它可以给你带来巨大的性能提升。

通常,当我们将模型部署到CPU或移动设备上时,往往只关注于轻量级的模型体系结构,而忽略了对快速推理机的研究。

在研究CPU设备上的快速推理时,我测试了提供稳定python API的各种框架。今天将重点介绍Onnxruntime、opencvdnn和Darknet框架,并从性能(运行时间)和准确性方面对它们进行度量。

  • Onnxruntime

    • https://github.com/microsoft/onnxruntime

  • opencvdnn

    • https://docs.opencv.org/master/d2/d58/tutorial_table_of_content_dnn.html

  • Darknet

    • https://github.com/AlexeyAB/darknet

我们将使用两种常见的目标检测模型进行性能测量:

  • Yolov3架构:

  1. image_size = 480*480
  2. classes = 98
  3. BFLOPS =87.892
  • Tiny-Yolov3_3layers 体系结构:

  1. image_size= 1024*1024
  2. classes =98
  3. BFLOPS= 46.448

这两个模型都是使用AlexeyAB的Darknet框架对自定义数据进行训练的。

现在,让我们用我们要测试的探测器来运行推理。

Darknet探测器

Darknet是训练 YOLO 目标检测模型的官方框架。

此外,它还提供了对*.weights文件格式的模型进行推理的能力,该文件格式与训练输出的格式相同。

推理有两种方法:

  • 不同数量的图像:

  1. darknet detector test cfg/coco.data cfg/yolov3.cfg yolov3.weights -thresh 0.25
  • 一个图像

  1. darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights dog.png
OpenCV DNN探测器

Opencv-DNN是计算机视觉领域常用的Opencv库的扩展。Darknet声称OpenCV-DNN是“CPU设备上YOLV4/V3最快的推理实现”,因为它高效的C&C++实现。

由于其方便的Python API,直接将darknet权重加载到opencv-dnn即可。

这是E2E推理的代码片段:

  1. import cv2
  2. import numpy as np
  3. # 指定模型的网络大小
  4. network_size = (480,480)
  5. # Darknet cfg文件路径
  6. cfg_path = 'yolov3.cfg'
  7. # Darknet 权重路径
  8. weights_path = 'yolov3.weights'
  9. # 定义推理引擎
  10. net = cv2.dnn.readNetFromDarknet(cfg_path, weights_path)
  11. net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
  12. net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
  13. _layer_names = net.getLayerNames()
  14. _output_layers = [_layer_names[i[0] - 1for i in net.getUnconnectedOutLayers()]
  15. # 读取图像作为输入
  16. img_path = 'dog.png'
  17. img = cv2.imread(img_path)
  18. image_blob = cv2.dnn.blobFromImage(img, 1 / 255.0, network_size, swapRB=True, crop=False)
  19. net.setInput(image_blob, "data")
  20. # 运行推理
  21. layers_result = net.forward(_output_layers)
  22. # 将layers_result转换为bbox,conf和类
  23. def get_final_predictions(outputs, img, threshold, nms_threshold):
  24.     height, width = img.shape[0], img.shape[1]
  25.     boxes, confs, class_ids = [], [], []
  26.     for output in outputs:
  27.         for detect in output:
  28.             scores = detect[5:]
  29.             class_id = np.argmax(scores)
  30.             conf = scores[class_id]
  31.             if conf > threshold:
  32.                 center_x = int(detect[0] * width)
  33.                 center_y = int(detect[1] * height)
  34.                 w = int(detect[2] * width)
  35.                 h = int(detect[3] * height)
  36.                 x = int(center_x - w/2)
  37.                 y = int(center_y - h / 2)
  38.                 boxes.append([x, y, w, h])
  39.                 confs.append(float(conf))
  40.                 class_ids.append(class_id)
  41.     
  42.     merge_boxes_ids = cv2.dnn.NMSBoxes(boxes, confs, threshold, nms_threshold)
  43.     
  44.     # 仅过滤nms之后剩余的框 
  45.     boxes = [boxes[int(i)] for i in merge_boxes_ids]
  46.     confs = [confs[int(i)] for i in merge_boxes_ids]
  47.     class_ids = [class_ids[int(i)] for i in merge_boxes_ids]
  48.     return boxes, confs, class_ids
  49. boxes, confs, class_ids = get_final_predictions(layers_result, img, 0.30.3)
Onnxruntime检测器

Onnxruntime是由微软维护的,由于其内置的优化和独特的ONNX权重格式文件,它声称可以显著加快推理速度。

正如你在下一张图片中看到的,它支持各种风格和技术。

在我们的比较中,我们将使用Python\x64\CPU风格。

ONNX格式定义了一组通用的操作符(机器学习和深度学习模型的构建块)和一种通用的文件格式,使AI开发人员能够将模型与各种框架、工具、运行时和编译器一起使用。

转换Darknet权重> Onnx权重

为了使用Onnxruntime运行推理,我们必须将*.weights格式转换为*.onnx fomrat。

我们将使用专门为将darknet*.weights格式转换为*.pt(PyTorch)和*.onnx(onnx格式)而创建的存储库。

https://github.com/matankley/Yolov3_Darknet_PyTorch_Onnx_Converter

  • 克隆repo并安装需求。

  • 用cfg和weights和img_size参数运行converter.py。

  1. python converter.py yolov3.cfg yolov3.weights 1024 1024

将在yolov3.weights目录中创建一个yolov3.onnx文件。

请记住,在使用ONNX格式进行推理时,由于转换过程的原因,精度会降低约0.1 mAP%。转换器模仿PyTorch中的Darknet功能,但并非完美无缺

为了支持除yolov3之外的其他darknet架构的转换,可以随意创建issues/PR

在我们成功地将模型转换为ONNX格式之后,我们可以使用Onnxruntime运行推理。

下面是E2E推理的代码片段:

  1. import onnxruntime
  2. import cv2
  3. import numpy as np
  4. # 转换后的onnx权重
  5. onnx_weights_path = 'yolov3.onnx'
  6. # 指定模型的网络大小
  7. network_size = (480480)
  8. # 声明onnxruntime会话
  9. session = onnxruntime.InferenceSession(onnx_weights_path)
  10. session.get_modelmeta()
  11. input_name = session.get_inputs()[0].name
  12. output_name_1 = session.get_outputs()[0].name
  13. output_name_2 = session.get_outputs()[1].name
  14. # 阅读图片
  15. img_path = 'dog.png'
  16. img = cv2.imread(img_path)
  17. image_blob = cv2.dnn.blobFromImage(img, 1 / 255.0, network_size, swapRB=True, crop=False)
  18. # 运行推理
  19. layers_result = session.run([output_name_1, output_name_2],
  20.                                          {input_name: image_blob})
  21. layers_result = np.concatenate([layers_result[1], layers_result[0]], axis=1)
  22. # 将layers_result转换为bbox,conf和类
  23. def get_final_predictions(outputs, img, threshold, nms_threshold):
  24.     height, width = img.shape[0], img.shape[1]
  25.     boxes, confs, class_ids = [], [], []
  26.     matches = outputs[np.where(np.max(outputs[:, 4:], axis=1) > threshold)]
  27.     for detect in matches:
  28.         scores = detect[4:]
  29.         class_id = np.argmax(scores)
  30.         conf = scores[class_id]
  31.         center_x = int(detect[0] * width)
  32.         center_y = int(detect[1] * height)
  33.         w = int(detect[2] * width)
  34.         h = int(detect[3] * height)
  35.         x = int(center_x - w/2)
  36.         y = int(center_y - h / 2)
  37.         boxes.append([x, y, w, h])
  38.         confs.append(float(conf))
  39.         class_ids.append(class_id)
  40.     
  41.     merge_boxes_ids = cv2.dnn.NMSBoxes(boxes, confs, threshold, nms_threshold)
  42.     
  43.     #将layers_result转换为bbox,conf和类
  44.     boxes = [boxes[int(i)] for i in merge_boxes_ids]
  45.     confs = [confs[int(i)] for i in merge_boxes_ids]
  46.     class_ids = [class_ids[int(i)] for i in merge_boxes_ids]
  47.     return boxes, confs, class_ids
  48. boxes, confs, class_ids = get_final_predictions(layers_result, img, 0.30.3)

性能比较

祝贺你,我们已经完成了所有的技术细节,你现在应该有足够的知识来推理每一个探测器。

现在让我们来讨论我们的主要目标——性能比较。

在PC cpu(英特尔i7第9代)上,分别针对上述每个型号(Yolov3,Tiny-Yolov3)分别测量了性能**。**

对于opencv和onnxruntime,我们只测量前向传播的执行时间,以便将其与前/后进程隔离开来。

概要分析:

  1. opencv

  1. layers_result = self.net.forward(_output_layers)
  1. Onnxruntime

  1. layers_result = session.run([output_name_1, output_name_2], {input_name: image_blob})
  2. layers_result = np.concatenate([layers_result[1], layers_result[0]], axis=1)
  1. Darknet

  1. darknet detector test cfg/coco.data cfg/yolov3.cfg yolov3.weights -thresh 0.25

判断

Yolov3

Yolov3在400张独特的图片上进行了测试。

  1. ONNX Detector是推断我们的Yolov3模型的最快方法。确切地说,它比opencv-dnn快43%,后者被认为是可用的最快的检测器之一。

  1. 每个图像的平均时间:

Tiny-Yolov3

Tiny-Yolov3在600张独特的图像上进行了测试。

  1. 在我们的Tiny-Yolov3模型上,ONNX探测器比opencv-dnn快33%。

  1. 每个图像的平均时间:

结论

  1. 我们已经看到 onnxruntime 比 opencvdnn 运行的速度要快得多。

  2. 即使Yolvo3更大,我们也可以用比Tiny-Yolov3更少的时间运行Yolov3。

  3. 我们拥有必要的工具,可以将在darknet中训练的模型转换为*.onnx格式。

☆ END ☆

如果看到这里,说明你喜欢这篇文章,请转发、点赞。微信搜索「uncle_pn」,欢迎添加小编微信「 mthler」,每日朋友圈更新一篇高质量博文。

扫描二维码添加小编↓

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/248954
推荐阅读
相关标签
  

闽ICP备14008679号