Luokkapainot ja päätöskynnyksen siirtäminen
Oppijat asettavat sklearn-luokittelijoissa arvon class_weight='balanced' ja siirtävät sitten todennäköisyystulosten päätöskynnystä harvinaisten tapahtumien recall-arvon optimoimiseksi.
Luokkapainot ja päätöskynnyksen siirtäminen on ilmainen Machine Learning Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Machine Learning Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Machine Learning Academy-kurssilla on yhteensä 4 oppituntia.
Kaksi vaihtoehtoa uudelleennäytteistykselle
Uudelleennäytteistäminen (SMOTE, alinäytteistäminen) muuttaa harjoitusdataa fyysisesti. Kaksi vaihtoehtoista menetelmää toimii suoraan alkuperäisellä datalla: luokkien painotus rankaisee vähemmistönäytteiden virheellisestä luokittelusta voimakkaammin harjoittelun aikana, ja kynnysarvon siirtäminen säätää päätösrajaa harjoittelun jälkeen. Molemmat ovat yksinkertaisempia ja nopeampia ja välttävät uudelleennäytteistämiseen liittyvän tiedon menetyksen tai synteettisen kohinan riskit.
Luokkien painot: vähemmistöluokan virheiden voimakkaampi rankaiseminen
Useimmat sklearn-luokittelijat hyväksyvät class_weight-parametrin. Kun asetatte arvoksi class_weight='balanced', painot lasketaan automaattisesti kääntäen verrannollisina luokkien esiintymistiheyksiin: weight[c] = n_samples / (n_classes * count[c]). Harvinaisen positiivisen näytteen virheellisestä luokittelusta rangaistaan tällöin paljon voimakkaammin kuin yleisen negatiivisen näytteen virheellisestä luokittelusta, mikä ohjaa mallia oppimaan vähemmistöluokan paremmin.
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)))Tasapainotettujen painojen laskeminen manuaalisesti
Voitte myös antaa mukautettujen painojen sanakirjan, jos tarvitsette tarkempaa hallintaa kuin 'balanced' tarjoaa. Jos esimerkiksi liiketoimintanne mukaan havaitsematta jäänyt petos on 20 kertaa kalliimpi kuin väärä hälytys, asettakaa class_weight={0: 1, 1: 20}. Näin väärän luokittelun kustannussuhde koodataan suoraan harjoitteluun.
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)Mitkä algoritmit tukevat class_weight-parametria?
scikit-learnissa seuraavat estimaattorit hyväksyvät class_weight-parametrin: LogisticRegression, LinearSVC, SVC, SGDClassifier, DecisionTreeClassifier, RandomForestClassifier. Gradient boosting -mallit (XGBoost, LightGBM) käyttävät scale_pos_weight-parametria, jolla on sama tehtävä. Neuroverkot käsittelevät tämän häviöfunktiossa sample_weight-parametrin avulla.
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__)Kynnysarvon siirtäminen: päätösrajan säätäminen
Luokittelijat tuottavat todennäköisyyspistemäärän; oletusarvoinen päätöskynnys on 0.5 — positiivinen ennustetaan, jos proba >= 0.5. Epätasapainoisissa ongelmissa kynnyksen pienentäminen (esimerkiksi arvoon 0.3) kasvattaa recall-arvoa (löytää enemmän positiivisia tapauksia), mutta lisää vääriä positiivisia. Kynnyksen suurentaminen kasvattaa precision-arvoa, mutta samalla useampia positiivisia tapauksia jää havaitsematta. Kynnysarvon siirtäminen tehdään harjoittelun jälkeen, eikä mallia tarvitse kouluttaa uudelleen.
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}')Optimaalisen kynnysarvon löytäminen
Käyttäkää funktioita precision_recall_curve ja roc_curve kaikkien mahdollisten kynnysarvojen läpikäymiseen ja niitä vastaavien precision- ja recall-arvojen laskemiseen. Valitkaa sen jälkeen kynnysarvo, joka maksimoi F1-arvon (tai jonkin muun liiketoimintamittarin). Tämä on järjestelmällisempi tapa kuin kynnysarvon arvaaminen.
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 vs. uudelleennäytteistäminen: kompromissit
class_weight-menetelmän etuja ovat datan muuttumattomuus, synteettisiin näytteisiin ylisovittamisen riskin puuttuminen, toimivuus tavallisessa sklearn-Pipeline-putkessa ja nopeus. Uudelleennäytteistämisen etuja ovat toimivuus algoritmeilla, jotka eivät tue class_weight-parametria (esimerkiksi joillakin boosting-muunnelmilla), sekä mahdollinen hyöty erittäin voimakkaassa epätasapainossa (>100:1), jossa pelkkä luokkien painotus ei riitä. Käytännössä kokeilkaa ensin luokkien painotusta — se on yksinkertaisempaa ja usein riittävää.
Kynnysarvon siirtäminen ROC-käyrän avulla
ROC-käyrä näyttää tosi positiivisen osuuden (recall) ja väärän positiivisen osuuden välisen kompromissin jokaisella kynnysarvolla. Youdenin J-tunnusluku (TPR - FPR) on suurimmillaan optimaalisessa kynnysarvossa, kun herkkyys ja spesifisyys halutaan tasapainottaa. Käyttäkää tätä, kun molempia virhetyyppejä halutaan kohdella yhtä tärkeinä.
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))Luokkien painot XGBoostissa ja LightGBM:ssä
XGBoost käyttää scale_pos_weight-parametria — harjoitusjoukon negatiivisten ja positiivisten näytteiden suhdetta — positiivisten esimerkkien painottamiseen. Kun epätasapaino on 95:5, asettakaa scale_pos_weight=19 (95/5). LightGBM käyttää vastaavasti asetusta is_unbalance=True tai parametria scale_pos_weight. Molemmat vastaavat sklearnin asetusta class_weight='balanced', mutta käyttävät eri parametrinimeämiskäytäntöä.
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))Kaikkien epätasapainon käsittelystrategioiden vertailu
Systemaattinen strategioiden vertailu samalla aineistolla ja testijoukolla paljastaa, mikä menetelmä toimii parhaiten juuri teidän ongelmassanne ja algoritmillanne. Suorittakaa tämä vertailu ja esittäkää tulokset taulukossa perustellaksenne lopullisen strategiavalintanne.
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}')Todennäköisyystulosten kalibrointi
Luokkien painotus ja kynnysarvon siirtäminen perustuvat molemmat mallin todennäköisyyspisteisiin. Jos luokittelija on huonosti kalibroitu (esimerkiksi SVM, jossa probability=True käyttää Plattin skaalausta), kynnysarvon säätäminen ei välttämättä toimi hyvin. Käyttäkää sklearn.calibration.CalibratedClassifierCV-luokkaa tai verratkaa kalibrointikäyriä calibration_curve-funktion avulla varmistaaksenne todennäköisyyksien luotettavuuden ennen kynnysarvon optimointia.
Pikatesti
Testatkaa tämän oppitunnin ymmärtämistänne luokkien painotuksesta ja kynnysarvon siirtämisestä.
Oppitunnin yhteenveto
Tässä oppitunnissa opitte, että class_weight='balanced'` rankaisee vähemmistöluokan virheellisistä luokitteluista suhteellisesti ja toimii muuttamatta harjoitusdataa, kynnysarvon siirtäminen säätää päätösrajaa harjoittelun jälkeen ja muuttaa recall- ja precision-arvojen välistä kompromissia, ja optimaalinen kynnysarvo voidaan löytää maksimoimalla F1 precision-recall-käyrällä tai Youdenin J-tunnusluku ROC-käyrällä. Seuraavaksi tallennamme koulutetut mallit joblibilla ja picklellä tuotantokäyttöönottoa varten.
Opi Python tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 30
- Oppitunnit
- 120
Usein kysytyt kysymykset
Onko oppitunti ”Luokkapainot ja päätöskynnyksen siirtäminen” ilmainen?
Kyllä – oppitunnin ”Luokkapainot ja päätöskynnyksen siirtäminen” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Machine Learning Academy-kurssin, päivitä CoddyKit PROhon. Machine Learning Academy-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Luokkapainot ja päätöskynnyksen siirtäminen”?
Oppijat asettavat sklearn-luokittelijoissa arvon class_weight='balanced' ja siirtävät sitten todennäköisyystulosten päätöskynnystä harvinaisten tapahtumien recall-arvon optimoimiseksi. Harjoittelet Machine Learning Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni Machine Learning Academy-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin Machine Learning Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.
Kuinka kauan ”Luokkapainot ja päätöskynnyksen siirtäminen”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä Machine Learning Academy-oppitunnilla?
Kyllä. Jokainen Machine Learning Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Epätasapainon tunnistaminen: luokkajakauma ja perustason sudenkuopat
- Satunnainen ylinäytteistys ja SMOTE
- Satunnainen alinäytteistys ja klusterikeskipisteet
- Luokkapainot ja päätöskynnyksen siirtäminen