一. 概述
經由上幾篇文章介紹了 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】系列 !!
評論