다음을 통해 공유


WASM(WebAssembly) 데이터 흐름 그래프에서 ONNX 유추 실행

이 문서에서는 Azure IoT Operations 데이터 흐름 그래프의 일부로 대역 내 유추를 수행하기 위해 WebAssembly 모듈 내에 작은 ONNX(Open Neural Network Exchange) 모델을 포함하고 실행하는 방법을 보여 줍니다. 외부 예측 서비스를 호출하지 않고 스트리밍 데이터에서 직접 낮은 지연 시간의 데이터 보강 및 분류에 이 방법을 사용합니다.

중요합니다

데이터 흐름 그래프는 현재 MQTT(메시지 큐 원격 분석 전송), Kafka 및 OpenTelemetry 엔드포인트만 지원합니다. Data Lake, Microsoft Fabric OneLake, Azure Data Explorer 및 로컬 스토리지와 같은 다른 엔드포인트 형식은 지원되지 않습니다. 자세한 내용은 알려진 문제참조하세요.

대역 내 ONNX 간섭을 사용하는 이유

Azure IoT Operations 데이터 흐름 그래프를 사용하면 외부 예측 서비스를 호출하는 대신 파이프라인에 직접 작은 ONNX 모델 유추를 포함할 수 있습니다. 이 방법은 다음과 같은 몇 가지 실질적인 이점을 제공합니다.

  • 짧은 대기 시간: 데이터가 도착하는 동일한 연산자 경로에서 실시간 보강 또는 분류를 수행합니다. 각 메시지는 로컬 CPU 유추만 발생하므로 네트워크 왕복을 방지합니다.
  • 컴팩트 디자인: MobileNet 클래스 모델과 같은 소형 모델을 대상으로 합니다. 이 기능은 대형 변압기 모델, GPU/TPU 가속 또는 빈번한 A/B 모델 롤아웃용이 아닙니다.
  • 특정 사용 사례에 최적화됨:
    • 기능이 그래프에 이미 반영되어 있는 다중 소스 스트림 처리가 포함된 인라인
    • 이벤트 시간 의미 체계에 맞게, 추론에서 다른 연산자와 동일한 타임스탬프를 사용합니다.
    • 실제 WASM 크기 및 메모리 제약 조건을 초과하지 않고 모듈에 포함할 수 있을 만큼 작게 유지
  • 간단한 업데이트: WASM 및 포함된 모델을 사용하여 새 모듈을 배송한 다음 그래프 정의 참조를 업데이트합니다. 별도의 모델 레지스트리 또는 외부 엔드포인트를 변경할 필요가 없습니다.
  • 하드웨어 제약 조건: ONNX 백 엔드는 WASI(WebAssembly 시스템 인터페이스) wasi-nn를 통해 CPU에서 실행됩니다. GPU/TPU 대상이 없습니다. 지원되는 ONNX 연산자만 실행됩니다.
  • 수평 크기 조정: 추론은 데이터 흐름 그래프가 확장됨에 따라 확장됩니다. 런타임에서 처리량에 더 많은 작업자를 추가하면 각 작업자가 포함된 모델을 로드하고 부하 분산에 참여합니다.

대역 내 ONNX 유추를 사용하는 경우

다음과 같은 요구 사항이 있는 경우 대역 내 유추를 사용합니다.

  • 낮은 대기 시간이 필요해 수집 시간에 메시지를 인라인으로 보강하거나 분류해야 합니다.
  • MobileNet 클래스 비전 또는 유사한 컴팩트 모델과 같은 작고 효율적인 모델
  • 이벤트 시간 처리 및 다른 연산자와 동일한 타임스탬프에 맞춰야 하는 유추
  • 업데이트된 모델을 사용하여 새 모듈 버전을 배송하여 간단한 업데이트

다음과 같은 요구 사항이 있는 경우 대역 내 유추를 피합니다.

  • 대형 트랜스포머 모델, GPU/TPU 가속 또는 정교한 A/B 출시
  • 다중 텐서 입력, 키-값 캐싱 또는 지원되지 않는 ONNX 연산자가 필요한 모델

비고

모듈 및 포함된 모델을 작게 유지하려고 합니다. 대형 모델 및 메모리가 많은 워크로드는 지원되지 않습니다. 이미지 분류에는 컴팩트 아키텍처와 224×224와 같은 작은 입력 크기를 사용합니다.

필수 조건

시작하기 전에 다음이 있는지 확인합니다.

  • 데이터 흐름 그래프 기능이 있는 Azure IoT Operations 배포
  • Azure Container Registry와 같은 컨테이너 레지스트리에 액세스합니다.
  • WebAssembly 모듈 개발을 위한 개발 환경 설정

자세한 설정 지침은 WebAssembly 모듈 개발을 참조하세요.

아키텍처 패턴

데이터 흐름 그래프에서 ONNX 유추에 대한 일반적인 패턴은 다음과 같습니다.

  1. 전처리 데이터: 원시 입력 데이터를 모델의 예상 형식과 일치하도록 변환합니다. 이미지 모델의 경우 이 프로세스에는 일반적으로 다음이 포함됩니다.
    • 이미지 바이트 디코딩
    • 대상 차원으로 크기 조정(예: 224×224).
    • 색 공간 변환(예: RGB에서 BGR로).
    • 픽셀 값을 예상 범위(0~1 또는 -1~1)로 정규화합니다.
    • 올바른 텐서 레이아웃으로 데이터 정렬: NCHW(일괄 처리, 채널, 높이, 너비) 또는 NHWC(일괄 처리, 높이, 너비, 채널).
  2. 유추 실행: 인터페이스를 사용하여 전처리된 데이터를 텐서로 변환하고, CPU 백 엔드를 사용하여 wasi-nn 포함된 ONNX 모델을 로드하고, 실행 컨텍스트에서 입력 텐서를 설정하고, 모델의 전달 패스를 호출하고, 원시 예측을 포함하는 출력 텐서를 검색합니다.
  3. 후처리 출력: 원시 모델 출력을 의미 있는 결과로 변환합니다. 일반적인 작업:
    • softmax를 적용하여 분류 확률을 생성합니다.
    • 상위 K 예측을 선택합니다.
    • 신뢰도 임계값을 적용하여 저신뢰성 결과를 필터링합니다.
    • 예측 인덱스를 사람이 읽을 수 있는 레이블에 매핑합니다.
    • 다운스트림 사용량에 대한 결과 서식을 지정합니다.

Rust WASM 연산자의 IoT 샘플에서 다음 패턴을 따르는 두 가지 샘플을 찾을 수 있습니다.

그래프 정의 구성

데이터 흐름 그래프에서 ONNX 유추를 사용하도록 설정하려면 그래프 구조와 모듈 매개 변수를 모두 구성해야 합니다. 그래프 정의는 파이프라인 흐름을 지정하는 반면 모듈 구성은 전처리 및 유추 동작의 런타임 사용자 지정을 허용합니다.

WASI-NN 지원 활성화하기

WebAssembly 신경망 인터페이스 지원을 활성화하려면 그래프 정의에 wasi-nn 기능을 추가하십시오.

moduleRequirements:
  apiVersion: "1.1.0"
  runtimeVersion: "1.1.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 로드 시간 및 메모리 제약 조건을 위해 모델을 50MB 미만으로 유지합니다.
  • 모델이 공통 형식(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 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()?;
  }
}

모듈 배포

간소화된 샘플 작성기를 다시 사용하거나 로컬로 빌드합니다.

다음 배포 프로세스를 따릅니다.

  1. 릴리스 모드에서 WASM 모듈을 빌드하고 파일을 생성합니다 <module-name>-<version>.wasm .
  2. OCI 레지스트리를 스토리지(ORAS)로 사용하여 모듈 및 선택적으로 그래프 정의를 레지스트리에 푸시합니다.
  3. Azure IoT Operations에서 레지스트리 엔드포인트를 만들거나 다시 사용합니다.
  4. 그래프 정의 아티팩트를 참조하는 데이터 흐름 그래프 리소스를 만듭니다.

예: MobileNet 이미지 분류

IoT 공용 샘플은 이미지 분류를 위해 그래프에 유선으로 연결되는 두 개의 샘플을 제공합니다. "형식" 샘플 은 이미지 디코딩 및 크기 조정 기능을 제공하며, "스냅샷" 샘플 은 ONNX 유추 및 소프트맥스 처리를 제공합니다.

이 예제를 배포하려면 공용 레지스트리에서 아티팩트를 끌어오고, 레지스트리에 푸시하고, 예제 2: 복잡한 그래프 배포와 같이 데이터 흐름 그래프를 배포합니다. 복잡한 그래프는 이러한 모듈을 사용하여 이미지 스냅샷을 처리하고 분류 결과를 내보낸다.

제한점

WASM 데이터 흐름 그래프의 유추에는 다음과 같은 제한 사항이 있습니다.

  • ONNX만 해당합니다. 데이터 흐름 그래프는 TFLite와 같은 다른 형식을 지원하지 않습니다.
  • CPU만 해당합니다. GPU/TPU 가속이 없습니다.
  • 작은 모델을 권장합니다. 대용량 모델 및 메모리 집약적 유추는 지원되지 않습니다.
  • 단일 텐서 입력 모델이 지원됩니다. 다중 입력 모델, 키-값 캐싱 및 고급 시퀀스 또는 생성 시나리오는 지원되지 않습니다.
  • WASM 런타임의 ONNX 백 엔드가 모델의 연산자를 지원하는지 확인합니다. 연산자가 지원되지 않으면 로드 또는 실행 시 유추가 실패합니다.

다음 단계