ML Atlas

Kurs uczenia maszynowego od zera · Lekcja 2.5 · 12 z 33 gotowych lekcji · 12 min

Niezrównoważone klasy

W skrócie

Gdy jednej klasy jest mało, trafność ukrywa straty, a model uczy się mówić „częsta klasa”. Czułość, zbalansowana trafność i wagi klas pokazują i łatają problem.

Rozgrzewka Dwa pytania z wcześniejszych lekcji. Minuta, a lekcji nie blokuje.
Z lekcji 2.4: Kalibracja: czy 0,8 znaczy 80%? Naiwny Bayes ma ROC AUC 0,966, a deklaruje 98,3% przy trafności 88,3%. Co z tego wynika?

Pokaż odpowiedź

Poprawna odpowiedź: Dobrze sortuje biopsje, ale jego procenty są przesadzone

ROC AUC mierzy kolejność, a kalibracja procenty. To dwie różne rzeczy: temperatura poprawia procenty naiwnego Bayesa, a ROC AUC zostaje 0,966.

Z lekcji 2.3: Próg, ROC i PR: wszystkie progi naraz Kiedy krzywa PR mówi więcej niż krzywa ROC?

Pokaż odpowiedź

Poprawna odpowiedź: Gdy klasa pozytywna jest rzadka, a liczy się, ile alarmów jest trafnych

Przy rzadkiej klasie krzywa ROC wygląda tak samo, a precyzja spada. W labie przy 19 złośliwych AP spada do średnio 0,137, choć ROC AUC prawie stoi.

W lekcji 1.3 „zawsze łagodny” miał wysoką trafność i nie wykrywał nikogo. Tam rzadka klasa była tylko w teście. Dziś jest rzadka w danych do nauki, tak jak w prawdziwych badaniach przesiewowych, oszustwach czy awariach. Zobaczysz, co to robi z modelem, jak to zmierzyć i co da się naprawić bez nowych danych.

Przewidź, potem sprawdź Regresja logistyczna uczy się na biopsjach. W danych do nauki zostają wszystkie łagodne, ale tylko 8 zamiast 159 złośliwych. Co spadnie bardziej na tej samej części walidacyjnej (odłożonej do sprawdzania modelu, nie do nauki): trafność czy czułość?
Jak bardzo jesteś pewny(-a)?

Eksperyment

Ten interaktywny eksperyment działa w przeglądarce z włączonym JavaScriptem. Tekst wokół opisuje, co w nim widać.

Pokaż odpowiedź

Poprawna odpowiedź: Czułość, o ponad 20 pkt proc., a trafność tylko o około 6 pkt proc.

Czułość spada z 83,0% do 61,5% (średnia z 5 losowań, które złośliwe zostają), a trafność tylko z 91,5% do 85,6%. Na walidacji łagodnych jest 89 ze 142, a te model rozpoznaje coraz pewniej, więc trafność prawie nie widzi straty.

Spróbuj

  • Bez wag przesuwaj suwak od 159 do 8 złośliwych. Która linia spada szybciej: czułość czy trafność?
  • Przy 8 złośliwych włącz wagi klas. Ile czułości wraca, a ile wciąż brakuje do pełnych danych?
  • Spójrz na średnie przewidywane P(złośliwy). Jak zmienia się, gdy złośliwych ubywa?

Gdy już sprawdzisz to w labie, nazwijmy, co widzisz.

Pomysł w prostych słowach

Klasy są niezrównoważone, gdy jednej jest dużo mniej niż drugiej. Model uczy się wtedy dwóch rzeczy naraz: jak wyglądają złośliwe biopsje i jak rzadko się zdarzają. Ta druga lekcja działa jak uprzedzenie: przy niejasnych pomiarach model coraz częściej wybiera „łagodny”. Średnie przewidywane P(złośliwy) spada z 0,36 do 0,24, gdy w nauce zostaje 8 złośliwych zamiast 159.

Trafność tego prawie nie widzi. Czułość spada z 83,0% do 61,5%, a trafność tylko z 91,5% do 85,6%. Dlatego przy rzadkiej klasie podaje się czułość klasy rzadkiej albo zbalansowaną trafność, czyli średnią czułości obu klas. „Zawsze łagodny” ma zbalansowaną trafność 0,5, bez względu na proporcje.

Pierwsza korekta jest prosta: wagi klas. Pomyłka na rzadkiej klasie kosztuje w nauce tyle razy więcej, ile razy ta klasa jest rzadsza (class_weight='balanced'). Przy 8 złośliwych wagi podnoszą czułość z 61,5% do 80,0%, a trafność nawet rośnie, do 91,4%. Do pełnych danych z wagami (86,8%) wciąż brakuje: wagi zmieniają proporcje, ale nie dodają przykładów.

Jak to policzono
  • Biopsje: Breast Cancer Wisconsin (Diagnostic), UCI, licencja CC BY 4.0. 569 biopsji, 10 pomiarów „mean”.
  • Część walidacyjna: 25% biopsji, stratyfikowana (142: 53 złośliwe, 89 łagodnych), taka sama we wszystkich ustawieniach. W nauce 268 łagodnych i od 159 do 8 złośliwych; przy mniej niż 159 to średnia z 5 losowań, które złośliwe zostają.
  • Model: standaryzacja na danych do nauki i regresja logistyczna (C = 1), z wagami klas albo bez; próg 0,5.

Dlaczego to działa

Regresja logistyczna szuka modelu, który średnio najmniej się myli na danych do nauki. Gdy łagodnych jest 268, a złośliwych 8, złośliwe biopsje to tylko 2,9% całej kary. Model przesuwa granicę w stronę złośliwych: oddaje kilka złośliwych, żeby rzadziej mylić łagodne. Wagi klas wyrównują karę: 8 złośliwych waży razem tyle samo co 268 łagodnych.

Jeden podział mógłby wypaść tak przypadkiem. W kodzie niżej ten sam eksperyment idzie na 50 różnych podziałach: czułość spada średnio z 89,3% do 67,3%, trafność z 93,5% do 87,7%, a wagi przy 8 złośliwych dają 84,0%. Czułość spada bardziej niż trafność w 50 podziałach na 50, a wagi podnoszą ją w każdym z nich.

Trafność potrafi nawet rosnąć, gdy model w ogóle się nie zmienia. W widżecie Atlasu o zbalansowanej trafności słaby model na dwóch pomiarach ocenia te same biopsje, a złośliwych ubywa z 212 do 20. Jego trafność rośnie z 75,9% do 83,8%, a zbalansowana trafność stoi: 0,728 i 0,726. „Zawsze łagodny” przy 20 złośliwych ma aż 94,7% trafności.

Częsty błąd

Prawda czy fałsz: „SMOTE zawsze pomaga”. Fałsz. SMOTE dorabia syntetyczne przykłady rzadkiej klasy na odcinkach między prawdziwymi przykładami a ich najbliższymi sąsiadami, aż obu klas jest tyle samo. To przesuwa granicę decyzji tak jak wagi klas: model częściej mówi „złośliwy”. W widżecie Atlasu o SMOTE (357 łagodnych, 30 złośliwych, dwa pomiary, walidacja krzyżowa 5 × 5) bez korekty model wykrywa 60,0% złośliwych przy precyzji 90,0%. SMOTE podnosi wykrywalność do 86,7%, ale precyzja spada do 42,9%. Wagi klas dają tę samą czułość przy precyzji 40,2%, a zwykłe powielanie przy 39,6%. To jedno losowanie 30 złośliwych, akurat z największym spadkiem: średnio z 20 losowań wykrywalność rośnie z 68,7% do 91,9%, a precyzja spada z 91,0% do 54,7%. Spada w każdym z nich. SMOTE nie dodaje wiedzy, tylko zmienia proporcje. Czy to pomaga, zależy od kosztu pomyłek: więcej wykrytych za cenę wielu fałszywych alarmów.

Uwaga: korektę stosuje się tylko do danych do nauki. Syntetyczne albo powielone przykłady w walidacji czy teście dałyby wynik, który nic nie znaczy.

Quiz

Trzy pytania na sprawdzenie się. Wybierz odpowiedź albo rozwiń odpowiedź pod pytaniem. Z klawiatury: strzałki wybierają, Enter albo spacja zatwierdza.

1. W nauce zostaje 8 zamiast 159 złośliwych biopsji. Dlaczego trafność prawie tego nie pokazuje?
Pokaż odpowiedź

Poprawna odpowiedź: Bo łagodnych jest na walidacji więcej, a model rozpoznaje je coraz pewniej

Na walidacji łagodnych jest 89 ze 142. Czułość spada z 83,0% do 61,5%, a trafność tylko z 91,5% do 85,6%.

2. Co mierzy zbalansowana trafność?
Pokaż odpowiedź

Poprawna odpowiedź: Średnią czułości obu klas, więc „zawsze częstsza klasa” dostaje dokładnie 0,5

Każda klasa liczy się po równo, bez względu na to, jak jest liczna. Słaby model z widżetu ma zbalansowaną trafność 0,728 przy 212 złośliwych i 0,726 przy 20, choć jego trafność rośnie z 75,9% do 83,8%.

3. Wagi klas przy 8 złośliwych w nauce podnoszą czułość z 61,5% do 80,0%. Czego nie robią?
Pokaż odpowiedź

Poprawna odpowiedź: Nie dodają nowych przykładów, więc do pełnych danych wciąż czegoś brakuje

Wagi zmieniają tylko to, ile kosztuje pomyłka na każdej klasie. Pełne dane z wagami dają czułość 86,8%, więc przy 8 złośliwych brakuje 6,8 pkt proc.

Najważniejsze wnioski

  • Gdy klasa jest rzadka, trafność ukrywa straty: podawaj czułość klasy rzadkiej albo zbalansowaną trafność.
  • Model uczy się też proporcji klas; przy rzadkiej klasie w nauce coraz częściej wybiera klasę częstą.
  • Wagi klas i SMOTE przesuwają granicę: więcej wykrytych za cenę fałszywych alarmów, bez nowej wiedzy.
  • Korekty stosuj tylko do danych do nauki, a wynik sprawdzaj na nietkniętej walidacji, najlepiej na wielu podziałach.

Głębiej: wzór, kod, praktyka

Matematyka

Trafność = (TP + TN) / n, czułość = TP / (TP + FN), swoistość = TN / (TN + FP), zbalansowana trafność = (czułość + swoistość) / 2. „Zawsze łagodny” ma czułość 0 i swoistość 1, więc zbalansowaną trafność (0 + 1) / 2 = 0,5.

Regresja logistyczna minimalizuje średnią stratę logistyczną L = Σᵢ wᵢ · ℓ(yᵢ, pᵢ) / Σᵢ wᵢ. Bez wag wᵢ = 1, więc 8 złośliwych to 8 / 276 ≈ 2,9% całej straty. Wagi „balanced” to w = n / (2 · n_klasy): 276 / 16 = 17,25 dla złośliwej i 276 / 536 ≈ 0,51 dla łagodnej, więc każda klasa waży razem n / 2 = 138.

W kodzie (Python)

Ten sam eksperyment w scikit-learn na 50 różnych podziałach: wszystkie łagodne i 159 albo 8 złośliwych w nauce, z wagami klas i bez, a wynik na nietkniętej części walidacyjnej.

import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import recall_score, accuracy_score

X, y = load_breast_cancer(return_X_y=True)
X, y = X[:, :10], 1 - y  # 10 pomiarów „mean”, jak w labie; 1 = złośliwy
def run(seed, m, weights):
    tr, va = train_test_split(np.arange(len(y)), test_size=0.25, stratify=y, random_state=seed)
    mal = np.random.default_rng(seed).choice(tr[y[tr] == 1], m, replace=False)
    keep = np.r_[tr[y[tr] == 0], mal]  # wszystkie łagodne + m złośliwych do nauki
    clf = make_pipeline(StandardScaler(), LogisticRegression(class_weight="balanced" if weights else None))
    pred = clf.fit(X[keep], y[keep]).predict(X[va])  # część walidacyjna się nie zmienia
    return recall_score(y[va], pred), accuracy_score(y[va], pred)
for m, w in ((159, False), (8, False), (8, True)):
    rec, acc = np.mean([run(s, m, w) for s in range(50)], axis=0)
    print(f"{m} malignant{' + class weights' if w else ''}: recall {rec:.1%}, accuracy {acc:.1%} (mean of 50 splits)")
drop = np.array([np.subtract(run(s, 159, False), run(s, 8, False)) for s in range(50)])
gain = np.array([run(s, 8, True)[0] - run(s, 8, False)[0] for s in range(50)])
print(f"recall fell more than accuracy in {(drop[:, 0] > drop[:, 1]).sum()} of 50 splits; weights raised recall in {(gain > 0).sum()}")

Wynik

159 malignant: recall 89.3%, accuracy 93.5% (mean of 50 splits)
8 malignant: recall 67.3%, accuracy 87.7% (mean of 50 splits)
8 malignant + class weights: recall 84.0%, accuracy 91.0% (mean of 50 splits)
recall fell more than accuracy in 50 of 50 splits; weights raised recall in 50

Lab pokazuje jeden podział i 5 losowań, kod 50 podziałów, więc liczby są inne: na podziale z labu czułość przy pełnych danych wynosi 83,0%, a średnio 89,3%. Wniosek jest ten sam w każdym podziale.

Dla praktyka
  • W scikit-learn wagi daje class_weight="balanced" (regresja logistyczna, SVM, drzewa, lasy), a zbalansowaną trafność balanced_accuracy_score. Podawaj też czułość i precyzję klasy rzadkiej.
  • Dziel dane ze stratyfikacją (stratify=y, StratifiedKFold), żeby rzadka klasa trafiła do każdej części.
  • SMOTE i powielanie są w bibliotece imbalanced-learn. Używaj ich w jej Pipeline, żeby działały tylko na częściach do nauki w walidacji krzyżowej.
  • Wagi klas i niższy próg robią podobną rzecz: przesuwają decyzję w stronę klasy rzadkiej. Próg wybierzesz na walidacji z krzywej PR (lekcja 2.3), bez uczenia modelu od nowa.

Więcej w Atlasie

Hasła encyklopedii o pojęciach z tej lekcji: