この記事では、小規模な Open Neural Network Exchange (ONNX) モデルを WebAssembly モジュール内に埋め込んで実行し、Azure IoT Operations データ フロー グラフの一部としてインバンド推論を実行する方法について説明します。 このアプローチは、外部予測サービスを呼び出さずにストリーミング データに対して直接、低待機時間のエンリッチメントと分類を行う場合に使用します。
Important
現在、データ フロー グラフは MQTT (メッセージ キュー テレメトリ トランスポート)、Kafka、および OpenTelemetry エンドポイントのみをサポートしています。 Data Lake、Microsoft Fabric OneLake、Azure Data Explorer、ローカル ストレージなどの他のエンドポイントの種類はサポートされていません。 詳細については、「既知の問題の」を参照してください。
インバンド ONNX 干渉を使用する理由
Azure IoT Operations データ フロー グラフを使用すると、外部予測サービスを呼び出す代わりに、小さな ONNX モデル推論をパイプラインに直接埋め込むことができます。 この方法には、いくつかの実用的な利点があります。
- 待機時間が短い: データが到着するのと同じオペレーター パスで、リアルタイムのエンリッチメントまたは分類を実行します。 各メッセージではローカル CPU 推論のみが発生し、ネットワーク ラウンド トリップは回避されます。
- コンパクトなフットプリント: MobileNet クラスのモデルなどのコンパクト モデルをターゲットに設定します。 この機能は、大規模なトランスフォーマー モデル、GPU/TPU アクセラレーション、または頻繁な A/B モデルロールアウト用ではありません。
-
特定のユース ケース向けに最適化されています。
- 特徴が既にグラフに配置されている、複数のソースを用いたストリーム処理に合わせる
- 推論で他の演算子と同じタイムスタンプが使用されるように、イベント時間セマンティクスに合わせて調整
- 実際の WASM サイズとメモリの制約を超えることなく、モジュールに埋め込むのに十分なサイズを保持する
- 簡単な更新: WASM と埋め込みモデルを使用して新しいモジュールを出荷し、グラフ定義参照を更新します。 別のモデル レジストリや外部エンドポイントを変更する必要はありません。
-
ハードウェアの制約: ONNX バックエンドは、WebAssembly System Interface (WASI)
wasi-nnを介して CPU 上で実行されます。 GPU/TPU ターゲットなし。サポートされている ONNX 演算子のみが実行されます。 - 水平スケーリング: 推論は、データ フロー グラフのスケーリングに合わせてスケーリングされます。 ランタイムによってスループット用のワーカーが追加されると、各ワーカーは埋め込みモデルを読み込み、負荷分散に参加します。
インバンド ONNX 推論を使用するタイミング
次の要件がある場合は、インバンド推論を使用します。
- レイテンシーが低い場合、インジェスト時にメッセージをインラインで補足または分類する必要があります。
- MobileNet クラスのビジョンや同様のコンパクト モデルなど、小型で効率的なモデル
- イベント時間の処理と他の演算子と同じタイムスタンプに合わせる必要がある推論
- 更新されたモデルを使用して新しいモジュール バージョンを出荷することで簡単な更新を行う
次の要件がある場合は、インバンド推論を避けます。
- 大規模トランスフォーマー モデル、GPU/TPU アクセラレーション、または高度な A/B ロールアウト
- 複数のテンソル入力、キー値キャッシュ、またはサポートされていない ONNX 演算子を必要とするモデル
注
モジュールと埋め込みモデルを小さくする必要があります。 大規模なモデルとメモリ負荷の高いワークロードはサポートされていません。 画像分類には、コンパクトなアーキテクチャと 224 ×224 のような小さな入力サイズを使用します。
[前提条件]
開始する前に、次のことを確認します。
- データ フロー グラフ機能を備えた Azure IoT Operations デプロイ。
- Azure Container Registry などのコンテナー レジストリへのアクセス。
- WebAssembly モジュール開発用に設定された開発環境。
詳細なセットアップ手順については、「 WebAssembly モジュールの開発」を参照してください。
アーキテクチャ パターン
データ フロー グラフでの ONNX 推論の一般的なパターンは次のとおりです。
-
データの前処理: モデルの予想される形式に合わせて生の入力データを変換します。 イメージ モデルの場合、通常、このプロセスには次の処理が含まれます。
- 画像バイトをデコードする。
- ターゲット ディメンションへのサイズ変更 (例: 224×224)。
- 色空間の変換 (RGB から BGR など)。
- ピクセル値を想定される範囲 (0 ~ 1 または -1 から 1) に正規化します。
- 適切なテンソル レイアウトでのデータの配置: NCHW (バッチ、チャネル、高さ、幅) または NHWC (バッチ、高さ、幅、チャネル)。
-
実行推論:
wasi-nnインターフェイスを使用して前処理されたデータをテンソルに変換し、CPU バックエンドで埋め込まれた ONNX モデルを読み込み、実行コンテキストに入力テンソルを設定し、モデルのフォワード パスを呼び出して、生の予測を含む出力テンソルを取得します。 -
後処理の出力: 生モデルの出力を意味のある結果に変換します。 一般的な操作:
- softmax を適用して分類確率を生成します。
- top-K 予測を選択します。
- 信頼度しきい値を適用して、信頼度の低い結果をフィルター処理します。
- 予測インデックスを人間が判読できるラベルにマップします。
- 下流処理で使用するために結果をフォーマットします。
Rust WASM Operator の IoT サンプルでは、このパターンに従う 2 つのサンプルを見つけることができます。
- データ変換の "書式" サンプル: 画像をデコードして RGB24 224×224 にサイズ変更します。
- 画像/ビデオ処理の "スナップショット" サンプル: MobileNet v2 ONNX モデルを埋め込み、CPU 推論を実行し、softmax を計算します。
グラフ定義を構成する
データ フロー グラフで ONNX 推論を有効にするには、グラフ構造とモジュール パラメーターの両方を構成する必要があります。 グラフ定義ではパイプライン フローを指定しますが、モジュール構成では、前処理と推論の動作をランタイムでカスタマイズできます。
WASI-NN サポートを有効にする
WebAssembly ニューラル ネットワーク インターフェイスのサポートを有効にするには、グラフ定義に wasi-nn 機能を追加します。
moduleRequirements:
apiVersion: "0.2.0"
hostlibVersion: "0.2.0"
features:
- name: "wasi-nn"
操作とデータ フローを定義する
推論パイプラインを形成する操作を構成します。 この例では、一般的な画像分類ワークフローを示します。
operations:
- operationType: "source"
name: "camera-input"
- operationType: "map"
name: "module-format/map"
module: "format:1.0.0"
- operationType: "map"
name: "module-snapshot/map"
module: "snapshot:1.0.0"
- operationType: "sink"
name: "results-output"
connections:
- from: { name: "camera-input" }
to: { name: "module-format/map" }
- from: { name: "module-format/map" }
to: { name: "module-snapshot/map" }
- from: { name: "module-snapshot/map" }
to: { name: "results-output" }
この構成では、次の場所にパイプラインが作成されます。
-
camera-inputソースから生画像データを受信する -
module-format/map画像の前処理 (デコード、サイズ変更、フォーマット変換) -
module-snapshot/mapONNX 推論と後処理を実行する -
results-outputシンクに分類結果を出力する
モジュール パラメーターを構成する
再構築せずにモジュールの動作をカスタマイズするためのランタイム パラメーターを定義します。 これらのパラメーターは、初期化時に WASM モジュールに渡されます。
moduleConfigurations:
- name: module-format/map
parameters:
width:
name: width
description: "Target width for image resize (default: 224)"
required: false
height:
name: height
description: "Target height for image resize (default: 224)"
required: false
pixelFormat:
name: pixel_format
description: "Output pixel format (rgb24, bgr24, grayscale)"
required: false
- name: module-snapshot/map
parameters:
executionTarget:
name: execution_target
description: "Inference execution target (cpu, auto)"
required: false
labelMap:
name: label_map
description: "Label mapping strategy (embedded, imagenet, custom)"
required: false
scoreThreshold:
name: score_threshold
description: "Minimum confidence score to include in results (0.0-1.0)"
required: false
topK:
name: top_k
description: "Maximum number of predictions to return (default: 5)"
required: false
オペレーター init は、モジュール構成インターフェイスを介してこれらの値を読み取ることができます。 詳細については、 モジュール構成パラメーターを参照してください。
モデルをパッケージ化する
ONNX モデルを WASM コンポーネントに直接埋め込むと、アトミックデプロイとバージョンの一貫性が確保されます。 この方法により、配布が簡略化され、外部モデル ファイルまたはレジストリへのランタイムの依存関係が削除されます。
ヒント
埋め込みでは、モデルと演算子ロジックが一緒にバージョン管理されます。 モデルを更新するには、新しいモジュール バージョンを発行し、それを参照するようにグラフ定義を更新します。 このアプローチにより、モデルの誤差が解消され、再現可能なデプロイが保証されます。
モデルの準備のガイドライン
モデルを埋め込む前に、WASM デプロイの要件を満たしていることを確認します。
- 実際の WASM 読み込み時間とメモリ制約のために、モデルを 50 MB 未満にしてください。
- モデルが共通の形式 (float32 または uint8) で 1 つのテンソル入力を受け入れることを確認します。
- WASM ONNX ランタイム バックエンドが、モデルで使用するすべてのオペレーターをサポートしていることを確認します。
- ONNX 最適化ツールを使用して、モデルのサイズを小さくし、推論速度を向上させます。
埋め込みワークフロー
モデルと関連リソースを埋め込むには、次の手順に従います。
-
モデル アセットを整理する:
.onnxモデル ファイルとオプションのlabels.txtをソース ツリーに配置します。 明確な編成には、src/fixture/models/やsrc/fixture/labels/などの専用のディレクトリ構造を使用します。 -
コンパイル時に埋め込む: 言語固有のメカニズムを使用して、バイナリにモデル バイトを含めます。 Rust では、バイナリ データに
include_bytes!を使用し、テキスト ファイルのinclude_str!を使用します。 -
WASI-NN グラフを初期化する: オペレーターの
init関数で、埋め込まれたバイトからwasi-nnグラフを作成し、ONNX のエンコーディングと CPU 実行ターゲットを指定します。 - 推論ループを実装する: 受信メッセージごとに、モデル要件に合わせて入力を前処理し、入力テンソルを設定し、推論を実行し、出力を取得し、後処理を適用します。
- エラーを適切に処理する: モデルの読み込みエラー、サポートされていない演算子、ランタイム推論エラーに対して適切なエラー処理を実装します。
完全な実装パターンについては、 "スナップショット" サンプルを参照してください。
推奨されるプロジェクト構造
懸念事項を明確に分離して WASM モジュール プロジェクトを整理します。
src/
├── lib.rs # Main module implementation
├── model/
│ ├── mod.rs # Model management module
│ └── inference.rs # Inference logic
└── fixture/
├── models/
│ ├── mobilenet.onnx # Primary model
│ └── mobilenet_opt.onnx # Optimized variant
└── labels/
├── imagenet.txt # ImageNet class labels
└── custom.txt # Custom label mappings
ファイル参照の例
「スナップショット」サンプルの次のファイル レイアウトを参照として使用します。
- ラベル ディレクトリ - さまざまなラベル マッピング ファイルが含まれています
- Models ディレクトリ - ONNX モデル ファイルとメタデータが含まれています
最小埋め込みの例
次の例は、最小限の Rust 埋め込みを示しています。 パスは、マクロを含むソース ファイルに対する相対パスです。
// src/lib.rs (example)
// Embed ONNX model and label map into the component
static MODEL: &[u8] = include_bytes!("fixture/models/mobilenet.onnx");
static LABEL_MAP: &[u8] = include_bytes!("fixture/labels/synset.txt");
fn init_model() -> Result<(), anyhow::Error> {
// Create wasi-nn graph from embedded ONNX bytes using the CPU backend
// Pseudocode – refer to the snapshot sample for the full implementation
// use wasi_nn::{graph::{load, GraphEncoding, ExecutionTarget}, Graph};
// let graph = load(&[MODEL.to_vec()], GraphEncoding::Onnx, ExecutionTarget::Cpu)?;
// let exec_ctx = Graph::init_execution_context(&graph)?;
Ok(())
}
パフォーマンスの最適化
すべてのメッセージの ONNX グラフと実行コンテキストを再作成しないようにするには、1 回初期化して再利用します。
パブリック サンプルでは、静的なLazyLockを使用します。
use std::sync::LazyLock;
use wasi_nn::{graph::{load, Graph, GraphEncoding, ExecutionTarget}, GraphExecutionContext};
static mut CONTEXT: LazyLock<GraphExecutionContext> = LazyLock::new(|| {
let graph = load(&[MODEL.to_vec()], GraphEncoding::Onnx, ExecutionTarget::Cpu).unwrap();
Graph::init_execution_context(&graph).unwrap()
});
fn run_inference(/* input tensors, etc. */) {
unsafe {
// (*CONTEXT).set_input(...)?; (*CONTEXT).compute()?; (*CONTEXT).get_output(...)?;
}
}
モジュールをデプロイする
合理化されたサンプル ビルダーを再利用するか、ローカルでビルドします。
- 合理化された Docker ビルダーを使用するには、Rust Docker ビルダーのサンプルを参照してください
- 一般的な WebAssembly のデプロイとレジストリの手順については、データ フロー グラフでの WebAssembly の使用に関するページを参照してください。
次のデプロイ プロセスに従います。
- リリース モードで WASM モジュールをビルドし、
<module-name>-<version>.wasmファイルを生成します。 - OCI Registry as Storage (ORAS) を使用して、モジュールと必要に応じてグラフ定義をレジストリにプッシュします。
- Azure IoT Operations でレジストリ エンドポイントを作成または再利用します。
- グラフ定義成果物を参照するデータ フロー グラフ リソースを作成します。
例: MobileNet の画像分類
IoT パブリック サンプルには、画像分類用のグラフにワイヤードされた 2 つのサンプルが用意されています。 "format" サンプル は画像のデコードとサイズ変更の機能を提供し、 "スナップショット" サンプル は ONNX 推論とソフトマックス処理を提供します。
この例をデプロイするには、「 例 2: 複雑なグラフをデプロイする」に示すように、パブリック レジストリから成果物をプルし、それらをレジストリにプッシュし、データ フロー グラフをデプロイします。 複雑なグラフでは、これらのモジュールを使用してイメージ スナップショットを処理し、分類結果を出力します。
制限事項
WASM データ フロー グラフでの推論には、次の制限があります。
- ONNX のみ。 データ フロー グラフは、TFLite などの他の形式をサポートしていません。
- CPU のみ。 GPU/TPU アクセラレーションなし。
- 小さいモデルをお勧めします。 大規模なモデルとメモリ集中型推論はサポートされていません。
- 単一テンソル入力モデルがサポートされています。 複数入力モデル、キー値キャッシュ、高度なシーケンスまたは生成シナリオはサポートされていません。
- WASM ランタイムの ONNX バックエンドがモデルの演算子をサポートしていることを確認します。 演算子がサポートされていない場合、読み込み時または実行時に推論が失敗します。