Kurs uczenia maszynowego od zera · Lekcja 5.6 · 31 z 33 gotowych lekcji · 12 min
Który model kiedy?
W skrócie
Pięć modeli, pięć zbiorów, 20 podziałów. Żaden model nie jest w czołówce wszędzie, a różnica 0,3 pkt proc. bywa remisem: 10 do 7 podziałów.
Rozgrzewka Dwa pytania z wcześniejszych lekcji. Minuta, a lekcji nie blokuje.
Znasz już pięć rodzin modeli: regresję logistyczną, k najbliższych sąsiadów (kNN), las losowy, gradient boosting i SVM z jądrem RBF. Który wybrać? Uczciwa odpowiedź brzmi: ten, który wypada najlepiej w walidacji na twoich danych. Ta lekcja sprawdza, jak bardzo wynik zależy od danych i jak odróżnić prawdziwą różnicę od szumu. Robimy zoo modeli: każdy z pięciu modeli z ustawieniami domyślnymi, bez strojenia, na pięciu zbiorach, a na każdym zbiorze na tych samych 20 podziałach. Za każdym razem 75% wierszy idzie do nauki, a 25% do walidacji.
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ź: Nie: na cyfrach SVM wygrywa z regresją, a na pingwinach z nią przegrywa
Nie. Na cyfrach SVM ma więcej trafień niż regresja logistyczna w 17 podziałach z 20, a regresja więcej niż SVM w 3. Na pingwinach regresja ma więcej trafień w 9 podziałach, SVM w żadnym, a w 11 jest remis. Najlepszą średnią też mają na różnych zbiorach różne modele, ale na czterech zbiorach lider ma więcej trafień niż drugi model najwyżej w 10 z 20 podziałów. To, kto prowadzi, zależy tam w dużej mierze od podziałów.
Spróbuj
- Na starcie: cyfry, kNN przeciw SVM. Różnica średnich jest mniejsza niż rozrzut kNN między podziałami. W ilu podziałach z 20 SVM ma więcej trafień?
- Przejdź do guzów piersi i porównaj SVM z gradient boostingiem. Jaka jest różnica średnich, a jak rozkładają się podziały?
- Znajdź parę modeli, w której na cyfrach prowadzi jeden, a na pingwinach drugi.
- Na każdym zbiorze sprawdź, jak daleko od siebie leżą najgorszy i najlepszy podział jednego modelu.
Gdy już sprawdzisz to w labie, nazwijmy, co widzisz.
Pomysł w prostych słowach
Ten sam model na tym samym zbiorze dostaje w różnych podziałach różne wyniki, bo do walidacji trafiają raz łatwiejsze, raz trudniejsze przykłady. Na guzach piersi regresja logistyczna ma w pojedynczych podziałach od 89,4% do 96,5%. Różnice między modelami bywają dużo mniejsze. Jeden podział nie wystarczy więc, żeby je uszeregować.
Lepiej porównywać modele parami: na tym samym podziale oba mają tak samo łatwo albo tak samo trudno. Liczymy, w ilu podziałach który ma więcej trafień. Na guzach SVM ma średnio 94,2%, a gradient boosting 93,9%. SVM ma więcej trafień w 10 podziałach, boosting w 7, a w 3 jest remis. Taka różnica 0,3 pkt proc. to remis, nie zwycięstwo. Bywa też odwrotnie. Na cyfrach SVM ma tylko 0,7 pkt proc. więcej niż kNN, a wynik kNN skacze między podziałami od 96,0% do 99,1%. Mimo to SVM ma więcej trafień w 16 podziałach z 20, a kNN w 2. Liczy się, czy przewaga się powtarza, a nie, jak duża jest średnia.
Kiedy przewaga się powtarza? Gdyby oba modele były równie dobre, każdy podział bez remisu byłby jak rzut monetą. Wynik 16 do 2 moneta daje rzadziej niż raz na 100 serii, a 10 do 7 często. Lab nazywa przewagę powtarzalną, gdy taki wynik wypadałby rzadziej niż raz na 100 serii. To test znaków. Remisy go nie obciążają, bo po prostu się ich nie liczy.
Najważniejsze: kolejność zależy od danych. Na cyfrach SVM ma więcej trafień niż regresja logistyczna w 17 podziałach z 20, a regresja więcej niż SVM w 3. Na pingwinach jest odwrotnie: regresja ma więcej trafień w 9 podziałach, SVM w żadnym, a w 11 jest remis. Obie przewagi przechodzą test znaków. Na cyfrach SVM ma więcej trafień niż każdy z pozostałych modeli w co najmniej 16 podziałach.
To wszystko przy ustawieniach domyślnych, bez strojenia, na małych zbiorach (od 150 do 1797 wierszy) z samymi cechami liczbowymi. To nie wyrok o modelach w ogóle. Na większych tabelach, z cechami różnego typu i po strojeniu, kolejność może wyglądać inaczej (o boostingu w lekcji 5.3). Wniosek jest inny: rankingu nie bierze się z cudzej tabeli, tylko sprawdza na swoich danych.
Lidera też łatwo przecenić. Weźmy model z najlepszą średnią na podziałach 1–10 i sprawdźmy, kto prowadzi na podziałach 11–20. Na irysach, winach i pingwinach prowadzi tam już inny model. Gdy kilka modeli leży blisko siebie, o tym, który wyjdzie na prowadzenie, w dużej części decyduje szum, jak przy wyborze ustawień w lekcji 3.5.
Jak to policzono
- Dane: Iris (Fisher, 1936), Wine (Aeberhard i Forina, 1992), Breast Cancer Wisconsin (Wolberg i in., 1993; 10 pomiarów „mean”) i Digits (Alpaydin i Kaynak, 1998), wszystkie z UCI na licencji CC BY 4.0; Palmer Penguins (Gorman i in., 2014; Horst i in., 2020; licencja CC0 1.0), 342 pingwiny z 4 pomiarami ciała.
- Modele: ustawienia domyślne scikit-learn bez strojenia. Regresja logistyczna (C = 1), kNN (k = 5), las losowy (100 drzew), gradient boosting (100 drzew, krok 0,1, głębokość 3), SVM z jądrem RBF (C = 1). Regresja, kNN i SVM dostają cechy skalowane w każdym podziale tylko na części do nauki.
- Zgodność ze scikit-learn na tych samych 20 podziałach: SVM trafia te same wiersze w każdym podziale, regresja i kNN różnią się najwyżej o 3 wiersze w podziale. Drzewa w labie dzielą każdą cechę na najwyżej 32 przedziały, a scikit-learn liczy drzewa dokładnie, więc tu różnice są większe: na guzach las ma ten sam wynik w 3 podziałach z 20, a boosting w 4. Z dokładnymi drzewami najlepszą średnią na guzach ma boosting, nie SVM. Wnioski o parach z tej lekcji wychodzą w obu wersjach tak samo.
- Podziały: 20 podziałów ze stratyfikacją, 75% do nauki i 25% do walidacji, te same dla każdego modelu. Testu tu nie ma. Wszystkie 500 wyników policzył kod labu, a test przelicza je od nowa. Test znaków jest dwustronny, liczony na podziałach bez remisu, z progiem 1%.
Dlaczego to działa
Twierdzenie „nie ma darmowego obiadu” (Wolpert, 1996) mówi, że uśrednione po wszystkich możliwych zadaniach żaden algorytm uczący nie jest lepszy od innego. Model wygrywa tylko wtedy, gdy jego założenia pasują do konkretnych danych: proste granice do regresji logistycznej, podobieństwo sąsiadów do kNN, progi na pojedynczych cechach do drzew. Dlatego wybór modelu to pomiar, nie wiedza z tabeli.
Porównanie parami działa, bo trudność podziału dotyka obu modeli naraz i w różnicy się znosi. Uwaga: 20 podziałów tego samego zbioru dzieli wiersze, więc to nie 20 niezależnych rzutów monetą. Zwykłe testy statystyczne na takich wynikach bywają zbyt pewne siebie (Dietterich, 1998). Dlatego próg jest surowy, a powtarzalna przewaga to dobra wskazówka, nie dowód.
Model może mieć też własną losowość. Las losuje wiersze i cechy dla każdego drzewa. Na cyfrach, na tych samych 20 podziałach, las z ziarnem losowania 1 ma więcej trafień niż regresja w 15 podziałach i mniej w 5, a z ziarnem 4 więcej w 11 i mniej w 9. Przy żadnym ziarnie od 1 do 5 ta przewaga nie przechodzi testu znaków. Dlatego przykłady w tej lekcji opierają się na modelach bez losowości: regresji, kNN i SVM.
Kod niżej robi to samo w scikit-learn, na innych podziałach i z dokładnymi drzewami. Czołówka się zmienia: SVM ma tam najlepszą średnią na czterech zbiorach, a regresja logistyczna na pingwinach. Kierunek par z tej lekcji zostaje, ale nie każda przewaga przechodzi tam test znaków. To kolejny powód, żeby różnic w granicach szumu nie nazywać zwycięstwem.
Częsty błąd
Prawda czy fałsz: „model ze średnią wyższą o 0,3 pkt proc. jest lepszy”. Fałsz. Na guzach SVM ma 94,2%, a gradient boosting 93,9%, ale SVM ma więcej trafień tylko w 10 podziałach z 20, boosting w 7. W kodzie niżej ta sama para dzieli się podziałami 10 do 7, a różnica średnich to 0,002.
Drugi częsty błąd: „zawsze bierz boosting, na przykład XGBoost”. W tym zoo, z ustawieniami domyślnymi, na winach regresja logistyczna ma więcej trafień niż gradient boosting w 15 podziałach z 20, a boosting więcej niż ona w 2. To nie wyrok przeciw boostingowi, tylko przeciw wybieraniu bez walidacji.
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
- Żaden model nie jest w czołówce wszędzie: SVM wygrywa z regresją logistyczną na cyfrach (17 do 3 podziałów), a przegrywa na pingwinach (9 do 0).
- Sam podział przesuwa wynik o kilka punktów, więc różnica średnich 0,3 pkt proc. bywa remisem (10 do 7 podziałów).
- Porównuj modele na tych samych podziałach i licz, w ilu który wygrywa; mała, ale powtarzalna przewaga też się liczy (SVM nad kNN na cyfrach: 0,7 pkt proc., 16 do 2).
- Wybieraj walidacją na swoich danych, przy remisie bierz prostszy model, a test oglądaj raz, na końcu.
Głębiej: wzór, kod, praktyka
Matematyka
Dla podziałów k = 1, …, K różnica w parze to d_k = acc_A(k) − acc_B(k); patrzymy na średnią d̄ = (1/K) · Σ_k d_k i na liczbę podziałów z d_k > 0.
Wariancja różnicy: Var(A − B) = Var(A) + Var(B) − 2 · Cov(A, B). Gdy podział jest trudny dla obu modeli naraz, kowariancja jest duża, więc różnica waha się dużo mniej niż każdy wynik osobno.
Test znaków: model A wygrywa w w podziałach, B w l, remisy pomijamy. Przy równych modelach liczba wygranych A ma rozkład Bin(w + l, ½), a p = min(1, 2 · P(X ≤ min(w, l))). Dla 16 do 2 wychodzi p ≈ 0,001.
W kodzie (Python)
Ten sam eksperyment w scikit-learn: pięć modeli z ustawieniami domyślnymi na pięciu zbiorach i 20 podziałach, potem cztery pary podział po podziale, z testem znaków.
import pandas as pd
from scipy.stats import binomtest
from sklearn.datasets import load_iris, load_wine, load_breast_cancer, load_digits
from sklearn.model_selection import StratifiedShuffleSplit, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm import SVC
body = ["bill_length_mm", "bill_depth_mm", "flipper_length_mm", "body_mass_g"]
peng = pd.read_csv("data/raw/penguins.csv").dropna(subset=body)
cancer = load_breast_cancer()
data = {"iris": load_iris(return_X_y=True), "wine": load_wine(return_X_y=True), "cancer": (cancer.data[:, :10], cancer.target),
"penguins": (peng[body].values, peng["species"].values), "digits": load_digits(return_X_y=True)}
# skaler siedzi w każdym pipeline'ie, więc średnie liczy tylko z części do nauki każdego podziału
models = {"logistic": make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000)),
"kNN": make_pipeline(StandardScaler(), KNeighborsClassifier()),
"forest": RandomForestClassifier(random_state=0),
"boosting": GradientBoostingClassifier(random_state=0),
"SVM": make_pipeline(StandardScaler(), SVC())}
splits = StratifiedShuffleSplit(n_splits=20, test_size=0.25, random_state=0)
acc = {}
for name, (X, y) in data.items():
acc[name] = {m: cross_val_score(model, X, y, cv=splits) for m, model in models.items()}
row = " | ".join(f"{m} {a.mean():.3f}" for m, a in acc[name].items())
best = max(acc[name], key=lambda m: acc[name][m].mean())
print(f"{name:8s} {row} best: {best}")
# test znaków: tylko podziały bez remisu; przy równych modelach każdy z nich to rzut monetą
for name, a, b in [("digits", "SVM", "kNN"), ("digits", "SVM", "logistic"), ("penguins", "logistic", "SVM"), ("cancer", "SVM", "boosting")]:
d = acc[name][a] - acc[name][b]
win, lose = (d > 0).sum(), (d < 0).sum()
print(f"{name}: {a} beats {b} in {win} of 20 splits, loses in {lose}; sign test p = {binomtest(win, win + lose).pvalue:.3f}; mean difference {d.mean():.3f}")
Wynik
iris logistic 0.955 | kNN 0.945 | forest 0.949 | boosting 0.947 | SVM 0.959 best: SVM
wine logistic 0.982 | kNN 0.964 | forest 0.981 | boosting 0.946 | SVM 0.986 best: SVM
cancer logistic 0.931 | kNN 0.938 | forest 0.930 | boosting 0.939 | SVM 0.942 best: SVM
penguins logistic 0.988 | kNN 0.985 | forest 0.978 | boosting 0.973 | SVM 0.983 best: logistic
digits logistic 0.969 | kNN 0.974 | forest 0.975 | boosting 0.964 | SVM 0.979 best: SVM
digits: SVM beats kNN in 14 of 20 splits, loses in 4; sign test p = 0.031; mean difference 0.006
digits: SVM beats logistic in 17 of 20 splits, loses in 3; sign test p = 0.003; mean difference 0.010
penguins: logistic beats SVM in 7 of 20 splits, loses in 1; sign test p = 0.070; mean difference 0.005
cancer: SVM beats boosting in 10 of 20 splits, loses in 7; sign test p = 0.629; mean difference 0.002
Podziały i implementacja drzew są inne niż w labie, więc czołówka też: SVM ma najlepszą średnią na czterech zbiorach, regresja logistyczna na pingwinach. Też żaden model nie wygrywa wszędzie. Kierunek par z lekcji zostaje: na cyfrach SVM wygrywa z regresją w 17 podziałach z 20, a z kNN w 14, na pingwinach regresja wygrywa z SVM w 7, a przegrywa w 1. Test znaków przechodzi tu tylko pierwsza para (p = 0,003). Dwie pozostałe mają ten sam kierunek, ale na tych podziałach za mało wygranych, więc wniosek z jednego zestawu podziałów warto sprawdzić na drugim. SVM i boosting na guzach znów dzielą się podziałami 10 do 7.
Pobierz notebook (.ipynb). Otwórz go w Jupyterze albo wgraj do Google Colab; brakujące dane pobierze sam.
Dla praktyka
- Zacznij od punktu odniesienia (lekcja 1.3), potem weź kilka modeli różnego rodzaju: liniowy, sąsiedzi, drzewa, jądro. Wszystkie oceniaj na tych samych podziałach (
cross_validatez jednymcv) i z przetwarzaniem w pipeline'ie (lekcja 4.6). - Patrz na rozrzut i na pary, nie tylko na średnie. Przewagę sprawdzaj testem znaków (
binomtestz SciPy) na podziałach bez remisu. Przy remisie wybierz model tańszy w utrzymaniu albo łatwiejszy do wyjaśnienia. - Strój dopiero finalistów (lekcja 3.5). Domyślne ustawienia bywają dalekie od najlepszych, zwłaszcza w boostingu i SVM.
- Rankingi „który model kiedy” z internetu traktuj jak listę kandydatów do walidacji, nie jak wyrok. Test zostaw na sam koniec i oglądaj go raz.
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: