グラフ

グラフ

CalculatorGraphConfig プロトコルは、デバイスのトポロジと機能を指定します。 MediaPipe グラフ。グラフの各 node は、特定の計算機または 必要な構成を指定します。たとえば、登録済みの 計算/サブグラフ タイプ、入力、出力、オプション フィールド オプション、入力ポリシー、エグゼキュータがあります。 同期

CalculatorGraphConfig には、グローバルなグラフレベルを構成するためのフィールドが他にもいくつかあります。 必要があります。例:グラフ エグゼキュータの構成、スレッド数、最大キューサイズ 生成します。グラフレベルのいくつかの設定は、 さまざまなプラットフォーム(パソコンとモバイルなど)でのグラフのパフォーマンスを確認できます。対象 負荷の高いモデル推定計算ツールを別のコンピュータ システムに エグゼキュータはリアルタイム アプリケーションのパフォーマンスを改善できます。 スレッドの局所性を有効にします。

以下は、一連の CalculatorGraphConfig の簡単な例です。 パススルー計算ツール :

# This graph named main_pass_throughcals_nosubgraph.pbtxt contains 4
# passthrough calculators.
input_stream: "in"
output_stream: "out"
node {
    calculator: "PassThroughCalculator"
    input_stream: "in"
    output_stream: "out1"
}
node {
    calculator: "PassThroughCalculator"
    input_stream: "out1"
    output_stream: "out2"
}
node {
    calculator: "PassThroughCalculator"
    input_stream: "out2"
    output_stream: "out3"
}
node {
    calculator: "PassThroughCalculator"
    input_stream: "out3"
    output_stream: "out"
}

MediaPipe は、複雑なグラフの代替の C++ 表現(ML パイプライン、モデル メタデータの処理、オプションのノードなど)を提供します。上記のグラフは次のようになります。

CalculatorGraphConfig BuildGraphConfig() {
  Graph graph;

  // Graph inputs
  Stream<AnyType> in = graph.In(0).SetName("in");

  auto pass_through_fn = [](Stream<AnyType> in,
                            Graph& graph) -> Stream<AnyType> {
    auto& node = graph.AddNode("PassThroughCalculator");
    in.ConnectTo(node.In(0));
    return node.Out(0);
  };

  Stream<AnyType> out1 = pass_through_fn(in, graph);
  Stream<AnyType> out2 = pass_through_fn(out1, graph);
  Stream<AnyType> out3 = pass_through_fn(out2, graph);
  Stream<AnyType> out4 = pass_through_fn(out3, graph);

  // Graph outputs
  out4.SetName("out").ConnectTo(graph.Out(0));

  return graph.GetConfig();
}

詳しくは、C++ でグラフを作成するをご覧ください。

サブグラフ

CalculatorGraphConfig をサブモジュールにモジュール化し、再利用を支援する MediaPipe グラフは Subgraph として定義できます。「 サブグラフのパブリック インターフェースは、一連の入力ストリームと出力ストリームで構成される 計算ツールの公開インターフェースに似ていますその後、サブグラフを 計算機のように CalculatorGraphConfig を返します。MediaPipe グラフが CalculatorGraphConfig から読み込んだ場合、各サブグラフ ノードは 計算のグラフですその結果、セマンティクスとパフォーマンスが サブグラフの は、対応する計算機のグラフと同じです。

TwoPassThroughSubgraph という名前のサブグラフを作成する方法の例を次に示します。

  1. サブグラフの定義。

    # This subgraph is defined in two_pass_through_subgraph.pbtxt
    # and is registered as "TwoPassThroughSubgraph"
    
    type: "TwoPassThroughSubgraph"
    input_stream: "out1"
    output_stream: "out3"
    
    node {
        calculator: "PassThroughCalculator"
        input_stream: "out1"
        output_stream: "out2"
    }
    node {
        calculator: "PassThroughCalculator"
        input_stream: "out2"
        output_stream: "out3"
    }
    

    サブグラフに対する公開インターフェースは、次のものから構成されます。

    • 入力ストリームをグラフ化する
    • 出力ストリームをグラフ化する
    • 入力側のパケットをグラフ化する
    • 出力側のパケットをグラフ化する
  2. BUILD ルール mediapipe_simple_subgraph を使用してサブグラフを登録します。「 パラメータ register_as は、新しいサブグラフのコンポーネント名を定義します。

    # Small section of BUILD file for registering the "TwoPassThroughSubgraph"
    # subgraph for use by main graph main_pass_throughcals.pbtxt
    
    mediapipe_simple_subgraph(
        name = "twopassthrough_subgraph",
        graph = "twopassthrough_subgraph.pbtxt",
        register_as = "TwoPassThroughSubgraph",
        deps = [
                "//mediapipe/calculators/core:pass_through_calculator",
                "//mediapipe/framework:calculator_graph",
        ],
    )
    
  3. メイングラフのサブグラフを使用します。

    # This main graph is defined in main_pass_throughcals.pbtxt
    # using subgraph called "TwoPassThroughSubgraph"
    
    input_stream: "in"
    node {
        calculator: "PassThroughCalculator"
        input_stream: "in"
        output_stream: "out1"
    }
    node {
        calculator: "TwoPassThroughSubgraph"
        input_stream: "out1"
        output_stream: "out3"
    }
    node {
        calculator: "PassThroughCalculator"
        input_stream: "out3"
        output_stream: "out4"
    }
    

グラフ オプション

「グラフ オプション」とMediaPipe グラフ用の protobuf Calculator Options と類似 MediaPipe 計算ツールに指定された protobuf。これらの「グラフ オプション」は グラフの呼び出し先を指定し、電卓オプションと サブグラフ オプションがあります。

CalculatorGraphConfig では、サブグラフにグラフ オプションを指定できます。 計算オプションとまったく同じです

node {
  calculator: "FlowLimiterCalculator"
  input_stream: "image"
  output_stream: "throttled_image"
  node_options: {
    [type.googleapis.com/mediapipe.FlowLimiterCalculatorOptions] {
      max_in_flight: 1
    }
  }
}

node {
  calculator: "FaceDetectionSubgraph"
  input_stream: "IMAGE:throttled_image"
  node_options: {
    [type.googleapis.com/mediapipe.FaceDetectionOptions] {
      tensor_width: 192
      tensor_height: 192
    }
  }
}

CalculatorGraphConfig では、グラフ オプションを受け入れて入力できます。 次のようなオプションがあります。

graph_options: {
  [type.googleapis.com/mediapipe.FaceDetectionOptions] {}
}

node: {
  calculator: "ImageToTensorCalculator"
  input_stream: "IMAGE:image"
  node_options: {
    [type.googleapis.com/mediapipe.ImageToTensorCalculatorOptions] {
        keep_aspect_ratio: true
        border_mode: BORDER_ZERO
    }
  }
  option_value: "output_tensor_width:options/tensor_width"
  option_value: "output_tensor_height:options/tensor_height"
}

node {
  calculator: "InferenceCalculator"
  node_options: {
    [type.googleapis.com/mediapipe.InferenceCalculatorOptions] {}
  }
  option_value: "delegate:options/delegate"
  option_value: "model_path:options/model_path"
}

この例では、FaceDetectionSubgraph はグラフ オプション protobuf を受け入れます。 FaceDetectionOptionsFaceDetectionOptions は、いくつかのフィールドを定義するために使用されます。 計算オプションの ImageToTensorCalculatorOptions と一部のフィールドに値が含まれています。 サブグラフ オプション InferenceCalculatorOptions の値。フィールド値 option_value: 構文を使用して定義されます。

CalculatorGraphConfig::Node プロトコル バッファで、フィールド node_options:option_value: は一緒に、次のような計算ツールのオプション値を定義します。 ImageToTensorCalculatornode_options: フィールドは、一連のリテラルを定義します。 テキスト protobuf 構文を使用して定数値を生成します。各 option_value: フィールド 含まれる 1 つの情報を使用して、1 つの protobuf フィールドの値を定義 特に包含のグラフ オプションのフィールドの値から 表示されます。上記の例では、option_value: "output_tensor_width:options/tensor_width" は、フィールドを定義します。 次の値を使用して ImageToTensorCalculatorOptions.output_tensor_width FaceDetectionOptions.tensor_width

option_value: の構文は input_stream: の構文に似ています。「 構文は option_value: "LHS:RHS" です。LHS は電卓オプションを明示 RHS はグラフ オプション フィールドを示します。具体的には LHS は RHS は、ネストされたフィールドを識別する、一連の protobuf フィールド名から構成される 「/」で区切られた protobuf メッセージとフィールド。これは「ProtoPath」と呼ばれ 説明します。LHS または RHS で参照されるネストされたメッセージは、 含まれる protobuf に定義されている文字列を option_value:

季節と時間

デフォルトでは、MediaPipe は計算機グラフが非巡回であることを求め、サイクルを扱います。 エラーとして表示されます。グラフにサイクルがある場合、サイクルは アノテーションが付けられます。このページでは、その方法について説明します。

注: 現在のアプローチは試験運用版であり、変更される可能性があります。歓迎 フィードバックをお寄せください。

CalculatorGraphTest.Cycle 単体テストを使用してください mediapipe/framework/calculator_graph_test.cc をサンプルコードとして使用。以下は、 循環グラフが作成されます加算器の sum 出力は、 整数ソース計算ツールで生成された整数。

整数のストリームを追加する巡回グラフ

このシンプルなグラフは、巡回グラフのサポートにおけるすべての問題を示しています。

バックエッジのアノテーション

各サイクルのエッジには、バックエッジとしてアノテーションを付ける必要があります。これにより、 MediaPipe のトポロジソートが機能するように、後端をすべて削除しました。

通常、後端を選択するには複数の方法があります。どのエッジがマークされますか バックエンド エッジがどのノードをアップストリームとみなすか、どのノードをアップストリーム これは下流とみなされるため、MediaPipe が割り当てる優先度に影響します。 ノードにルーティングされます。

たとえば、CalculatorGraphTest.Cycle テストでは、old_sum のエッジが 遅延ノードは加算器のダウンストリーム ノードと より優先度が高くなります。または、sum を バックエッジとして遅延ノードに入力され、 アップストリーム ノードと見なされ、より低い優先度が与えられます。

最初のパケット

整数の最初の整数が入力された時点で加算器計算ツールを実行可能にするには、 到着したときに、値が 0、かつ同じ値を持つ最初のパケットが必要です。 加算器への old_sum 入力ストリームのタイムスタンプ。この最初のパケットは、 Open() メソッドの遅延計算ツールで出力する必要があります。

ループ再生の遅延

各ループで、前の sum 出力を次の出力に合わせて調整するために遅延が発生します。 使用します。これは遅延ノードによっても行われます。そのため遅延ノードは 整数ソース計算ツールのタイムスタンプについて、以下を把握してください。

  • 最初の出力のタイムスタンプ。

  • 連続する出力間のタイムスタンプのデルタ。

パケットのみを考慮した代替スケジューリング ポリシーを追加する予定です。 パケットのタイムスタンプが無視されるため、この不便さがなくなります。

1 つの入力ストリームが終了したときの電卓の早期終了

デフォルトでは、MediaPipe は、次の場合にソース以外の計算ツールの Close() メソッドを呼び出します。 すべての入力ストリームが完了しましたサンプルのグラフでは、2 つ目の Pod に 加算器ノードを返すことができます。これを実現するのが 代替入力ストリーム ハンドラで加算器ノードを構成する EarlyCloseInputStreamHandler

関連するソースコード

遅延計算ツール

最初のパケットを出力する Open() のコードと、 入力パケットに(単位)の遅延を追加する Process()。前述のとおり、この 遅延ノードは、その出力ストリームが入力ストリームとともに使用され、 パケットのタイムスタンプ 0、1、2、3、...

class UnitDelayCalculator : public Calculator {
 public:
  static absl::Status FillExpectations(
      const CalculatorOptions& extendable_options, PacketTypeSet* inputs,
      PacketTypeSet* outputs, PacketTypeSet* input_side_packets) {
    inputs->Index(0)->Set<int>("An integer.");
    outputs->Index(0)->Set<int>("The input delayed by one time unit.");
    return absl::OkStatus();
  }

  absl::Status Open() final {
    Output()->Add(new int(0), Timestamp(0));
    return absl::OkStatus();
  }

  absl::Status Process() final {
    const Packet& packet = Input()->Value();
    Output()->AddPacket(packet.At(packet.Timestamp().NextAllowedInStream()));
    return absl::OkStatus();
  }
};

グラフ構成

back_edge アノテーションと代替の input_stream_handler に注意してください。

node {
  calculator: 'GlobalCountSourceCalculator'
  input_side_packet: 'global_counter'
  output_stream: 'integers'
}
node {
  calculator: 'IntAdderCalculator'
  input_stream: 'integers'
  input_stream: 'old_sum'
  input_stream_info: {
    tag_index: ':1'  # 'old_sum'
    back_edge: true
  }
  output_stream: 'sum'
  input_stream_handler {
    input_stream_handler: 'EarlyCloseInputStreamHandler'
  }
}
node {
  calculator: 'UnitDelayCalculator'
  input_stream: 'sum'
  output_stream: 'old_sum'
}