你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

在 WebAssembly (WASM) 数据流图中运行 ONNX 推理(预览版)

重要

WASM 数据流图中的 ONNX 推理处于预览状态,不适用于生产工作负荷。

有关 beta 版本、预览版或尚未正式发布的版本的 Azure 功能所适用的法律条款,请参阅 Microsoft Azure 预览版的补充使用条款

本文介绍如何在 WebAssembly 模块中嵌入和运行小型开放式神经网络交换(ONNX)模型,以作为 Azure IoT作数据流图的一部分执行带内推理。 在流式处理数据上直接使用此方法进行低延迟扩充和分类,而无需调用外部预测服务。

ONNX 是一种开放模型格式。 在此预览版中,推理仅通过 WebAssembly 系统接口 (WASI) 神经网络 API (wasi-nn) 在 CPU 上运行。

重要

数据流图目前仅支持 MQTT(消息队列遥测传输)、Kafka 和 OpenTelemetry 终结点。 不支持其他终结点类型,例如 Data Lake、Microsoft Fabric OneLake、Azure 数据资源管理器和本地存储。 有关详细信息,请参阅 已知问题

为何使用带内 ONNX 推理

使用 Azure IoT 操作数据流图,您可以直接在管道中嵌入小型 ONNX 模型推理,而不是调用外部预测服务。 此方法提供了多种实际优势:

  • 低延迟:在数据到达的相同运算符路径中执行实时扩充或分类。 每条消息只涉及本地 CPU 推断,避免网络往返。
  • 紧凑设计:面向 MobileNet 级模型等紧凑模型。 此功能不适用于大型转换器模型、GPU/TPU 加速或频繁的 A/B 模型推出。
  • 针对特定用例进行优化
    • 与多源流处理一致,其中特征已在图中并置
    • 与事件时间语义保持一致,因此推理使用与其他运算符相同的时间戳
    • 保持足够小以嵌入到模块中,且不会超出实际的 WASM 大小和内存限制
  • 简单更新:使用 WASM 和嵌入式模型交付新模块,然后更新图形定义参考。 无需单独更改模型注册表或外部终结点。
  • 硬件约束:在此预览版中,ONNX 后端通过 WebAssembly 系统接口(WASI) wasi-nn在 CPU 上运行。 无 GPU/TPU 目标;仅运行支持的 ONNX 算子。
  • 水平缩放:推理缩放为数据流图缩放。 当运行时为吞吐量添加更多辅助角色时,每个辅助角色都会加载嵌入式模型并参与负载均衡。

何时使用带内 ONNX 推理

如果您有以下需求,请使用带内推理:

  • 低延迟需求,要求在引入时对消息进行内联扩充或分类
  • 小型高效模型,如 MobileNet 类视觉模型或类似的紧凑型模型
  • 推理需要与事件时间处理保持一致,且时间戳与其他算子相同
  • 通过交付具有更新的模型的新模块版本来简单更新

具有以下需求时,不要使用带内推理:

  • 大型 Transformer 模型、GPU/TPU 加速或复杂的 A/B 部署
  • 模型需要多个张量输入、键值缓存或不受支持的 ONNX 算子

注释

你想要使模块和嵌入式模型保持较小。 不支持大型模型和内存密集型工作负荷。 使用紧凑体系结构和小型输入大小(如 224×224)进行图像分类。

先决条件

在开始之前,请确保具备:

  • 具有数据流图功能的 Azure IoT 运维部署。
  • 访问容器注册表(如 Azure 容器注册表)。
  • 为 WebAssembly 模块开发设置的开发环境。

有关详细的设置说明,请参阅 开发 WebAssembly 模块

体系结构模式

数据流图中 ONNX 推理的常见模式包括:

  1. 预处理数据:转换原始输入数据以匹配模型的预期格式。 对于图像模型,此过程通常涉及:
    • 解码图像字节。
    • 调整为目标尺寸(例如 224×224)。
    • 将颜色空间(例如 RGB 转换为 BGR)。
    • 将像素值规范化为预期范围(0-1 或 -1 到 1)。
    • 在正确的张量布局中组织数据:NCHW(批量、通道、高度、宽度)或 NHWC(批量、高度、宽度、通道)。
  2. 运行推理:使用接口将预处理的数据转换为张量,使用 wasi-nn CPU 后端加载嵌入式 ONNX 模型,在执行上下文上设置输入张量,调用模型的转发传递,并检索包含原始预测的输出张量。
  3. 后处理输出:将原始模型输出转换为有意义的结果。 常见操作
    • 应用 softmax 以生成分类概率。
    • 选择前 K 个预测结果。
    • 应用置信度阈值来筛选低置信度结果。
    • 将预测索引映射到人工可读标签。
    • 格式化结果以供下游使用。

Rust WASM 操作员的 IoT 示例 中,可以找到两个遵循此模式的示例:

配置图形定义

若要在数据流图中启用 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/map 运行 ONNX 推理和后处理
  • 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)的单张张量输入。
  • 验证 WASM ONNX 运行时后端是否支持模型使用的每个操作员。
  • 使用 ONNX 优化工具减小模型大小并提高推理速度。

嵌入工作流

按照以下步骤嵌入模型和关联的资源:

  1. 组织模型资产:将 .onnx 模型文件和可选 labels.txt 文件放在源树中。 使用专用目录结构,例如 src/fixture/models/src/fixture/labels/ 用于明确的组织。
  2. 在编译时嵌入:使用特定于语言的机制在二进制文件中包括模型字节。 在 Rust 中,使用 include_bytes! 处理二进制数据,使用 include_str! 处理文本文件。
  3. 初始化 WASI-NN 图:在操作员的 init 函数中,根据嵌入的字节创建图形 wasi-nn ,并指定 ONNX 编码和 CPU 执行目标。
  4. 实现推理循环:对于每个传入消息,预处理输入以匹配模型要求,设置输入张量,执行推理,检索输出并应用后处理。
  5. 正常处理错误:为模型加载失败、不受支持的运算符和运行时推理错误实现正确的错误处理。

有关完整的实现模式,请参阅 “快照”示例

组织 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

示例文件引用

使用“快照”示例中的以下文件布局作为参考:

最小嵌入示例

以下示例演示了最少的 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 图形和执行上下文,请初始化一次并重复使用它。 公共示例使用静态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(...)?;
  }
}

部署您的模块

重用简化的示例生成器或在本地构建:

遵循以下部署过程:

  1. 在发布模式下生成 WASM 模块并生成 <module-name>-<version>.wasm 文件。
  2. 使用 OCI 注册表作为存储(ORAS),将模块及(可选的)图形定义推送到注册表。
  3. 在 Azure IoT操作中创建或复用注册表终结点。
  4. 创建引用图定义项目的数据流图资源。

示例:MobileNet 图像分类

IoT 公共示例提供两个用于图像分类的样本,这些样本被连接到图中:“格式”示例 提供图像解码和调整大小功能,“快照”示例 提供 ONNX 推理和 softmax 处理。

若要部署此示例,请从公共注册表拉取项目,将其推送到注册表,并部署数据流图,如 示例 2:部署复杂图形。 复杂图形使用这些模块来处理图像快照并发出分类结果。

局限性

此预览版具有以下限制:

  • 仅限 ONNX。 数据流图不支持其他格式(如 TFLite)。
  • 仅限 CPU。 无 GPU/TPU 加速。
  • 建议使用小型模型。 不支持大型模型和内存密集型推理。
  • 支持单张量输入模型。 不支持多输入模型、键值缓存和高级序列或生成方案。
  • 确保 WASM 运行时中的 ONNX 后端支持模型的运算符。 如果不支持运算符,推理会在加载或执行时失败。