OpenVINO™ モデル変換

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

Binder Google Colab GitHub

このノートブックでは、モデルを元のフレームワーク形式から OpenVINO 中間表現 (IR) に変換する方法を示します。

目次

# Required imports. Please execute this cell first.
%pip install --upgrade pip
%pip install -q --extra-index-url https://download.pytorch.org/whl/cpu \
"openvino-dev>=2023.2.0" "requests" "tqdm" "transformers[onnx]>=4.21.1" "torch" "torchvision" "tensorflow_hub" "tensorflow"
Requirement already satisfied: pip in /opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-609/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages (24.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.

OpenVINO IR 形式

OpenVINO 中間表現 (IR) は、OpenVINO 独自のモデル形式です。モデル変換 API によってモデルを変換して作成します。モデル変換 API は、頻繁に使用されるディープラーニング操作を OpenVINO の同様の表現に変換し、トレーニングされたモデルからの関連する重みとバイアスを使用して調整します。結果の IR には 2 つのファイルが含まれます: ネットワーク・トポロジーに関する情報を含む .xml ファイルと、重みとバイアスのバイナリーデータを含む .bin ファイル。

モデルを元のフレームワーク形式から OpenVINO IR に変換するには、Python 変換 API と OVC コマンドライン・ツールの 2 つの方法があります。あなたにとって便利なほうを選択できます。

OpenVINO 変換 API は、次のモデル形式をサポートします: PyTorchTensorFlowTensorFlow LiteONNX、および PaddlePaddle。これらのモデル形式は、自動的または明示的に読み取り、コンパイルして、OpenVINO IR に変換できます。

詳細については、モデル準備のドキュメントを参照してください。

# OVC CLI tool parameters description

! ovc --help
usage: ovc INPUT_MODEL... [-h] [--output_model OUTPUT_MODEL]
           [--compress_to_fp16 [True | False]] [--version] [--input INPUT]
           [--output OUTPUT] [--extension EXTENSION] [--verbose]

positional arguments:
  INPUT_MODEL           Input model file(s) from TensorFlow, ONNX,
                        PaddlePaddle. Use openvino.convert_model in Python to
                        convert models from PyTorch.

optional arguments:
  -h, --help            show this help message and exit
  --output_model OUTPUT_MODEL
                        This parameter is used to name output .xml/.bin files
                        of converted model. Model name or output directory can
                        be passed. If output directory is passed, the
                        resulting .xml/.bin files are named by original model
                        name.
  --compress_to_fp16 [True | False]
                        Compress weights in output OpenVINO model to FP16. To
                        turn off compression use "--compress_to_fp16=False"
                        command line parameter. Default value is True.
  --version             Print ovc version and exit.
  --input INPUT         Information of model input required for model
                        conversion. This is a comma separated list with
                        optional input names and shapes. The order of inputs
                        in converted model will match the order of specified
                        inputs. The shape is specified as comma-separated
                        list. Example, to set input_1 input with shape
                        [1,100] and sequence_len input with shape [1,?]:
                        "input_1[1,100],sequence_len[1,?]", where "?" is a
                        dynamic dimension, which means that such a dimension
                        can be specified later in the runtime. If the
                        dimension is set as an integer (like 100 in [1,100]),
                        such a dimension is not supposed to be changed later,
                        during a model conversion it is treated as a static
                        value. Example with unnamed inputs: "[1,100],[1,?]".
  --output OUTPUT       One or more comma-separated model outputs to be
                        preserved in the converted model. Other outputs are
                        removed. If output parameter is not specified then
                        all outputs from the original model are preserved. Do
                        not add :0 to the names for TensorFlow. The order of
                        outputs in the converted model is the same as the
                        order of specified names. Example: ovc model.onnx
                        output=out_1,out_2
  --extension EXTENSION
                        Paths or a comma-separated list of paths to libraries
                        (.so or .dll) with extensions. To disable all
                        extensions including those that are placed at the
                        default location, pass an empty string.
  --verbose             Print detailed information about conversion.

サンプルモデルの取得

このノートブックでは、変換例に 2 つのモデルを使用します。

from pathlib import Path

# create a directory for models files
MODEL_DIRECTORY_PATH = Path("model")
MODEL_DIRECTORY_PATH.mkdir(exist_ok=True)

Hugging Face から distilbert NLP モデルを取得し、ONNX 形式でエクスポートします。

from transformers import AutoModelForSequenceClassification, AutoTokenizer
from transformers.onnx import export, FeaturesManager

ONNX_NLP_MODEL_PATH = MODEL_DIRECTORY_PATH / "distilbert.onnx"

# download model
hf_model = AutoModelForSequenceClassification.from_pretrained(
    "distilbert-base-uncased-finetuned-sst-2-english"
)
# initialize tokenizer
tokenizer = AutoTokenizer.from_pretrained(
    "distilbert-base-uncased-finetuned-sst-2-english"
)

# get model onnx config function for output feature format sequence-classification
model_kind, model_onnx_config = FeaturesManager.check_supported_model_or_raise(
    hf_model, feature="sequence-classification"
)
# fill onnx config based on pytorch model config
onnx_config = model_onnx_config(hf_model.config)

# export to onnx format
export(
    preprocessor=tokenizer,
    model=hf_model,
    config=onnx_config,
    opset=onnx_config.default_onnx_opset,
    output=ONNX_NLP_MODEL_PATH,
)
2024-02-09 23:07:37.212192: I tensorflow/core/util/port.cc:110] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable TF_ENABLE_ONEDNN_OPTS=0.
2024-02-09 23:07:37.247733: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 AVX512F AVX512_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
2024-02-09 23:07:37.883428: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT
/opt/home/k8sworker/ci-ai/cibuilds/ov-notebook/OVNotebookOps-609/.workspace/scm/ov-notebook/.venv/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py:246: TracerWarning: torch.tensor results are registered as constants in the trace. You can safely ignore this warning if you use this function to create tensors out of constant variables that would be the same every time you call this function. In any other case, this might cause the trace to be incorrect.
                                        mask, torch.tensor(torch.finfo(scores.dtype).min)
(['input_ids', 'attention_mask'], ['logits'])

Torchvision から Resnet50 CV 分類モデルを取得します。

from torchvision.models import resnet50, ResNet50_Weights

# create model object
pytorch_model = resnet50(weights=ResNet50_Weights.DEFAULT)
# switch model from training to inference mode
pytorch_model.eval()
ResNet(
  (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
  (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (relu): ReLU(inplace=True)
  (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
  (layer1): Sequential(
    (0): Bottleneck(
      (conv1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
      (downsample): Sequential(
        (0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
        (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
    )
    (1): Bottleneck(
      (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
    (2): Bottleneck(
      (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
  )
  (layer2): Sequential(
    (0): Bottleneck(
      (conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
      (downsample): Sequential(
        (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)
        (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
    )
    (1): Bottleneck(
      (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
    (2): Bottleneck(
      (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
    (3): Bottleneck(
      (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
  )
  (layer3): Sequential(
    (0): Bottleneck(
      (conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
      (downsample): Sequential(
        (0): Conv2d(512, 1024, kernel_size=(1, 1), stride=(2, 2), bias=False)
        (1): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
    )
    (1): Bottleneck(
      (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
    (2): Bottleneck(
      (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
    (3): Bottleneck(
      (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
    (4): Bottleneck(
      (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
    (5): Bottleneck(
      (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
  )
  (layer4): Sequential(
    (0): Bottleneck(
      (conv1): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
      (downsample): Sequential(
        (0): Conv2d(1024, 2048, kernel_size=(1, 1), stride=(2, 2), bias=False)
        (1): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
    )
    (1): Bottleneck(
      (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
    (2): Bottleneck(
      (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (relu): ReLU(inplace=True)
    )
  )
  (avgpool): AdaptiveAvgPool2d(output_size=(1, 1))
  (fc): Linear(in_features=2048, out_features=1000, bias=True)
)

PyTorch モデルから ONNX 形式へ変換します。

import torch
import warnings

ONNX_CV_MODEL_PATH = MODEL_DIRECTORY_PATH / "resnet.onnx"

if ONNX_CV_MODEL_PATH.exists():
    print(f"ONNX model {ONNX_CV_MODEL_PATH} already exists.")
else:
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore")
        torch.onnx.export(
            model=pytorch_model, args=torch.randn(1, 3, 224, 224), f=ONNX_CV_MODEL_PATH
        )
    print(f"ONNX model exported to {ONNX_CV_MODEL_PATH}")
ONNX model exported to model/resnet.onnx

変換

モデルを OpenVINO IR に変換するには、次の API を使用します。

import openvino as ov

# ov.convert_model returns an openvino.runtime.Model object
print(ONNX_NLP_MODEL_PATH)
ov_model = ov.convert_model(ONNX_NLP_MODEL_PATH)

# then model can be serialized to *.xml & *.bin files
ov.save_model(ov_model, MODEL_DIRECTORY_PATH / "distilbert.xml")
model/distilbert.onnx
! ovc model/distilbert.onnx --output_model model/distilbert.xml
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
    - Avoid using tokenizers before the fork if possible
    - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
[ INFO ] Generated IR will be compressed to FP16. If you get lower accuracy, please consider disabling compression by removing argument "compress_to_fp16" or set it to false "compress_to_fp16=False".
Find more information about compression to FP16 at https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_FP16_Compression.html
[ SUCCESS ] XML file: model/distilbert.xml
[ SUCCESS ] BIN file: model/distilbert.bin

入力形状の設定

モデル変換は、未定義の次元を含む動的入力形状を持つモデルに対してサポートされます。ただし、データの形状が推論要求ごとに変わらない場合は、入力に対して静的な形状を設定することを推奨します (すべての次元が完全に定義されている場合)。これは、実行時ではなく、モデルの準備段階で行うと、パフォーマンスとメモリー消費の点で有利になる可能性があります。

詳細については、入力形状の設定ドキュメントを参照してください。

import openvino as ov

ov_model = ov.convert_model(
    ONNX_NLP_MODEL_PATH, input=[("input_ids", [1, 128]), ("attention_mask", [1, 128])]
)
! ovc model/distilbert.onnx --input input_ids[1,128],attention_mask[1,128] --output_model model/distilbert.xml
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
    - Avoid using tokenizers before the fork if possible
    - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
[ INFO ] Generated IR will be compressed to FP16. If you get lower accuracy, please consider disabling compression by removing argument "compress_to_fp16" or set it to false "compress_to_fp16=False".
Find more information about compression to FP16 at https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_FP16_Compression.html
[ SUCCESS ] XML file: model/distilbert.xml
[ SUCCESS ] BIN file: model/distilbert.bin

モデルトポロジーでサポートされている場合、input パラメーターを使用して元の入力形状をオーバーライドできます。元のモデルの動的な次元を持つ形状を、変換されたモデルの静的な形状に置き換えることも、その逆も可能です。動的次元は、ovc を使用する場合、モデル変換 API パラメーターで -1 または ? としてマークできます。

import openvino as ov

ov_model = ov.convert_model(
    ONNX_NLP_MODEL_PATH, input=[("input_ids", [1, -1]), ("attention_mask", [1, -1])]
)
! ovc model/distilbert.onnx --input "input_ids[1,?],attention_mask[1,?]" --output_model model/distilbert.xml
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
    - Avoid using tokenizers before the fork if possible
    - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
[ INFO ] Generated IR will be compressed to FP16. If you get lower accuracy, please consider disabling compression by removing argument "compress_to_fp16" or set it to false "compress_to_fp16=False".
Find more information about compression to FP16 at https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_FP16_Compression.html
[ SUCCESS ] XML file: model/distilbert.xml
[ SUCCESS ] BIN file: model/distilbert.bin

実行時に次元が定義されていないモデルのメモリー消費を最適化するため、モデル変換 API には次元の境界を定義する機能が用意されています。未定義の次元の境界は、コマンドラインの省略記号を使用するか、Python の openvino.Dimension クラスで指定できます。例えば、ONNX Bert モデルのモデル変換を起動し、シーケンス長の次元の境界を指定します。

import openvino as ov


sequence_length_dim = ov.Dimension(10, 128)

ov_model = ov.convert_model(
    ONNX_NLP_MODEL_PATH, input=[("input_ids", [1, sequence_length_dim]), ("attention_mask", [1, sequence_length_dim])]
)
! ovc model/distilbert.onnx --input input_ids[1,10..128],attention_mask[1,10..128] --output_model model/distilbert.xml
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
    - Avoid using tokenizers before the fork if possible
    - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
[ INFO ] Generated IR will be compressed to FP16. If you get lower accuracy, please consider disabling compression by removing argument "compress_to_fp16" or set it to false "compress_to_fp16=False".
Find more information about compression to FP16 at https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_FP16_Compression.html
[ SUCCESS ] XML file: model/distilbert.xml
[ SUCCESS ] BIN file: model/distilbert.bin

モデルを FP16 に圧縮

デフォルトでは、OpenVINO モデルを IR に保存するときに、モデルの重みは FP16 形式に圧縮されます。これにより、モデルファイルのストレージスペースが最大 2 倍節約され、ほとんどの場合、モデルの精度が犠牲になることはありません。重みの圧縮は、compress_to_fp16 フラグを False に設定することで無効にできます。

import openvino as ov

ov_model = ov.convert_model(ONNX_NLP_MODEL_PATH)
ov.save_model(ov_model, MODEL_DIRECTORY_PATH / 'distilbert.xml', compress_to_fp16=False)
! ovc model/distilbert.onnx --output_model model/distilbert.xml --compress_to_fp16=False
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
    - Avoid using tokenizers before the fork if possible
    - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
[ SUCCESS ] XML file: model/distilbert.xml
[ SUCCESS ] BIN file: model/distilbert.bin

メモリーからモデルを変換

モデル変換 API は、元のフレームワークの Python オブジェクトを直接渡すことをサポートします。詳細については、PyTorchTensorFlowPaddlePaddle フレームワーク変換ガイドをご覧ください。

import openvino as ov
import torch

example_input = torch.rand(1, 3, 224, 224)

ov_model = ov.convert_model(pytorch_model, example_input=example_input, input=example_input.shape)
WARNING:tensorflow:Please fix your imports. Module tensorflow.python.training.tracking.base has been moved to tensorflow.python.trackable.base. The old module will be deleted in version 2.11.
import openvino as ov
import tensorflow_hub as hub

model = hub.load("https://www.kaggle.com/models/google/movenet/frameworks/TensorFlow2/variations/singlepose-lightning/versions/4")
movenet = model.signatures['serving_default']

ov_model = ov.convert_model(movenet)
2024-02-09 23:07:56.008045: E tensorflow/compiler/xla/stream_executor/cuda/cuda_driver.cc:266] failed call to cuInit: CUDA_ERROR_COMPAT_NOT_SUPPORTED_ON_DEVICE: forward compatibility was attempted on non supported HW
2024-02-09 23:07:56.008076: I tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:168] retrieving CUDA diagnostic information for host: iotg-dev-workstation-07
2024-02-09 23:07:56.008081: I tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:175] hostname: iotg-dev-workstation-07
2024-02-09 23:07:56.008267: I tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:199] libcuda reported version is: 470.223.2
2024-02-09 23:07:56.008284: I tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:203] kernel reported version is: 470.182.3
2024-02-09 23:07:56.008287: E tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:312] kernel version 470.182.3 does not match DSO version 470.223.2 -- cannot find working devices in this configuration

レガシー変換 API からの移行

2023.1 OpenVINO リリースでは、OpenVINO モデル変換 API が、対応する Python API openvino.convert_model メソッドとともに導入されました。 ovc および openvino.convert_model は、現在レガシー API と見なされている mo および openvino.tools.mo.convert_model の軽量の代替です。 mo.convert_model() 幅広い前処理パラメーターを提供します。これらのパラメーターのほとんどは、OVC に類似のものがあるか、ov.PrePostProcessor クラスの機能で置き換えることができます。前処理 API の詳細については、前処理の最適化ノートブックを参照してください。従来のモデルの前処理から前処理 API への移行ガイドを次に示します。

レイアウトを指定

レイアウトは形状の次元の平均を定義し、入力と出力の両方に指定できます。一部の前処理では、バッチの設定、平均値またはスケールの適用、入力チャネル (BGR<->RGB) の反転など、入力レイアウトの設定が必要です。レイアウト構文の詳細については、レイアウト API の概要を参照してください。レイアウトを指定するには、レイアウトオプションの後にレイアウト値を使用します。

次の例では、ONNX 形式にエクスポートされた Pytorch Resnet50 モデルの NCHW レイアウトを指定します。

# Converter API
import openvino as ov

ov_model = ov.convert_model(ONNX_CV_MODEL_PATH)

prep = ov.preprocess.PrePostProcessor(ov_model)
prep.input('input.1').model().set_layout(ov.Layout("nchw"))
ov_model = prep.build()
# Legacy Model Optimizer API
from openvino.tools import mo

ov_model = mo.convert_model(ONNX_CV_MODEL_PATH, layout="nchw")
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
    - Avoid using tokenizers before the fork if possible
    - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)

モデルのレイアウトを変更

行列/テンソルの転置はディープラーニングでは一般的な操作です。{480, 640, 3} 要素の配列である 640x480 の BMP 画像がある場合、ディープラーニング・モデルは形状 {1, 3, 480, 640} の入力を要求することがあります。

変換は、ユーザーのテンソルのレイアウトと元のモデルのレイアウトを使用して暗黙的に行われます。

# Converter API
import openvino as ov

ov_model = ov.convert_model(ONNX_CV_MODEL_PATH)

prep = ov.preprocess.PrePostProcessor(ov_model)
prep.input('input.1').tensor().set_layout(ov.Layout("nhwc"))
prep.input('input.1').model().set_layout(ov.Layout("nchw"))
ov_model = prep.build()
# Legacy Model Optimizer API
from openvino.tools import mo

ov_model = mo.convert_model(ONNX_CV_MODEL_PATH, layout="nchw->nhwc")

# alternatively use source_layout and target_layout parameters
ov_model = mo.convert_model(
    ONNX_CV_MODEL_PATH, source_layout="nchw", target_layout="nhwc"
)

平均とスケール値の指定

前処理 API を使用すると、平均値スケール値を設定できます。これらの API を使用して、モデルは入力データの平均値正規化に対応する前処理ブロックを埋め込み、このブロックを最適化します。その他の例については、前処理の最適化ノートブックを参照してください。

# Converter API
import openvino as ov

ov_model = ov.convert_model(ONNX_CV_MODEL_PATH)

prep = ov.preprocess.PrePostProcessor(ov_model)
prep.input("input.1").tensor().set_layout(ov.Layout("nchw"))
prep.input("input.1").preprocess().mean([255 * x for x in [0.485, 0.456, 0.406]])
prep.input("input.1").preprocess().scale([255 * x for x in [0.229, 0.224, 0.225]])

ov_model = prep.build()
# Legacy Model Optimizer API
from openvino.tools import mo


ov_model = mo.convert_model(
    ONNX_CV_MODEL_PATH,
    mean_values=[255 * x for x in [0.485, 0.456, 0.406]],
    scale_values=[255 * x for x in [0.229, 0.224, 0.225]],
)

入力チャネルの反転

状況によっては、アプリケーションの入力画像が RGB (または BGR) 形式である場合があり、モデルはカラーチャネルの順序が逆である BGR (または RGB) 形式の画像でトレーニングされることがあります。この場合、推論前にカラーチャネルを元に戻すことで入力画像を前処理することが重要です。

# Converter API
import openvino as ov

ov_model = ov.convert_model(ONNX_CV_MODEL_PATH)

prep = ov.preprocess.PrePostProcessor(ov_model)
prep.input('input.1').tensor().set_layout(ov.Layout("nchw"))
prep.input('input.1').preprocess().reverse_channels()
ov_model = prep.build()
# Legacy Model Optimizer API
from openvino.tools import mo

ov_model = mo.convert_model(ONNX_CV_MODEL_PATH, reverse_input_channels=True)

モデルの一部を切り取り

新しい変換 API では、モデルからモデル入力とモデル出力を切り取ることはできなくなりました。代わりに、元のフレームワークでカットすることをお勧めします。Tensorflow および ONNX フレームワークで提供されるツールを使用した TensorFlow protobuf、TensorFlow SavedModel、および ONNX 形式のモデルカットの例については、ドキュメント・ガイドを参照してください。PyTorch、TensorFlow 2 Keras、PaddlePaddle では、元のモデルコードを変更してモデルカットを行うことを推奨します。