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

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

Google ColabGitHub

このチュートリアルでは、OpenVINO ランタイムを使用して PyTorch 分類モデルで推論を行う方法を段階的に説明します。OpenVINO 2023.0 リリース以降、OpenVINO は ONNX 形式に変換する中間ステップを必要とせず、直接 PyTorch モデルの変換をサポートします。OpenVINO の以前のバージョンを使用する場合、または ONNX の使用する場合は、このチュートリアルを確認してください。

このチュートリアルでは、torchvisionRegNetY_800MF モデルを使用して、PyTorch モデルを OpenVINO 中間表現に変換する方法を説明します。

RegNet モデルは、Ilija Radosavovic、Raj Prateek Kosaraju、Ross Girshick、Kaiming He、Piotr Dollár による Designing Network Design Spaces で提案されました。著者らは、ニューラル・アーキテクチャー検索 (NAS) を実行する検索空間を設計しています。まず高次元の検索空間から開始し、現在の検索空間でサンプリングされた最もパフォーマンスの高いモデルに基づいて制約を実験的に適用することで、検索空間を反復的に縮小します。個々のネットワーク・インスタンスの設計に重点を置くのではなく、ネットワークの集団をパラメーター化するネットワーク設計空間を設計します。全体のプロセスは、従来のネットワークの手動設計に似ていますが、設計空間レベルにまで高められています。RegNet 設計空間は、さまざまなフロップレジームにわたって適切に機能するシンプルで高速なネットワークを提供します。

目次:

必要条件#

ノートブックの依存関係をインストールします

%pip install -q "openvino>=2023.1.0" scipy Pillow torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu
Note: you may need to restart the kernel to use updated packages.

入力データとラベルマップをダウンロードします

import requests 
from pathlib import Path 
from PIL import Image 

MODEL_DIR = Path("model") 
DATA_DIR = Path("data") 

MODEL_DIR.mkdir(exist_ok=True) 
DATA_DIR.mkdir(exist_ok=True) 
MODEL_NAME = "regnet_y_800mf" 

image = Image.open(requests.get("https://farm9.staticflickr.com/8225/8511402100_fea15da1c5_z.jpg", stream=True).raw) 

labels_file = DATA_DIR / "imagenet_2012.txt" 

if not labels_file.exists(): 
    resp = 
requests.get("https://raw.githubusercontent.com/openvinotoolkit/open_model_zoo/master/data/dataset_classes/imagenet_2012.txt") 
    with labels_file.open("wb") as f: 
        f.write(resp.content) 

imagenet_classes = labels_file.open("r").read().splitlines()

PyTorch モデルのロード#

一般に、PyTorch モデルは、モデルの重みを含む状態辞書によって初期化された torch.nn.Module クラスのインスタンスを表します。事前トレーニングされたモデルを取得する一般的な手順:

  1. クラスのインスタンスを作成します。

  2. 事前トレーニングされたモデルの重みを含むチェックポイント状態辞書をロードします

  3. 一部の操作を推論モードに切り替えるためモデルを評価に切り替えます

torchvision モジュールは、モデルクラスの初期化に使用できる関数セットを提供します。torchvision.models.regnet_y_800mf を使用します。重み列挙型 RegNet_Y_800MF_Weights.DEFAULT を使用して、事前トレーニングされたモデルの重みをモデル初期化関数に直接渡すことができます。

import torchvision 

# モデルの利用可能な重み列挙タイプを使用してデフォルトの重みを取得 
weights = torchvision.models.RegNet_Y_800MF_Weights.DEFAULT 

# モデルトポロジーを作成し、重みをロード 
model = torchvision.models.regnet_y_800mf(weights=weights) 

# モデルを推論モードに切り替え 
model.eval();

入力データを準備#

以下のコードは、torchvision のモデル固有の変換モジュールを使用して入力データを前処理する方法を示しています。変換後、画像をバッチ化されたテンソルに連結する必要がありますが、この場合、バッチ 1 でモデルを実行するため、最初の次元の入力を圧縮解除するだけです。

import torch 

# 重み変換を初期化 
preprocess = weights.transforms() 

# 入力画像に適用 
img_transformed = preprocess(image) 

# 画像テンソルにバッチ次元を追加 
input_tensor = img_transformed.unsqueeze(0)

PyTorch モデル推論を実行#

モデルは生のロジット形式で確率のベクトルを返します。softmax を適用して [0, 1] の範囲で正規化された値を取得できます。元のモデルと変換された OpenVINO の出力が同じであることを示すため、後で再利用できる共通の後処理関数を定義しました。

import numpy as np 
from scipy.special import softmax 

# 入力テンソルに対してモデル推論を実行 
result = model(input_tensor) 

# PyTorch モデル推論と OpenVINO の両方で同じ方法で結果を取得する後処理関数 
def postprocess_result(output_tensor: np.ndarray, top_k: int = 5): 
    """ 
    Posprocess model results.This function applied sofrmax on output tensor and returns specified top_k number of labels with highest probability 
    Parameters: 
        output_tensor (np.ndarray): model output tensor with probabilities 
        top_k (int, *optional*, default 5): number of labels with highest probability for return 
    Returns: 
        topk_labels: label ids for selected top_k scores 
        topk_scores: selected top_k highest scores predicted by model 
    """ 
    softmaxed_scores = softmax(output_tensor, -1)[0] 
    topk_labels = np.argsort(softmaxed_scores)[-top_k:][::-1] 
    topk_scores = softmaxed_scores[topk_labels] 
    return topk_labels, topk_scores 

# 後処理の結果 
top_labels, top_scores = postprocess_result(result.detach().numpy()) 

# 結果を表示 
display(image) 
for idx, (label, score) in enumerate(zip(top_labels, top_scores)):
    _, predicted_label = imagenet_classes[label].split(" ", 1) 
    print(f"{idx + 1}: {predicted_label} - {score * 100 :.2f}%")
../_images/pytorch-to-openvino-with-output_11_0.png
1: tiger cat - 25.91% 
2: Egyptian cat - 10.26% 
3: computer keyboard, keypad - 9.22% 
4: tabby, tabby cat - 9.09% 
5: hamper - 2.35%

PyTorch モデル推論のベンチマーク#

%%timeit 

# モデル推論を実行 
model(input_tensor)
16.4 ms ± 702 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

PyTorch モデルを OpenVINO 中間表現に変換#

2023.0 リリース以降、OpenVINO は PyTorch モデルを OpenVINO 中間表現 (IR) 形式に直接変換できるようになりました。これには、OpenVINO モデル・トランスフォーメーション API を使用する必要があります。PyTorch モデル変換の詳細については、OpenVINO のドキュメントを参照してください。

convert_model 関数は PyTorch モデル・オブジェクトを受け入れ、core.compile_model を使用してデバイスにロードする準備が整った openvino.Model インスタンスを返すか、ov.save_model によって次回使用するためにディスクに保存します。オプションで、次のような追加パラメーターを指定することもできます:

  • compress_to_fp16 - モデルの重みを FP16 データ形式に圧縮するフラグ。これにより、ディスク上のモデル保存に必要なスペースが削減され、FP16 計算がサポートされる推論デバイスの速度が向上する可能性があります。

  • example_input - モデルトレースに使用できる入力データサンプル。

  • input_shape - 変換のための入力テンソルの形状

そして、モデル変換 Python API でサポートされるその他の高度なオプション。詳細はこちらのページをご覧ください

import openvino as ov 

# OpenVINO Core オブジェクト・インスタンスを作成 
core = ov.Core() 

# モデルを openvino.runtime.Model オブジェクトに変換 
ov_model = ov.convert_model(model) 

# openvino.runtime.Model オブジェクトをディスクに保存 
ov.save_model(ov_model, MODEL_DIR / f"{MODEL_NAME}_dynamic.xml") 

ov_model
<Model: 'Model30' 
inputs[ 
<ConstOutput: names[x] shape[?,3,?,?] type: f32> 
] 
outputs[ 
<ConstOutput: names[x.21] shape[?,1000] type: f32> 
]>

推論デバイスの選択#

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

import ipywidgets as widgets 

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 モデルをロード 
compiled_model = core.compile_model(ov_model, device.value) 
compiled_model
<CompiledModel: 
inputs[ 
<ConstOutput: names[x] shape[?,3,?,?] type: f32> 
] 
outputs[ 
<ConstOutput: names[x.21] shape[?,1000] type: f32> 
]>

OpenVINO モデル推論を実行#

# モデル推論を実行 
result = compiled_model(input_tensor)[0] 

# 処理結果 
top_labels, top_scores = postprocess_result(result) 

# 結果を表示 
display(image) 
for idx, (label, score) in enumerate(zip(top_labels, top_scores)):
    _, predicted_label = imagenet_classes[label].split(" ", 1) 
    print(f"{idx + 1}: {predicted_label} - {score * 100 :.2f}%")
../_images/pytorch-to-openvino-with-output_20_0.png
1: tiger cat - 25.91% 
2: Egyptian cat - 10.26% 
3: computer keyboard, keypad - 9.22% 
4: tabby, tabby cat - 9.09% 
5: hamper - 2.35%

OpenVINO モデル推論のベンチマーク#

%%timeit 

compiled_model(input_tensor)
3.2 ms ± 7.83 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

静的入力形状を使用して PyTorch モデルを変換#

デフォルトの変換パスでは動的な入力形状が保持されるため、静的形状のモデルを変換する場合は、変換中に input_shape パラメーターを使用して明示的に指定するか、変換後にモデルを目的の形状に再形成します。モデルの再形成の例については、このチュートリアルを確認してください。

# モデルを openvino.runtime.Model オブジェクトに変換 
ov_model = ov.convert_model(model, input=[[1, 3, 224, 224]]) 
# openvino.runtime.Model オブジェクトをディスクに保存 
ov.save_model(ov_model, MODEL_DIR / f"{MODEL_NAME}_static.xml") 
ov_model
<Model: 'Model65' 
inputs[ 
<ConstOutput: names[x] shape[1,3,224,224] type: f32> 
] 
outputs[ 
<ConstOutput: names[x.21] shape[1,1000] type: f32> 
]>

推論デバイスの選択#

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

device
Dropdown(description='Device:', index=1, options=('CPU', 'AUTO'), value='AUTO')
# デバイスに OpenVINO モデルをロード 
compiled_model = core.compile_model(ov_model, device.value) 
compiled_model
<CompiledModel: 
inputs[ 
<ConstOutput: names[x] shape[1,3,224,224] type: f32> 
] 
outputs[ 
<ConstOutput: names[x.21] shape[1,1000] type: f32> 
]>

ここで、変換されたモデルの入力は、以前変換されたモデルによって報告された [?, 3, ?, ?] ではなく、 [1, 3, 224, 224] 形状のテンソルであることがわかります。

静的な入力形状を使用して OpenVINO モデル推論を実行#

# モデル推論を実行 
result = compiled_model(input_tensor)[0] 

# 処理結果 
top_labels, top_scores = postprocess_result(result) 

# 結果を表示 
display(image) 
for idx, (label, score) in enumerate(zip(top_labels, top_scores)): 
    _, predicted_label = imagenet_classes[label].split(" ", 1) 
    print(f"{idx + 1}: {predicted_label} - {score * 100 :.2f}%")
../_images/pytorch-to-openvino-with-output_31_0.png
1: tiger cat - 25.91% 
2: Egyptian cat - 10.26% 
3: computer keyboard, keypad - 9.22% 
4: tabby, tabby cat - 9.09% 
5: hamper - 2.35%

静的入力形状を使用した OpenVINO モデル推論のベンチマーク#

%%timeit 

compiled_model(input_tensor)
2.84 ms ± 8.04 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

TorchScript モデルを OpenVINO 中間表現に変換#

TorchScript は、PyTorch コードからシリアル化かつ最適化可能なモデルを作成します。任意の TorchScript プログラムを Python プロセスから保存し、Python に依存しないプロセスに読み込むことができます。TorchScript の詳細については、PyTorch のドキュメントを参照してください。

PyTorch モデルを TorchScript に変換するには、次の 2 つの方法があります:

  • torch.jit.script - 関数または nn.Module をスクリプト化すると、ソースコードを調査し、TorchScript コンパイラーで TorchScript コードとしてコンパイルし、ScriptModule または ScriptFunction を返します。

  • torch.jit.trace - 関数をトレースし、ジャストインタイム・コンパイルによって最適化される実行可能ファイルまたは ScriptFunction を返します。

両方のアプローチと、それらの OpenVINO IR への変換について考えてみましょう。

スクリプト化されたモデル#

torch.jit.script は、モデルのソースコードを調査し、ScriptModule にコンパイルします。コンパイル後、モデルは推論に使用したり、torch.jit.save 関数を使用してディスクに保存したり、その後、元の PyTorch モデルコード定義なしで他の環境で torch.jit.load を使用して復元したりできます。

TorchScript 自体は Python 言語のサブセットであるため、Python のすべての機能が動作するわけではありませんが、TorchScript はテンソルを計算し、制御に依存する操作を行うのに十分な機能を提供します。完全なガイドについては、TorchScript 言語リファレンスを参照してください。

# Get model path 
scripted_model_path = MODEL_DIR / f"{MODEL_NAME}_scripted.pth" 

# 以前にコンパイルされていない場合はモデルをコンパイルして保存するか、コンパイル済みのモデルをロード 
if not scripted_model_path.exists(): 
    scripted_model = torch.jit.script(model) 
    torch.jit.save(scripted_model, scripted_model_path) 
else: 
    scripted_model = torch.jit.load(scripted_model_path) 

# スクリプト化されたモデル推論を実行 
result = scripted_model(input_tensor) 

# 後処理の結果 
top_labels, top_scores = postprocess_result(result.detach().numpy()) 

# 結果を表示 
display(image) 
for idx, (label, score) in enumerate(zip(top_labels, top_scores)):
    _, predicted_label = imagenet_classes[label].split(" ", 1) 
    print(f"{idx + 1}: {predicted_label} - {score * 100 :.2f}%")
../_images/pytorch-to-openvino-with-output_35_0.png
1: tiger cat - 25.91% 
2: Egyptian cat - 10.26% 
3: computer keyboard, keypad - 9.22% 
4: tabby, tabby cat - 9.09% 
5: hamper - 2.35%

クリプト化されたモデル推論のベンチマーク#

%%timeit 

scripted_model(input_tensor)
14.1 ms ± 70.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

PyTorch クリプト化モデルを OpenVINO 中間表現に変換#

スクリプトモデルから OpenVINO IR への変換手順は、元の PyTorch モデルと同様です。

# モデルを openvino.runtime.Model オブジェクトに変換 
ov_model = ov.convert_model(scripted_model) 

# デバイスに OpenVINO モデルをロード 
compiled_model = core.compile_model(ov_model, device.value) 

# OpenVINO モデル推論を実行 
result = compiled_model(input_tensor, device.value)[0] 

# 後処理の結果 
top_labels, top_scores = postprocess_result(result) 

# 結果を表示 
display(image) 
for idx, (label, score) in enumerate(zip(top_labels, top_scores)):
    _, predicted_label = imagenet_classes[label].split(" ", 1) 
    print(f"{idx + 1}: {predicted_label} - {score * 100 :.2f}%")
../_images/pytorch-to-openvino-with-output_39_0.png
1: tiger cat - 25.91% 
2: Egyptian cat - 10.26% 
3: computer keyboard, keypad - 9.22% 
4: tabby, tabby cat - 9.09% 
5: hamper - 2.35%

スクリプト化されたモデルの変換された OpenVINO モデル推論のベンチマーク#

%%timeit 

compiled_model(input_tensor)
3.17 ms ± 9.55 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

トレースされたモデル#

torch.jit.trace を使用すると、既存のモジュールまたは Python 関数を TorchScript ScriptFunction または ScriptModule に変換できます。サンプル入力を提供する必要があり、モデルが実行され、すべてのテンソルに対して行われた操作が記録されます。

  • スタンドアロン関数の記録の結果、ScriptFunction が生成されます。

  • nn.Module.forward または nn.Module の結果の記録により、ScriptModule が生成されます。

スクリプトモデルと同様に、トレースされたモデルは推論に使用したり、torch.jit.save 関数を使用してディスクに保存したり、その後、元の PyTorch モデルコード定義なしで他の環境で torch.jit.load を使用して復元したりできます。

# モデルパスを取得 
traced_model_path = MODEL_DIR / f"{MODEL_NAME}_traced.pth" 

# 以前にトレースされていない場合はモデルをトレースして保存するか、トレース済みのモデルをロード 
if not traced_model_path.exists(): 
    traced_model = torch.jit.trace(model, example_inputs=input_tensor) 
    torch.jit.save(traced_model, traced_model_path) 
else: 
    traced_model = torch.jit.load(traced_model_path) 

# トレースされたモデル推論を実行 
result = traced_model(input_tensor) 

# 後処理の結果 
top_labels, top_scores = postprocess_result(result.detach().numpy()) 

# 結果を表示 
display(image) 
for idx, (label, score) in enumerate(zip(top_labels, top_scores)):
    _, predicted_label = imagenet_classes[label].split(" ", 1) 
    print(f"{idx + 1}: {predicted_label} - {score * 100 :.2f}%")
../_images/pytorch-to-openvino-with-output_43_0.png
1: tiger cat - 25.91% 
2: Egyptian cat - 10.26% 
3: computer keyboard, keypad - 9.22% 
4: tabby, tabby cat - 9.09% 
5: hamper - 2.35%

トレースされたモデルの推論のベンチマーク#

%%timeit 

traced_model(input_tensor)
14.8 ms ± 412 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

PyTorch トレースモデルを OpenVINO 中間表現に変換#

トレースされたモデルの OpenVINO IR への変換手順は、元の PyTorch モデルと同様です。

# モデルを openvino.runtime.Model オブジェクトに変換 
ov_model = ov.convert_model(traced_model) 

# デバイスに OpenVINO モデルをロード 
compiled_model = core.compile_model(ov_model, device.value) 

# OpenVINO モデル推論を実行 
result = compiled_model(input_tensor)[0] 

# 後処理の結果 
top_labels, top_scores = postprocess_result(result) 

# 結果を表示 
display(image) 
for idx, (label, score) in enumerate(zip(top_labels, top_scores)):
    _, predicted_label = imagenet_classes[label].split(" ", 1) 
    print(f"{idx + 1}: {predicted_label} - {score * 100 :.2f}%")
../_images/pytorch-to-openvino-with-output_47_0.png
1: tiger cat - 25.91% 
2: Egyptian cat - 10.26% 
3: computer keyboard, keypad - 9.22% 
4: tabby, tabby cat - 9.09% 
5: hamper - 2.35%

トレースモデルの変換された OpenVINO モデル推論のベンチマーク#

%%timeit 

compiled_model(input_tensor)[0]
3.2 ms ± 8.84 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)