Hoppa till innehållet
AI-grafen
D· AI-utvecklaredatahantering· ca 45 min· grundläggande — ändras sällan· verifierad 2026-09-20

Obalanserade klasser

Kunna hantera skeva klasser med omviktning och rätt mått.

Förkunskaper

Intuition

1 % av transaktionerna är bedrägerier. Din modell säger «inget bedrägeri» om allt och får 99 % träffsäkerhet.

Den har inte lärt sig något alls.

Träffsäkerhet är oanvändbart vid obalans. Använd i stället:

MåttSvarar på
Precisionav dem vi larmade om, hur många var äkta?
Recallav de äkta, hur många hittade vi?
F1harmoniskt medel av de två
PR-AUChela avvägningen, känslig för den sällsynta klassen
ROC-AUChela avvägningen — men ser optimistisk ut vid stark obalans

Precision och recall drar åt olika håll. Larmar du om allt får du perfekt recall och usel precision. Larmar du bara om det du är säker på blir det tvärtom. Vilken som är viktigast avgörs av vad ett fel kostar — inte av statistiken.

Formellt

Tre familjer av åtgärder:

ÅtgärdHurKommentar
Klassvikterclass_weight="balanced"enklast, oftast först; ändrar förlusten, inte datan
Översamplingduplicera eller generera (SMOTE)risk för överanpassning på den lilla klassen
Undersamplingkasta bort från majoritetenslänger information
Tröskeljusteringändra 0,5 till något annatgratis, och ofta det mest verksamma

Den sista raden är underskattad. En modell tränad utan några åtgärder alls, men med tröskeln satt efter kostnad, slår ofta en omsamplad modell med tröskel 0,5.

Tröskeln ska väljas efter kostnad, inte efter symmetri. Om ett missat bedrägeri kostar 5 000 kr och ett falsklarm kostar 50 kr i granskningstid ska du larma så snart sannolikheten överstiger ungefär

p∗=CFPCFP+CFN=5050+5000≈0,01p^* = \frac{C_{FP}}{C_{FP} + C_{FN}} = \frac{50}{50 + 5000} \approx 0{,}01

Alltså 1 %, inte 50 %. Att behålla standardtröskeln är i praktiken att påstå att de två feltyperna kostar lika mycket.

Två regler som är lätta att bryta:

  1. Omsampla bara träningsdatan. Validering och test ska ha den verkliga klassfördelningen, annars mäter du på en värld som inte finns.
  2. Omsampla inne i korsvalideringen, inte före. SMOTE före uppdelningen skapar syntetiska punkter från testvecket och ger läckage.

Om den sällsynta klassen har extremt få exempel (tiotal, inte tusental) är klassificering ofta fel ansats. Överväg anomalidetektion, som modellerar den normala klassen och larmar på avvikelser — den behöver inte exempel på det ovanliga.

Kod

import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (classification_report, average_precision_score,
                             roc_auc_score, precision_recall_curve)

X, y = make_classification(n_samples=20000, weights=[0.99], flip_y=0.01, random_state=0)
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, stratify=y, random_state=0)
print("andel positiva:", round(float(y.mean()), 4))            # 0.0155

for vikt in (None, "balanced"):
    m = LogisticRegression(max_iter=2000, class_weight=vikt).fit(Xtr, ytr)
    p = m.predict_proba(Xte)[:, 1]
    print(f"class_weight={vikt}: ROC-AUC {roc_auc_score(yte, p):.3f}  "
          f"PR-AUC {average_precision_score(yte, p):.3f}")
# class_weight=None:     ROC-AUC 0.962  PR-AUC 0.585
# class_weight=balanced: ROC-AUC 0.962  PR-AUC 0.580
#  ← vikterna ändrade knappt rangordningen; det är tröskeln som gör jobbet

# Välj tröskel efter kostnad
C_FP, C_FN = 50, 5000
m = LogisticRegression(max_iter=2000).fit(Xtr, ytr)
p = m.predict_proba(Xte)[:, 1]

def kostnad(t):
    pred = p >= t
    fp = int(((pred == 1) & (yte == 0)).sum())
    fn = int(((pred == 0) & (yte == 1)).sum())
    return fp * C_FP + fn * C_FN, fp, fn

for t in (0.5, 0.2, 0.05, C_FP / (C_FP + C_FN)):
    k, fp, fn = kostnad(t)
    print(f"  tröskel {t:.3f}: kostnad {k:>8,} kr  (FP {fp:>4}, FN {fn:>3})")

bast = min(np.linspace(0.001, 0.999, 999), key=lambda t: kostnad(t)[0])
print("billigaste tröskel:", round(float(bast), 3))

Utskriften visar det viktiga: skillnaden mellan tröskel 0,5 och en kostnadsvald tröskel är oftast större än skillnaden mellan modeller. Och den kostar ingen träningstid alls.

Behärskning innebär

  • Väljer mått som fungerar vid obalans
  • Använder klassvikter eller omsampling
  • Sätter beslutströskeln efter kostnad

Logga in för att göra övningarna och bygga upp din behärskning.

Källor

Alla källor och licenser