Executar inferência ONNX em gráficos de fluxo de dados WebAssembly (WASM)

Este artigo mostra como incorporar e executar pequenos modelos Open Neural Network Exchange (ONNX) dentro dos seus módulos WebAssembly para realizar inferência em banda como parte de Operações IoT do Azure grafos de fluxo de dados. Use essa abordagem para enriquecimento e classificação de baixa latência diretamente em dados de streaming sem chamar serviços de previsão externos.

Pré-requisitos

Antes de começar, certifique-se de que tem os seguintes itens:

  • Uma implementação do Operações IoT do Azure com capacidade de grafos de fluxo de dados.
  • Um registo de contentores, como o Azure Container Registry, e um endpoint de registo de contentores configurado. Para saber mais, consulte Configurar endpoints do registo.
  • Um ambiente de desenvolvimento criado para o desenvolvimento de módulos WebAssembly. Para opções e instruções detalhadas de configuração do ambiente, consulte os módulos Develop WebAssembly.

Importante

Atualmente, os gráficos de fluxo de dados suportam apenas os pontos de extremidade MQTT (Message Queuing Telemetry Transport), Kafka e OpenTelemetry. Outros tipos de endpoints como Azure Data Lake, Microsoft Fabric OneLake, Azure Data Explorer e armazenamento local não são suportados.

Benefícios da inferência ONNX em banda

Com os grafos de fluxo de dados do Operações IoT do Azure, pode incorporar pequenas inferências do modelo ONNX diretamente no pipeline, em vez de chamar um serviço de previsão externo. Esta abordagem oferece os seguintes benefícios:

  • Baixa latência: execute enriquecimento ou classificação em tempo real no mesmo trajeto do operador onde os dados chegam. Cada mensagem requer apenas inferência local da CPU, evitando viagens de ida e volta na rede.
  • Em conjunto com o processamento de fluxos: Executar a inferência a par do processamento de fluxos de múltiplas fontes, em que os atributos já se encontram localizados no grafo, e alinhar com a semântica temporal dos eventos para que a inferência utilize as mesmas marcas temporais que os outros operadores.
  • Atualizações simples: envie um novo módulo com WASM e modelo incorporado e, em seguida, atualize a referência de definição de gráfico. Não precisas de um registo de modelo separado ou de uma alteração de endpoint externo.
  • Dimensionamento horizontal: a inferência é dimensionada à medida que o gráfico de fluxo de dados é dimensionado. Quando o runtime adiciona mais trabalhadores para aumentar a taxa de transferência, cada trabalhador carrega o modelo incorporado e participa no balanceamento de carga.

A inferência é executada num CPU através da interface wasi-nn do WebAssembly System Interface (WASI). Para formatos de modelo suportados e restrições de hardware, veja Limitações da inferência ONNX em gráficos de fluxo de dados WASM.

Quando é que deve usar a inferência in-band do ONNX?

Utilize a inferência ONNX in-band em grafos de fluxo de dados quando precisar das seguintes capacidades:

  • Baixa latência para enriquecer ou classificar mensagens em linha no momento da ingestão.
  • Modelos pequenos e eficientes, como modelos de visão do tipo MobileNet.
  • Inferência que está alinhada com o processamento em tempo de evento e utiliza as mesmas marcas temporais que os outros operadores.
  • Atualizações simples do modelo enviando uma nova versão do módulo.

Não use a inferência ONNX em banda quando precisar das seguintes capacidades:

  • Modelos de transformadores grandes, aceleração de GPU ou TPU, ou implementações sofisticadas de A/B.
  • Modelos que requerem múltiplas entradas tensoriais, cache chave-valor ou operadores ONNX não suportados.

Observação

Mantenha os módulos e modelos embebidos pequenos. Não há suporte para modelos grandes e cargas de trabalho com muita memória. Use arquiteturas compactas e tamanhos de entrada pequenos, como 224×224 para classificação de imagens.

Padrão de arquitetura para inferência ONNX em grafos de fluxo de dados

O padrão comum para inferência ONNX em grafos de fluxo de dados inclui as seguintes etapas:

  1. Dados de pré-processamento: transforme os dados de entrada brutos para corresponder ao formato esperado do seu modelo. Para modelos de imagem, esse processo normalmente envolve:
    • Decodificação de bytes de imagem.
    • Redimensionamento para uma dimensão de destino (por exemplo, 224×224).
    • Converter o espaço de cores (por exemplo, RGB para BGR).
    • Normalização de valores de pixel para o intervalo esperado (0–1 ou -1 a 1).
    • Organizando os dados no layout tensor correto: NCHW (lote, canais, altura, largura) ou NHWC (lote, altura, largura, canais).
  2. Executar inferência: converta dados pré-processados em tensores usando a wasi-nn interface, carregue seu modelo ONNX incorporado com o back-end da CPU, defina tensores de entrada no contexto de execução, invoque a passagem direta do modelo e recupere tensores de saída contendo previsões brutas.
  3. Saídas pós-processo: transforme saídas brutas do modelo em resultados significativos. Operações comuns:
    • Aplique softmax para produzir probabilidades de classificação.
    • Selecione as principais K previsões.
    • Aplique um limite de confiança para filtrar resultados de baixa confiança.
    • Mapeie índices de previsão para rótulos legíveis por humanos.
    • Formatar resultados para consumo a jusante.

Nos exemplos IoT para operadores Rust WASM pode encontrar dois exemplos que seguem este padrão:

Configurar definição de gráfico

Para permitir a inferência ONNX no seu grafo de fluxo de dados, configure tanto a estrutura do grafo como os parâmetros do módulo. A definição do gráfico especifica o fluxo do pipeline, enquanto as configurações do módulo permitem a personalização do tempo de execução do comportamento de pré-processamento e inferência.

Ativar a funcionalidade wasi-nn

Para ativar a interface WebAssembly Neural Network (wasi-nn) para inferência ONNX, adicione a wasi-nn funcionalidade à definição do seu grafo:

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

Definir operações para o pipeline de inferência

Configure as operações que formam o seu pipeline de inferência ONNX. Este exemplo mostra um fluxo de trabalho típico de classificação de imagens:

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

Esta configuração cria um pipeline onde:

  • camera-input recebe dados brutos de imagem de uma fonte
  • module-format/map pré-processa imagens (decodificar, redimensionar, formatar a conversão)
  • module-snapshot/map executa a inferência e o pós-processamento ONNX
  • results-output emite resultados de classificação para um sumidouro

Configurar parâmetros do módulo

Definir parâmetros de execução para personalizar o comportamento dos módulos WASM sem necessidade de reconstruir. Estes parâmetros passam para os seus módulos WASM na inicialização.

Para obter detalhes, consulte Parâmetros de configuração do módulo.

Empacotar o modelo

A incorporação de modelos ONNX diretamente em seu componente WASM garante a implantação atômica e a consistência da versão. Essa abordagem simplifica a distribuição e remove dependências de tempo de execução em arquivos de modelo externos ou registros.

Sugestão

A incorporação mantém a versão conjunta do modelo e da lógica do operador. Para atualizar um modelo, publique uma nova versão do módulo e atualize sua definição de gráfico para fazer referência a ele. Essa abordagem elimina o desvio do modelo e garante implantações reproduzíveis.

Requisitos de preparação do modelo ONNX

Antes de incorporar o seu modelo, certifique-se de que cumpre os requisitos para a implementação do WASM:

  • Mantenha os modelos abaixo de 50 MB para tempos de carregamento WASM práticos e restrições de memória.
  • Verifica se o teu modelo aceita uma entrada de tensor único num formato comum (float32 ou uint8).
  • Verifique se o backend em runtime do WASM ONNX suporta todos os operadores que o seu modelo utiliza.
  • Use ferramentas de otimização ONNX para reduzir o tamanho do modelo e melhorar a velocidade de inferência.

Passos para incorporar um modelo ONNX num módulo WASM

Siga estes passos para incorporar o seu modelo ONNX e os recursos associados num módulo WASM:

  1. Organizar recursos do modelo: coloque o arquivo de modelo e, se necessário, o .onnxlabels.txt na sua árvore de origem. Use uma estrutura de diretórios dedicada, como src/fixture/models/ e src/fixture/labels/ para uma organização clara.
  2. Incorporar em tempo de compilação: Use ferramentas específicas da linguagem para incluir bytes de modelo no seu binário. Em Rust, use include_bytes! para dados binários e include_str! para arquivos de texto.
  3. Inicialize o grafo wasi-nn: Na função init do seu operador, crie um grafo wasi-nn a partir dos bytes integrados, especificando a codificação ONNX e o destino de execução na CPU.
  4. Implementar loop de inferência: para cada mensagem recebida, pré-processe entradas para corresponder aos requisitos do modelo, defina tensores de entrada, execute inferência, recupere saídas e aplique pós-processamento.
  5. Manipule erros normalmente: implemente o tratamento adequado de erros para falhas de carregamento de modelo, operadores sem suporte e erros de inferência de tempo de execução.

Para um padrão completo de implementação, veja a amostra "snapshot".

Organize seu projeto de módulo WASM com separação clara de preocupações:

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

Exemplo de layout de ficheiro a partir da amostra snapshot

Use o seguinte layout de arquivo do exemplo de "instantâneo" como referência:

Exemplo mínimo em Rust para integrar um modelo ONNX

O seguinte exemplo de Rust mostra o código mínimo necessário para incorporar um modelo ONNX num módulo WASM. Os caminhos são relativos ao arquivo de origem que contém a 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(())
}

Reutilizar o grafo ONNX e o contexto de execução entre mensagens

Para evitar recriar o grafo ONNX e o contexto de execução para cada mensagem, inicialize-os uma vez e reutilize-os. A amostra public snapshot utiliza um LazyLock estático para inicializar o gráfico e o contexto de execução uma vez por trabalhador:

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

Depura e testa o teu módulo ONNX localmente

Antes de implementar no Operações IoT do Azure, teste localmente o seu módulo de inferência ONNX para validar funcionalidade e desempenho. Para saber mais, consulte:

Configure e implemente o seu módulo ONNX

Quando estiver pronto para usar o seu módulo de inferência ONNX num fluxo de dados ou conector Operações IoT do Azure, siga estes passos:

Exemplo: classificação de imagem MobileNet

As amostras públicas IoT fornecem duas amostras ligadas a um gráfico para classificação de imagens:

  • A "format" sample fornece funcionalidade de descodificação e redimensionamento de imagens.
  • O exemplo "snapshot" fornece inferência ONNX e processamento de softmax.

Para saber mais sobre e executar a amostra que utiliza estes módulos, veja o Exemplo 2: Implementar um grafo complexo.

Limitações da inferência ONNX em grafos de fluxo de dados WASM

A inferência em gráficos de fluxo de dados WASM tem as seguintes limitações:

  • Formato do modelo: apenas ONNX. Os gráficos de fluxo de dados não suportam outros formatos como o TFLite.
  • Hardware: apenas CPU. A aceleração de GPU e TPU não é suportada.
  • Tamanho do modelo: Modelos grandes e inferência que consome muita memória não são suportados. Utilize modelos pequenos, como arquiteturas de classe MobileNet.
  • Entradas de modelo: Apenas são suportados modelos de entrada de tensor único. Não há suporte para modelos com várias entradas, cache de chave-valor e cenários avançados de sequência e de geração.
  • Suporte ao operador: O backend ONNX no runtime WASM deve suportar todos os operadores que o seu modelo utiliza. Se um operador não for suportado, a inferência falhará no tempo de carga ou execução.