ML Atlas

Kurs uczenia maszynowego od zera · Lekcja 3.2 · 15 z 33 gotowych lekcji · 10 min

Walidacja i walidacja krzyżowa

W skrócie

Wybieranie ustawień na zbiorze testowym go psuje. Walidacja krzyżowa sprawdza model po kolei na każdej części danych i pokazuje, jak stabilny jest wynik.

Rozgrzewka Dwa pytania z wcześniejszych lekcji. Minuta, a lekcji nie blokuje.
Z lekcji 3.1: Przeuczenie: gdy model uczy się na pamięć Dlaczego więcej danych treningowych zwykle czyni elastyczny model bezpieczniejszym?

Pokaż odpowiedź

Poprawna odpowiedź: Bo przy większej liczbie punktów szum się uśrednia, więc krzywa nie goni szumu

Szum w pojedynczych punktach zostaje, ale przy wielu punktach się znosi, więc dopasowanie idzie za prawdziwym trendem.

Z lekcji 2.6: Metryka i próg pod koszt pomyłek Przeoczenie kosztuje 10, fałszywy alarm 1, a model jest skalibrowany. Jaki próg wynika z teorii decyzji?

Pokaż odpowiedź

Poprawna odpowiedź: 1 / (1 + 10) ≈ 0,09, bo alarm opłaca się już przy małym ryzyku

Alarm opłaca się, gdy p · 10 > (1 − p) · 1, czyli p > 1/11. W labie próg 0,09 kosztuje 125. Najtańszy próg 0,06 kosztuje 111 tylko na wynikach, na których go wybrano.

W poprzedniej lekcji wybraliśmy najlepszy stopień wielomianu, zerkając na zbiór testowy. Wydawało się to niewinne, ale pomyśl, co to znaczy: wypróbowaliśmy kilka modeli i zostawiliśmy ten, który zbiór testowy lubił najbardziej. Zbiór testowy nie jest już nieznanym egzaminem — stał się sędzią, którego radziliśmy się w trakcie przygotowań. Jak wybierać ustawienia, nie psując jedynego uczciwego pomiaru?

Przewidź, potem sprawdź Zwiększasz liczbę części k z 2 do 10 (w sumie 178 win). Co dzieje się z różnicą między najgorszą a najlepszą częścią?
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ź: Rośnie, bo każda część testowa ma mniej win

Dla k = 2 najgorsza część ma 94,4%, a najlepsza 97,8% (różnica 3,4 pkt proc.). Dla k = 10 rozstęp to 83,3% do 100,0% (16,7 pkt proc.), bo część testowa liczy 17 lub 18 win i jedna pomyłka waży ponad 5 punktów. Średnia prawie się nie zmienia (96,1% i 96,7%).

Spróbuj

  • Ustaw suwak k na najmniejszą wartość i odczytaj trafność każdego foldu. Jak bardzo foldy różnią się między sobą?
  • Podnoś suwak k krok po kroku. Obserwuj średnią i rozrzut i zauważ, że w każdym foldzie jest teraz mniej win.
  • Przy dużym k poszukaj foldów z wyjątkowo niską albo wysoką trafnością. Gdy w foldzie jest tylko garstka win, jedna pomyłka mocno rusza jego wynikiem.
  • Porównaj rozrzut z różnicami między ustawieniami, które chciałbyś stroić. Czy mały zysk w średniej jest większy niż rozrzut?

Gdy już sprawdzisz to w labie, zobaczmy, co się tam właściwie działo.

Pomysł w prostych słowach

Pierwsze rozwiązanie to trzecia kupka danych. Dzielimy wiersze na zbiór treningowy, walidacyjny i testowy. Kandydatów trenujemy na treningowym, porównujemy na walidacyjnym, wybieramy zwycięzcę i dopiero wtedy otwieramy zbiór testowy, raz, żeby podać końcowy wynik. Zbiór walidacyjny przyjmuje na siebie całe zerkanie, więc testowy nie musi.

Kłopot w tym, że każdy podział kosztuje dane. Przy małym zbiorze zbiór walidacyjny na tyle duży, żeby był wiarygodny, zostawia za mało do nauki, a lekcja 2 pokazała, że mały zbiór daje rozchwiany wynik. Walidacja krzyżowa rozwiązuje to, sprytnie używając tych samych danych wiele razy.

Wyobraź sobie ocenianie nowej kucharki. Jedno danie mówi niewiele: może było jej najlepsze, może najgorsze. Prosisz więc o kilka dań w różne dni i patrzysz na średnią i na to, jak bardzo się różnią. Walidacja krzyżowa robi to samo z modelem: zamiast jednego egzaminu urządza kilka, za każdym razem na innych pytaniach, i dopiero z całej serii wyciąga wniosek.

Dlaczego to działa

W k-krotnej walidacji krzyżowej dane (bez końcowego zbioru testowego) tnie się na k równych części, zwanych foldami. Model trenuje się k razy. Za każdym razem jeden fold odkłada się do walidacji, a pozostałe k − 1 służą do treningu. Na koniec każdy wiersz był w walidacji dokładnie raz, a w treningu k − 1 razy. Każdy trening widzi taką część danych:

część treningowa = (k − 1) / k

Wynikiem nie jest jedna ocena, tylko k ocen, po jednej na fold. Ich średnia jest pewniejszym oszacowaniem niż pojedynczy podział, bo nie zależy od jednego szczęśliwego albo pechowego podziału wierszy. Ich rozrzut jest równie cenny: pokazuje, jak bardzo wynik zależy od tego, które wiersze model akurat dostał. Dwóch modeli, których średnie różnią się mniej niż ten rozrzut, nie da się naprawdę odróżnić.

Wybór k to kompromis. Przy małym k (powiedzmy 2) każdy model uczy się tylko na połowie danych, więc jest słabszy od tego, który na koniec zbudujesz, a oszacowanie jest pesymistyczne. Przy dużym k model uczy się prawie na wszystkim, ale każdy fold jest mały, więc oceny poszczególnych foldów są bardziej rozchwiane, a obliczenia trwają k razy dłużej niż jedno dopasowanie. Częstym kompromisem jest 5 albo 10.

Przykładem w tej lekcji jest zbiór Wine: 178 win z tego samego regionu Włoch, zrobionych z trzech różnych odmian winorośli, każde opisane 13 wynikami analizy chemicznej. Zadanie polega na rozróżnieniu tych trzech odmian.

Częsty błąd

Walidacja krzyżowa chroni cię tylko wtedy, gdy wszystko, czego uczysz się z danych, dzieje się wewnątrz każdego foldu. Jeśli najpierw skalujesz cechy albo wybierasz „najlepsze” cechy na wszystkich wierszach, a dopiero potem robisz walidację krzyżową, informacja z foldów walidacyjnych już przeciekła do treningu i wyniki są zbyt optymistyczne. Drugi błąd to strojenie dziesiątek ustawień walidacją krzyżową i podanie najlepszego wyniku jako końcowego. Po tylu porównaniach zwycięzca miał też trochę szczęścia; dlatego osobny zbiór testowy, użyty raz, wciąż ma znaczenie.

Z uczciwą oceną w zestawie narzędzi możemy badać mocniejsze modele, nie oszukując samych siebie. W następnej lekcji poznamy najbardziej czytelny z nich: drzewo decyzyjne.

Quiz

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

1. Ile razy w 5-krotnej walidacji krzyżowej każdy wiersz trafia do walidacji?
Pokaż odpowiedź

Poprawna odpowiedź: Dokładnie raz — w jednym z pięciu foldów

Każdy wiersz należy do jednego foldu, a każdy fold jest zbiorem walidacyjnym w dokładnie jednym z pięciu przebiegów.

2. Model A ma średnią trafność z walidacji krzyżowej odrobinę wyższą niż model B, ale rozrzut między foldami jest dużo większy niż ta różnica. Co z tego wynika?
Pokaż odpowiedź

Poprawna odpowiedź: Dane nie pokazują wiarygodnej różnicy między tymi modelami

Gdy różnica średnich jest mniejsza niż zmienność między foldami, łatwo może wynikać z przypadku.

3. Dlaczego skalowanie cech na wszystkich wierszach przed walidacją krzyżową to błąd?
Pokaż odpowiedź

Poprawna odpowiedź: Bo statystyki z foldów walidacyjnych przeciekają do treningu i zawyżają wyniki

Każdy krok, który uczy się z danych, trzeba dopasować wyłącznie na foldach treningowych, inaczej foldy walidacyjne przestają być nieznane.

Najważniejsze wnioski

  • Wybieranie ustawień na zbiorze testowym go psuje; zamiast tego używaj zbioru walidacyjnego albo walidacji krzyżowej.
  • W k-krotnej walidacji krzyżowej każdy wiersz trafia do walidacji dokładnie raz.
  • Średnia ocen foldów szacuje jakość modelu; rozrzut pokazuje, jak bardzo zależy ona od podziału.
  • Każda obróbka danych, która się z nich uczy, musi się odbywać wewnątrz foldu, żeby uniknąć wycieku.

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

Matematyka

W k-krotnej walidacji krzyżowej n wierszy dzielimy na k foldów. Każde dopasowanie uczy się na n · (k − 1) / k wierszach i testuje na pozostałych n / k. Wynik to średnia po foldach: CV = (1/k) · Σ wynik_f. Rozrzut to ich odchylenie standardowe s = √( Σ (wynik_f − CV)² / (k − 1) ).

Fold z m wierszami testowymi daje trafność zmieniającą się skokami co 1 / m. Jeśli prawdziwa trafność to p, jej błąd standardowy wynosi około √( p · (1 − p) / m ). Rośnie on, gdy m maleje, dlatego pojedyncze foldy stają się bardziej zaszumione, gdy k rośnie, choć średnia jest mniej pesymistyczna. Przy 178 winach i k = 10 jedna pomyłka w foldzie z 17 lub 18 win kosztuje od 5,6 do 5,9 punktu.

Wyniki foldów nie są w pełni niezależne: zbiory treningowe się pokrywają. Dlatego s / √k zaniża prawdziwą niepewność średniej.

W kodzie (Python)

178 win, 5 najbliższych sąsiadów i standaryzowane cechy. Żeby zobaczyć prawidłowość stojącą za pojedynczym tasowaniem z laba, różnicę między najgorszym a najlepszym foldem uśredniamy po 20 tasowaniach. Foldy są stratyfikowane i tasowane z ziarnem, więc liczby nie są labowymi 3,4 i 16,7 punktu:

import numpy as np
from sklearn.datasets import load_wine
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

X, y = load_wine(return_X_y=True)
model = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5))  # skaler jest dopasowywany od nowa w każdym foldzie
for k in (2, 10):
    runs = [cross_val_score(model, X, y, cv=StratifiedKFold(k, shuffle=True, random_state=s)) for s in range(20)]
    gap = np.mean([a.max() - a.min() for a in runs])
    print(f"k={k:2d}: mean accuracy {np.mean(runs):.3f}, average worst-to-best gap {gap:.3f}")

Wynik

k= 2: mean accuracy 0.956, average worst-to-best gap 0.020
k=10: mean accuracy 0.966, average worst-to-best gap 0.113

Średnia ledwo się rusza (0,956 i 0,966), ale przeciętna różnica między najgorszym a najlepszym foldem rośnie z 0,020 do 0,113, gdy k idzie od 2 do 10.

Dla praktyka
  • Każdy krok, który uczy się z danych (skalowanie, uzupełnianie braków, wybór cech), dopasowuj wewnątrz każdego foldu. W scikit-learn wstaw je do Pipeline i podaj go do cross_val_score.
  • W klasyfikacji używaj foldów stratyfikowanych, żeby każdy fold miał wszystkie klasy. cross_val_score robi to domyślnie dla klasyfikatorów, gdy cv jest liczbą, ale to domyślne cięcie nie tasuje.
  • Tasuj przed cięciem na foldy, jeśli wiersze są uporządkowane, np. według klasy albo daty. load_wine jest posortowane według klasy, więc podaj StratifiedKFold(k, shuffle=True, random_state=0), jak wyżej. Dla szeregów czasowych nie tasuj: użyj podziału „do przodu”.
  • Jeśli wiersze należą do grup (kilka wierszy na pacjenta), trzymaj każdą grupę w jednym foldzie przez GroupKFold. Inaczej ten sam pacjent trafi i do treningu, i do walidacji.
  • Podawaj średnią i rozrzut. Jeśli dwa modele różnią się mniej niż rozrzut, nie mów, że któryś jest lepszy.

Więcej w Atlasie

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