Workflowsコンパイラ
WorkflowsコンパイラがWorkflow定義を実行可能な計算グラフに変換する仕組み。
コンパイルとは、プログラミング言語で記述されたドキュメントを受け取り、その正しさを検証し、実行環境が理解できる形式に変換するプロセスです。
ワークフロー定義を実行したい場合、Workflows エコシステムでも同様のプロセスが発生します。Workflows Compiler は、JSON ドキュメントを計算グラフへ変換するために複数のステップを実行し、そのグラフは Workflows Execution Engine によって実行されます。このプロセスは複雑になることがありますが、エコシステムに貢献する開発者にとって、その理解は役立ちます。このドキュメントでは、Workflow ブロックの構築を支援し、コア Execution Engine への貢献を促進するため、コンパイルプロセスの重要な詳細を説明します。
このドキュメントでは、Execution Engine の設計について扱います v1 (これは現在の安定版です)。以下に関する情報をご確認ください バージョニング Execution Engine の開発サイクルを理解するために。
コンパイルの段階
ワークフローのコンパイルには、次のようないくつかの段階があります。
利用可能なブロックの読み込み: 実行環境の設定に基づき、ワークフローで使用できるすべてのブロックを収集する
動的ブロックのコンパイル: 動的ブロック定義 を標準の Workflow ブロックに変換する
ワークフロー定義の解析: ワークフローを定義する JSON ドキュメントを読み取り、解釈し、構文エラーを検出する
Workflow 実行グラフの構築: 実行中にデータがワークフロー内をどのように流れるかを定義するグラフを作成し、Workflow の整合性を検証する
ブロックからの Workflow ステップの初期化: 利用可能なブロック、ステップ定義、実行環境の設定に基づいて、個々のワークフローステップをセットアップする。
定義に roboflow_core/inner_workflow@v1 ステップが含まれている場合、コンパイラは まず 保存済みワークフロー参照を解決し、ネストした 合成 (深さ、総数、非循環性)を検証し、 インライン展開し、 子ステップを親に これは、メインの「ワークフロー定義の解析」ステップの 解析および実行グラフ構築の前に処理します。完全なパイプラインについては、 内部ワークフロー(ネストされた定義) を参照してください。 parameter_bindings、および環境の制限。
ワークフローの各コンパイルステップを詳しく見ていきましょう。
Workflows ブロックの読み込み
以下で説明されているように ブロックのバンドルガイド、Workflow ブロックのグループはワークフロープラグインとしてパッケージ化できます。プラグインは基本的に標準の Python ライブラリであり、そのメインモジュールで特定の関数を公開することで、Workflow ブロックを動的に読み込めるようにします。
Workflows Compiler と Execution Engine は、特定の Workflow ブロックに依存しないよう設計されており、Compiler にはプラグインからブロックを検出して読み込む機能があります。
Roboflow は roboflow_core プラグインを提供しています。これには基本的な Workflow ブロックのセットが含まれ、Compiler とこれらのブロックの両方が inference パッケージにバンドルされているため、常に Compiler によって読み込まれます。
カスタムプラグインの場合、Python 環境にインストールされた後、 WORKFLOWS_PLUGINSという環境変数を使用して参照する必要があります。この変数には、プラグインを含む Python パッケージの名前をカンマ区切りで指定します。
たとえば、2 つのカスタムプラグイン、 numpy_plugin と pandas_pluginがある場合、次のように設定して Workflows 環境で有効にできます。
どちらも numpy_plugin と pandas_plugin ライブラリリポジトリへのパスではなく、プラグインを提供するライブラリのメインモジュール名です(import numpy_plugin が Python 環境で動作しなければ、プラグインを読み込むことはできません)。
Compiler がすべてのプラグインを読み込むと、次のコンパイル段階に進む準備が整います。
動的ブロックのコンパイル
以下のトピックである 動的 Python ブロック については、別のドキュメントページで扱っています。このセクションの内容を理解するために必要なのは、Workflow 定義内で Workflow ブロックをインプレースで定義する方法があることです。つまり、ブロックマニフェストと Python コードの両方を JSON ドキュメントで指定します。この機能は、自身のハードウェアで Workflows Execution Engine を実行する場合にのみ使用でき、Roboflow のホストプラットフォームでは無効化されています。
Workflows Compiler は、Workflow 定義内で直接定義された Dynamic Python Blocks を、実行時に完全な Workflow ブロックへ変換できます。Compiler はブロック定義に基づいてこれらのブロッククラスを動的に生成するため、開発者がプラグインの場合のように手動で作成する必要はありません。
このプロセスが完了すると、動的ブロックは利用可能な Workflow ブロックのプールに追加されます。これらのブロックは、その後、 steps Workflow 定義のセクションで、ほかの標準ブロックと同様に使用できます。
Workflow 定義の解析
すべての Workflow ブロックが読み込まれると、Compiler は各ブロックのマニフェストクラスを取得します。これらのマニフェストは pydantic データクラスであり、定義内のステップエントリの構造を定義します。解析段階では、たとえば次のような Workflows 定義のエラーが通知されます。
存在しないブロックの使用
ステップの無効な設定
ステップに必要なパラメータの不足
以下のおかげで pydantic、Workflows Compiler は独自のパーサーを必要としません。さらに、ブロック作成者は標準の Python ライブラリを使用してブロックマニフェストを定義できます。
Workflow 実行グラフの構築
Workflow 実行グラフの構築は、Workflow コンパイルにおける最も重要な段階です。仕組みは次のとおりです。
頂点の追加
まず、各入力、ステップ、出力がグラフの頂点として追加され、将来の識別のために各頂点には特別なラベルが付与されます。これらの頂点には、データ来歴追跡用のシードを入力頂点に付与するなどのメタデータも含まれます(これについては後述します)。
エッジの追加
頂点を配置した後、次のステップは Workflow 内で定義されたセレクターに基づいて頂点間にエッジを作成することです。Compiler はブロックマニフェストを調べ、どのプロパティがセレクターを受け入れられるか、およびそれらのセレクターに期待される「種類」を判定します。これにより、Compiler は次のような Workflow 定義のエラーを検出できます。
あるステップの出力の種類が、次のステップの入力に期待される種類と一致しない。
存在しないステップまたは入力を参照している。
各エッジには、出力データがどの入力プロパティに供給されるかを示すメタデータも含まれ、これはコンパイルの後段および実行時に役立ちます
通常、ステップ入力はステップ出力からデータを「要求」し、Step B の処理中に Step A の出力から Step B の入力へのエッジを形成します。ただし、 フロー制御ブロック は例外で、データを受け入れると同時にマニフェスト内で他のステップを宣言し、グラフ内に特別なフロー制御エッジを作成します。
構造検証
グラフが構築されると、Compiler はグラフが適切に実行できることを保証するため、循環などの構造上の問題を確認します。
データ来歴の検証
最後に、データ来歴プロパティが入力ノードから設定され、グラフ全体に伝播されます。では、データ来歴とは何でしょうか。来歴は、ステップを通じたバッチの作成とネストを追跡する識別子のリストであり、次のことを判定します。
データのソースパス
次元レベルデータのステップが参照する可能性のある異なるデータ断片の互換性。これにより、ステップが複数のソースから対応するバッチ要素のみを取得することが保証されます(つまりバッチ要素インデックス
例: (1, 2)は、2 つのバッチ指向入力がステップに接続された場合、同じデータ断片を正確に参照し、一緒に処理しても意味をなさない異なる来歴を持つランダムなバッチを参照するわけではありません)
ステップによって新しいネストされたバッチが作成されるたびに、一意の識別子が出力の来歴に追加されます。これにより、Compiler はステップ間の入力に互換性があるかを追跡および検証できます。
データ来歴の基本的な前提は、すべてのバッチ指向入力に同じ来歴識別子が与えられることです。したがって暗黙的に、すべての入力バッチには、バッチ内の対応する位置に対応するデータポイントを持つデータが供給されることが強制されます。たとえば、Workflow で image_1 to image_2 を比較する場合(これら 2 つの入力を Workflow 定義で宣言した場合)、Compiler は image_1[3] が image_2[3].
に対応すると仮定します。来歴追跡により、Compiler は潜在的な誤りを検出できます。たとえば、2 つの動的クロップ出力を単一ステップの入力に接続しようとすると、Compiler は各出力内のクロップ数が一致しない可能性を検知します。これにより、インデックスが一致しないネストされたバッチ要素が生じ、これを防止しない場合、実行中に予測不能な結果につながる可能性があります。
来歴不一致の例
次のシナリオを想像してください。
Workflow 内で単一の画像入力を宣言する
最初に、2 つの異なるモデルを使用して物体検出を実行する
2 つの動的クロップステップを使用し、それぞれ 1 番目と 2 番目のモデル予測に基づいてクロップする
次に、2 つの画像特徴を比較するブロックを使用したい(従来のコンピュータビジョン手法を使用)
これら 2 つのクロップステップからの入力を比較ブロックに接続すると、何が起こると期待しますか?
以下がなければ 来歴を追跡せずに、これら 2 つのバッチを「フラット化」して「zip」し、画像のペアを比較ブロックに渡すことになります。しかし問題は、この場合、それらの要素間の比較が実際に意味を持つかを判定できないことです。おそらく意味を持ちません。
以下があれば 来歴追跡により、Compiler は最後のネストレベルに関して一致しない来歴を持つ 2 つのバッチを供給しようとしていることを認識し、コンパイルエラーを発生させます。
「なるほど、しかし両方のクロップに二次分類器を適用し、最後に結果をマージして、すべての結果を単一の出力で取得したい場合は可能ですか?」と疑問に思うかもしれません。答えは はい です。前述のとおり、ネストされたバッチは最後の来歴レベルでのみ異なります。そのため、「次元圧縮」カテゴリのブロックを使用すると、二次分類器の結果を 次元レベル 一致する来歴を持つレベル 1 のバッチに整列できます。
以下のセクションで説明されているように ブロック開発、各ブロックはその入力および出力に期待される次元性を定義できます。これはデータをどのように構造化すべきかを指します。たとえば、ブロックが image バッチより 1 レベル上の入力を必要とする場合 predictions、Compiler は Workflow ステップの検証時にこの要件が満たされていることを確認します。ステップ間の接続が期待される次元性と一致しない場合、エラーが発生します。さらに、各入力についてデータ来歴に基づく互換性も検証されます。ステップが検証に合格すると、出力の次元性が決定され、後続ステップとの互換性の確認に使用されます。
ブロックは次元性の要件を絶対値ではなく相対的な条件で定義することに注意することが重要です。つまり、ブロックは入力と出力の間の次元性の差分(またはオフセット)を指定します。この方法により、ブロックは任意の次元性レベルで柔軟に動作できます。
バージョン 1 では、Workflows Compiler は 2 種類の異なる 次元レベルにまたがって動作するブロックのみをサポートします。これは設計を単純に保つためです。今後、より多くの 次元レベル を扱うブロックが必要になった場合は、このサポートの拡張を検討します。
フロー制御の表現
Workflows Compiler は、Execution Engine がワークフロー内のフロー制御構造を管理するのを支援します。これは、フロー制御が特定ステップの入力構築およびワークフローグラフの実行にどのような影響を与えるかをシステムが理解できるようにする、特定の属性をマークします(詳細については、 Execution Engine ドキュメント).
を参照してください)。 データ来歴の検証.
ワークフロー構造が正しいことを保証するため、Compiler は、次のセクションで説明した方法と同様に、フロー制御ステップのデータ来歴を確認します。
フロー制御ステップが非バッチ指向入力に対して動作する この場合、フロー制御ステップは、入力がデータのバッチであっても、接続されたステップ(および関連ステップ)の実行を完全に許可または阻止できます。すべてのバッチ要素が影響を受けます。
フロー制御ステップが、互換性のある来歴を持つバッチ指向入力に対して動作する ここでは、フロー制御ステップはバッチ内の各要素について、どれを続行し、どれを停止するかを個別に決定できます。
バッチ指向性の互換性
前述のように、Workflows は バッチ指向データ と スカラーを定義します。 Workflows におけるデータの性質の説明から、バッチ指向データに対して実行される操作には、ほぼ同等の 2 つの実行方法があることもわかります。
一括処理: データのバッチ全体を取得して処理する
逐次処理: バッチ要素をループし、順番に結果を取得する
Workflow ブロックがバッチを扱う際のデフォルトの方法は要素ごとに消費することなので、 実質的な違いはありません 次の間には バッチ指向データ と スカラー この場合、Execution Engine は単にバッチからスカラーを展開し、各ステップに渡します。
ブロックがバッチ入力を受け入れる場合、プロセスは複雑になる可能性があります。詳細については、 ブロック開発ガイドで学べますが、ブロックは提供が必須である各入力を バッチ単位 であること、また同時にバッチ指向データとスカラーの両方を供給できるすべての入力(これはかなり少ないケースです)を示す必要があります。そのような場合、 来歴 を使用して、各ステップ入力に実際に供給されるデータが バッチ または スカラーであるかどうかを推論します。違反が検出された場合(たとえば スカラー がバッチを必要とする入力に提供された場合、またはその逆の場合)、エラーが発生します。
将来の改善の可能性
現時点では、上記で説明した挙動が Workflows エコシステムの可能性を制限しているかどうかはわかりません。説明したメカニズムによるエラーのために Workflows を実行できない場合は、 GitHub Issues.
ブロックからの Workflow ステップの初期化
ドキュメントでは、Workflow Step は、そのプロトタイプとなる Workflow Block のインスタンスとしてよく参照されます。簡単に言えば、Workflow Block は特定の動作を実装するクラスであり、その動作は設定によってカスタマイズできます。設定は、Execution Engine を実行する環境、Workflow 定義、または実行時の入力によって設定されます。
プログラミングでは、通常は初期化パラメータを必要とするコンストラクターを使用して、クラスのインスタンスを作成します。同様に、Workflow 内のステップがそのブロックを参照すると、Workflow Blocks は Workflows Compiler によって初期化されます。特定の初期化パラメータが必要なブロックもあれば、不要なブロックもあります。
ブロックが初期化パラメータを必要とする場合:
ブロックは、必要とするパラメータを宣言する必要があります。これについては、以下で詳しく説明されています ブロック開発ガイド
これらのパラメータの値は、Workflow が実行される環境から提供する必要があります。
これらのパラメータの値は、Workflow が実行される環境から提供する必要があります。
この 2 番目の部分は難しく思えるかもしれませんので、例を見てみましょう。 ユーザーガイドの、 inference Python パッケージを使用して Workflows と統合する方法を示すセクションには、次のようなコードがあるかもしれません。
この例では、 workflow_init_parameters には ブロックの要求に基づいて Workflow ステップを初期化するときに Compiler が使用する値が含まれます。
初期化パラメータ(しばしば「init parameters」と呼ばれます)は、2 つの方法で Compiler に渡すことができます。
明示的: 特定の値(数値、文字列、オブジェクトなど)を指定します。
暗黙的: デフォルト値は Workflows プラグイン内で定義されます。特定の値の場合もあれば、環境変数などから動的に値を生成する、パラメータを受け取らない関数の場合もあります。
辞書 workflow_init_parameters には、明示的に渡された初期化パラメータが示されます。キーの構造は重要です。 {plugin_name}.{init_parameter_name}。次のように指定することもできます {init_parameter_name}ですが、これによりパラメータの解決方法が変わります。
パラメータはどのように解決されますか?
Compiler がブロックに必要な初期化パラメータを探す際は、次のプロセスに従います。
完全一致: まず、明示的に指定されたパラメータに完全一致するものがあるか確認します
{plugin_name}.{init_parameter_name}.デフォルトパラメータ: 一致するものが見つからない場合、プラグインのデフォルトパラメータを確認します。
一般一致: 最後に、単に
{init_parameter_name}と明示的に指定されたパラメータとの一般一致を探します。
このメカニズムにより柔軟性が得られます。一部のブロックパラメータにはデフォルト値を設定でき、他のパラメータは明示的に指定する必要があります。さらに、特定のパラメータを異なるプラグイン間で共有することもできます。
最終更新
役に立ちましたか?