Kurs uczenia maszynowego od zera · Lekcja 2.3 · 10 z 33 gotowych lekcji · 12 min
Próg, ROC i PR: wszystkie progi naraz
W skrócie
Krzywe ROC i precyzja–czułość pokazują wszystkie progi naraz. Gdy klasa jest rzadka, ROC AUC prawie stoi, a AP spada, bo fałszywe alarmy zjadają precyzję.
Rozgrzewka Dwa pytania z wcześniejszych lekcji. Minuta, a lekcji nie blokuje.
W lekcji 2.2 przesuwałeś jeden próg i patrzyłeś, jak zmienia się macierz pomyłek. Każdy próg to inna decyzja, choć wyniki modelu są te same. Dziś zobaczysz wszystkie progi naraz, na dwóch krzywych: ROC i precyzja–czułość (PR). Sprawdzisz też, która z nich mówi prawdę, gdy chorych jest mało. Model jest celowo słaby: regresja logistyczna widzi tylko jeden pomiar, średnią teksturę guza, więc krzywe nie przyklejają się do rogów.
Wybierz odpowiedź i pewność.
Odpowiedz, żeby odblokować lab.
Eksperyment
Ten interaktywny eksperyment działa w przeglądarce z włączonym JavaScriptem. Tekst wokół opisuje, co w nim widać.
Pokaż odpowiedź
Poprawna odpowiedź: ROC AUC prawie się nie zmieni (około 0,77), a AP spadnie do około 0,14
ROC AUC wynosi 0,773 przy wszystkich złośliwych i średnio 0,772 przy 19 (z 200 losowań, które złośliwe zostają). AP spada z 0,591 do średnio 0,137. Model niczego nie zapomniał, zmieniły się tylko proporcje klas. ROC tego nie widzi, krzywa PR widzi.
Spróbuj
- Zacznij od 212 złośliwych i progu 0,5. Odczytaj czułość i FPR, a potem przesuń próg na 0,3. W którą stronę idzie punkt na obu krzywych?
- Przełącz na 19 złośliwych przy progu 0,3. Fałszywych alarmów jest tyle samo co przed chwilą. Co stało się z precyzją?
- Porównaj ROC AUC i AP przy 212, 40 i 19 złośliwych.
Gdy już sprawdzisz to w labie, nazwijmy, co widzisz.
Pomysł w prostych słowach
Model nie mówi „złośliwy” albo „łagodny”. Daje wynik od 0 do 1, a decyzję podejmuje próg. Każdy próg daje inną parę liczb: ile złośliwych wykryto (czułość) i ile łagodnych dostało fałszywy alarm. Krzywa ROC rysuje te pary dla wszystkich progów naraz: na osi poziomej odsetek fałszywych alarmów wśród łagodnych (FPR), na pionowej czułość. Wysoki próg to lewy dolny róg (mało alarmów), niski to prawy górny (alarm przy każdej biopsji).
Krzywa precyzja–czułość (PR) pokazuje te same progi inaczej: na osi poziomej czułość, na pionowej precyzja, czyli jaka część alarmów jest trafna. Pole pod krzywą ROC to ROC AUC, pole pod krzywą PR to AP (average precision). Każda z tych liczb streszcza wszystkie progi naraz, więc nie zależy od tego, który próg wybierzesz.
Na wszystkich 569 biopsjach model ma ROC AUC 0,773 i AP 0,591. Losowe zgadywanie dałoby ROC AUC 0,5, a AP równe udziałowi złośliwych, czyli 0,373. Model jest wyraźnie lepszy od zgadywania, ale daleko mu do ideału, czyli 1.
Jak to policzono
- Biopsje: Breast Cancer Wisconsin (Diagnostic), UCI, licencja CC BY 4.0. 569 biopsji (357 łagodnych, 212 złośliwych). Model widzi jeden pomiar: średnią teksturę guza.
- Wyniki z walidacji krzyżowej w 5 częściach (
StratifiedKFold(5)w scikit-learn): każdą biopsję ocenia model, który jej nie widział. - Rzadsza klasa: zostają wszystkie 357 łagodne i 40 albo 19 losowo wybranych złośliwych. Wykres pokazuje jedno losowanie, średnie są z 200 losowań.
- Model: standaryzacja i regresja logistyczna (C = 1).
Dlaczego to działa
ROC liczy fałszywe alarmy względem łagodnych: FPR to fałszywe alarmy podzielone przez 357. Czułość liczy trafienia względem złośliwych. Gdy losowo zabierasz złośliwe biopsje, oba ułamki zostają mniej więcej takie same, więc krzywa ROC i pole pod nią prawie się nie ruszają. Przy progu 0,3 model podnosi alarm u 144 z 357 łagodnych (FPR 40,3%), czy złośliwych jest 212, czy 19.
Precyzja liczy fałszywe alarmy względem wszystkich alarmów. Przy 212 złośliwych i progu 0,3 model wykrywa 177, więc precyzja wynosi 55,1%. Przy 19 złośliwych wykrywa 17, a 144 fałszywe alarmy zostają: precyzja spada do 10,6%, czyli 8,5 fałszywego alarmu na jeden trafny. Krzywa PR spada razem z precyzją, a AP z 0,591 do średnio 0,137. Linia losowego zgadywania na PR też spada, do udziału złośliwych: 0,051.
To nie jest przypadek jednego losowania ani jednego podziału na części. Przy 40 złośliwych (10%) ROC AUC wynosi średnio 0,773, a AP 0,232. W kodzie niżej ten sam model na 20 innych podziałach na części ma przy 19 złośliwych AP poniżej połowy wyniku z pełnymi danymi za każdym razem.
Częsty błąd
Prawda czy fałsz: „wysokie ROC AUC oznacza mało fałszywych alarmów”. Fałsz. ROC AUC mierzy, jak dobrze model sortuje: z jakim prawdopodobieństwem losowa złośliwa biopsja dostaje wyższy wynik niż losowa łagodna. Nie mówi, ile alarmów będzie fałszywych, bo to zależy od tego, jak rzadka jest klasa. W labie przy 19 złośliwych ROC AUC wynosi średnio 0,772, prawie tyle co przy 212, a przy progu 0,3 na jeden trafny alarm przypada 8,5 fałszywego. Gdy klasa jest rzadka, patrz na krzywą PR i na precyzję przy progu, którego naprawdę użyjesz.
Uwaga w drugą stronę: AP też nie jest liczbą „o samym modelu”. Zależy od udziału klasy w danych, więc AP z dwóch zbiorów o różnych proporcjach nie da się porównać wprost. Porównuj ją z linią losowego zgadywania na tych samych danych.
Quiz
Trzy pytania na sprawdzenie się. Wybierz odpowiedź albo rozwiń odpowiedź pod pytaniem. Z klawiatury: strzałki wybierają, Enter albo spacja zatwierdza.
Najważniejsze wnioski
- Krzywa ROC i krzywa PR pokazują wszystkie progi naraz; próg wybierasz potem, pod koszt pomyłek.
- ROC AUC mierzy, jak dobrze model sortuje, i prawie nie zależy od proporcji klas.
- Gdy klasa pozytywna jest rzadka, te same fałszywe alarmy zjadają precyzję: patrz na krzywą PR i AP.
- AP porównuj z linią losowego zgadywania, czyli z udziałem klasy pozytywnej w tych samych danych.
Głębiej: wzór, kod, praktyka
Matematyka
Dla progu t model mówi „złośliwy”, gdy jego wynik s ≥ t. Wtedy czułość = TP / (TP + FN), FPR = FP / (FP + TN), precyzja = TP / (TP + FP).
FPR ma w mianowniku tylko łagodne, czułość tylko złośliwe. Gdy losowo zabierasz złośliwe, żaden z tych ułamków nie zmienia się systematycznie, więc krzywa ROC zostaje. Precyzja ma w mianowniku oba rodzaje alarmów. Przy progu 0,3: 177 / (177 + 144) ≈ 0,551 przy 212 złośliwych i 17 / (17 + 144) ≈ 0,106 przy 19.
ROC AUC to prawdopodobieństwo, że losowa złośliwa biopsja dostanie wyższy wynik niż losowa łagodna: AUC = P(s₊ > s₋), remisy liczą się po połowie. AP to średnia precyzja ważona przyrostami czułości: AP = Σₖ (Rₖ − Rₖ₋₁) · Pₖ. Losowe wyniki dają AUC 0,5 i AP równe udziałowi klasy pozytywnej: 212 / 569 ≈ 0,373 albo 19 / 376 ≈ 0,051.
W kodzie (Python)
Ten sam eksperyment w scikit-learn: wyniki z walidacji krzyżowej, ROC AUC i AP przy 212, 40 i 19 złośliwych (średnie z 1000 losowań), a potem to samo na 20 innych podziałach na części.
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import StratifiedKFold, cross_val_predict
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score, average_precision_score
X, y = load_breast_cancer(return_X_y=True)
X, y = X[:, [1]], 1 - y # tylko średnia tekstura, jak w labie; 1 = złośliwy
model = make_pipeline(StandardScaler(), LogisticRegression())
def scores(cv): # wyniki z walidacji: każdą biopsję ocenia model, który jej nie widział
return cross_val_predict(model, X, y, cv=cv, method="predict_proba")[:, 1]
ben, mal = np.flatnonzero(y == 0), np.flatnonzero(y == 1)
def rare(p, keep, draws, seed): # wszystkie 357 łagodne + `keep` losowych złośliwych, średnia z losowań
rng = np.random.default_rng(seed)
rows = [np.r_[ben, rng.choice(mal, keep, replace=False)] for _ in range(draws)]
return np.mean([(roc_auc_score(y[i], p[i]), average_precision_score(y[i], p[i])) for i in rows], axis=0)
p = scores(StratifiedKFold(5))
print(f"212 malignant ({212 / 569:.0%}): ROC AUC {roc_auc_score(y, p):.3f}, AP {average_precision_score(y, p):.3f}")
for keep in (40, 19):
auc, ap = rare(p, keep, 1000, 0)
print(f"{keep} malignant ({keep / (357 + keep):.0%}), mean of 1000 draws: ROC AUC {auc:.3f}, AP {ap:.3f}")
r = []
for s in range(20): # 20 innych podziałów danych na 5 części
q = scores(StratifiedKFold(5, shuffle=True, random_state=s))
r.append([roc_auc_score(y, q), average_precision_score(y, q), *rare(q, 19, 200, s)])
r = np.array(r)
print(f"20 other fold splits, 212 -> 19 malignant: ROC AUC {r[:, 0].mean():.3f} -> {r[:, 2].mean():.3f}, AP {r[:, 1].mean():.3f} -> {r[:, 3].mean():.3f}; AP below half in {(r[:, 3] < r[:, 1] / 2).sum()} of 20")
Wynik
212 malignant (37%): ROC AUC 0.773, AP 0.591
40 malignant (10%), mean of 1000 draws: ROC AUC 0.773, AP 0.231
19 malignant (5%), mean of 1000 draws: ROC AUC 0.774, AP 0.140
20 other fold splits, 212 -> 19 malignant: ROC AUC 0.774 -> 0.775, AP 0.595 -> 0.140; AP below half in 20 of 20
Przy wszystkich złośliwych liczby są takie same jak w labie. Przy 40 i 19 kod losuje złośliwe biopsje inaczej niż lab i robi 1000 losowań zamiast 200, dlatego średnie różnią się w trzecim miejscu po przecinku: AP 0,140 zamiast 0,137. Na 20 innych podziałach na części obraz jest ten sam: ROC AUC stoi, a AP spada poniżej połowy za każdym razem.
Pobierz notebook (.ipynb). Otwórz go w Jupyterze albo wgraj do Google Colab; brakujące dane pobierze sam.
Dla praktyka
- W scikit-learn liczby dają
roc_auc_scoreiaverage_precision_score, a krzywe rysująRocCurveDisplayiPrecisionRecallDisplay. Licz je na wynikach z walidacji (cross_val_predict(..., method="predict_proba")), nie na danych do nauki. - Przy rzadkiej klasie (oszustwa, rzadkie choroby, awarie) podawaj AP obok ROC AUC, a obok AP linię losowego zgadywania, czyli udział klasy pozytywnej.
- Krzywa to menu progów. Próg wybierasz na walidacji pod koszt pomyłek, na przykład „czułość co najmniej 90% przy najwyższej możliwej precyzji”, i dopiero potem sprawdzasz go na teście.
- ROC AUC z dwóch zbiorów o różnych proporcjach klas da się porównać, ale nie powie, ile alarmów będzie fałszywych; AP mówi o tym więcej, ale porównuj ją tylko przy tych samych proporcjach.
Czy to było pomocne?
Dzięki! To pomaga nam poprawiać Atlas.
Więcej w Atlasie
Hasła encyklopedii o pojęciach z tej lekcji: