Eseguire l'inferenza ONNX nei grafici del flusso di dati WebAssembly (WASM)

Questo articolo illustra come incorporare ed eseguire modelli Open Neural Network Exchange (ONNX) all'interno dei moduli WebAssembly per eseguire l'inferenza in banda come parte dei grafici del flusso di dati Operazioni di Azure IoT. Usare questo approccio per l'arricchimento a bassa latenza e la classificazione direttamente sui dati di streaming senza chiamare servizi di stima esterni.

Prerequisiti

Prima di iniziare, assicurarsi di avere gli elementi seguenti:

  • Una distribuzione di Operazioni di Azure IoT con capacità di grafici del flusso di dati.
  • Un registro contenitore come Registro Azure Container e un endpoint del registro contenitore configurato. Per altre informazioni, vedere Configurare gli endpoint del Registro di sistema.
  • Ambiente di sviluppo configurato per lo sviluppo di moduli WebAssembly. Per le opzioni e istruzioni dettagliate sull'installazione dell'ambiente, vedere Sviluppare moduli WebAssembly.

Importante

I grafici del flusso di dati attualmente supportano solo gli endpoint MQTT (Message Queuing Telemetry Transport), Kafka e OpenTelemetry. Altri tipi di endpoint, ad esempio Azure Data Lake, Microsoft Fabric OneLake, Esplora dati di Azure e archiviazione locale non sono supportati.

Vantaggi dell'inferenza ONNX in banda

Con i grafici del flusso di dati di Operazioni di Azure IoT, è possibile incorporare l'inferenza di un modello ONNX di piccole dimensioni direttamente nella pipeline anziché chiamare un servizio di predizione esterno. Questo approccio offre i vantaggi seguenti:

  • Bassa latenza: consente di eseguire l'arricchimento o la classificazione in tempo reale nello stesso percorso dell'operatore in cui arrivano i dati. Ogni messaggio richiede solo l'inferenza eseguita localmente dalla CPU, evitando andate e ritorni di rete.
  • In linea con l'elaborazione dei flussi: eseguire l'inferenza insieme all'elaborazione di flussi da più origini, in cui le caratteristiche sono già presenti nel grafo, e allinearsi alla semantica del tempo degli eventi, in modo che l'inferenza utilizzi gli stessi timestamp degli altri operatori.
  • Aggiornamenti semplici: spedire un nuovo modulo con WASM e un modello incorporato, quindi aggiornare il riferimento alla definizione del grafo. Non è necessaria una modifica separata del registro dei modelli o dell'endpoint esterno.
  • Scalabilità orizzontale: l'inferenza si espande man mano che aumenta la scala del grafico del flusso di dati. Quando il runtime aggiunge più lavoratori per la velocità effettiva, ogni lavoratore carica il modello integrato e partecipa al bilanciamento del carico.

L'inferenza viene eseguita su una CPU tramite l'interfaccia WASI (WebAssembly System Interface) wasi-nn . Per i formati di modello supportati e i vincoli hardware, vedere Limitazioni dell'inferenza ONNX nei grafici del flusso di dati WASM.

Quando è consigliabile usare l'inferenza ONNX in banda?

Usare l'inferenza ONNX in banda nei grafici del flusso di dati quando sono necessarie le funzionalità seguenti:

  • Bassa latenza per arricchire o classificare i messaggi inline in fase di inserimento.
  • Modelli di visione piccoli ed efficienti, come i modelli di visione della classe MobileNet.
  • Inferenza che si allinea all'elaborazione basata sul tempo dell'evento e utilizza gli stessi timestamp degli altri operatori.
  • Aggiornamenti semplici del modello tramite spedizione di una nuova versione del modulo.

Non usare l'inferenza ONNX in banda quando sono necessarie le funzionalità seguenti:

  • Modelli di trasformatore di grandi dimensioni, accelerazione GPU o TPU o implementazioni A/B sofisticate.
  • Modelli che richiedono input di tensori multipli, cache chiave-valore o operatori ONNX non supportati.

Annotazioni

Mantenere i moduli e i modelli incorporati di piccole dimensioni. I modelli di grandi dimensioni e i carichi di lavoro con utilizzo elevato di memoria non sono supportati. Usare architetture compatta e piccole dimensioni di input come 224×224 per la classificazione delle immagini.

Modello di architettura per l'inferenza ONNX nei grafici del flusso di dati

Il modello comune per l'inferenza ONNX nei grafici del flusso di dati include le fasi seguenti:

  1. Pre-elaborare i dati: trasformare i dati di input non elaborati in modo che corrispondano al formato previsto del modello. Per i modelli di immagine, questo processo comporta in genere:
    • Decodifica dei byte di immagini.
    • Ridimensionamento in una dimensione di destinazione, ad esempio 224×224.
    • Conversione dello spazio dei colori, ad esempio RGB in BGR.
    • Normalizzazione dei valori pixel nell'intervallo previsto (da 0 a 1 o da -1 a 1).
    • Disposizione dei dati nel layout del tensore corretto: NCHW (batch, canali, altezza, larghezza) o NHWC (batch, altezza, larghezza, canali).
  2. Eseguire l'inferenza: convertire i dati pre-elaborati in tensori usando l'interfaccia wasi-nn , caricare il modello ONNX incorporato con il back-end della CPU, impostare i tensori di input nel contesto di esecuzione, richiamare il passaggio in avanti del modello e recuperare i tensori di output contenenti stime non elaborate.
  3. Output post-elaborazione: trasformare gli output grezzi del modello in risultati significativi. Operazioni comuni:
    • Applicare softmax per produrre probabilità di classificazione.
    • Selezionare le previsioni Top-K.
    • Applicare una soglia di attendibilità per filtrare i risultati con bassa confidenza.
    • Eseguire il mapping degli indici di previsione alle etichette leggibili dall'utente.
    • Formattare i risultati per il consumo downstream.

Negli esempi IoT per gli operatori WASM rust è possibile trovare due esempi che seguono questo modello:

  • Esempio di trasformazione dati 'format': decodifica e ridimensiona le immagini in RGB24 224×224.
  • Esempio "snapshot" di elaborazione di Image/Video: incorpora un modello ONNX di MobileNet v2, esegue l'inferenza su CPU e calcola la funzione softmax.

Configurare la definizione del grafo

Per abilitare l'inferenza ONNX nel grafico del flusso di dati, configurare sia la struttura del grafo che i parametri del modulo. La definizione del grafo specifica il flusso della pipeline, mentre le configurazioni del modulo consentono la personalizzazione del runtime del comportamento di pre-elaborazione e inferenza.

Abilitare la funzionalità wasi-nn

Per abilitare l'interfaccia WebAssembly Neural Network (wasi-nn) per l'inferenza ONNX, aggiungere la wasi-nn funzionalità alla definizione del grafo:

moduleRequirements:
  apiVersion: "1.1.0"
  runtimeVersion: "1.1.0"
  features:
    - name: "wasi-nn"

Definire le operazioni per la pipeline di inferenza

Configurare le operazioni che formano la pipeline di inferenza ONNX. Questo esempio mostra un tipico flusso di lavoro di classificazione delle immagini:

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" }

Questa configurazione crea una pipeline in cui:

  • camera-input riceve dati di immagine non elaborati da un'origine
  • module-format/map pre-elabora le immagini (decodifica, ridimensionamento, conversione del formato)
  • module-snapshot/map esegue l'inferenza ONNX e l'elaborazione successiva
  • results-output genera i risultati della classificazione in un sink

Configurare i parametri del modulo

Definire i parametri di runtime per personalizzare il comportamento del modulo WASM senza ricompilare. Questi parametri passano ai moduli WASM all'inizializzazione.

Per informazioni dettagliate, vedere Parametri di configurazione del modulo.

Impacchettare il modello

L'incorporamento di modelli ONNX direttamente nel componente WASM garantisce la coerenza delle versioni e della distribuzione atomica. Questo approccio semplifica la distribuzione e rimuove le dipendenze di runtime da registri o file di modello esterni.

Suggerimento

L'incorporazione mantiene il modello e la logica dell'operatore versionati insieme. Per aggiornare un modello, pubblicare una nuova versione del modulo e aggiornare la definizione del grafo per farvi riferimento. Questo approccio elimina la deriva del modello e garantisce distribuzioni riproducibili.

Requisiti di preparazione del modello ONNX

Prima di incorporare il modello, assicurarsi che soddisfi i requisiti per la distribuzione WASM:

  • Mantenere i modelli sotto i 50 MB per tempi di caricamento e vincoli di memoria pratici per WASM.
  • Verificare che il modello accetti un singolo input tensore in un formato comune (float32 o uint8).
  • Verificare che il back-end del runtime WASM ONNX supporti ogni operatore usato dal modello.
  • Usare gli strumenti di ottimizzazione ONNX per ridurre le dimensioni del modello e migliorare la velocità di inferenza.

Passaggi per incorporare un modello ONNX in un modulo WASM

Seguire questa procedura per incorporare il modello ONNX e le risorse associate in un modulo WASM:

  1. Organizzare gli asset del modello: posizionare il file del modello .onnx e il labels.txt facoltativo nella struttura di origine. Usare una struttura di directory dedicata, src/fixture/models/ ad esempio e src/fixture/labels/ per un'organizzazione chiara.
  2. Incorporare in fase di compilazione: usare strumenti specifici del linguaggio per includere i byte del modello nel file binario. In Rust usare include_bytes! per i dati binari e include_str! per i file di testo.
  3. Inizializza il grafo wasi-nn: nella funzione init del tuo operatore, crea un grafo wasi-nn dai byte incorporati, specificando la codifica ONNX e il target di esecuzione CPU.
  4. Implementare il ciclo di inferenza: per ogni messaggio in ingresso, pre-elaborare gli input in modo che corrispondano ai requisiti del modello, impostare tensori di input, eseguire inferenza, recuperare gli output e applicare la post-elaborazione.
  5. Gestire gli errori con eleganza: implementare una corretta gestione degli errori per il caricamento del modello, gli operatori non supportati e gli errori di inferenza di runtime.

Per un modello di implementazione completo, vedere l'esempio "snapshot".

Organizzare il progetto di modulo WASM con una netta separazione delle problematiche:

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

Layout di file tratto dal campione di snapshot di esempio

Usare il layout di file seguente dell'esempio "snapshot" come riferimento:

  • directory Labels - Contiene vari file di mapping delle etichette
  • directory Models - Contiene file e metadati del modello ONNX

Esempio minimo di Rust per l'incorporamento di un modello ONNX

L'esempio di Rust seguente illustra il codice minimo necessario per incorporare un modello ONNX in un modulo WASM. I percorsi sono relativi al file di origine che contiene la macro:

// 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(())
}

Riutilizzare il grafo ONNX e il contesto di esecuzione tra i messaggi

Per evitare di ricreare il grafo ONNX e il contesto di esecuzione per ogni messaggio, inizializzarli una volta e riutilizzarli. L'esempio pubblico di snapshot usa un LazyLock statico per inizializzare il grafo e il contesto di esecuzione una sola volta per ogni worker:

use crate::wasi::nn::{
    graph::{load, ExecutionTarget, Graph, GraphEncoding, GraphExecutionContext},
    tensor::{Tensor, TensorData, TensorDimensions, TensorType},
};

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).compute()?;
    }
}

Eseguire il debug e testare il modulo ONNX in locale

Prima di eseguire la distribuzione in Operazioni di Azure IoT, testare il modulo di inferenza ONNX in locale per convalidare funzionalità e prestazioni. Per ulteriori informazioni, vedere:

Configurare e distribuire il modulo ONNX

Quando si è pronti a usare il modulo di inferenza ONNX in un flusso di dati o in un connettore di Operazioni di Azure IoT, seguire questi passaggi:

Esempio: Classificazione delle immagini mobileNet

Gli esempi pubblici IoT forniscono due esempi cablati in un grafo per la classificazione delle immagini:

  • L'esempio "format" fornisce funzionalità di decodifica e ridimensionamento delle immagini.
  • L'esempio "snapshot" fornisce inferenza ONNX ed elaborazione softmax.

Per altre informazioni ed eseguire l'esempio che usa questi moduli, vedere Esempio 2: Distribuire un grafico complesso.

Limitazioni dell'inferenza ONNX nei grafici del flusso di dati WASM

L'inferenza nei grafici del flusso di dati WASM presenta le limitazioni seguenti:

  • Formato del modello: solo ONNX. I grafici del flusso di dati non supportano altri formati come TFLite.
  • Hardware: solo CPU. L'accelerazione GPU e TPU non sono supportate.
  • Dimensioni del modello: i modelli di grandi dimensioni e l'inferenza a elevato utilizzo di memoria non sono supportati. Usare modelli di piccole dimensioni, ad esempio architetture di classe MobileNet.
  • Input del modello: sono supportati solo modelli di input a tensore singolo. I modelli multi-input, la memorizzazione nella cache chiave-valore e gli scenari avanzati di sequenza o generazione non sono supportati.
  • Supporto dell'operatore: il back-end ONNX nel runtime WASM deve supportare ogni operatore usato dal modello. Se un operatore non è supportato, l'inferenza ha esito negativo in fase di caricamento o esecuzione.