一. 概述
经由上几篇文章介绍了 ONNX 的模组转换方法后,本篇章将说明如何对 ONNX模组(.onnx) 进行优化? 如何利用 ONNX 模组(.onnx) 进行推理 ? ONNX 效能如何? 后续将一步步带领各位在 Windows + Colab 环境上运行 !! 如下图所示,为系列博文之示意架构图。此架构图隶属于 i.MX8M Plus 的方案博文中,并属于机器学习内的推理引擎(Inference Engine) 的 ONNX 部分,目前章节介绍 “ONNX 进行优化与推理”
若欲理解与架设 Tensorflow 框架,请参考另一个博文系列
大大通精彩博文 AI 即将来临 !! 利用 Tensorflow 与 i.MX8 迈入新领域
大大通精彩博文 【ATU Book-i.MX8系列】博文索引
ONNX 系列博文-文章架构示意图 (1)
ONNX 系列博文-文章架构示意图 (2)
二. ONNX Runtime 使用方式
这里整理上述的资讯,可以将ONNX 使用方式分为三大步骤…
第一步 : 取得 ONNX 模型。
第二步 : 优化 ONNX 模型。
第三步 : 执行 ONNX Runtime 推理引擎
第一步 : 取得 ONNX 模型
可直接下载官方的标准模型或是利用其他框架转换而得。
第二步 : ONNX 优化模型
可利用 ONNX 模型优化 来获得更好的推理结果
载入模型 :
import onnx
import onnxoptimizer
onnx_model = onnx.load("faster_rcnn_R_50_FPN_1x.onnx")
onnx.checker.check_model(onnx_model)
进行优化(基础) :
optimized_model = optimizer.optimize(onnx_model)
进行优化(进阶) :
# Pick one pass as example
passes = ['fuse_consecutive_transposes']
# Apply the optimization on the original model
optimized_model = optimizer.optimize(original_model, passes)
依官方文件指出有提供数种方式来进行优化,分别为
--> eliminate_deadend(消除无作用的节点)
--> eliminate_identity(消除理想值的节点)
--> eliminate_nop_transpose(消除无操作转置的节点)
--> eliminate_nop_pad(消除无操作补值的节点)
--> fuse_consecutive_transposes(断开连续转置的节点)
--> fuse_pad_into_transpose(断开补值至转置的节点)
第三步 : 执行 ONNX Runtime 推理引擎
ONNX Rutime 推理引擎能够支援大部分的神经网路架构,目标以最小的计算延迟和资源占用进行推理,适用于各硬体平台上。如下图所示,此推理引擎能够活用各硬体资源,像是 CPU、GPU、NPU、FPGA 皆能提供最佳化演算。
ONNX 合作企业,如下图所示。故有支援常见的 Nvidia GPU 加速器、 Intel AI 芯片、Cadence DNA 处理器等等…
顺带一提,NXP 于 2018 年 11月发布支援 ONNX 推理界面,能够运行在 S32V、i.MX8 系列的平台之中。如下图所示,为 i.MX8 的 eIQ 机器学习开发环境的架构,其中所提供的开源资料库就含有 ONNX 。
ONNX Runtime 进行推理方式 :
ONNX Runtime 推理引擎的使用方式是非常简单的 !! 仅须要引用相应的资料库并指下以下代码即可…
$import onnxruntime
$onnxruntime.InferenceSession( *.onnx )
三. ONNX Object Detection
为了加深读者印象,下列直接以一个范例来实现 ONNX Runtime 的物件识别范例之演示 !! 读者们不访随著进行演练以下代码,或是直接运行 Colab 代码 !!
安装必要套件 :
!pip install onnx==1.5.0
!pip install onnxruntime==0.4.0
!pip install numpy==1.15.1
!pip install onnxoptimizer
运行范例 :
import onnx
import onnxruntime
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import urllib.request # urllib is a built-in Python library to download files from URLs
# ---------------------------------------------------------------------------------------
# 下载官方模组、标签、图片
# ---------------------------------------------------------------------------------------
onnx_model_url = "https://github.com/onnx/models/blob/master/vision/object_detection_segmentation/faster-rcnn/model/FasterRCNN-10.onnx"
output_classes_url = "https://github.com/onnx/models/blob/master/vision/object_detection_segmentation/faster-rcnn/dependencies/coco_classes.txt"
test_image_url = "https://github.com/onnx/models/raw/master/vision/object_detection_segmentation/faster-rcnn/dependencies/demo.jpg"
urllib.request.urlretrieve(onnx_model_url, filename="faster_rcnn_R_50_FPN_1x.onnx")
urllib.request.urlretrieve(output_classes_url, filename="coco_classes.txt")
urllib.request.urlretrieve(test_image_url, filename="frcnn_demo.jpg")
# ---------------------------------------------------------------------------------------
# API
# ---------------------------------------------------------------------------------------
# Image Preprocess 影像预处理
def preprocess(image):
# Resize
ratio = 800.0 / min(image.size[0], image.size[1])
image = image.resize((int(ratio * image.size[0]), int(ratio * image.size[1])), Image.BILINEAR)
# Convert to BGR
image = np.array(image)[:, :, [2, 1, 0]].astype('float32')
# HWC -> CHW
image = np.transpose(image, [2, 0, 1])
# Normalize
mean_vec = np.array([102.9801, 115.9465, 122.7717])
for i in range(image.shape[0]):
image[i, :, :] = image[i, :, :] - mean_vec[i]
# Pad to be divisible of 32
import math
padded_h = int(math.ceil(image.shape[1] / 32) * 32)
padded_w = int(math.ceil(image.shape[2] / 32) * 32)
padded_image = np.zeros((3, padded_h, padded_w), dtype=np.float32)
padded_image[:, :image.shape[1], :image.shape[2]] = image
image = padded_image
return image
# Show Result 结果展示
def display_objdetect_image(image, boxes, labels, scores, score_threshold=0.7):
# Resize boxes
ratio = 800.0 / min(image.size[0], image.size[1])
boxes = boxes / ratio
_, ax = plt.subplots(1, figsize=(12,9))
ax.imshow(image)
# Showing boxes with score > 0.7
for box, label, score in zip(boxes, labels, scores):
if score > score_threshold:
rect = patches.Rectangle((box[0], box[1]), box[2] - box[0], box[3] - box[1],\
linewidth=1, edgecolor='b',facecolor='none')
ax.annotate(classes[label] + ':' + str(np.round(score, 2)), (box[0], box[1]),\
color='w', fontsize=12)
ax.add_patch(rect)
plt.show()
# ---------------------------------------------------------------------------------------
# MAIN
# ---------------------------------------------------------------------------------------
import time
start = time.time()
#载入标签
classes = [line.rstrip('\n') for line in open('coco_classes.txt')]
#使用 onnxruntime 载入模组
session = onnxruntime.InferenceSession('faster_rcnn_R_50_FPN_1x.onnx')
#载入影像
img = Image.open('frcnn_demo.jpg')
img_data = preprocess(img)
#开始预测
boxes, labels, scores = session.run(None, {session.get_inputs()[0].name: img_data});end = time.time()
print(end-start)
print('Boxes:', boxes.shape);print('Labels:', labels.shape);print('Scores:', scores.shape)
#显示结果 & 标示
display_objdetect_image(img, boxes, labels, scores)
运行结果 :
目前以 Google Colab 进行实际测试的硬体配置,如下图所示:
在未优化模型的情况下,进行运行则花费时间约 11.8 ms 。
在已优化模型的情况下,进行运行则花费时间约 7.88 ms 。
四. 结语
经由上述的介绍,相信各位已经完成一支 ONNX Runtime 物件侦测的程式 !! 并大致上理解 ONNX 是如何优化,是如何进行推理,并也得知目前官方所提供的优化几种方式。这里利用 CPU 进行测试,实际运行 ONNX 物件侦测范例程式大约花费 7.88 ms 是个不算太差的运行速度 !! 因此由 ONNX 的优化与推理是有期望可以得到更好的成果 !! 本系列的最后,会将此范例移至 NXP i.MX8 的平台上实现 !! 敬请期待 !!
五. 参考文件
[1] 官方网站 - ONNX
[2] 官方网站 - ONNX Tutorials
[3] 官方网站 – Netron
[4] 官方网站 – VisualDL
[5] 参考文献 - VisualDL 工具简介
[6] 参考文献 - Object Detection with ONNX Runtime: Faster RCNN
如有任何相关 ONNX 技术问题,欢迎至博文底下留言提问 !!
接下来还会分享更多 ONNX 的技术文章 !!敬请期待 【ATU Book-i.MX8 系列 - ONNX】系列 !!
评论