Exécuter l’inférence ONNX dans les graphiques de flux de données WebAssembly (WASM)

Cet article explique comment incorporer et exécuter de petits modèles Open Neural Network Exchange (ONNX) à l’intérieur de vos modules WebAssembly pour effectuer une inférence en bande dans le cadre de Opérations Azure IoT graphiques de flux de données. Utilisez cette approche pour l’enrichissement et la classification à faible latence directement sur les données de streaming sans appeler des services de prédiction externes.

Prerequisites

Avant de commencer, vérifiez que vous disposez des éléments suivants :

  • Un déploiement Opérations Azure IoT avec une capacité de graphes de flux de données.
  • Un registre de conteneurs tel qu’Azure Container Registry, ainsi qu’un point de terminaison configuré pour le registre de conteneurs. Pour plus d’informations, consultez Configurer des points de terminaison de Registre.
  • Un environnement de développement configuré pour le développement de modules WebAssembly. Pour obtenir des options et des instructions détaillées sur la configuration de l’environnement, consultez Développer des modules WebAssembly.

Important

Actuellement, les graphiques de flux de données prennent uniquement en charge les points de terminaison MQTT (Transport de télémétrie Message Queuing), Kafka et OpenTelemetry. D’autres types de points de terminaison tels qu’Azure Data Lake, Microsoft Fabric OneLake, Azure Data Explorer et le stockage local ne sont pas pris en charge.

Avantages de l’inférence ONNX en bande

Avec les graphes de flux de données d'Opérations Azure IoT, vous pouvez intégrer directement une inférence de modèle ONNX de petite taille dans le pipeline, plutôt que de faire appel à un service de prédiction externe. Cette approche offre les avantages suivants :

  • Faible latence : effectuez un enrichissement ou une classification en temps réel dans le même chemin d’opérateur que celui où les données arrivent. Chaque message nécessite uniquement l’inférence du processeur local, ce qui évite les allers-retours réseau.
  • Intégrée au traitement de flux : Exécuter l’inférence parallèlement au traitement de flux multisource, où les variables sont déjà co-localisées dans le graphe, et l’aligner sur la sémantique temporelle des événements afin qu’elle utilise les mêmes horodatages que les autres opérateurs.
  • Mises à jour simples : envoyez un nouveau module avec WASM et un modèle incorporé, puis mettez à jour la référence de définition de graphique. Vous n’avez pas besoin d’un registre de modèles distinct ou d’un changement de point de terminaison externe.
  • Mise à l’échelle horizontale : l’inférence augmente à mesure que le graphe de flux de données s'étend. Lorsque le runtime ajoute davantage de Workers pour le débit, chaque Worker charge le modèle incorporé et participe à l’équilibrage de charge.

L’inférence s’exécute sur un processeur via l’interface WASI (WebAssembly System Interface) wasi-nn . Pour connaître les formats de modèle pris en charge et les contraintes matérielles, consultez Limitations de l’inférence ONNX dans les graphiques de flux de données WASM.

Quand devez-vous utiliser l’inférence ONNX en bande ?

Utilisez l’inférence ONNX en bande dans les graphiques de flux de données lorsque vous avez besoin des fonctionnalités suivantes :

  • Les besoins en faible latence exigent d’enrichir ou de classifier les messages en temps réel lors de l’ingestion.
  • Modèles petits et efficaces tels que des modèles de vision de classe MobileNet.
  • Inférence qui doit s’aligner sur le traitement en temps d’événement et les mêmes marques temporelles que les autres opérateurs.
  • Mises à jour de modèle simples en expédiant une nouvelle version de module.

N’utilisez pas l’inférence ONNX en bande quand vous avez besoin des fonctionnalités suivantes :

  • Grands modèles transformateurs, accélération GPU ou TPU, ou déploiements A/B sophistiqués.
  • Modèles qui nécessitent plusieurs entrées de tenseur, la mise en cache clé-valeur ou des opérateurs ONNX non pris en charge.

Note

Conservez les modules et les modèles incorporés petits. Les grands modèles et les charges de travail gourmandes en mémoire ne sont pas pris en charge. Utilisez des architectures compactes et de petites tailles d’entrée comme 224×224 pour la classification d’images.

Modèle d’architecture pour l’inférence ONNX dans les graphiques de flux de données

Le modèle courant pour l’inférence ONNX dans les graphiques de flux de données comprend les étapes suivantes :

  1. Prétraitement des données : transformez les données d’entrée brutes en fonction du format attendu de votre modèle. Pour les modèles d’image, ce processus implique généralement :
    • Décodage des octets d’image.
    • Redimensionnement vers une dimension cible (par exemple, 224×224).
    • Conversion de l’espace de couleur (par exemple, RVB en BGR).
    • Normalisation des valeurs de pixels à la plage attendue (0 à 1 ou -1 à 1).
    • Organisation des données dans la disposition correcte du tenseur : NCHW (lot, canaux, hauteur, largeur) ou NHWC (lot, hauteur, largeur, canaux).
  2. Exécuter l’inférence : convertissez des données prétraite en tenseurs à l’aide de l’interface wasi-nn , chargez votre modèle ONNX incorporé avec le back-end du processeur, définissez des tenseurs d’entrée sur le contexte d’exécution, appelez la passe de transfert du modèle et récupérez les tenseurs de sortie contenant des prédictions brutes.
  3. Post-traitement des sorties : transformez les sorties brutes du modèle en résultats significatifs. Opérations courantes :
    • Appliquez softmax pour produire des probabilités de classification.
    • Sélectionnez les prédictions « top-K ».
    • Appliquez un seuil de confiance pour filtrer les résultats à faible confiance.
    • Associer des indices de prédiction à des étiquettes faciles à lire pour l'homme.
    • Mettre en forme les résultats pour la consommation en aval.

Dans les exemples IoT pour les opérateurs RUST WASM vous trouverez deux exemples qui suivent ce modèle :

  • Exemple de « format » de transformation de données : décode et redimensionne les images en RVB24 224×224.
  • Exemple de traitement d'images/vidéos « snapshot » : intègre un modèle ONNX MobileNet v2, exécute l'inférence sur le processeur et calcule la fonction softmax.

Configurer la définition de graphique

Pour activer l’inférence ONNX dans votre graphe de flux de données, configurez à la fois la structure du graphique et les paramètres de module. La définition de graphique spécifie le flux de pipeline, tandis que les configurations de module autorisent la personnalisation du runtime du prétraitement et du comportement d’inférence.

Activer la fonctionnalité wasi-nn

Pour activer l’interface WebAssembly Neural Network (wasi-nn) pour l’inférence ONNX, ajoutez la wasi-nn fonctionnalité à votre définition de graphique :

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

Définir des opérations pour le pipeline d’inférence

Configurez les opérations qui forment votre pipeline d’inférence ONNX. Cet exemple montre un flux de travail de classification d’images classique :

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

Cette configuration crée un pipeline dans lequel :

  • camera-input reçoit les données d’image brutes d’une source
  • module-format/map prétraite des images (décodage, redimensionnement, conversion de format)
  • module-snapshot/map exécute l’inférence et le post-traitement ONNX
  • results-output émet des résultats de classification dans un récepteur

Configurer les paramètres du module

Définissez des paramètres d’exécution pour personnaliser le comportement du module WASM sans recompiler. Ces paramètres passent à vos modules WASM lors de l’initialisation.

Pour plus d’informations, consultez les paramètres de configuration du module.

Empaqueter le modèle

L’incorporation de modèles ONNX directement dans votre composant WASM assure un déploiement atomique et une cohérence de version. Cette approche simplifie la distribution et supprime les dépendances d’exécution sur les fichiers ou registres de modèles externes.

Conseil / Astuce

L'intégration permet de versionner ensemble le modèle et la logique de l'opérateur. Pour mettre à jour un modèle, publiez une nouvelle version de module et mettez à jour votre définition de graphique pour la référencer. Cette approche élimine la dérive du modèle et garantit des déploiements reproductibles.

Exigences de préparation du modèle ONNX

Avant d’incorporer votre modèle, vérifiez qu’il répond aux exigences du déploiement WASM :

  • Conservez les modèles de moins de 50 Mo pour les temps de chargement WASM pratiques et les contraintes de mémoire.
  • Vérifiez que votre modèle accepte une entrée tensoriel unique dans un format commun (float32 ou uint8).
  • Vérifiez que le serveur principal du runtime WASM ONNX prend en charge chaque opérateur que votre modèle utilise.
  • Utilisez les outils d’optimisation ONNX pour réduire la taille du modèle et améliorer la vitesse d’inférence.

Étapes d’incorporation d’un modèle ONNX dans un module WASM

Procédez comme suit pour incorporer votre modèle ONNX et les ressources associées dans un module WASM :

  1. Organisez les ressources du modèle : placez le fichier de modèle ainsi que le fichier facultatif .onnx dans la hiérarchie de votre source. Utilisez une structure d’annuaires dédiée comme src/fixture/models/ et src/fixture/labels/ pour une organisation claire.
  2. Incorporer au moment de la compilation : utilisez des outils spécifiques au langage pour inclure des octets de modèle dans votre fichier binaire. Dans Rust, utilisez-le include_bytes! pour les données binaires et include_str! pour les fichiers texte.
  3. Initialisez le graphe wasi-nn : dans la fonction init de votre opérateur, créez un graphe wasi-nn à partir des octets intégrés, en spécifiant l’encodage ONNX et la cible d’exécution CPU.
  4. Implémentez une boucle d’inférence : Pour chaque message entrant, prétraitez les entrées pour répondre aux exigences du modèle, définissez des tenseurs d’entrée, exécutez l’inférence, récupérez des sorties et appliquez le post-traitement.
  5. Gérer correctement les erreurs : implémentez une gestion appropriée des erreurs pour les échecs de chargement de modèle, les opérateurs non pris en charge et les erreurs d’inférence d’exécution.

Pour obtenir un modèle d’implémentation complet, consultez l’exemple « instantané » .

Organisez votre projet de module WASM avec une séparation claire des préoccupations :

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

Exemple de disposition de fichier à partir de l’exemple d’instantané

Utilisez la disposition de fichier suivante à partir de l’exemple « instantané » comme référence :

  • répertoire Labels - Contient différents fichiers de mappage d’étiquettes
  • répertoire Models - Contient des fichiers et métadonnées de modèle ONNX

Exemple Rust minimal pour incorporer un modèle ONNX

L’exemple Rust suivant montre le code minimal nécessaire pour incorporer un modèle ONNX dans un module WASM. Les chemins d’accès sont relatifs au fichier source qui contient 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(())
}

Réutiliser le graphique ONNX et le contexte d’exécution entre les messages

Pour éviter de recréer le graphique ONNX et le contexte d’exécution pour chaque message, initialisez-les une fois et réutilisez-les. L’exemple d’instantané public utilise un LazyLock statique pour initialiser le graphe et le contexte d’exécution une fois par 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()?;
    }
}

Déboguer et tester votre module ONNX localement

Avant de déployer sur Opérations Azure IoT, testez votre module d’inférence ONNX localement pour valider les fonctionnalités et les performances. Pour en savoir plus, consultez :

Configurer et déployer votre module ONNX

Lorsque vous êtes prêt à utiliser votre module d'inférence ONNX dans un flux de données ou un connecteur Opérations Azure IoT, procédez comme suit :

Exemple : classification d’images MobileNet

Les exemples publics pour l’IoT fournissent deux exemples connectés dans un graphe pour la classification d’images :

  • L’exemple « format » fournit des fonctionnalités de décodage et de redimensionnement d’image.
  • L’exemple « snapshot » fournit l’inférence ONNX et le traitement softmax.

Pour en savoir plus sur l’exemple qui utilise ces modules, consultez l’exemple 2 : Déployer un graphique complexe.

Limitations de l’inférence ONNX dans les graphiques de flux de données WASM

L’inférence dans les graphiques de flux de données WASM présente les limitations suivantes :

  • Format du modèle : ONNX uniquement. Les graphiques de flux de données ne prennent pas en charge d’autres formats comme TFLite.
  • Matériel : processeur uniquement. L’accélération par GPU et TPU n’est pas prise en charge.
  • Taille du modèle : les grands modèles et l’inférence gourmande en mémoire ne sont pas pris en charge. Utilisez de petits modèles tels que des architectures de classe MobileNet.
  • Entrées du modèle : seuls les modèles avec une seule entrée de type tenseur sont pris en charge. Les modèles multi-entrée, la mise en cache clé-valeur et les scénarios de séquence ou de génération avancés ne sont pas pris en charge.
  • Prise en charge des opérateurs : le back-end ONNX dans le runtime WASM doit prendre en charge chaque opérateur que votre modèle utilise. Si un opérateur n’est pas pris en charge, l’inférence échoue au moment du chargement ou de l’exécution.