OpenVINO API を使用したトランスフォーメーション・パターン#

パターンマッチングは、OpenVINO™ トランスフォーメーションの重要なコンポーネントです。グラフのサブグラフに対してトランスフォーメーションを実行する前に、グラフ内でそのサブグラフを見つける必要があります。パターンは、トランスフォーメーションの対象となるノードを識別する検索ユーティリティーとして機能します。この記事では、OpenVINO™ API を使用したパターン作成の基本と、それらの操作を容易にする便利なユーティリティーについて説明します。このガイドではパターンの作成に重点を置いていますが、MatcherPass について詳しく知りたい場合は、OpenVINO マッチャーパスの記事を参照してください。一部の例は、理解しやすいように簡略化されている場合があります。

次に進む前に、いくつかのインポートを追加する必要があります。これらのインポートには、使用する操作と、このガイドで説明されている追加ユーティリティーが含まれます。次の行をファイルに追加します:

import pytest 
from openvino import PartialShape 
from openvino.runtime import opset13 as ops 
from openvino.runtime.passes import Matcher, WrapType, Or, AnyInput, Optional
#include <gtest/gtest.h> 

#include "common_test_utils/matcher.hpp" 
#include "openvino/op/abs.hpp" 
#include "openvino/op/add.hpp" 
#include "openvino/op/matmul.hpp" 
#include "openvino/op/parameter.hpp" 
#include "openvino/op/relu.hpp" 
#include "openvino/op/sigmoid.hpp" 
#include "openvino/pass/pattern/op/optional.hpp" 
#include "openvino/pass/pattern/op/or.hpp" 
#include "openvino/pass/pattern/op/wrap_type.hpp" 
#include "transformations/utils/utils.hpp" 

using namespace ov; 
using namespace ov::pass; 
using namespace std;

パターン作成#

パターンは、一致させることを目的としたノードで構成された簡略化モデルです。モデルとしての機能が一部欠けているため、モデルとして機能しません。

特定のモデル内にある 3 つのノードで構成される単純なパターンを考えてみましょう。

../../../_images/simple_pattern_example.png

モデルとパターンを作成します:

def simple_model_and_pattern():
    # サンプルモデルを作成 
    model_param1 = ops.parameter(PartialShape([2, 2])) 
    model_param2 = ops.parameter(PartialShape([2, 2])) 
    model_add = ops.add(model_param1, model_param2) 
    model_param3 = ops.parameter(PartialShape([2, 2])) 
    model_mul = ops.matmul(model_add, model_param3, False, False) 
    model_abs = ops.abs(model_mul) 
    model_relu = ops.relu(model_abs) 
    model_result = ops.result(model_relu) 

    # サンプルパターンを作成 
    pattern_mul = ops.matmul(AnyInput(), AnyInput(), False, False) 
    pattern_abs = ops.abs(pattern_mul) 
    pattern_relu = ops.relu(pattern_abs) 

    # マッチャーを作成し、ノードを一致させる 
    matcher = Matcher(pattern_relu, "FindPattern") 

    # 完全に一致するはずです 
    assert matcher.match(model_relu)
TEST(pattern, simple_model_and_pattern) { 
    // サンプルモデルを作成 
    PartialShape shape{2, 2}; 
    auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0)); 
    auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false); 
    auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0)); 
    auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0)); 
    auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0)); 

    // サンプルモデルを作成 
    auto pattern_mul = std::make_shared<ov::op::v0::MatMul>(pattern::any_input(), pattern::any_input(), false, false); 
    auto pattern_abs = std::make_shared<ov::op::v0::Abs>(pattern_mul->output(0)); 
    auto pattern_relu = std::make_shared<ov::op::v0::Relu>(pattern_abs->output(0)); 

    // マッチャーを作成し、ノードを一致させる 
    TestMatcher tm; 

    // 完全に一致するはずです 
    ASSERT_TRUE(tm.match(pattern_relu, model_relu)); 
}

この例では、指定されたノードのシーケンスを直接比較するテスト・ユーティリティーを使用します。実際には、モデル内のパターンを検出するプロセスは複雑です。ただし、パターンとその機能のみに焦点を当てるため、その詳細は意図的に省略されています。

コードは機能しますが、OpenVINO では通常、モデルの作成に使用されたノードと同じノードを使用してパターンは作成されません。代わりに、追加機能を提供するラッパーが推奨されます。この場合、WrapType が使用され、コードは次のようになります:

def simple_model_and_pattern_wrap_type(): 
    model_param1 = ops.parameter(PartialShape([2, 2])) 
    model_param2 = ops.parameter(PartialShape([2, 2])) 
    model_add = ops.add(model_param1, model_param2) 
    model_param3 = ops.parameter(PartialShape([2, 2])) 
    model_mul = ops.matmul(model_add, model_param3, False, False) 
    model_abs = ops.abs(model_mul) 
    model_relu = ops.relu(model_abs) 
    model_result = ops.result(model_relu) 

    # サンプルパターンを作成 
    pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()]) 
    pattern_abs = WrapType("opset13.Abs", pattern_mul) 
    pattern_relu = WrapType("opset13.Relu", pattern_abs) 

    # マッチャーを作成し、ノードを一致させる 
    matcher = Matcher(pattern_relu, "FindPattern") 

    # 完全に一致するはずです 
    assert matcher.match(model_relu)
TEST(pattern, simple_model_and_pattern_wrap_type) { 
    // サンプルパターンを作成 
    PartialShape shape{2, 2}; 
    auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0)); 
    auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false); 
    auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0)); 
    auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0)); 
    auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0)); 

    // サンプルパターンを作成 
    auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()}); 
    auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)}); 
    auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)}); 

    // マッチャーを作成し、ノードを一致させる 
    TestMatcher tm; 

    // 完全に一致するはずです 
    ASSERT_TRUE(tm.match(pattern_relu, model_relu)); 
}

1. WrapType#

WrapType は、1 つまたは複数のタイプを一致させて格納する際に使用されるラッパーです。前述のように、WrapType で単一のタイプを指定してマッチングに使用することもできます。ただし、特定のノードのすべての可能なタイプを一覧表示することもできます。次に例を示します:

def wrap_type_list(): 
    model_param1 = ops.parameter(PartialShape([2, 2])) 
    model_param2 = ops.parameter(PartialShape([2, 2])) 
    model_add = ops.add(model_param1, model_param2) 
    model_param3 = ops.parameter(PartialShape([2, 2])) 
    model_mul = ops.matmul(model_add, model_param3, False, False) 
    model_abs = ops.abs(model_mul) 
    model_relu = ops.relu(model_abs) 
    model_result = ops.result(model_relu) 
    model_sig = ops.sigmoid(model_abs)
    # Abs の後にシグモイドノードを追加したことに注意 
    model_result1 = ops.result(model_sig) 

    # サンプルパターンを作成 
    pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()]) 
    pattern_abs = WrapType("opset13.Abs", pattern_mul) 
    pattern_relu = WrapType(["opset13.Relu", "opset13.Sigmoid"], pattern_abs) 

    # マッチャーを作成し、ノードを一致させます 
    matcher = Matcher(pattern_relu, "FindPattern") 

    # 同じパターンが 2 つの異なるノードに完全に一致 
    assert matcher.match(model_relu) 
    assert matcher.match(model_sig)
TEST(pattern, wrap_type_list) { 
    // サンプルモデルを作成 
    PartialShape shape{2, 2}; 
    auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0)); 
    auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false); 
    auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0)); 
    auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0)); 
    auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0)); 
    auto model_sig = std::make_shared<ov::op::v0::Sigmoid>(model_abs->output(0)); 
    auto model_result1 = std::make_shared<ov::op::v0::Result>(model_sig->output(0)); 

    // サンプルモデルを作成 
    auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()}); 
    auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)}); 
    auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu, ov::op::v0::Sigmoid>({pattern_abs->output(0)}); 

    // マッチャーを作成し、ノードを一致させる 
    TestMatcher tm; 

    // 同じパターンが 2 つの異なるノードに完全に一致 
    ASSERT_TRUE(tm.match(pattern_relu, model_relu)); 
    ASSERT_TRUE(tm.match(pattern_relu, model_sig)); 
}

pattern_sig はリスト ["opset13.Relu", "opset13.Sigmoid"] を使用して作成されることに注意してください。つまり、Relu または Sigmoid のいずれかになります。この機能により、同じパターンを異なるノードに対して照合できるようになります。基本的に、WrapType は “リストされたタイプのいずれか” を表すことができます。WrapType は 2 つ以上のタイプ指定をサポートします。

ノードにチェックを追加するには、関数またはラムダを指定してプレディケートを作成します。この関数はマッチング中に実行され、関数のロジックで指定された検証を行います。例えば、特定のノードのコンシューマーの数を確認したい場合があります:

 WrapType("opset13.Relu", AnyInput(), consumers_count(2))
ov::pass::pattern::wrap_type<ov::op::v0::Relu>
({pattern::any_input()}, pattern::consumers_count(2));

2. AnyInput#

AnyInput は、ノードに対して特定の入力を指定する必要がない場合に使用されます。

# 任意の入力を受け取る MatMul ノードを使用してパターンを作成します。 
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()]) 
pattern_abs = WrapType("opset13.Abs", pattern_mul) 
pattern_relu = WrapType("opset13.Relu", pattern_abs)
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()}); 

auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)}); 

auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});

入力の追加チェックが必要な場合、プレディケートを使用して AnyInput() を作成することもできます。これは、ラムダまたは関数を使用した WrapType に似ています。例えば、入力のランクが 4 であることを確認するには、次のようにします:

# ランク 4 の入力を受け取る MatMul ノードを使用してパターンを作成 
pattern_mul = WrapType("opset13.MatMul", [AnyInput(lambda output: 
        len(output.get_shape()) == 4), AnyInput(lambda output: 
        len(output.get_shape()) == 4)]) 

pattern_abs = WrapType("opset13.Abs", pattern_mul) 

pattern_relu = WrapType("opset13.Relu", pattern_abs)
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input([](const Output<Node>& value){ 

return value.get_shape().size() == 4;}), 

pattern::any_input([](const Output<Node>& value){ 

return value.get_shape().size() == 4;})}); 
    auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)}); 
    auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});

3. Or#

Or 関数は WrapType と似ていますが、WrapType はリストに提供されたタイプの 1 つにしか一致できないのに対し、Or はノードの異なるブランチに一致するために使用されます。モデルを 2 つの異なるノードシーケンスと照合することが目標であるとします。Or タイプは、次のように 2 つの異なるブランチ (Or は 3 つ以上のブランチをサポートします) を作成することでこれを行います:

../../../_images/or_branches.png

赤いブランチは一致しませんが、青いブランチでは完璧に機能します。コードで示すと次のようになります:

def pattern_or(): 
    model_param1 = ops.parameter(PartialShape([2, 2])) 
    model_param2 = ops.parameter(PartialShape([2, 2])) 
    model_add = ops.add(model_param1, model_param2) 
    model_param3 = ops.parameter(PartialShape([2, 2])) 
    model_mul = ops.matmul(model_add, model_param3, False, False) 
    model_abs = ops.abs(model_mul) 
    model_relu = ops.relu(model_abs) 
    model_result = ops.result(model_relu) 

    # 赤いブランチを作成 
    red_pattern_add = WrapType("opset13.Add", [AnyInput(), AnyInput()]) 
    red_pattern_relu = WrapType("opset13.Relu", red_pattern_add) red_pattern_sigmoid = WrapType(["opset13.Sigmoid"], 
    red_pattern_relu) 

    # 青いブランチを作成 
    blue_pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()]) 
    blue_pattern_abs = WrapType("opset13.Abs", blue_pattern_mul) 
    blue_pattern_relu = WrapType(["opset13.Relu"], blue_pattern_abs) 

    # Or ノードを作成 
    pattern_or = Or([red_pattern_sigmoid, blue_pattern_relu]) 

    # マッチャーを作成し、ノードを一致させる 
    matcher = Matcher(pattern_or, "FindPattern") 

    # 同じパターンが 2 つの異なるノードに完全に一致 
    assert matcher.match(model_relu)
TEST(pattern, pattern_or) { 
    // Create a sample model 
    PartialShape shape{2, 2}; 
    auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0)); 
    auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false); 
    auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0)); 
    auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0)); 
    auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0)); 

    // 赤いブランチを作成 
    auto red_pattern_add = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()}); 
    auto red_pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({red_pattern_add->output(0)}); 
    auto red_pattern_sigmoid = ov::pass::pattern::wrap_type<ov::op::v0::Sigmoid>({red_pattern_relu->output(0)}); 

    // 青いブランチを作成 
    auto blue_pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()}); 
    auto blue_pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({blue_pattern_mul->output(0)}); 
    auto blue_pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({blue_pattern_abs->output(0)}); 

    // Or ノードを作成 
    auto pattern_or = std::make_shared<ov::pass::pattern::op::Or>(OutputVector{red_pattern_sigmoid->output(0), blue_pattern_relu->output(0)}); 

    // マッチャーを作成し、ノードを一致させる 
    TestMatcher tm; 

    // 同じパターンが 2 つの異なるノードに完全に一致 
    ASSERT_TRUE(tm.match(pattern_or, model_relu)); 
}

最初に一致するブランチの一致は成功し、残りのブランチはチェックされないことに注意してください。

4. Optional#

Optional は若干トリッキーです。モデル内にノードが存在するか存在しないかを指定できます。内部的には、パターンは Or を使用して 2 つのブランチを作成します。1 つはオプションノードが存在するブランチ、もう 1 つはオプションノードがないブランチです。Optional を 2 つのブランチに展開すると、次のようになります:

../../../_images/optional.png

モデルのコードは次のようになります:

def pattern_optional_middle(): 
    model_param1 = ops.parameter(PartialShape([2, 2])) 
    model_param2 = ops.parameter(PartialShape([2, 2])) 
    model_add = ops.add(model_param1, model_param2) 
    model_param3 = ops.parameter(PartialShape([2, 2])) 
    model_mul = ops.matmul(model_add, model_param3, False, False) 
    model_abs = ops.abs(model_mul) 
    model_relu = ops.relu(model_abs) 
    model_result = ops.result(model_relu) 

    # 中央にオプションノードを配置したサンプルパターンを作成 
    pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()]) 
    pattern_abs = WrapType("opset13.Abs", pattern_mul) 
    pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_abs) 
    pattern_relu = WrapType("opset13.Relu", pattern_sig_opt) 

    # マッチャーを作成し、ノードを一致させる 
    matcher = Matcher(pattern_relu, "FindPattern") 

    # 完全に一致するはずです 
    assert matcher.match(model_relu)
TEST(pattern, pattern_optional_middle) { 
    // サンプルモデルを作成 
    PartialShape shape{2, 2}; 
    auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0)); 
    auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false); 
    auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0)); 
    auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0)); 
    auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0)); 

    // 中央にオプションノードを配置したサンプルパターンを作成 
    auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()}); 
    auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)}); 
    auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>({pattern_abs->output(0)}); 
    auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_sig_opt->output(0)}); 

    // マッチャーを作成し、ノードを一致させる 
    TestMatcher tm; 

    // 完全に一致するはずです 
    ASSERT_TRUE(tm.match(pattern_relu, model_relu)); 
}

Optional は必ずしもパターンの中央にある必要はありません。トップノードとルートノードになることもできます。

トップノード:

def pattern_optional_top(): 
    model_param1 = ops.parameter(PartialShape([2, 2])) 
    model_param2 = ops.parameter(PartialShape([2, 2])) 
    model_add = ops.add(model_param1, model_param2) 
    model_param3 = ops.parameter(PartialShape([2, 2])) 
    model_mul = ops.matmul(model_add, model_param3, False, False) 
    model_abs = ops.abs(model_mul) 
    model_relu = ops.relu(model_abs) 
    model_result = ops.result(model_relu) 

    # オプションのトップノードにサンプルパターンを作成 
    pattern_sig_opt = Optional(["opset13.Sigmoid"], AnyInput()) 
    pattern_mul = WrapType("opset13.MatMul", [pattern_sig_opt, AnyInput()]) 
    pattern_abs = WrapType("opset13.Abs", pattern_mul) 
    pattern_relu = WrapType("opset13.Relu", pattern_abs) 

    matcher = Matcher(pattern_relu, "FindPattern") 

    # MatMul にシグモイドがなくても完全に一致する 
    assert matcher.match(model_relu)
TEST(pattern, pattern_optional_top) { 
    // サンプルモデルを作成 
    PartialShape shape{2, 2}; 
    auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0)); 
    auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false); 
    auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0)); 
    auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0)); 
    auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0)); 

    // オプションのトップノードにサンプルパターンを作成 
    auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern::any_input()); 
    auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern_sig_opt, pattern::any_input()}); 
    auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)}); 
    auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)}); 

    // マッチャーを作成し、ノードを一致させる 
    TestMatcher tm; 

    // 完全に一致するはず 
    ASSERT_TRUE(tm.match(pattern_relu, model_relu)); 
}

ルートノード:

def pattern_optional_root(): 
    model_param1 = ops.parameter(PartialShape([2, 2])) 
    model_param2 = ops.parameter(PartialShape([2, 2])) 
    model_add = ops.add(model_param1, model_param2) 
    model_param3 = ops.parameter(PartialShape([2, 2])) 
    model_mul = ops.matmul(model_add, model_param3, False, False) 
    model_abs = ops.abs(model_mul) 
    model_relu = ops.relu(model_abs) 
    model_result = ops.result(model_relu) 

    # サンプルパターンを作成 
    pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()]) 
    pattern_abs = WrapType("opset13.Abs", pattern_mul) 
    pattern_relu = WrapType("opset13.Relu", pattern_abs) 
    pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_relu) 

    matcher = Matcher(pattern_sig_opt, "FindPattern") 

    # ルートとしてシグモイドがなくても完全に一致するはず 
    assert matcher.match(model_relu)
TEST(pattern, pattern_optional_root) { 
    // サンプルモデルを作成 
    PartialShape shape{2, 2}; 
    auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0)); 
    auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape); 
    auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false); 
    auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0)); 
    auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0)); 
    auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0)); 

    // オプションのトップノードにサンプルパターンを作成 
    auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()}); 
    auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)}); 
    auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)}); 
    auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern_relu); 

    // マッチャーを作成し、ノードを一致させる 
    TestMatcher tm; 

    // 完全に一致するはず 
    ASSERT_TRUE(tm.match(pattern_relu, model_relu)); 
}

Optional は、WrapTypeAnyInput と同じ方法でプレディケートの追加もサポートします:

pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_relu, consumers_count(1))
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern_relu, pattern::consumers_count(2));