# On-Premises SLM and Phi Model Deployment **Last updated:** 2026-02 **Status:** GA **Category:** Hybrid Cloud & Edge AI --- ## Introduksjon Small Language Models (SLM) er kompakte AI-modeller med faerre enn 10 milliarder parametere som kan kjores effektivt pa lokal hardware uten skyavhengighet. Microsofts Phi-modellserie — fra Phi-2 (2.7B) til Phi-4 (14B) — representerer state-of-the-art for SLM, med ytelse som konkurrerer med modeller mange ganger storre. For norsk offentlig sektor er lokal SLM-deployment sarlig attraktivt: full datakontroll uten at data forlater organisasjonens nettverk, forutsigbare kostnader uten per-token-prising, og mulighet for drift i miljoer med begrenset eller ingen internettilkobling. Phi-modellene er spesielt godt egnet fordi de er optimalisert for oppgaver som klassifisering, oppsummering, enhetstuttrekking og enkel sporsmalsbesvaring. Microsoft tilbyr flere deploymentsveier for lokale SLM: Azure App Service sidecar, AKS Edge Essentials med KAITO, ONNX Runtime pa Windows/Linux, og Windows ML pa Copilot+ PC-er. Valget avhenger av skaleringsbehovet, tilgjengelig hardware og integrasjonskrav. --- ## Kjernekomponenter | Komponent | Formal | Teknologi | |-----------|--------|-----------| | Phi-3 Mini | 3.8B parameter SLM for generelle oppgaver | MIT-lisens | | Phi-3 Small | 7B parameter SLM for hoeyere kvalitet | MIT-lisens | | Phi-3 Medium | 14B parameter SLM for komplekse oppgaver | MIT-lisens | | Phi-3.5 Mini | Forbedret 3.8B med multilingual stoette | MIT-lisens | | Phi-4 Mini | Nyeste 3.8B med forbedret resonnering | MIT-lisens | | ONNX Runtime | Cross-platform inferensmotor | Open source | | KAITO | Kubernetes AI Toolchain Operator | Azure Arc | | Olive | Modelloptimalisering for deployment | Microsoft | | Windows ML | Lokal inferens pa Windows | Windows SDK | --- ## Phi-3/Phi-4 Deployment ### Modelloversikt | Modell | Parametere | Kontekst | RAM-krav | GPU-krav | Styrker | |--------|-----------|----------|----------|----------|---------| | Phi-3 Mini 4K | 3.8B | 4K tokens | 8 GB | Valgfritt | Enkel Q&A, klassifisering | | Phi-3 Mini 128K | 3.8B | 128K tokens | 8 GB | Anbefalt | Lange dokumenter | | Phi-3 Small | 7B | 128K tokens | 16 GB | Anbefalt | Flerspraklig, koding | | Phi-3 Medium | 14B | 128K tokens | 32 GB | Pakrevd | Kompleks resonnering | | Phi-3.5 Mini | 3.8B | 128K tokens | 8 GB | Valgfritt | Multilingual, forbedret | | Phi-4 Mini | 3.8B | 128K tokens | 8 GB | Valgfritt | Beste resonnering i klassen | ### Deployment med Azure App Service Sidecar ```yaml # App Service sidecar-konfigurasjon for Phi-3.5 Mini apiVersion: apps/v1 kind: Deployment metadata: name: phi-slm-app spec: template: spec: containers: # Hoved-applikasjon - name: web-app image: myregistry.azurecr.io/myapp:latest ports: - containerPort: 8080 env: - name: SLM_ENDPOINT value: "http://localhost:11434" # SLM sidecar - name: phi-sidecar image: mcr.microsoft.com/oss/ollama/ollama:latest ports: - containerPort: 11434 resources: requests: memory: "8Gi" cpu: "4" limits: memory: "16Gi" cpu: "8" command: ["ollama", "serve"] lifecycle: postStart: exec: command: ["ollama", "pull", "phi3.5"] ``` ### Deployment med KAITO pa AKS Edge ```yaml # KAITO Workspace for Phi-3 Mini pa edge apiVersion: kaito.sh/v1alpha1 kind: Workspace metadata: name: phi3-edge annotations: kaito.sh/enablelb: "false" # Ikke ekstern lastbalansering pa edge spec: resource: instanceType: "Standard_NC4as_T4_v3" # GPU-node labelSelector: matchLabels: apps: phi3-edge inference: preset: name: "phi-3-mini-128k-instruct" adapters: - source: name: "custom-norwegian-adapter" image: "myregistry/phi3-no-adapter:v1" ``` ### ONNX Runtime deployment (CPU) ```python # Phi-3 Mini deployment med ONNX Runtime (ingen GPU nodvendig) import onnxruntime_genai as og class PhiLocalDeployment: def __init__(self, model_path: str): """ Initialiser Phi-3/4 lokal deployment. model_path: Sti til ONNX-optimalisert Phi-modell """ self.model = og.Model(model_path) self.tokenizer = og.Tokenizer(self.model) self.search_options = { "max_length": 2048, "temperature": 0.7, "top_p": 0.9, "do_sample": True } def generate(self, prompt: str, system_message: str = None, max_tokens: int = 1024) -> str: """Generer svar fra lokal Phi-modell""" if system_message: full_prompt = ( f"<|system|>\n{system_message}<|end|>\n" f"<|user|>\n{prompt}<|end|>\n" f"<|assistant|>\n" ) else: full_prompt = ( f"<|user|>\n{prompt}<|end|>\n" f"<|assistant|>\n" ) input_tokens = self.tokenizer.encode(full_prompt) params = og.GeneratorParams(self.model) params.set_search_options(**{ **self.search_options, "max_length": max_tokens }) params.input_ids = input_tokens generator = og.Generator(self.model, params) output_tokens = [] while not generator.is_done(): generator.compute_logits() generator.generate_next_token() new_token = generator.get_next_tokens()[0] output_tokens.append(new_token) return self.tokenizer.decode(output_tokens) def generate_streaming(self, prompt: str, system_message: str = None): """Streaming-generering for lavere opplevd latens""" full_prompt = self._format_prompt(prompt, system_message) input_tokens = self.tokenizer.encode(full_prompt) params = og.GeneratorParams(self.model) params.set_search_options(**self.search_options) params.input_ids = input_tokens generator = og.Generator(self.model, params) tokenizer_stream = self.tokenizer.create_stream() while not generator.is_done(): generator.compute_logits() generator.generate_next_token() token = generator.get_next_tokens()[0] yield tokenizer_stream.decode(token) ``` --- ## Resource-Constrained Sizing ### Hardware-dimensjonering | Scenario | Modell | CPU | RAM | GPU | Disk | Inferens-hastighet | |----------|--------|-----|-----|-----|------|-------------------| | Minimal (PC) | Phi-3 Mini INT4 | 4 kjerner | 8 GB | Ingen | 5 GB | ~10 tokens/s | | Standard (server) | Phi-3 Mini FP16 | 8 kjerner | 16 GB | T4 16GB | 10 GB | ~40 tokens/s | | Ytelse (GPU) | Phi-3 Small FP16 | 8 kjerner | 32 GB | A10G 24GB | 20 GB | ~50 tokens/s | | Enterprise | Phi-3 Medium FP16 | 16 kjerner | 64 GB | A100 40GB | 40 GB | ~60 tokens/s | | Edge (NPU) | Phi-3 Mini INT4 | Snapdragon X | 16 GB | NPU | 5 GB | ~20 tokens/s | ### Minnesoptimalisering ```python # Konfigurasjon for ressursbegrensede miljoer import onnxruntime as ort def create_optimized_session(model_path: str, max_memory_gb: float = 4.0): """Opprett ONNX-session optimalisert for begrenset minne""" session_options = ort.SessionOptions() # Reduser minnebruk session_options.enable_mem_pattern = True session_options.enable_mem_reuse = True # Begrens trader basert pa tilgjengelige kjerner import os available_cores = os.cpu_count() or 4 session_options.intra_op_num_threads = max(1, available_cores // 2) session_options.inter_op_num_threads = max(1, available_cores // 4) # Velg execution provider basert pa tilgjengelig hardware providers = [] if ort.get_device() == "GPU": providers.append(('CUDAExecutionProvider', { 'device_id': 0, 'arena_extend_strategy': 'kSameAsRequested', 'gpu_mem_limit': int(max_memory_gb * 1024 * 1024 * 1024), 'cudnn_conv_algo_search': 'HEURISTIC' })) providers.append('CPUExecutionProvider') return ort.InferenceSession( model_path, sess_options=session_options, providers=providers ) ``` --- ## Prompt Optimization for SLM ### SLM-spesifikke prompt-teknikker SLM-er har begrenset kontekstvindu og resonneringskapasitet sammenlignet med LLM-er. Prompt-optimalisering er kritisk: | Teknikk | Beskrivelse | Effekt | |---------|-------------|--------| | Konsist system-melding | Kort, presis rolledefinisjon (< 100 tokens) | Bedre oppgavefokus | | Strukturert output | Be om JSON/tabell-format | Mer palitelig parsing | | Few-shot eksempler | 1-3 konkrete eksempler | Hoyere noyaktighet | | Decomposition | Del opp komplekse oppgaver | Bedre resultater | | Constraint-basert | Eksplisitte begrensninger | Unnga hallusinasjoner | ### Prompt-maler for norsk offentlig sektor ```python # Optimaliserte prompt-maler for SLM i offentlig sektor SLM_PROMPTS = { "klassifisering": """<|system|> Du klassifiserer dokumenter. Svar KUN med en av kategoriene. Kategorier: {categories} <|end|> <|user|> Klassifiser folgende tekst: "{text}" Kategori:<|end|> <|assistant|>""", "oppsummering": """<|system|> Du oppsummerer tekst pa norsk. Maks {max_words} ord. <|end|> <|user|> Oppsummer folgende: "{text}" <|end|> <|assistant|> Oppsummering:""", "uttrekking": """<|system|> Du trekker ut strukturert informasjon. Svar i JSON-format. <|end|> <|user|> Trekk ut folgende felter fra teksten: {fields} Tekst: "{text}" JSON:<|end|> <|assistant|> {{""", "qa_med_kontekst": """<|system|> Du svarer pa sporsmal basert pa konteksten. Svar KUN basert pa informasjonen gitt. Hvis svaret ikke finnes i konteksten, si "Ikke tilstrekkelig informasjon." <|end|> <|user|> Kontekst: {context} Sporsmal: {question} <|end|> <|assistant|>""" } ``` --- ## Fine-Tuning at Edge ### Lokal fine-tuning av Phi-modeller ```python # LoRA fine-tuning av Phi-3 for norsk offentlig sektor from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer ) from peft import LoraConfig, get_peft_model import torch def finetune_phi_lora( base_model: str = "microsoft/phi-3-mini-4k-instruct", dataset_path: str = "training_data.jsonl", output_dir: str = "./phi3-finetuned" ): """Fine-tune Phi-3 med LoRA for norsk offentlig sektor""" # Last modell med 4-bit kvantisering for a spare minne model = AutoModelForCausalLM.from_pretrained( base_model, torch_dtype=torch.bfloat16, load_in_4bit=True, device_map="auto" ) tokenizer = AutoTokenizer.from_pretrained(base_model) # LoRA-konfigurasjon (minimal for edge) lora_config = LoraConfig( r=16, # Lav rank for edge-deployment lora_alpha=32, target_modules=["q_proj", "v_proj", "k_proj", "o_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM" ) model = get_peft_model(model, lora_config) # Treningsargumenter optimalisert for begrenset hardware training_args = TrainingArguments( output_dir=output_dir, num_train_epochs=3, per_device_train_batch_size=2, gradient_accumulation_steps=8, learning_rate=2e-4, fp16=True, logging_steps=10, save_strategy="epoch", optim="paged_adamw_8bit", # Minneeffektiv optimizer max_grad_norm=0.3, warmup_ratio=0.03 ) trainer = Trainer( model=model, args=training_args, train_dataset=load_dataset(dataset_path), tokenizer=tokenizer ) trainer.train() # Lagre kun LoRA-adapteret (liten filstorrelse) model.save_pretrained(output_dir) # Adapter-storrelse: typisk 20-50 MB vs 7+ GB for full modell ``` ### ONNX-eksport for deployment ```bash # Konverter fine-tuned Phi-3 til ONNX for deployment python -m olive run \ --config olive-config.json \ --model-id ./phi3-finetuned \ --output-dir ./phi3-onnx-optimized \ --precision int4 \ --target-device cpu ``` ```json // olive-config.json { "input_model": { "type": "HfModel", "model_path": "./phi3-finetuned" }, "passes": [ {"type": "OnnxConversion"}, {"type": "OnnxQuantization", "quant_mode": "dynamic", "weight_type": "QInt4"}, {"type": "OrtTransformersOptimization", "model_type": "gpt2"} ], "engine": { "target": "onnxruntime", "execution_providers": ["CPUExecutionProvider"] } } ``` --- ## Norsk offentlig sektor ### Hvorfor lokal SLM for offentlig sektor? - **Datakontroll**: Ingen data forlater organisasjonens nettverk — viktig for personopplysninger og gradert informasjon - **Kostnadskontroll**: Fast infrastrukturkostnad uten per-token-prising — enklere budsjettforvaltning - **Tilgjengelighet**: Fungerer uten internettilkobling — relevant for felt, krisesituasjoner, og isolerte miljoer - **Etterlevelse**: Enklere a demonstrere compliance med Schrems II, GDPR, og NSM-krav ### Anbefalte bruksomrader for SLM | Bruksomrade | Modell | Beskrivelse | |-------------|--------|-------------| | Dokumentklassifisering | Phi-3 Mini INT4 | Klassifiser innkommende post/henvendelser | | Oppsummering | Phi-3.5 Mini | Oppsummer lange utredninger og hoeringsnotater | | Informasjonsuttrekking | Phi-3 Mini | Trekk ut nokkeldata fra skjemaer | | Intern Q&A | Phi-4 Mini + RAG | Svar pa sporsmal fra regelverk | | Tekstgenerering | Phi-3 Small | Utkast til brev og standardsvar | --- ## Beslutningsrammeverk | Scenario | Anbefaling | Begrunnelse | |----------|------------|-------------| | Enkel klassifisering/uttrekking | Phi-3 Mini INT4 pa CPU | Minimal hardware, rask inferens | | Norsk sprakbehandling | Phi-3.5 Mini eller Phi-4 Mini | Bedre multilingual stoette | | Kompleks resonnering | Phi-3 Medium pa GPU | Nodvendig kapasitet | | Edge-deployment (IoT) | Phi-3 Mini INT4 ONNX | Minst fotavtrykk | | Windows-klient | Windows ML + Phi-4 Mini | Automatisk hardware-optimalisering | | Copilot+ PC | Windows ML med NPU | Best ytelse/watt | | Server-deployment | KAITO pa AKS Edge | Skalerbart, Kubernetes-managed | --- ## For Cosmo - **Phi-3 Mini INT4 er det naturlige startpunktet** for de fleste offentlige sektors SLM-bruk — 3.8B parametere gir overraskende god kvalitet for klassifisering, uttrekking og enkel Q&A, og krever kun 8 GB RAM uten GPU - **Fine-tuning med LoRA er nodvendig for doenmespesifikke oppgaver** — en LoRA-adapter pa 20-50 MB er mye enklere a distribuere til edge enn en full modell, og gir dramatisk forbedring for norsk fagsprak - **Prompt-optimalisering er viktigere for SLM enn for LLM** — korte, strukturerte prompts med eksplisitte output-formater og 1-3 few-shot-eksempler oker kvaliteten betydelig - **ONNX Runtime + Olive er den foretrukne deployment-pipeline** — konverter til ONNX, kvantiser til INT4, og deploy pa CPU for maksimal portabilitet og ytelse - **For norsk offentlig sektor: Lokal SLM-deployment eliminerer de fleste Schrems II-utfordringer** — data forlater aldri nettverket, noe som forenkler personvernkonsekvensvurdering og compliance-dokumentasjon