> For the complete documentation index, see [llms.txt](https://docs.roboflow.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.roboflow.com/workflows/ja/gaido/developer-guide/workflow-execution.md).

# Workflowの実行

ワークフローの実行は複雑な विषयですが、効果的に始めるために細部まですべて理解する必要はありません。いくつかの基本概念を把握するだけで、Workflows エコシステムでの学習を大幅に加速できます。このドキュメントでは、基礎をすばやく理解し、より強力なアプリケーションを構築できるよう、明確でわかりやすい概要を示します。

より深い技術的な理解に関心がある方には、詳細情報について開発者ガイドをご覧いただくことをお勧めします。

## コンパイル

ワークフローの実行は、Workflow 定義のコンパイルから始まります。ご存じのとおり、Workflow 定義は入力、ステップ、出力、および要素間の接続を記述した JSON ドキュメントです。このドキュメントを実行可能な形式に変換するには、コンパイルする必要があります。

Execution Engine の視点から見ると、このプロセスは計算グラフを作成し、その整合性と正確性をチェックすることを意味します。この検証ステップは、問題を早期に見つけて警告でき、デバッグをより簡単かつ迅速にするため重要です。たとえば、互換性のないブロックを接続したり、無効なセレクタを使用したり、ワークフロー内にループを作成したりすると、コンパイラがエラーメッセージで通知します。

コンパイルが完了したら、ワークフローが実行準備完了であることを意味します。これは次のことを確認します:

* あなたのワークフローは、環境内の Execution Engine のバージョンと互換性があります。
* ワークフロー内のすべてのブロックが正常に読み込まれ、初期化されました。
* ブロック間の接続は有効です。
* ワークフローに対して提供した入力データが検証されました。

この時点で、Execution Engine はワークフローの実行を開始できます。

## ワークフロー実行におけるデータ

ワークフローを実行するたびに、毎回入力データを提供します。プログラミングにおける関数が異なる入力値を扱えるのと同じように、ワークフローも実行するたびに異なるデータを処理できます。ワークフローの実行をトリガーした後、データに何が起こるのかを見てみましょう。

ワークフローで定義された入力のプレースホルダーを置き換える形で入力データを提供します。これらのプレースホルダーは、セレクタを使ってワークフローのステップから参照されます。ステップが実行されると、その瞬間に提供した実際のデータが計算に使われます。その出力は、Workflow 定義で宣言されたステップ出力セレクタに基づいて後で他のステップで使用でき、ワークフローが完了してすべての出力が生成されるまでこのプロセスが続きます。

Workflow 定義内で固定値を持つパラメータを除けば、定義自体には実際のデータ値は含まれません。定義は、入力として提供したデータをどのように指示し、扱うかを Execution Engine に伝えるだけです。

## データとは？

ワークフロー内の入力データは、次の 2 種類に分けられます:

* 処理対象のバッチ指向データ: 結果を導き出すことを期待する、処理対象の主データです（例: モデルによる推論）。
* スカラー: これは、特定の設定や構成に使われる単一値です。

以下に示すような標準的なデータ処理について考えると、スカラーとバッチ指向データの区別は人工的に感じられるかもしれません。

```python
def is_even(number: int) -> bool:
    return number % 2 == 0
```

異なる値を `数` パラメータとして簡単に送信できるので、そのパラメータを 2 つのカテゴリのいずれかに関連付けることを気にする必要はありません。

```python
is_even(number=1)
is_even(number=2)
is_even(number=3)
```

状況は機械学習モデルになるとより複雑になります。次のような単純な関数である `is_even(...)`とは異なり、これは一度に 1 つの数値を処理しますが、ML モデルはしばしば複数のデータを一度に処理します。たとえば、分類モデルに 1 枚の画像だけを与える代わりに、通常は画像のリストを送信して、 **同じ操作** を各画像に対して一度に適用した予測を受け取ることができます。

これは、結果のリストを得るために各数値ごとに個別に呼び出す必要がある私たちの `is_even(...)` 関数とは異なります。この違いは ML モデルの動作、特に GPU がデータを処理する方法に由来します。つまり、多数のデータに同時に同じ操作を適用し、 [単一命令複数データ](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) 演算を実行するのです。

{% embed url="<https://www.youtube.com/watch?v=-P28LKWTzrI>" %}

この `is_even(...)` 関数は、次のようにループを使ってデータのバッチを扱えるように適応できます:

```python
results = []
for number in [1, 2, 3, 4]:
    results.append(is_even(number))
```

Workflows では通常、 **操作をデータのバッチにブロードキャストすることを** 心配する必要はありません。Execution Engine が裏側でそれを行ってくれますが、 *バッチ指向の* データの役割を理解したら、すべてのデータがバッチとして表現できるのか考えてみましょう。

分類モデルから予測を行う標準的な方法は、次の疑似コードで示せます:

```python
images = [PIL.Image(...), PIL.Image(...), PIL.Image(...), PIL.Image(...)]
model = MyClassificationModel()

predictions = model.infer(images=images, confidence_threshold=0.5)
```

おそらく、 `images` と `confidence_threshold`の違いに気づくでしょう。前者は単一の操作（モデルからの予測）を適用するためのデータのバッチであり、後者はバッチ内のすべての要素の処理に影響するパラメータです。この種のデータを私たちは **スカラー**.

{% hint style="success" %}
**の性質&#x20;*****バッチ*****&#x20;と&#x20;*****スカラー***

私たちが呼ぶ *スカラー* ものは、Workflows エコシステムにおいては、通常「単一の値」と結び付けられる数学用語と 100% 同じではありませんが、Workflows では少し異なる定義を好みます。

Workflows エコシステムでは、 *スカラー* は、処理される要素の数に関係なく一定に保たれるデータ片です。オブジェクトのリストを *スカラー* 値として持つことを妨げるものはありません。たとえば、入力画像のリストと固定された参照画像のリストがある場合、各入力を処理しても参照画像は変わりません。したがって、参照画像は *スカラー* データと見なされ、入力画像のリストは *バッチ指向の*.

**朗報です！**

Execution Engine `v1.6.0`、 *スカラー* と *バッチ* を扱う実際的な側面は Execution Engine にオフロードされます（ [changelog](/workflows/ja/gaido/developer-guide/execution-engine-changelog.md) で詳細を確認してください）。ブロック開発者としては、その違いを理解しておくことは依然として重要ですが、ブロックを構築するときにそのニュアンスまで深く考える必要はあまりありません。
{% endhint %}

この違いを示すために、Workflow 定義は 2 つのカテゴリの入力を保持します:

* **スカラー入力** - たとえば `WorkflowParameter`
* **バッチ入力** - たとえば `WorkflowImage`, `WorkflowVideoMetadata` または `WorkflowBatchInput`

単一の画像を `WorkflowImage` 入力として提供すると、自動的に展開されてバッチが形成されます。Workflow 定義に複数の `WorkflowImage` プレースホルダーが含まれている場合、実行のために提供する実際のデータは、これらすべての入力で同じバッチサイズでなければなりません。唯一の例外は、単一の画像を送信した場合で、それは他の入力のバッチサイズ要件に合わせるためにブロードキャストされます。

## ステップとデータの相互作用

次のシナリオにおけるステップ出力の性質について尋ねられたら、

* **A**: ステップはスカラー パラメータのみを入力として受け取ります。
* **B**: ステップはバッチ指向データを入力として受け取ります。
* **C**: ステップはスカラー パラメータとバッチ指向データの両方を入力として受け取ります。

おそらく、次のように答えるでしょう:

* A の場合、出力は非バッチになります。
* B と C の場合、出力はバッチになります。C では、非バッチ指向のパラメータはデータのバッチサイズに合わせてブロードキャストされます。

そのとおりです。これで、Workflows のエキスパートになるために理解すべき概念はあと 2 つだけです。

では、次のステップを持つ Workflow を作成したいとしましょう:

1. 入力画像のバッチからオブジェクトを検出する。
2. 検出された各オブジェクトを画像から切り出す。
3. 切り出した各オブジェクトを 2 つ目のモデルで分類し、詳細なラベルを追加する。

クロップのステップでデータに何が起こるかを見てみましょう:

1. まず、画像のバッチから始めます。たとえば、 `n` 枚の画像があるとします。
2. オブジェクト検出モデルは、画像ごとに異なる数のオブジェクトを見つけます。
3. 次にクロップのステップが、検出された各オブジェクトごとに新しい画像を作成し、その結果、元の画像ごとに新しい画像バッチが生まれます。

その結果、画像のネストされたリストになり、サイズは例えば `[(k[1], ), (k[2], ), ... (k[n])]`のようになります。ここで各 `k[i]` は、検出数に基づいて可変サイズを持つ画像バッチです。2 つ目のモデル（分類器）は、このネストされたクロップ画像のバッチを処理します。さらに深くネストされたバッチの世界へ進むことも、もちろん可能です。

ここが少し複雑になりますが、Execution Engine がこの複雑さを簡略化します。データのネストを仮想的に管理するため、ブロックは常に平坦化された、非ネスト形式のデータを受け取ります。これにより、オブジェクト検出モデルや分類器のような同じブロックを、データがどれだけ深くネストされていても適用しやすくなります。ただし、代償があります。それが「 `次元レベル` どのステップを接続でき、どのステップを接続できないかを規定する概念です。

この `次元レベル` この概念はバッチのネストのレベルを指します。バッチ指向の Workflow 入力は `次元レベル 1`を持ち、例で説明したクロップは `次元レベル 2` を持ちます。したがって、特定のステップに入力を接続する観点で重要なのは:

* の違い `次元レベル` ステップ入力間での
* ステップが `次元レベル` 出力の次元への影響（ステップは次元を下げることも、同じに保つことも、上げることもできます）

ほとんどのブロックは、同じ次元レベルの入力で動作し、出力の次元は変えないように設計されていますが、そのルールの例外もあります。私たちの例では、オブジェクト検出モデルからの予測は `次元レベル 1`次元レベル 1 `次元レベル 2`にあり、分類結果は

になっています。これは、クロップのステップによって新しい動的な次元レベルが導入されたためです。では、オブジェクト検出予測と分類予測の両方を受け付けるブロックが見つかったとすれば、ブロックがそのような `次元レベル`の組み合わせを明示的に受け入れると指定している場合にのみ、予測を一緒に使えます。そうでなければ、コンパイルエラーが表示されます。うまくいけば、この文脈で使えるブロックがあります。

![検出クラスの置換](https://media.roboflow.com/inference/detections_classes_replacement.png)

Detections Classes Replacement ブロックは、最初のモデルによって予測されたバウンディングボックスに基づいて元画像をクロップした部分で実行した分類モデルの予測で、バウンディングボックスのクラスラベルを置き換えるように設計されています。

{% hint style="warning" %}
私たちはこれを変えるべく懸命に取り組んでいますが、現時点では Roboflow APP の Workflow UI は「データ系譜」という概念を表示できません。 `次元レベル`UX の観点からは最適ではなく、非常にわかりにくいことは承知していますが、この状況が改善するまでご辛抱ください。
{% endhint %}

{% hint style="info" %}
Workflows Compiler は `データ系譜` を Workflow 定義内で追跡しており、より高い `次元レベル` 起源が同じでないデータを混在させることを不可能にしています。この概念は開発者ガイドで詳しく説明されています。ユーザーの観点から重要なのは、画像が異なるモデルの予測に基づいて切り出される場合（あるいは同じモデルでも crop ステップを 2 回使う場合）、同じ次元レベルにある crop の出力であっても、同じステップの入力として使えないということです。
{% endhint %}

## 条件付き実行

率直に言うと、プログラマーは分岐が大好きですし、それには正当な理由があります。これはプログラミング言語でよく使われる便利な構文です。

たとえば、次のコードで何が起こっているかは簡単に理解できます:

```python
def is_string_lower_cased(my_string: str) -> str:
    if my_string.lower() == my_string:
        return "文字列は小文字化されていた"
    return "文字列は小文字化されていなかった"
```

では、このコードはどうでしょうか？

```python
def is_string_lower_cased_batched(my_string: Batch[str]) -> str:
    pass
```

この場合、文字列のバッチに対して分岐がどのように機能するかはすぐには明確ではありません。単一の項目に対して判断を扱うという概念は単純ですが、バッチを扱う場合は、ロジックが複数の入力を一度に考慮する必要があります。問題は、バッチの各要素ごとに独立した判断を下さなければならず、その結果、バッチの要素ごとに異なる実行分岐が生じる可能性があることにあります。このような単純な例であれば、簡単に対処できます:

```python
def is_string_lower_cased_batched(my_string: Batch[str]) -> Batch[str]:
    result = []
    for element in my_string:
        if element.lower() == my_string:
            result.append("文字列は小文字化されていた")
        else:
            result.append("文字列は小文字化されていなかった") 
    return result
```

しかし、Workflows では、ブロック本文の中で条件文を実装して結合した結果を返すのではなく、どこへ実行を進めるかをブロックに決めさせたいのです。これが、Workflows Execution Engine において条件付き実行の仕組み全体が生まれた理由です。この概念は重要で、技術的な深さもありますが、ユーザーの観点から理解しておくべき点は少数です:

* いくつかの Workflows ブロックは実行フローに影響を与えることができます。そうしたブロックで構成されるステップには一連のステップセレクタが指定され、 **バッチの各要素** （非バッチ指向のステップは、プログラミングにおける従来の if-else 文のように動作します）
* 条件付き実行によってデータ要素がバッチから除外されると、その要素は処理パス下流のすべての影響を受けるステップから隠され、出力では `None`
* 複数のフロー制御ステップが 1 つの次のステップに影響する場合があり、その場合は条件付き実行マスクの和集合が作成され、動的に適用されます
* 条件付き実行ロジックの評価後にステップへの入力が残っていなければ、そのステップは実行されない場合があります
* 代替の実行分岐を統合できる特別なブロックがあり、その分岐のデータを単一のセレクタで参照できます（たとえば出力を構築するため）。そのようなブロックの例は `最初の非空値またはデフォルト` - で、最初に見つかった値を採用し、値が見つからない場合は指定した値を既定値として実行分岐をまとめます
* 条件付き実行は通常 Workflow の出力にも影響します。分岐の影響を受けるすべての値は実際には任意であり（空の値を埋める特別なブロックを使わない場合）、ネストされた結果にデータが埋まらず、結果に空の（場合によってはネストされた）リストが残ることがあります。詳細は [出力構築を説明するセクションを参照してください](#output-construction).

## 出力構築

最も重要なのは、Workflow の出力がバッチ要素の順序に関して入力と揃っていることを理解することです。つまり、出力は常に辞書のリストであり、各辞書は入力バッチ内の 1 つの項目に対応します。この構造により、結果の解析や反復処理がしやすくなり、出力を入力に対応付けられます。

```python
input_images = [...]
workflow_results = execution_engine.run(
    runtime_parameters={"images": input_images}
)

for image, result in zip(input_images, workflow_results):
    pass
```

リストの各要素は、次のような宣言を通じて Workflow 定義で指定されたキーを持つ辞書です:

```json
{"type": "JsonField", "name": "predictions", "selector": "$steps.detection.predictions"}
```

ただし、それらのキーの下で期待できる値は、ワークフローの構造に依存します。すべての非バッチ結果はブロードキャストされ、同じ値で各出力辞書に入ります。 `次元レベル 1` 次元レベル 1 の要素は `次元レベル` より高い次元レベルの要素は

たとえば、オブジェクト検出モデル、クロップ、二次分類モデルの例をもう一度考えてみましょう。オブジェクト検出モデルからの予測が出力に次の名前で登録されていると仮定します `"object_detection_predictions"` そして分類器の結果が `"classifier_predictions"`として登録されているとすると、Workflow 実行への入力として 3 枚の画像が送られたとき、次の出力が予想されます:

```json
[
  {
    "object_detection_predictions": "ここに 2 個のバウンディングボックスを持つ sv.Detections オブジェクト",
    "classifier_predictions": [
      {"classifier_prediction": "最初の crop 用"},
      {"classifier_prediction": "2 番目の crop 用"}
    ]
  },
  {
    "object_detection_predictions": "空の sv.Detections",
    "classifier_predictions": []
  },
  {
    "object_detection_predictions": "ここに 3 個のバウンディングボックスを持つ sv.Detections オブジェクト",
    "classifier_predictions": [
      {"classifier_prediction": "最初の crop 用"},
      {"classifier_prediction": "2 番目の crop 用"},
      {"classifier_prediction": "3 番目の crop 用"}
    ]
  }
]
```

ご覧のとおり、 `"classifier_predictions"` フィールドには結果のリストが格納され、そのサイズは `"object_detection_predictions"`.

興味深いことに、もし私たちのワークフローに、バウンディングボックスの数が 2 と異なる場合にのみ crop と classifier を実行する ContinueIf ブロックがあるなら、それは `classifier_predictions` を最初の辞書では空のリストに変えます。条件付き実行が高い `次元レベル` のステップを実行の副作用として出力生成から除外すると、その値を選択する output field は\
`次元レベル - 1` に一致する深さの空リストのネストされたリストとして表示されます。

Execution Engine `v1.6.0`、ブロックはワークフロー内でバッチをスカラーに縮約することも、スカラー入力から新しいバッチを作成することもできます。最初のシナリオは比較的わかりやすく、出力リスト内の各辞書には同じスカラー値が単に格納されます。 *創発的な* バッチは少し複雑です。その場合、入力バッチと形状や要素の順序が一致しない、次元レベル 1 のバッチが見つかります。意味上の曖昧さを避けるため、私たちはそのようなバッチを、あたかも次元が 1 つ上であるかのように扱います（あたかも **動的にバッチを作成するブロックの入力に、サイズ 1 の追加のバッチ指向入力が付いているかのように**）。このような仮想的にネストされた出力はブロードキャストされ、出力リスト内の各辞書に同じネストされた出力を持つ新しいキーが与えられます。Workflow に入力由来の出力がない場合でも、このネスト特性は保持されます。その場合、出力は 1 件の辞書を含むサイズ 1 のリストとなり、その辞書にネストされた出力が含まれます。

一部の出力は、Workflows Execution Engine が HTTP API の背後で動作する際にシリアライズを必要とします。私たちは次のシリアライズ戦略を使用します:

* 画像は次の形式にシリアライズされます `base64`
* numpy 配列はリストにシリアライズされます
* sv.Detections は次の形式にシリアライズされます `inference` ワイヤの向こう側で次を使ってデコードできる形式 `sv.Detections.from_inference(...)`

{% hint style="info" %}
sv.Detections は、検出ベースの予測の標準的表現として、output constructor で特別に扱われます。 `JsonField` output definition では任意で `coordinates_system` プロパティを指定でき、これによりワークフロー内で検出座標を親画像の座標系に変換するよう強制できます。詳細は [出力定義を説明する docs ページを参照してください](/workflows/ja/gaido/developer-guide/definitions.md)
{% endhint %}
