OpenVINO™ ランタイムのモデル表現

OpenVINO™ ランタイムでは、モデルは ov::Model クラスによって表されます。

ov::Model オブジェクトは、入力、出力、グラフのシンクである ov::op::v0::Parameterov::op::v0::Result、および ov::op::Sink 操作への共有ポインターを格納します。グラフのシンクにはコンシューマーがなく、結果ベクトルには含まれません。他のすべての操作は共有ポインターを介して相互に保持され、子操作はハードリンクによって親を保持します。操作にコンシューマーがなく、共有ポインターカウンターが 0 である Result 操作でも Sink 操作でもない場合、操作は破棄されてアクセスできなくなります。

ov::Model の各操作には std::shared_ptr<ov::Node> タイプがあります。

OpenVINO ランタイムがモデルでどのように動作するか

OpenVINO™ ランタイムを使用すると、さまざまなアプローチによりモデルの入力/出力を操作できます。

  • ov::Model::inputs()/ov::Model::outputs() メソッドは、すべての入出力ポートのベクトルを取得するために使用されます。

  • 入力または出力が 1 つだけのモデルでは、引数なしで ov::Model::input() メソッドまたは ov::Model::output() メソッドを使用して、入力ポートまたは出力ポートをそれぞれ取得できます。

  • ov::Model::input() メソッドと ov::Model::output() メソッドをフレームワーク・モデルからの入力または出力のインデックスとともに使用して、インデックスによって特定のポートを取得できます。

  • 元のフレームワーク・モデルからの入力または出力のテンソル名を ov::Model::input() メソッドまたは ov::Model::output() メソッドとともに使用して、特定のポートを取得できます。これは、以前のように、フレームワークから OpenVINO への名前による追加のマッピングが必要ないことを意味します。OpenVINO ランタイムでは、次のようなネイティブ・フレームワーク・テンソル名が許可されます。

    inputs = model.inputs
    outputs = model.outputs
/* Take information about all topology inputs */
auto inputs = model->inputs();
/* Take information about all topology outputs */
auto outputs = model->outputs();

OpenVINO™ ランタイムでモデルを構築する方法の詳細については、OpenVINO ランタイムでモデルをビルドを参照してください。

OpenVINO™ ランタイムのモデル表現は、特別なクラスを使用してモデルのデータタイプと形状を操作します。ov::element::Type はデータタイプに使用されます。形状の表現は、下記を参照してください。

形状の表現

OpenVINO™ ランタイムは、形状表現に 2 つのタイプを提供します。

  • ov::Shape - 静的な (完全に定義された) 形状を表現します。

  • ov::PartialShape - 動的な形状を表現します。これは、ランクまたは一部の次元が動的であることを意味します (次元は間隔を定義するか未定義です)。

ov::PartialShape は、すべての次元が静的である場合、get_shape() メソッドを使用して ov::Shape に変換できます。それ以外は、変換で例外がスローされます。
例:

    partial_shape = node.output(0).get_partial_shape() # get zero output partial shape
    if not partial_shape.is_dynamic: # or partial_shape.is_static
        static_shape = partial_shape.get_shape()
    ov::Shape static_shape;
    ov::PartialShape partial_shape = node->output(0).get_partial_shape(); // get zero output partial shape
    if (!partial_shape.is_dynamic() /* or partial_shape.is_static() */) {
        static_shape = partial_shape.get_shape();
    }

ただし、ほとんどの場合、get_shape() メソッドで静的な形状を取得する前に、その形状が静的であるかどうか確認する必要があります。

操作の表現

ov::Op クラスは、モデル表現内の抽象的な操作を表します。このクラスを使用してカスタム操作を作成します。

操作セットの表現

操作セット (opset) は、モデルの構築に使用できる操作のコレクションです。ov::OpSet クラスは、操作セットを操作する機能を提供します。OpenVINO™ ランタイムは、操作セットごとに、opset8 など個別の名前空間を提供します。

OpenVINO™ の各リリースでは、新しい操作が導入され、新しい操作セットに追加されます。新しい操作によって以前の操作の動作が変更されます。操作セットを使用すると、新しい操作が導入されたときにアプリケーションを変更する必要はありません。OpenVINO™ ツールキットでサポートされている操作セットの完全なリストについては、利用可能な操作セットを参照してください。カスタム操作のサポートを追加するには、OpenVINO 拡張メカニズムを参照してください。

OpenVINO™ ランタイムでモデルをビルド

ソースからモデルを作成できます。このセクションでは、利用可能な操作セットから操作で構成されるモデルを構築する方法を説明します。

操作セット opsetX は、この目的のため機能する事前コンパイルされた操作のリストを統合します。言い換えれば、opsetX はグラフを構築する一連の操作を定義します。

opset8 操作から ov::Model インスタンスを構築するには、次のファイルをインクルードします。

import openvino as ov
#include <openvino/core/model.hpp>
#include <openvino/opsets/opset8.hpp>

次のコードは、単純なモデルを作成する方法を示します。

def create_simple_model():
    # This example shows how to create ov::Function
    #
    # Parameter--->Multiply--->Add--->Result
    #    Constant---'          /
    #              Constant---'
    data = ops.parameter([3, 1, 2], ov.Type.f32)
    mul_constant = ops.constant([1.5], ov.Type.f32)
    mul = ops.multiply(data, mul_constant)
    add_constant = ops.constant([0.5], ov.Type.f32)
    add = ops.add(mul, add_constant)
    res = ops.result(add)
    return ov.Model([res], [data], "model")
std::shared_ptr<ov::Model> create_simple_model() {
    // This example shows how to create ov::Model
    //
    // Parameter--->Multiply--->Add--->Result
    //    Constant---'          /
    //              Constant---'

    // Create opset8::Parameter operation with static shape
    auto data = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::Shape{3, 1, 2});

    auto mul_constant = ov::opset8::Constant::create(ov::element::f32, ov::Shape{1}, {1.5});
    auto mul = std::make_shared<ov::opset8::Multiply>(data, mul_constant);

    auto add_constant = ov::opset8::Constant::create(ov::element::f32, ov::Shape{1}, {0.5});
    auto add = std::make_shared<ov::opset8::Add>(mul, add_constant);

    // Create opset8::Result operation
    auto res = std::make_shared<ov::opset8::Result>(mul);

    // Create OpenVINO function
    return std::make_shared<ov::Model>(ov::ResultVector{res}, ov::ParameterVector{data});
}

次のコードは、複数の出力を持つモデルを作成します。

def create_advanced_model():
    # Advanced example with multi output operation
    #
    # Parameter->Split---0-->Result
    #               | `--1-->Relu-->Result
    #               `----2-->Result
    data = ops.parameter(ov.Shape([1, 3, 64, 64]), ov.Type.f32)
    # Create Constant for axis value
    axis_const = ops.constant(1, dtype=ov.Type.i64)

    # Create opset12::Split operation that splits input to three slices across 1st dimension
    split = ops.split(data, axis_const, 3)

    # Create opset12::Relu operation that takes 1st Split output as input
    relu = ops.relu(split.output(1))

    # Results operations will be created automatically based on provided OutputVector
    return ov.Model([split.output(0), relu.output(0), split.output(2)], [data], "model")
std::shared_ptr<ov::Model> create_advanced_model() {
    // Advanced example with multi output operation
    //
    // Parameter->Split---0-->Result
    //               | `--1-->Relu-->Result
    //               `----2-->Result

    auto data = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::Shape{1, 3, 64, 64});

    // Create Constant for axis value
    auto axis_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{} /*scalar shape*/, {1});

    // Create opset8::Split operation that splits input to three slices across 1st dimension
    auto split = std::make_shared<ov::opset8::Split>(data, axis_const, 3);

    // Create opset8::Relu operation that takes 1st Split output as input
    auto relu = std::make_shared<ov::opset8::Relu>(split->output(1) /*specify explicit output*/);

    // Results operations will be created automatically based on provided OutputVector
    return std::make_shared<ov::Model>(ov::OutputVector{split->output(0), relu, split->output(2)},
                                       ov::ParameterVector{data});
}

モデルのデバッグ機能

OpenVINO™ はいくつかのデバッグ機能を提供します。

  • 適用されたモデルの変更に関する追加メッセージを受信するには、-DENABLE_OPENVINO_DEBUG=ON オプションを使用して OpenVINO™ ランタイム・ライブラリーを再ビルドします。

  • モデルは xDot 形式の画像に視覚化できます。

def visualize_example(m : ov.Model):
    # Need import:
    # * import openvino.runtime.passes as passes
    pass_manager = passes.Manager()
    pass_manager.register_pass(passes.VisualizeTree(file_name='image.svg'))
    pass_manager.run_passes(m)
void visualize_example(const std::shared_ptr<ov::Model>& m) {
    // Need include:
    // * openvino/pass/manager.hpp
    // * openvino/pass/visualize_tree.hpp
    ov::pass::Manager manager;

    // Serialize ov::Model to before.svg file before transformation
    manager.register_pass<ov::pass::VisualizeTree>("image.svg");

    manager.run_passes(m);
}
`ov::pass::VisualizeTree` can be parametrized via environment variables:

OV_VISUALIZE_TREE_OUTPUT_SHAPES=1       - visualize shapes

OV_VISUALIZE_TREE_OUTPUT_TYPES=1        - visualize types

OV_VISUALIZE_TREE_MIN_MAX_DENORMAL=1    - pretty denormal values

OV_VISUALIZE_TREE_RUNTIME_INFO=1        - print runtime information

OV_VISUALIZE_TREE_IO=1                  - print I/O ports

OV_VISUALIZE_TREE_MEMBERS_NAME=1        - print member names
  • モデルを IR にシリアル化することもできます。

def serialize_example(m : ov.Model):
    ov.serialize(m, xml_path='model.xml', bin_path='model.bin')
void serialize_example(const std::shared_ptr<ov::Model>& model) {
    ov::serialize(model, "/path/to/file/model.xml", "/path/to/file/model.bin");
}