# Edge AI Inferencing Patterns

**Last updated:** 2026-02
**Status:** GA
**Category:** Hybrid Cloud & Edge AI

---

## Introduksjon

Edge AI-inferens handler om a kjore maskinlaeringsmodeller naermest mulig der data oppstar — pa enheter, gateways, lokale servere eller Azure Local-klynger. For norsk offentlig sektor er dette relevant i scenarioer som sanntids videoanalyse, dokumentbehandling i felt, naturspraakbehandling offline, og autonome systemer i omrader med begrenset nettverkstilkobling.

Microsoft tilbyr et bredt spekter av verktoy for edge-inferens: ONNX Runtime som universell inferensmotor, Azure IoT Edge for container-baserte modeller, Azure Stack Edge for hardware-akselerert inferens, og KAITO for LLM-deployment pa Kubernetes. Valget av moenster avhenger av modellstorrelse, latenskrav, tilgjengelig hardware og tilkoblingsstatus.

Denne referansen dekker de viktigste moensterne for modelloptimalisering, akselerasjon, caching og batching/streaming — alle med fokus pa Microsoft Azure-okosystemet og relevans for offentlig sektor.

---

## Model Quantization og Compression

### Hva er kvantisering?

Kvantisering reduserer presisjonen til modellvekter fra hoyere til lavere bit-representasjoner, noe som reduserer modellstorrelse og oker inferenshastighet med minimalt noyaktighetstap.

| Presisjon | Bits | Storrelse (7B modell) | Hastighet | Noyaktighet |
|-----------|------|----------------------|-----------|-------------|
| FP32 | 32 | ~28 GB | Baseline | 100% |
| FP16 | 16 | ~14 GB | 2x | ~99.9% |
| BF16 | 16 | ~14 GB | 2x | ~99.9% |
| INT8 | 8 | ~7 GB | 3-4x | ~99% |
| INT4 | 4 | ~3.5 GB | 5-8x | ~97% |

### Kvantiseringsmetoder i Azure-okosystemet

| Metode | Verktoy | Best for | Presisjon |
|--------|---------|----------|-----------|
| Post-Training Quantization (PTQ) | ONNX Runtime, Olive | Rask konvertering | INT8/INT4 |
| Quantization-Aware Training (QAT) | PyTorch + Azure ML | Hoyest noyaktighet | INT8 |
| GPTQ | HuggingFace + KAITO | LLM-kvantisering | INT4 |
| AWQ | HuggingFace + KAITO | LLM-kvantisering | INT4 |
| Dynamic Quantization | ONNX Runtime | CPU-inferens | INT8 |

### ONNX Runtime-kvantisering

```python
# Kvantiser ONNX-modell til INT8
from onnxruntime.quantization import quantize_dynamic, QuantType

quantize_dynamic(
    model_input="model_fp32.onnx",
    model_output="model_int8.onnx",
    weight_type=QuantType.QInt8,
    optimize_model=True
)
```

### Olive — Microsofts modelloptimalisering

Olive er Microsofts verktoy for helhetlig modelloptimalisering:

```python
# olive_config.json
{
    "input_model": {
        "type": "OnnxModel",
        "model_path": "model.onnx"
    },
    "systems": {
        "local_system": {
            "type": "LocalSystem",
            "accelerators": [{"device": "npu"}]
        }
    },
    "passes": {
        "quantization": {
            "type": "OnnxQuantization",
            "config": {
                "quant_mode": "static",
                "quant_format": "QDQ",
                "calibration_data_reader": "CalibrationDataReader"
            }
        },
        "perf_tuning": {
            "type": "OrtPerfTuning",
            "config": {
                "data_dir": "./calibration_data"
            }
        }
    },
    "engine": {
        "search_strategy": {
            "execution_order": "joint",
            "search_algorithm": "exhaustive"
        },
        "output_dir": "./optimized"
    }
}
```

### Modellkomprimering

| Teknikk | Beskrivelse | Storrelsereduksjon | Noyaktighetstap |
|---------|-------------|-------------------|-----------------|
| Pruning | Fjerner uvesentlige vekter | 30-70% | 1-3% |
| Knowledge Distillation | Laerer liten modell fra stor | 50-90% | 2-5% |
| Weight Sharing | Deler vekter mellom lag | 20-40% | <1% |
| Low-Rank Factorization | Dekomponerer vektmatriser | 30-50% | 1-2% |

---

## Real-time Inference Acceleration

### ONNX Runtime Execution Providers

ONNX Runtime stotter flere hardware-akseleratorer gjennom Execution Providers (EP):

| Execution Provider | Hardware | Best for | Latens |
|--------------------|----------|----------|--------|
| CPU EP | x86/ARM CPU | Universell | Basis |
| CUDA EP | NVIDIA GPU | GPU-inferens | 5-50x raskere |
| TensorRT EP | NVIDIA GPU | Optimalisert GPU | 10-100x raskere |
| OpenVINO EP | Intel CPU/GPU/VPU | Intel-optimalisert | 3-20x raskere |
| DirectML EP | Windows GPU/NPU | Windows-enheter | 5-30x raskere |
| QNN EP | Qualcomm NPU | Snapdragon-enheter | 10-50x raskere |

### GPU-akselerert inferens med ONNX Runtime

```python
import onnxruntime as ort

# Konfigurasjon for NVIDIA GPU (TensorRT)
session_options = ort.SessionOptions()
session_options.graph_optimization_level = \
    ort.GraphOptimizationLevel.ORT_ENABLE_ALL

providers = [
    ('TensorrtExecutionProvider', {
        'trt_max_workspace_size': 2147483648,  # 2 GB
        'trt_fp16_enable': True,
        'trt_engine_cache_enable': True,
        'trt_engine_cache_path': './trt_cache'
    }),
    ('CUDAExecutionProvider', {
        'device_id': 0,
        'arena_extend_strategy': 'kSameAsRequested',
        'gpu_mem_limit': 4 * 1024 * 1024 * 1024,  # 4 GB
        'cudnn_conv_algo_search': 'DEFAULT'
    }),
    'CPUExecutionProvider'
]

session = ort.InferenceSession(
    "model_fp16.onnx",
    sess_options=session_options,
    providers=providers
)

# Inferens
result = session.run(None, {"input": input_data})
```

### OpenVINO Model Server (OVMS) pa Edge

For Azure Arc/Azure Local-miljoer der Intel-hardware brukes:

```yaml
# ovms-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ovms-inference
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: ovms
          image: openvino/model_server:latest
          ports:
            - containerPort: 9000  # gRPC
            - containerPort: 8000  # REST
          volumeMounts:
            - name: model-store
              mountPath: /models
          env:
            - name: MODELS_CONFIG
              value: "/models/config.json"
          resources:
            limits:
              cpu: "4"
              memory: "8Gi"
      volumes:
        - name: model-store
          persistentVolumeClaim:
            claimName: model-pvc
```

### vLLM for LLM-inferens

KAITO bruker vLLM som standard inferensmotor for LLM-er:

| Funksjon | Beskrivelse | Fordel |
|----------|-------------|--------|
| PagedAttention | Effektiv KV-cache-haandtering | 2-4x gjennomstromning |
| Continuous batching | Dynamisk batching av foresporsler | Redusert latens |
| Tensor parallelism | Fordel modell over GPUer | Storre modeller |
| Quantization support | AWQ, GPTQ, SqueezeLLM | Lavere minnebruk |
| OpenAI-compatible API | Standard API-format | Enkel integrasjon |

---

## Caching Patterns for Edge

### Modellcaching-strategier

| Strategi | Implementasjon | Bruksomrade |
|----------|---------------|-------------|
| Model preloading | Last modell i minne ved oppstart | Sanntids inferens |
| TensorRT engine cache | Cach kompilerte TRT-motorer | GPU-inferens |
| ONNX session cache | Gjenbruk ORT-sesjoner | Repetitive inferenser |
| Result caching | Redis/memcached for resultater | Identiske inputs |
| Embedding cache | Cach vektorrepresentasjoner | RAG pa edge |

### Resultatchaching med Redis pa Edge

```yaml
# redis-cache-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inference-cache
spec:
  template:
    spec:
      containers:
        - name: redis
          image: redis:7-alpine
          ports:
            - containerPort: 6379
          resources:
            limits:
              memory: "2Gi"
          args:
            - "--maxmemory"
            - "1.5gb"
            - "--maxmemory-policy"
            - "allkeys-lru"
```

```python
# Inferens med caching
import redis
import hashlib
import json

cache = redis.Redis(host='inference-cache', port=6379)

def cached_inference(model_session, input_data, ttl=3600):
    # Generer cache-nokkel fra input
    cache_key = hashlib.sha256(
        json.dumps(input_data, sort_keys=True).encode()
    ).hexdigest()

    # Sjekk cache
    cached = cache.get(cache_key)
    if cached:
        return json.loads(cached)

    # Kjor inferens
    result = model_session.run(None, input_data)

    # Lagre i cache
    cache.setex(cache_key, ttl, json.dumps(result.tolist()))
    return result
```

### KV-cache for LLM-er

For LLM-inferens pa edge er KV-cache (key-value cache) kritisk:

| Teknikk | Beskrivelse | Minnebesparelse |
|---------|-------------|-----------------|
| Standard KV-cache | Full cache for alle tokens | Baseline |
| Sliding window | Begrens cache til siste N tokens | 50-80% |
| Grouped-query attention | Faerre KV-hoder | 4-8x |
| PagedAttention (vLLM) | Sidert minnehaandtering | Dynamisk |

---

## Batching vs. Streaming Inference

### Sammenligning

| Egenskap | Batch Inference | Streaming Inference |
|----------|----------------|---------------------|
| Latens | Hoyere (venter pa batch) | Lavere (umiddelbar) |
| Gjennomstromning | Hoyere (GPU-utnyttelse) | Lavere (per request) |
| GPU-utnyttelse | Optimal (fyller batch) | Variabel |
| Bruksomrade | Dokumentanalyse, batch-scoring | Chat, real-time |
| Skalering | Horisontal (flere workers) | Vertikal (GPU-kapasitet) |

### Beslutningstre

```
Trenger du sanntidssvar (<100ms)?
├── Ja → Streaming inference
│   ├── Enkelt request → Single request pipeline
│   └── Flere samtidige → Continuous batching (vLLM)
└── Nei → Batch inference
    ├── <1000 elementer → Micro-batch pa GPU
    └── >1000 elementer → Azure ML Batch Endpoints
```

### Batch Inference pa Edge

```python
import numpy as np
from collections import deque
import threading
import time

class EdgeBatchInferencer:
    def __init__(self, session, batch_size=8, max_wait_ms=50):
        self.session = session
        self.batch_size = batch_size
        self.max_wait = max_wait_ms / 1000
        self.queue = deque()
        self.lock = threading.Lock()

    def infer(self, input_data):
        """Legg til i koe og vent pa batch-resultat."""
        event = threading.Event()
        result_container = {}

        with self.lock:
            self.queue.append((input_data, event, result_container))

        # Trigger batch hvis full
        if len(self.queue) >= self.batch_size:
            self._process_batch()

        # Vent pa resultat (med timeout)
        event.wait(timeout=self.max_wait * 2)
        return result_container.get('result')

    def _process_batch(self):
        """Prosesser akkumulerte inputs som en batch."""
        with self.lock:
            items = []
            while self.queue and len(items) < self.batch_size:
                items.append(self.queue.popleft())

        if not items:
            return

        # Kombiner inputs til batch
        batch_input = np.stack([item[0] for item in items])

        # Kjor batch-inferens
        batch_results = self.session.run(
            None, {"input": batch_input}
        )

        # Distribuer resultater
        for i, (_, event, container) in enumerate(items):
            container['result'] = batch_results[0][i]
            event.set()
```

### Streaming Inference for LLM pa Edge

```python
# Streaming med vLLM-kompatibelt API (KAITO)
import requests
import json

def stream_inference(prompt, service_ip, max_tokens=500):
    """Stream tokens fra lokal LLM pa edge."""
    response = requests.post(
        f"http://{service_ip}/v1/completions",
        json={
            "model": "phi-4-mini-instruct",
            "prompt": prompt,
            "max_tokens": max_tokens,
            "stream": True
        },
        stream=True
    )

    for line in response.iter_lines():
        if line:
            data = line.decode('utf-8')
            if data.startswith('data: '):
                chunk = json.loads(data[6:])
                if chunk.get('choices'):
                    token = chunk['choices'][0].get('text', '')
                    yield token
```

---

## IoT Edge ML Inference Pattern

### Azure IoT Edge med dynamisk modellasting

```
┌─────────────────────────────────────────┐
│          Azure Cloud                     │
│  ┌──────────┐    ┌──────────────────┐   │
│  │ Blob     │    │ IoT Hub          │   │
│  │ Storage  │    │ (Device Twins)   │   │
│  │ (Models) │    │                  │   │
│  └────┬─────┘    └────────┬─────────┘   │
└───────┼───────────────────┼─────────────┘
        │                   │
        │   ┌───────────────▼──────────┐
        │   │    IoT Edge Runtime      │
        │   │  ┌─────────────────────┐ │
        └───┼─►│ Model Loader Module │ │
            │  └──────────┬──────────┘ │
            │  ┌──────────▼──────────┐ │
            │  │ Inference Module    │ │
            │  │ (ONNX/LiteRT)      │ │
            │  └──────────┬──────────┘ │
            │  ┌──────────▼──────────┐ │
            │  │ Local Storage       │ │
            │  │ (Model Cache)       │ │
            │  └─────────────────────┘ │
            └──────────────────────────┘
```

### Device Twin-basert modellstyring

```python
# Motta modelloppdatering via IoT Edge Device Twin
from azure.iot.device import IoTHubModuleClient

async def twin_update_handler(patch):
    if 'model_version' in patch:
        model_url = patch['model_url']
        model_version = patch['model_version']
        checksum = patch['model_checksum']

        # Last ned ny modell
        await download_model(model_url, checksum)

        # Hot-swap modell uten nedetid
        await reload_model(model_version)

        # Rapporter tilbake
        reported = {
            "active_model_version": model_version,
            "model_loaded_at": datetime.utcnow().isoformat()
        }
        client.patch_twin_reported_properties(reported)
```

---

## Ytelsesreferanser

### Typiske inferenstider pa Microsoft edge-hardware

| Hardware | Modell | Oppgave | Latens (ms) |
|----------|--------|---------|-------------|
| Azure Stack Edge Pro GPU (T4) | YOLOv8 | Objektdeteksjon | 8-15 |
| Azure Stack Edge Pro GPU (T4) | ResNet-50 | Bildeklassifisering | 3-5 |
| Azure Local (A2) | Phi-3-mini-4k | Tekst (128 tokens) | 200-400 |
| Azure Local (L4) | Phi-4-mini | Tekst (128 tokens) | 80-150 |
| Azure Local (L40S) | Mistral-7B | Tekst (128 tokens) | 50-100 |
| Intel CPU (Xeon) + OpenVINO | BERT-base | NER | 5-10 |
| CPU (ONNX Runtime) | DistilBERT | Sentiment | 3-8 |

---

## For Cosmo

- **ONNX Runtime er universalmotoren for edge-inferens** — stotter CPU, GPU, NPU via Execution Providers, med TensorRT for NVIDIA og OpenVINO for Intel som de viktigste akseleratorene.
- **Kvantisering (INT8/INT4) er den enkleste og mest effektive optimaliseringen** — reduserer modellstorrelse 2-8x med minimalt noyaktighetstap, spesielt viktig for edge-enheter med begrenset minne.
- **Velg batching for gjennomstromning, streaming for latens** — continuous batching (vLLM/KAITO) gir det beste av begge for LLM-inferens pa edge Kubernetes-klynger.
- **Caching pa flere nivaer er essensielt for edge-ytelse** — TensorRT engine cache, resultat-cache (Redis), og KV-cache for LLM-er reduserer bade latens og GPU-belastning.
- **IoT Edge med Device Twins gir skalerbar modellstyring** for distribuerte edge-enheter — modellversjonering, inkrementell oppdatering og hot-swap uten nedetid.
