PaddlePaddle モデルから OpenVINO™ IR への変換

この Jupyter ノートブックはオンラインで起動でき、ブラウザーのウィンドウで対話型環境を開きます。ローカルにインストールすることもできます。次のオプションのいずれかを選択します。

Binder Google Colab GitHub

このノートブックでは、ImageNet データセットで事前トレーニングされた MobileNetV3 モデルを PaddleHub から OpenVINO IR に変換する方法を示します。また、OpenVINO ランタイムを使用してサンプル画像に対して分類推論を実行する方法と、PaddlePaddle モデルの結果を IR モデルと比較する方法も示します。

モデルのソース。

目次

準備

インポート

import platform

if platform.system() == "Windows":
    %pip install -q "paddlepaddle>=2.5.1,<2.6.0"
else:
    %pip install -q "paddlepaddle>=2.5.1"
%pip install -q paddleclas --no-deps
%pip install -q "prettytable" "ujson" "visualdl>=2.2.0" "faiss-cpu>=1.7.1"
# Install openvino package
%pip install -q "openvino>=2023.1.0"
Note: you may need to restart the kernel to use updated packages.
Note: you may need to restart the kernel to use updated packages.
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
paddleclas 2.5.1 requires easydict, which is not installed.
paddleclas 2.5.1 requires faiss-cpu==1.7.1.post2, but you have faiss-cpu 1.7.4 which is incompatible.
paddleclas 2.5.1 requires gast==0.3.3, but you have gast 0.4.0 which is incompatible.
Note: you may need to restart the kernel to use updated packages.
Note: you may need to restart the kernel to use updated packages.
if (platform.system() == "Linux"):
    !wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb
    !sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb
--2024-02-09 22:36:08--  http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb
Resolving proxy-mu.intel.com (proxy-mu.intel.com)... 10.217.247.236
Connecting to proxy-mu.intel.com (proxy-mu.intel.com)|10.217.247.236|:911... connected.
Proxy request sent, awaiting response...
404 Not Found
2024-02-09 22:36:08 ERROR 404: Not Found.
dpkg: error: cannot access archive 'libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb': No such file or directory
import time
import tarfile
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np
import openvino as ov
from paddleclas import PaddleClas
from PIL import Image

# Fetch `notebook_utils` module
import urllib.request
urllib.request.urlretrieve(
    url='https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/main/notebooks/utils/notebook_utils.py',
    filename='notebook_utils.py'
)

from notebook_utils import download_file
2024-02-09 22:36:10 INFO: Loading faiss with AVX2 support.
2024-02-09 22:36:10 INFO: Successfully loaded faiss with AVX2 support.

設定

IMAGE_FILENAME を、使用する画像のファイル名に設定します。MODEL_NAME を PaddleHub からダウンロードする PaddlePaddle モデルに設定します。MODEL_NAME は、IR モデルのベース名にもなります。ノートブックは MobileNetV3_large_x1_0 モデルでテストされています。他のモデルでは異なる前処理が使用される場合があるため、元のモデルと変換されたモデルで同じ結果を得るには、何らかの変更が必要になります。

まず、モデルファイルをダウンロードして解凍する必要があります。このノートブックを初めて実行すると、PaddlePaddle モデルが PaddleHub からダウンロードされます。これは時間がかかる場合があります。

# Download the image from the openvino_notebooks storage
img = download_file(
    "https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/image/coco_close.png",
    directory="data"
)

IMAGE_FILENAME = img.as_posix()

MODEL_NAME = "MobileNetV3_large_x1_0"
MODEL_DIR = Path("model")
if not MODEL_DIR.exists():
    MODEL_DIR.mkdir()
MODEL_URL = 'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/inference/{}_infer.tar'.format(MODEL_NAME)
download_file(MODEL_URL, directory=MODEL_DIR)
file = tarfile.open(MODEL_DIR / '{}_infer.tar'.format(MODEL_NAME))
res = file.extractall(MODEL_DIR)
if not res:
    print(f"Model Extracted to \"./{MODEL_DIR}\".")
else:
    print("Error Extracting the model. Please check the network.")
data/coco_close.png:   0%|          | 0.00/133k [00:00<?, ?B/s]
model/MobileNetV3_large_x1_0_infer.tar:   0%|          | 0.00/19.5M [00:00<?, ?B/s]
Model Extracted to "./model".

PaddlePaddle モデルの推論を表示

次のセルでは、モデルをロードし、画像をロードして表示し、その画像に対して推論を実行して、上位 3 つの予測結果を表示します。

classifier = PaddleClas(inference_model_dir=MODEL_DIR / '{}_infer'.format(MODEL_NAME))
result = next(classifier.predict(IMAGE_FILENAME))
class_names = result[0]['label_names']
scores = result[0]['scores']
image = Image.open(IMAGE_FILENAME)
plt.imshow(image)
for class_name, softmax_probability in zip(class_names, scores):
    print(f"{class_name}, {softmax_probability:.5f}")
[2024/02/09 22:36:38] ppcls WARNING: The current running environment does not support the use of GPU. CPU has been used instead.
Labrador retriever, 0.75138
German short-haired pointer, 0.02373
Great Dane, 0.01848
Rottweiler, 0.01435
flat-coated retriever, 0.01144
../_images/103-paddle-to-openvino-classification-with-output_8_2.png

classifier.predict() 画像ファイル名を取得し、画像を読み取り、入力を前処理してから、画像のクラスのラベルとスコアを返します。画像の前処理はバックグラウンドで行われます。分類モデルは、1000 個の ImageNet クラスごとに浮動小数点値を含む配列を返します。値が大きいほど、ネットワークは、その値に対応するクラス番号 (ネットワーク出力配列内のその値のインデックス) が画像のクラス番号であると確信できます。

PaddlePaddle の分類関数とデータのロードと前処理の実装を確認するには、次の 2 つのセルのコメントを解除します。

# classifier??
# classifier.get_config()

classifier.get_config() モジュールは、モデルの前処理構成を表示します。画像が正規化、サイズ変更、トリミングされていること、および BGR 画像がネットワークを介して伝播される前に RGB に変換されていることを示す必要があります。次のセルでは、同じメソッドを使用して OpenVINO IR モデルで推論を行う前処理操作のリストを返す classifier.predictror.preprocess_ops プロパティーを取得します。

preprocess_ops = classifier.predictor.preprocess_ops


def process_image(image):
    for op in preprocess_ops:
        image = op(image)
    return image

process_image() 関数の出力を表示して、トリミングとサイズ変更の効果を確認すると便利です。正規化により色が奇妙に見えるため、matplotlib は値のクリッピングについて警告します。

pil_image = Image.open(IMAGE_FILENAME)
processed_image = process_image(np.array(pil_image))
print(f"Processed image shape: {processed_image.shape}")
# Processed image is in (C,H,W) format, convert to (H,W,C) to show the image
plt.imshow(np.transpose(processed_image, (1, 2, 0)))
2024-02-09 22:36:39 WARNING: Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Processed image shape: (3, 224, 224)
<matplotlib.image.AxesImage at 0x7f9b4c389670>
../_images/103-paddle-to-openvino-classification-with-output_15_3.png

モデルによって予測されたラベルをクラス名にデコードするには、それらのマッピングが必要です。モデル設定には、マッピングを保存する class_id_map_file に関する情報が含まれています。下のコードは、OpenVINO モデルで使用する辞書へのマッピングを解析する方法を示しています。

class_id_map_file = classifier.get_config()['PostProcess']['Topk']['class_id_map_file']
class_id_map = {}
with open(class_id_map_file, "r") as fin:
    lines = fin.readlines()
    for line in lines:
        partition = line.split("\n")[0].partition(" ")
        class_id_map[int(partition[0])] = str(partition[-1])

モデルを OpenVINO IR 形式に変換

OpenVINO モデル変換 API を呼び出して、FP32 精度で PaddlePaddle モデルを OpenVINO IR に変換します。ov.convert_model 関数は、PaddlePaddle モデルへのパスを受け入れ、このモデルを表す OpenVINO Model クラスのインスタンスを返します。取得したモデルはすぐに使用でき、ov.compile_model を使用してデバイスにロードするか、ov.save_model 関数を使用してディスクに保存できます。モデル変換 API の詳細については、モデル変換ガイドを参照してください。

model_xml = Path(MODEL_NAME).with_suffix('.xml')
if not model_xml.exists():
    ov_model = ov.convert_model("model/MobileNetV3_large_x1_0_infer/inference.pdmodel")
    ov.save_model(ov_model, str(model_xml))
else:
    print(f"{model_xml} already exists.")

推論デバイスの選択

OpenVINO を使用して推論を実行するためにドロップダウン・リストからデバイスを選択します。

import ipywidgets as widgets

core = ov.Core()
device = widgets.Dropdown(
    options=core.available_devices + ["AUTO"],
    value='AUTO',
    description='Device:',
    disabled=False,
)

device
Dropdown(description='Device:', index=1, options=('CPU', 'AUTO'), value='AUTO')

OpenVINO モデルの推論を表示

IR モデルをロードし、モデル情報を取得し、画像をロードし、推論を実行し、推論を意味のある結果に変換して、出力を表示します。詳細については、OpenVINO ランタイム API ノートブックを参照してください。

# Load OpenVINO Runtime and OpenVINO IR model
core = ov.Core()
model = core.read_model(model_xml)
compiled_model = core.compile_model(model=model, device_name="CPU")

# Get model output
output_layer = compiled_model.output(0)

# Read, show, and preprocess input image
# See the "Show Inference on PaddlePaddle Model" section for source of process_image
image = Image.open(IMAGE_FILENAME)
plt.imshow(image)
input_image = process_image(np.array(image))[None,]

# Do inference
ov_result = compiled_model([input_image])[output_layer][0]

# find the top three values
top_indices = np.argsort(ov_result)[-3:][::-1]
top_scores = ov_result[top_indices]

# Convert the inference results to class names, using the same labels as the PaddlePaddle classifier
for index, softmax_probability in zip(top_indices, top_scores):
    print(f"{class_id_map[index]}, {softmax_probability:.5f}")
Labrador retriever, 0.74909
German short-haired pointer, 0.02368
Great Dane, 0.01873
../_images/103-paddle-to-openvino-classification-with-output_23_1.png

タイミングと比較

50 枚の画像の推論にかかる時間を測定し、結果を比較します。タイミング情報はパフォーマンスを示します。公平に比較するため、画像の処理にかかる時間を含めます。より正確なベンチマークを行うには、OpenVINO ベンチマーク・ツールを使用します。パフォーマンスを向上するには多くの最適化が可能であることに注意してください。

num_images = 50

image = Image.open(fp=IMAGE_FILENAME)
# Show device information
core = ov.Core()
devices = core.available_devices

for device_name in devices:
    device_full_name = core.get_property(device_name, "FULL_DEVICE_NAME")
    print(f"{device_name}: {device_full_name}")
CPU: Intel(R) Core(TM) i9-10920X CPU @ 3.50GHz
# Show inference speed on PaddlePaddle model
start = time.perf_counter()
for _ in range(num_images):
    result = next(classifier.predict(np.array(image)))
end = time.perf_counter()
time_ir = end - start
print(
    f"PaddlePaddle model on CPU: {time_ir/num_images:.4f} "
    f"seconds per image, FPS: {num_images/time_ir:.2f}\n"
)
print("PaddlePaddle result:")
class_names = result[0]['label_names']
scores = result[0]['scores']
for class_name, softmax_probability in zip(class_names, scores):
    print(f"{class_name}, {softmax_probability:.5f}")
plt.imshow(image);
PaddlePaddle model on CPU: 0.0075 seconds per image, FPS: 133.16

PaddlePaddle result:
Labrador retriever, 0.75138
German short-haired pointer, 0.02373
Great Dane, 0.01848
Rottweiler, 0.01435
flat-coated retriever, 0.01144
../_images/103-paddle-to-openvino-classification-with-output_27_1.png

推論デバイスの選択

OpenVINO を使用して推論を実行するためにドロップダウン・リストからデバイスを選択します。

device
Dropdown(description='Device:', index=1, options=('CPU', 'AUTO'), value='AUTO')
# Show inference speed on OpenVINO IR model
compiled_model = core.compile_model(model=model, device_name=device.value)
output_layer = compiled_model.output(0)


start = time.perf_counter()
input_image = process_image(np.array(image))[None,]
for _ in range(num_images):
    ie_result = compiled_model([input_image])[output_layer][0]
    top_indices = np.argsort(ie_result)[-5:][::-1]
    top_softmax = ie_result[top_indices]

end = time.perf_counter()
time_ir = end - start

print(
    f"OpenVINO IR model in OpenVINO Runtime ({device.value}): {time_ir/num_images:.4f} "
    f"seconds per image, FPS: {num_images/time_ir:.2f}"
)
print()
print("OpenVINO result:")
for index, softmax_probability in zip(top_indices, top_softmax):
    print(f"{class_id_map[index]}, {softmax_probability:.5f}")
plt.imshow(image);
OpenVINO IR model in OpenVINO Runtime (AUTO): 0.0030 seconds per image, FPS: 328.87

OpenVINO result:
Labrador retriever, 0.74909
German short-haired pointer, 0.02368
Great Dane, 0.01873
Rottweiler, 0.01448
flat-coated retriever, 0.01153
../_images/103-paddle-to-openvino-classification-with-output_30_1.png