Machine Learning Academy · Lektion

Klassvikter och justering av tröskelvärden

Ni kommer att ange class_weight='balanced' i sklearn-klassificerare och sedan flytta beslutströskeln för sannolikhetsutdata för att ytterligare optimera recall för sällsynta händelser.

Lektion 4 av 413 steg

Klassvikter och justering av tröskelvärden är en gratis lektion i Machine Learning Academy på CoddyKit. Detta är lektion 4 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Machine Learning Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Machine Learning Academy innehåller totalt 4 lektioner.

Två alternativ till omsampling

Omsampling (SMOTE, undersampling) ändrar träningsdata fysiskt. Två alternativa metoder arbetar direkt med originaldata: klassviktning ger större straff under träningen när minoritetsobservationer klassificeras fel, medan tröskelförflyttning justerar beslutsgränsen efter träningen. Båda metoderna är enklare och snabbare och undviker riskerna för informationsförlust eller syntetiskt brus som omsampling kan medföra.

Klassvikter: ge större straff för fel i minoritetsklassen

De flesta sklearn-klassificerare accepterar parametern class_weight. När ni anger class_weight='balanced' beräknas vikter automatiskt, omvänt proportionella mot klassernas frekvenser: weight[c] = n_samples / (n_classes * count[c]). Att klassificera en sällsynt positiv observation fel straffas då mycket hårdare än att klassificera en vanlig negativ observation fel, vilket får modellen att lära sig minoritetsklassen bättre.

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

X, y = make_classification(n_samples=1000, weights=[0.95, 0.05], random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)

sc = StandardScaler()
X_tr = sc.fit_transform(X_train)
X_te = sc.transform(X_test)

# With balanced class weights
lr = LogisticRegression(class_weight='balanced').fit(X_tr, y_train)
print('--- class_weight=balanced ---')
print(classification_report(y_test, lr.predict(X_te)))

Beräkna balanserade vikter manuellt

Ni kan också skicka in en ordlista med anpassade vikter om ni vill ha större kontroll än med 'balanced'. Om verksamheten exempelvis anger att en missad bedrägeritransaktion kostar 20 gånger mer än ett falsklarm kan ni ange class_weight={0: 1, 1: 20}. På så sätt kodas kostnadsförhållandet för felklassificering direkt in i träningen.

from sklearn.linear_model import LogisticRegression
from sklearn.utils.class_weight import compute_class_weight
import numpy as np

# Compute balanced weights automatically
y_train = np.array([0] * 475 + [1] * 25)
classes = np.unique(y_train)
weights = compute_class_weight('balanced', classes=classes, y=y_train)
weight_dict = dict(zip(classes, weights))
print('Auto-balanced weights:', weight_dict)

# Custom: penalty 10x higher for missing positive
custom_weights = {0: 1, 1: 10}
print('Custom weights:', custom_weights)

lr = LogisticRegression(class_weight=custom_weights)
# lr.fit(X_train, y_train)

Vilka algoritmer stöder class_weight?

I scikit-learn accepterar följande estimatorer class_weight: LogisticRegression, LinearSVC, SVC, SGDClassifier, DecisionTreeClassifier, RandomForestClassifier. Gradient boosting-modeller (XGBoost, LightGBM) använder parametern scale_pos_weight, som har samma funktion. Neurala nätverk hanterar detta genom sample_weight i förlustfunktionen.

from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression

# All support class_weight='balanced'
models = [
    LogisticRegression(class_weight='balanced'),
    DecisionTreeClassifier(class_weight='balanced'),
    RandomForestClassifier(class_weight='balanced'),
    SVC(class_weight='balanced', probability=True)
]

print('All these models support class_weight:')
for m in models:
    print(' ', m.__class__.__name__)

Tröskelförflyttning: justera beslutsgränsen

Klassificerare returnerar ett sannolikhetsvärde; standardtröskeln är 0,5 – modellen förutsäger positiv klass om proba >= 0.5. Vid obalanserade problem ökar ett sänkt tröskelvärde (till exempel 0,3) recall (fångar fler positiva fall), men leder till fler falska positiva resultat. Ett höjt tröskelvärde ökar precisionen, men gör att fler positiva fall missas. Tröskelförflyttning tillämpas efter träningen och kräver inte att modellen tränas om.

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_score, recall_score, f1_score
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
import numpy as np

X, y = make_classification(n_samples=1000, weights=[0.95, 0.05], random_state=42)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, stratify=y, random_state=42)
sc = StandardScaler()
X_tr_s = sc.fit_transform(X_tr)
X_te_s = sc.transform(X_te)

lr = LogisticRegression(class_weight='balanced').fit(X_tr_s, y_tr)
proba = lr.predict_proba(X_te_s)[:, 1]

print(f'{'Threshold':>10}  {'Precision':>10}  {'Recall':>8}  {'F1':>6}')
for t in [0.2, 0.3, 0.4, 0.5, 0.6]:
    preds = (proba >= t).astype(int)
    p = precision_score(y_te, preds, zero_division=0)
    r = recall_score(y_te, preds)
    f = f1_score(y_te, preds, zero_division=0)
    print(f'{t:>10.1f}  {p:>10.4f}  {r:>8.4f}  {f:>6.4f}')

Hitta den optimala tröskeln

Använd funktionerna precision_recall_curve och roc_curve för att gå igenom alla möjliga tröskelvärden och beräkna motsvarande precision och recall. Välj sedan det tröskelvärde som maximerar F1 (eller något annat verksamhetsmått). Detta är en mer systematisk metod än att gissa ett tröskelvärde.

from sklearn.metrics import precision_recall_curve, f1_score
import numpy as np

# proba and y_te from previous step
precisions, recalls, thresholds = precision_recall_curve(y_te, proba)

# F1 at each threshold
f1_scores = 2 * (precisions * recalls) / (precisions + recalls + 1e-8)
best_idx = np.argmax(f1_scores)
best_threshold = thresholds[best_idx]
best_f1 = f1_scores[best_idx]

print(f'Optimal threshold: {best_threshold:.4f}')
print(f'Best F1 at that threshold: {best_f1:.4f}')

# Apply the optimal threshold
y_pred_opt = (proba >= best_threshold).astype(int)
print('Minority class recall:', recall_score(y_te, y_pred_opt).round(4))

class_weight jämfört med omsampling: avvägningar

Fördelar med class_weight: inga dataändringar, ingen risk för överanpassning till syntetiska observationer, fungerar i en vanlig sklearn Pipeline och är snabbare. Fördelar med omsampling: fungerar med algoritmer som inte stöder class_weight (till exempel vissa boosting-varianter) och kan hjälpa vid mycket kraftig obalans (>100:1), där klassviktning ensam inte räcker. I praktiken bör ni prova klassviktning först – det är enklare och räcker ofta.

Tröskelförflyttning med ROC-kurva

ROC-kurvan visar avvägningen mellan andelen sanna positiva (recall) och andelen falska positiva vid varje tröskelvärde. Youdens J-statistik (TPR - FPR) maximeras vid den optimala tröskeln för balanserad sensitivitet och specificitet. Använd detta när ni vill ge båda typerna av fel lika stor betydelse.

from sklearn.metrics import roc_curve
import numpy as np

fpr, tpr, thresholds = roc_curve(y_te, proba)

# Youden's J: maximise TPR - FPR
j_scores = tpr - fpr
best_idx = np.argmax(j_scores)
optimal_threshold = thresholds[best_idx]

print(f'Optimal threshold (Youden J): {optimal_threshold:.4f}')
print(f'TPR: {tpr[best_idx]:.4f}  FPR: {fpr[best_idx]:.4f}')

y_pred_youden = (proba >= optimal_threshold).astype(int)
from sklearn.metrics import classification_report
print(classification_report(y_te, y_pred_youden))

Klassvikter i XGBoost och LightGBM

XGBoost använder scale_pos_weight – förhållandet mellan negativa och positiva observationer i träningsmängden – för att vikta upp positiva exempel. Vid en obalans på 95:5 anger ni scale_pos_weight=19 (95/5). LightGBM använder på motsvarande sätt is_unbalance=True eller scale_pos_weight. Båda motsvarar sklearns class_weight='balanced', men använder en annan namnkonvention för parametern.

import xgboost as xgb
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score

X, y = make_classification(n_samples=1000, weights=[0.95, 0.05], random_state=42)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, stratify=y, random_state=42)

neg, pos = np.bincount(y_tr)
scale = neg / pos
print(f'scale_pos_weight = {scale:.1f}')

clf = xgb.XGBClassifier(scale_pos_weight=scale, random_state=42, eval_metric='logloss')
clf.fit(X_tr, y_tr)
print('AUC:', roc_auc_score(y_te, clf.predict_proba(X_te)[:, 1]).round(4))

Jämförelse av alla strategier för obalanserade data

En systematisk jämförelse av strategier på samma datamängd och testmängd visar vilken metod som fungerar bäst för just ert problem och er algoritm. Genomför jämförelsen och redovisa resultaten i en tabell för att motivera ert slutliga strategival.

from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
from imblearn.pipeline import Pipeline as ImbPipeline
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_auc_score
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
import numpy as np

X, y = make_classification(n_samples=1000, weights=[0.95, 0.05], random_state=42)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, stratify=y, random_state=0)
sc = StandardScaler()
X_tr_s = sc.fit_transform(X_tr)
X_te_s = sc.transform(X_te)

strategies = [
    ('Baseline', None, {}),
    ('class_weight', None, {'class_weight': 'balanced'}),
    ('RUS', RandomUnderSampler(random_state=0), {}),
    ('SMOTE', SMOTE(random_state=0), {})
]

for name, sampler, kwargs in strategies:
    Xr, yr = (sampler.fit_resample(X_tr_s, y_tr) if sampler else (X_tr_s, y_tr))
    lr = LogisticRegression(**kwargs).fit(Xr, yr)
    auc = roc_auc_score(y_te, lr.predict_proba(X_te_s)[:, 1])
    print(f'{name:15s}: AUC={auc:.4f}')

Kalibrera sannolikhetsutdata

Klassvikter och tröskelförflyttning bygger båda på modellens sannolikhetsvärden. Om klassificeraren är dåligt kalibrerad (till exempel en SVM med probability=True som använder Platt-skalning) kanske tröskeljustering inte fungerar bra. Använd sklearn.calibration.CalibratedClassifierCV eller jämför kalibreringskurvor med calibration_curve för att säkerställa att sannolikheterna är tillförlitliga innan ni optimerar tröskeln.

Snabbtest

Testa era kunskaper om klassvikter och tröskelförflyttning från den här lektionen.

Lektionssammanfattning

I den här lektionen har ni lärt er att class_weight='balanced' straffar felklassificeringar av minoritetsklassen proportionellt och fungerar utan att träningsdata ändras, att tröskelförflyttning justerar beslutsgränsen efter träningen för att förändra avvägningen mellan recall och precision och att den optimala tröskeln kan hittas genom att maximera F1 över precision-recall-kurvan eller Youdens J över ROC-kurvan. Nästa steg är att spara tränade modeller med joblib och pickle för driftsättning i produktion.

Gratis att börja

Lär dig Python med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
30
Lektioner
120

Vanliga frågor

Är lektionen ”Klassvikter och justering av tröskelvärden” gratis?

Ja – hela texten till ”Klassvikter och justering av tröskelvärden” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Machine Learning Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i Machine Learning Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Klassvikter och justering av tröskelvärden”?

Ni kommer att ange class_weight='balanced' i sklearn-klassificerare och sedan flytta beslutströskeln för sannolikhetsutdata för att ytterligare optimera recall för sällsynta händelser. Ni övar på Machine Learning Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Machine Learning Academy?

Du behöver inga förkunskaper. Utbildningen i Machine Learning Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.

Hur lång tid tar lektionen ”Klassvikter och justering av tröskelvärden”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Machine Learning Academy-lektionen?

Ja. Varje Machine Learning Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Upptäcka obalans: Klassfördelning och fallgropar med baslinjer
  2. Slumpmässig översampling och SMOTE
  3. Slumpmässig undersampling och klustercentroider
  4. Klassvikter och justering av tröskelvärden
← Tillbaka till Machine Learning Academy