如今,許多基於神經網路的突破性解決方案每天都在開發,越來越多的人採用這項技術來解決諸如語音識別等生活中的問題。由於計算的最新進展以及在生產環境中使用神經網路的趨勢不斷增長,因此人們非常重視在大型生產環境中執行此類解決方案,理想情況下,還要在“邊緣”即時執行。
假設您有一個在 PyTorch 中編寫的經過良好訓練的神經網路。您能做些什麼來加速 CPU、VPU、整合顯示卡、FPGA 或其組合上的推理步驟?幸運的是,無需對任何原始碼進行重大重新架構和重寫,現在就可以輕鬆地使用英特爾 OpenVINO 工具包提供的推理引擎來加快推理步驟的效能。在許多情況下,您可以獲得可觀的效能提升,而無需大幅犧牲推理精度。此外,模型轉換過程也很簡單快捷。劇透警告:在我們的示例中,我們已經將推理步驟加速了大約 2.2 倍!
有關英特爾軟體產品中效能和最佳化選擇的更多資訊,請參閱 https://software.intel.com/articles/optimization-notice。
那麼這個 OpenVINO 工具包是什麼呢?
開放式視覺推理和神經網路最佳化 (OpenVINO) 工具包 是一套庫、最佳化工具和資訊資源,可以促進計算機視覺和深度學習軟體的開發。當您想要最大限度地提高效能時,可以依靠它
或為英特爾平臺最佳化您的應用程式。那麼 OpenVINO 工具包內部有什麼呢?
- 模型最佳化器 - 一個命令列工具,用於使用靜態模型分析調整網路以實現最佳執行。模型最佳化器的結果是 中間表示 (IR),它可以與推理引擎一起啟動。
- 推理引擎 - 一個 API,用於讀取 IR、設定輸入和輸出,並在選定的裝置上進行推理。
- OpenCV、OpenCL、OpenVX 和英特爾媒體 SDK。
您可以在此處找到更多資訊:https://docs.openvinotoolkit.org/。
概述
為了演示它的工作原理,我們將選擇一個有趣且相關的模型,並執行以下步驟
- 準備環境。
- 使用 PyTorch 訓練模型(或使用預訓練模型)
- 將 PyTorch 模型轉換為 ONNX 格式。
- 使用 模型最佳化器,將模型從 ONNX 轉換為 中間表示 (IR) 格式。
- 使用 推理引擎 進行推理,並比較效能和結果。
本文的所有原始碼都可以在 GitHub 上找到。
1. 準備環境
- 安裝 Python 3.6 或 3.7 並執行
python3 -m pip install -r requirements.txt
requirements.txt 內容
torch numpy onnx networkx
- 使用官方 說明 安裝 OpenVINO 工具包版本 2020.1 或更高版本。
程式碼已針對指定版本進行測試。但如果您已安裝某些元件,也可以嘗試在其他版本上執行它。
2. 在 PyTorch 中執行推理
我們選擇了 FaceMesh 模型來進行我們的實驗。我們將利用一個現成且經過訓練的 實現。
該模型需要裁剪後的面部作為輸入,因此我們還需要一個面部檢測器。好訊息是,英特爾已經將面部檢測器轉換為 IR 並將其放入 Open Model Zoo 中。Open Model Zoo 包含許多模型,包括文字檢測和識別、姿態估計、分割、人員識別等。所有這些模型都已針對推理引擎進行了最佳化,可以開箱即用。
為了進行公平比較,我們將使用在 PyTorch 中的面部檢測模型重新建立管道的前處理步驟,我們將將其轉換為 OpenVINO。
對於此任務,我們將使用在此處實現的 BlazeFace 模型 here。作為第一步,讓我們建立並執行一個推理指令碼
import cv2
from facemesh import FaceMesh
from blazeface import BlazeFace
# load FaceMesh model
mesh_net = FaceMesh()
mesh_net.load_weights("facemesh.pth")
# load BlazeFace model
blaze_net = BlazeFace()
blaze_net.load_weights("blazeface.pth")
blaze_net.load_anchors("anchors.npy")
# postprocessing for face detector
def get_crop_face(detections, image):
w, h = image.shape[0], image.shape[1]
ymin = int(detections[0, 0] * w)
xmin = int(detections[0, 1] * h)
ymax = int(detections[0, 2] * w)
xmax = int(detections[0, 3] * h)
margin_x = int(0.25 * (xmax - xmin))
margin_y = int(0.25 * (ymax - ymin))
ymin -= margin_y
ymax += margin_y
xmin -= margin_x
xmax += margin_x
face_img = image[ymin:ymax, xmin:xmax]
face_img = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB)
image = cv2.rectangle(image, (xmin, ymin), (xmax, ymax), (0, 0, 255), 2)
return xmin, ymin, face_img
# postprocessing for mesh
def get_mesh_face(detections, face_img, image, xmin, ymin):
xscale, yscale = 192 / face_img.shape[1], 192 / face_img.shape[0]
for i in range(detections.shape[0]):
x, y = int(detections[i, 0] / xscale), int(detections[i, 1] / yscale)
image = cv2.circle(image, (xmin + x, ymin + y), 1, (255, 0, 0), 1)
videoCapture = cv2.VideoCapture(0)
while True:
ret, image = videoCapture.read()
if not ret:
break
# preprocess image
face_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
face_img = cv2.resize(face_img, (128, 128))
# get face detection boxes
detections = blaze_net.predict_on_image(face_img).numpy()
xmin, ymin, face_img = get_crop_face(detections, image)
# get face mesh
mesh_img = cv2.resize(face_img, (192, 192))
detections = mesh_net.predict_on_image(mesh_img).numpy()
get_mesh_face(detections, face_img, image, xmin, ymin)
# show processed image
cv2.imshow("capture", image)
if cv2.waitKey(3) & 0xFF == 27:
break
結果看起來不錯!你覺得怎麼樣?

使用 PyTorch 的 FaceMesh 結果。請注意,網格繪製在從場景中識別出的面部上,並且特徵已對齊。
現在讓我們測量效能。我們得到 FaceMesh 的 5.3 毫秒和 BlazeFace 的 8.1 毫秒。
我們只測量和比較推理時間。
測量是在以下環境中進行的:Ubuntu 18.04.3,英特爾酷睿 i7-8700 CPU @ 3.20GHz。
3. 將 PyTorch 模型轉換為 ONNX 格式
ONNX(開放式神經網路交換)是一種用於表示來自不同框架的模型的開放格式。
要轉換 PyTorch 模型,您需要 torch.onnx.export 函式,該函式需要以下引數:預訓練模型本身、大小與輸入資料相同的張量、ONNX 檔案的名稱以及輸入和輸出名稱。
net = BlazeFace()
net.load_weights("blazeface.pth")
torch.onnx.export(net, torch.randn(1, 3, 128, 128, device='cpu'), "blazeface.onnx",
input_names=("image", ), output_names=("preds", "confs"), opset_version=9
)
net = FaceMesh()
net.load_weights("facemesh.pth")
torch.onnx.export(net, torch.randn(1, 3, 192, 192, device='cpu'), "facemesh.onnx",
input_names=("image", ), output_names=("preds", "confs"), opset_version=9
)
要檢查模型是否已成功轉換,我們可以呼叫 onnx.checker.check_model 函式,如下所示
onnx_model = onnx.load(ONNX_FILE_PATH) onnx.checker.check_model(onnx_model)
您可以在 此處 找到支援的操作列表,並且它不斷擴充套件。
4. 將模型從 ONNX 轉換為 **中間表示**
現在是啟動 模型最佳化器 的時候了。在我們的案例中,我們將從 ONNX 轉換模型,但這並不是唯一支援的格式。
如果您有 Caffe、TensorFlow、MXNet 或 Kald 中的模型,也可以使用它們。
在開始使用 OpenVino 工具包之前,您應該始終透過以下命令啟用 OpenVINO 環境
source <path_to_openvino>/bin/setupvars.sh
如果一切順利,您將看到
[setupvars.sh] OpenVINO environment initialized
執行轉換
python3 <path_to_openvino>/deployment_tools/model_optimizer/mo.py --input_model [facemesh or blazeface].onnx
結果是,您將獲得兩個檔案,它們描述了您模型的最佳化版本
– *.xml – 包含有關網路拓撲的資訊
– *.bin – 包含權重
不幸的是,並非所有 ONNX 層都受支援。可以在 此處 找到受支援層的列表。
5. 使用 推理引擎 執行推理
要使用 OpenVino 執行推理,我們必須在 IR 中初始化並載入網路,準備輸入資料並呼叫 infer 函式。
然後我們可以獲取對結果的解釋。因此,最小指令碼將如下所示
from openvino.inference_engine import IECore
from openvino.inference_engine import IENetwork
ie = IECore()
net = IENetwork(model=model_path_xml, weights=model_path_bin)
exec_net = ie.load_network(network=net, device_name="CPU")
input_blob = next(iter(net.inputs))
result = exec_net.infer({input_blob: preprocessed_image})
在我們的特定示例中,我們應該看看如何使用 PyTorch 對 FaceMesh 和 BlazeFace 進行預處理,並使用 OpenCV 函式 cv2.dnn.blobFromImage 重複此過程。我們還需要重現後處理。對於 FaceMesh,這非常容易(參見下面的程式碼),但對於 BlazeFace,我們必須重新實現諸如 IOU、NMS 等操作。為此,我們可以使用 blazeface.py 指令碼,並將使用 PyTorch 張量的操作替換為使用 Numpy 陣列的對應操作。
最終指令碼將如下所示
import cv2
import numpy as np
from openvino.inference_engine import IECore
from openvino.inference_engine import IENetwork
from blazeface import BlazeFace
def load_to_IE(model):
# Loading the Inference Engine API
ie = IECore()
# Loading IR files
net = IENetwork(model=model + ".xml", weights=model + ".bin")
# Loading the network to the inference engine
exec_net = ie.load_network(network=net, device_name="CPU")
return exec_net
def do_inference(exec_net, image):
input_blob = next(iter(exec_net.inputs))
return exec_net.infer({input_blob: image})
# load BlazeFace model
blaze_net = load_to_IE("model/blazeface")
# load FaceMesh model
mesh_net = load_to_IE("model/facemesh")
# we need dynamically generated key for fetching output tensor
blaze_outputs = list(blaze_net.outputs.keys())
mesh_outputs = list(mesh_net.outputs.keys())
# to reuse postprocessing from BlazeFace
blazenet = BlazeFace()
blazenet.load_anchors("anchors.npy")
videoCapture = cv2.VideoCapture(0)
while True:
ret, image = videoCapture.read()
if not ret:
break
# get face detection boxes------------------------------------------------------------------
# preprocessing
face_img = cv2.dnn.blobFromImage(image, 1./127.5, (128, 128), (1, 1, 1), True)
# inference
output = do_inference(blaze_net, image=face_img)
# postprocessing
boxes = output[blaze_outputs[0]]
confidences = output[blaze_outputs[1]]
detections = blazenet._tensors_to_detections(boxes, confidences, blazenet.anchors)
detections = np.squeeze(detections, axis=0)
# take boxes
xmin, ymin, face_img = get_crop_face(detections, image)
# get face mesh ----------------------------------------------------------------------------
# preprocessing
mesh_img = cv2.dnn.blobFromImage(face_img, 1./127.5, (192, 192), (1, 1, 1), True)
#inference
output = do_inference(mesh_net, image=mesh_img)
# postprocessing
detections = output[mesh_outputs[1]].reshape(-1, 3)
# take mesh
get_mesh_face(detections, face_img, image, xmin, ymin)
# show processed image
cv2.imshow("capture", image)
if cv2.waitKey(3) & 0xFF == 27:
break

對於希望充分利用英特爾 OpenVINO™ 工具包的效能和功能的使用者,建議遵循使用模型最佳化器中的中間表示作為推理引擎輸入的本機工作流程。
對於希望快速開始使用已以 ONNX 格式訓練的模型(例如,PyTorch)的使用者,現在可以直接將該 ONNX 模型輸入到推理引擎中,以在英特爾架構上執行模型。
讓我們檢查結果,並確保它們與之前在 PyTorch 中獲得的結果相匹配。

比較效能
讓我們測量效能。我們得到 FaceMesh 的 2.6 毫秒和 BlazeFace 的 3.5 毫秒。
總的來說,我們的設定獲得了大約 2.2 倍的加速。
有關英特爾軟體產品中效能和最佳化選擇的更多資訊,請參閱 https://software.intel.com/articles/optimization-notice。

總結和最終思考
今天,我們研究瞭如何將模型轉換為 IR,以及哪些模型可以轉換。我們在沒有明顯降低預測質量的情況下,實現了有效的(~2.2)加速。最重要的是,我們在使用相同的英特爾硬體時,無需對我們的解決方案進行重大重寫或重新設計,就獲得了這些結果。基本上,我們毫不費力地獲得了“加速”!
有關英特爾軟體產品中效能和最佳化選擇的更多資訊,請參閱 https://software.intel.com/articles/optimization-notice。
測試日期:2020 年 9 月 21 日
完整系統配置詳細資訊:Ubuntu 18.04.3,英特爾酷睿 i7-8700 CPU @ 3.20GHz × 12
設定詳細資訊:OpenVINO 工具包版本 2020.1
測試人員:Julia Bareeva,OpenCV.AI
貢獻 - 如果您對如何改進產品有任何想法,我們歡迎您為開源的 OpenVINO™ 工具包 做出貢獻。
想了解更多資訊?加入 英特爾社群論壇中的對話,討論所有關於深度學習和 OpenVINO™ 工具包的事宜。
英特爾致力於尊重人權並避免參與侵犯人權的行為。請參閱英特爾的 全球人權原則。英特爾的產品和軟體僅供在不造成或促成違反國際公認人權行為的應用程式中使用。
英特爾、英特爾標識和其他英特爾標誌是英特爾公司或其子公司的商標。
OpenCV 內容合作伙伴關係
本文由 OpenCV 團隊成員為英特爾撰寫,作為 OpenCV 內容合作伙伴計劃的一部分。該計劃允許公司贊助與 OpenCV 使用者相關的文章。這些文章將與我們的時事通訊訂閱者和我們的社交媒體訂閱者共享。






