Condividi tramite


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

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

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 Data Lake, Microsoft Fabric OneLake, Esplora dati di Azure e Archiviazione locale, non sono supportati. Per altre informazioni, vedere Problemi noti.

Perché usare l'interferenza ONNX in banda

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

  • 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 comporta solo l'inferenza della CPU locale, evitando passaggi di rete.
  • Ingombro compatto: Modelli compatti come quelli della classe MobileNet. Questa funzionalità non è destinata a modelli trasformatori di grandi dimensioni, accelerazione GPU/TPU o implementazioni frequenti del modello A/B.
  • Ottimizzato per casi d'uso specifici:
    • In linea con l'elaborazione del flusso di dati multi-sorgente in cui le funzionalità sono già preesistenti nella rete
    • Allineato alla semantica del tempo degli eventi per consentire all'inferenza di utilizzare gli stessi timestamp degli altri operatori.
    • Mantenuto sufficientemente piccolo da incorporarsi con il modulo senza superare i vincoli pratici di memoria e dimensioni WASM
  • Aggiornamenti semplici: spedire un nuovo modulo con WASM e un modello incorporato, quindi aggiornare il riferimento alla definizione del grafo. Non è necessario modificare un registro modelli o un endpoint esterno separato.
  • Vincoli hardware: il back-end ONNX viene eseguito sulla CPU tramite WebAssembly System Interface (WASI). wasi-nn Nessuna destinazione GPU/TPU; vengono eseguiti solo gli operatori ONNX supportati.
  • Scalabilità orizzontale: l'inferenza si espande man mano che aumenta la scala del grafico del flusso di dati. Quando il runtime aggiunge più worker per aumentare la capacità di elaborazione, ciascun worker carica il modello integrato e partecipa al bilanciamento del carico.

Quando utilizzare l'inferenza ONNX in-band

Utilizza l'inferenza in banda quando si hanno questi requisiti:

  • La bassa latenza deve arricchire o classificare i messaggi in linea in fase di inserimento
  • Modelli piccoli ed efficienti, come i modelli di visione della classe MobileNet o modelli compatti simili
  • Inferenza che deve essere allineata all'elaborazione in fase di evento e agli stessi timestamp di altri operatori
  • Aggiornamenti semplici tramite spedizione di una nuova versione del modulo con un modello aggiornato

Evitare l'inferenza in banda in presenza di questi requisiti:

  • Modelli di trasformatore di grandi dimensioni, accelerazione GPU/TPU o implementazioni A/B sofisticate
  • Modelli che richiedono più input tensor, memorizzazione nella cache di chiave-valore o operatori ONNX non supportati

Annotazioni

Si vogliono 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.

Prerequisiti

Prima di iniziare, assicurarsi di disporre di:

  • Una distribuzione di Operazioni IoT di Azure con funzionalità di grafici del flusso di dati.
  • Accesso a un registro di container come Azure Container Registry.
  • Ambiente di sviluppo configurato per lo sviluppo di moduli WebAssembly.

Per istruzioni dettagliate sulla configurazione, vedere Sviluppare moduli WebAssembly.

Modello di architettura

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

  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 RUST WASM è possibile trovare due esempi che seguono questo modello:

Configurare la definizione del grafo

Per abilitare l'inferenza ONNX nel grafico del flusso di dati, è necessario 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 il supporto WASI-NN

Per abilitare il supporto dell'interfaccia di rete neurale WebAssembly, aggiungere la wasi-nn funzione alla definizione del grafo.

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

Definire operazioni e flussi di dati

Configurare le operazioni che formano la pipeline di inferenza. 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 senza ricompilare. Questi parametri vengono passati ai moduli WASM all'inizializzazione:

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

L'operatore init può leggere questi valori tramite l'interfaccia di configurazione del modulo. 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.

Linee guida per la preparazione del modello

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 di runtime ONNX WASM supporti ogni operatore usato dal modello.
  • Usare gli strumenti di ottimizzazione ONNX per ridurre le dimensioni del modello e migliorare la velocità di inferenza.

Incorporazione del flusso di lavoro

Seguire questa procedura per incorporare il modello e le risorse associate:

  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 meccanismi 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. Inizializzare il grafo WASI-NN: nella funzione init dell'operatore creare un grafo wasi-nn dai byte incorporati, specificando la codifica ONNX e la destinazione di esecuzione su 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 di "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

Riferimenti ai file di esempio

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

Esempio di incorporamento minimo

L'esempio seguente illustra l'incorporamento minimo di Rust. 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(())
}

Ottimizzazione delle prestazioni

Per evitare di ricreare il grafo ONNX e il contesto di esecuzione per ogni messaggio, inizializzarlo una volta e riutilizzarlo. L'esempio pubblico usa un oggetto staticoLazyLock:

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

Distribuire e implementare i moduli

Riutilizzare i generatori di esempi semplificati o compilare localmente:

Seguire questo processo di distribuzione:

  1. Compilare il modulo WASM in modalità di rilascio e produrre un <module-name>-<version>.wasm file.
  2. Eseguire il push del modello e facoltativamente di una definizione del grafo nel Registro di sistema usando ORAS (OCI Registry as Storage).
  3. Creare o riutilizzare un endpoint del Registro di sistema in Operazioni IoT di Azure.
  4. Creare una risorsa del grafo del flusso di dati che fa riferimento all'artefatto della definizione del grafo.

Esempio: Classificazione delle immagini mobileNet

Gli esempi pubblici IoT forniscono due esempi cablati in un grafico per la classificazione delle immagini: l'esempio "format" fornisce la funzionalità di decodifica e ridimensionamento delle immagini e l'esempio di "snapshot" fornisce l'inferenza ONNX e l'elaborazione softmax.

Per distribuire questo esempio, eseguire il pull degli artefatti dal registro pubblico, eseguirne il push nel registro e distribuire un grafico del flusso di dati, come illustrato nell'esempio 2: Distribuire un grafico complesso. Il grafico complesso usa questi moduli per elaborare gli snapshot delle immagini e generare risultati di classificazione.

Limitazioni

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

  • Solo compatibile con ONNX. I grafici del flusso di dati non supportano altri formati come TFLite.
  • Solo CPU. Nessuna accelerazione GPU/TPU.
  • Modelli di piccole dimensioni consigliati. I modelli di grandi dimensioni e l'inferenza a elevato utilizzo di memoria non sono supportati.
  • Sono supportati 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.
  • Verificare che il back-end ONNX nel runtime WASM supporti gli operatori del modello. Se un operatore non è supportato, l'inferenza ha esito negativo in fase di caricamento o esecuzione.

Passaggi successivi