Dobre praktyki ML: podział na trening i test, walidacja krzyżowa i unikanie przecieków danych

0
8
Rate this post

Nawigacja:

Po co w ogóle dzielić dane? Cel, ryzyko, złudzenia

Uogólnianie kontra dopasowanie do danych uczących

Model machine learning ma sens tylko wtedy, gdy potrafi uogólniać na nowe przypadki, których nie widział w czasie treningu. W praktyce oznacza to, że interesuje nas nie tyle wynik na danych uczących, ile wydajność na danych, które pojawią się w przyszłości. Pojedynczy zbiór danych to zwykle tylko próbka z większej populacji, a zadaniem modelu jest nauczyć się wzorców ogólnych, a nie „na pamięć” zapamiętać konkretne wiersze.

Kiedy uczysz model i oceniasz go wyłącznie na tych samych danych, dostajesz wynik z definicji zbyt optymistyczny. Model ma pełen dostęp do wszystkich przykładów, więc łatwo mu się dopasować nawet do przypadkowych szumów. Klasyczny przykład to drzewo decyzyjne bez ograniczeń głębokości, które potrafi praktycznie perfekcyjnie dopasować się do danych treningowych, a jednocześnie słabo działać na nowych obserwacjach.

Rozdzielenie danych na części (trening, walidacja, test) jest próbą przybliżenia sytuacji „świata zewnętrznego” w kontrolowanych warunkach. Zamiast wierzyć w wynik na zbiorze uczącym, używamy osobnych próbek do podejmowania decyzji o modelu i do końcowej oceny jakości. Bez takiego rozdziału trudno odróżnić, czy model naprawdę nauczył się struktury problemu, czy tylko wykorzystuje przypadkowe korelacje obecne wyłącznie w zebranych danych.

Dlaczego wynik na zbiorze uczącym jest z definicji zawyżony

Zbiór uczący służy do minimalizacji funkcji straty. Algorytm iteracyjnie dopasowuje parametry modelu tak, by błąd na tym zbiorze był jak najmniejszy. To trochę tak, jakby stale pisać test pod klucz odpowiedzi, który już znamy. W pewnym momencie model zaczyna „wykładać się” przy każdym nowym pytaniu, ale na dobrze znanych pytaniach wypada znakomicie.

Formalnie: oczekiwany błąd na danych uczących jest obciążony w dół, ponieważ parametry były wybierane warunkowo na tych danych. Statystycznie rzecz biorąc, maksymalizujemy (lub minimalizujemy) wynik na tym samym zbiorze, więc dostajemy ekstremum korzystne, ale niekoniecznie reprezentatywne dla danych spoza próby.

Przy modelach o dużej pojemności (głębokie sieci, drzewa złożone z wielu gałęzi, bogate zestawy cech) rozjazd między jakością na treningu a na danych niewidzianych może być dramatyczny. To klasyczny overfitting – dopasowanie zarówno do sygnału, jak i do szumu. Odczytywanie jakości modelu tylko ze zbioru treningowego to prosta droga do złudnego poczucia sukcesu.

Rola zbioru walidacyjnego i testowego w procesie

Trzy główne komponenty procesu to:

  • Zbiór treningowy – służy do nauki parametrów modelu.
  • Zbiór walidacyjny – służy do wyboru architektury, hiperparametrów, cech; jest częścią procesu projektowania.
  • Zbiór testowy – służy do końcowej, obiektywnej oceny; nie powinien mieć wpływu na podejmowane decyzje w trakcie budowy modelu.

Zbiór walidacyjny jest wewnętrznym sędzią procesu eksperymentowania. Na nim porównujesz różne modele, różne wartości hiperparametrów, inne sposoby przygotowania cech. Zbiór testowy pełni z kolei funkcję zewnętrznego audytora – odpowiada na pytanie, jaką jakość realnie można oczekiwać po całym pipeline’ie, jeśli wypuści się go „na świat”.

Jeśli wyniki na walidacji są znakomite, a na teście dużo słabsze, zazwyczaj oznacza to, że dostroiłeś pipeline pod konkretny podział walidacyjny. Jeśli natomiast wyniki na treningu są idealne, a walidacja i test wypadają podobnie, ale wyraźnie gorzej – model ma tendencję do overfittingu i wymaga uproszczenia, regularizacji albo większej ilości danych.

Iluzja postępu przy pracy na jednym zbiorze

Typowa pułapka w praktyce: zespół ma jeden, w miarę niewielki dataset, bez twardego podziału. Ktoś buduje model, ręcznie go dłubie, zmienia cechy, usuwa outliery, poprawia transformacje. Wynik na tym samym zbiorze rośnie, wykresy wyglądają coraz lepiej – więc pojawia się wrażenie, że „model jest coraz lepszy”.

Problem polega na tym, że każdy kolejny krok jest oceniany na tym samym materiale. W pewnym momencie model zapamiętuje charakterystyczne artefakty, pojedyncze obserwacje, nietypowe kombinacje cech – które występują tylko w tym zbiorze. Na nowych danych ten „postęp” często znika. Rzetelna ocena wymaga zewnętrznego punktu odniesienia, czyli właśnie niezależnego zbioru walidacyjnego lub struktury walidacji krzyżowej.

Overfitting bez skomplikowanych modeli? Jak najbardziej

Overfitting kojarzy się z sieciami neuronowymi czy rozbudowanymi modelami nieliniowymi, ale można go łatwo wywołać nawet prostą regresją liniową, jeśli:

  • liczba cech jest porównywalna lub większa niż liczba obserwacji,
  • cechy są ze sobą mocno skorelowane,
  • dane zawierają nietypowe, rzadkie przypadki, które nie powtarzają się w testach.

Bez odpowiedniego podziału na zbiory, wynik takiej regresji na danych uczących może wyglądać imponująco, choć model tak naprawdę nie przenosi się na nowe przypadki. Test na niezależnym zbiorze to nie fanaberia teoretyków, ale praktyczny filtr, który pozwala odsiać pozornie świetne, a w rzeczywistości kruche rozwiązania.

Podstawowe schematy podziału danych: train/validation/test

Kiedy wystarcza prosty podział train/test

Najprostszy schemat to podział danych na dwa zbiory: train i test. Jest on sensowny w sytuacjach, gdy:

  • model ma niewiele hiperparametrów,
  • nie planujesz intensywnego tuningu i porównywania wielu architektur,
  • masz stosunkowo duży zbiór danych, więc ryzyko przypadkowych odchyleń metryk na teście jest mniejsze.

Taki podział sprawdza się np. przy szybkich prototypach lub prostych modelach liniowych, gdzie decyzji projektowych jest mało. Wtedy można czasem „oszczędzić” trzeci zbiór, zakładając, że ocena na teście ma charakter orientacyjny, a model i tak zostanie w przyszłości rozwinięty i ponownie zweryfikowany.

Ograniczenie tego podejścia jest jasne: jeśli wykorzystujesz zbiór testowy do iteracyjnego ulepszania modelu (zmieniasz cechy, hiperparametry, architekturę), to test przestaje być testem, a staje się faktycznie walidacją. Konsekwencją jest zaniżone oszacowanie błędu produkcyjnego.

Klasyczny schemat train/validation/test i proporcje danych

Bardziej rzetelny proces zwykle korzysta z trzech części:

  • Train – np. 60–80% danych,
  • Validation – zwykle 10–20%,
  • Test – zwykle 10–20%.

Często spotykane są proporcje 60/20/20 lub 70/15/15. Nie są to dogmaty, tylko punkty wyjścia. Przy bardzo dużych zbiorach (setki tysięcy i więcej przykładów) sensowne bywa np. 80/10/10, a nawet 90/5/5, bo kilka procent z ogromnego datasetu to nadal tysiące lub dziesiątki tysięcy obserwacji do solidnego testu.

Przy mniejszych datasetach proporcje trzeba dobrać ostrożniej: zbyt duży test oznacza mniejszą pulę do treningu, a zbyt mały test – bardzo duży szum statystyczny w ocenie. Zwykle szuka się kompromisu, w którym:

  • zestaw treningowy umożliwia naukę stabilnego modelu,
  • zbiory walidacyjny i testowy są na tyle duże, by metryki nie „skakały” losowo.

Zbiór walidacyjny jako przestrzeń eksperymentów

Na zbiorze walidacyjnym wykonuje się większość prób i błędów:

  • wybór typu modelu (np. model liniowy vs gradient boosting),
  • dobór hiperparametrów (głębokość drzew, współczynniki regularizacji, liczba neuronów),
  • strategię przetwarzania cech (standaryzacja, logarytmowanie, binning, encoding kategorii),
  • decyzje związane z balansowaniem klas, wagami błędów, progami decyzji.

Każda taka decyzja mniej lub bardziej „dopasowuje się” do konkretnego zbioru walidacyjnego. Im więcej iteracji, tym silniej model jest zestrojony z tą próbką. Z tego powodu zbiór testowy musi być trzymany z boku jako niezależny punkt odniesienia, żeby wychwycić sytuację, w której przetrenowaliśmy się na walidacji.

Zbiór testowy jako zamrożony sędzia

Zdrowa praktyka: po utworzeniu podziału nie dotykać zbioru testowego aż do końca cyklu eksperymentów. Nie obliczać na nim metryk przy każdej iteracji, nie oglądać rozkładów błędów, nie używać go do projektowania cech. Test powinien zostać wyciągnięty tylko raz, do „ostatecznego egzaminu” całego pipeline’u.

Jeśli zespół zaczyna podejmować decyzje w oparciu o wyniki na teście („model A ma na teście 0,87, model B ma 0,88, więc bierzemy B i jeszcze go podtuningujemy”), to w praktyce test staje się częścią procedury strojenia. Skutkiem jest systematyczne zaniżenie szacowanego błędu rzeczywistego, szczególnie jeśli liczba prób i modeli rośnie.

Przykład: klasyfikator churn z prostym schematem podziału

Wyobraźmy sobie problem przewidywania odejścia klienta (churn) w firmie subskrypcyjnej. Mamy dane o zachowaniu użytkowników za ostatnie miesiące oraz informację, czy klient zrezygnował z usługi w kolejnym miesiącu. Rozsądny schemat działania może wyglądać tak:

  1. Losowo podzielić klientów (nie pojedyncze interakcje) na train (70%), validation (15%) i test (15%), dbając o stratyfikację względem etykiety churn.
  2. Zbudować kilka bazowych modeli (logistyczna regresja, drzewo, prosty gradient boosting) i dobrać ogólne hiperparametry na walidacji.
  3. Wybrać dwa–trzy najlepsze modele, jeszcze lekko je dostroić i ostatecznie wybrać jeden, kierując się wynikiem na walidacji oraz stabilnością metryk.
  4. Na samym końcu uruchomić pipeline na zbiorze testowym i odczytać metryki (AUC, recall, precision przy wybranym progu), traktując je jako ostrożne oszacowanie jakości produkcyjnej.

Zastosowanie prostego, ale przemyślanego trójdzielnego podziału zapobiega sytuacji, w której ocena modelu bazuje na „przyzwyczajonych” danych, do których zdążył się on zbyt dobrze dopasować.

Ręce analizujące dane na biurku z wykresami i notatkami
Źródło: Pexels | Autor: Lukas Blazek

Jak poprawnie losować podział: stratyfikacja, balans, rozmiar

Losowy podział to często za mało

Domyślny odruch to użycie funkcji typu train_test_split z ustalonym random_state. Samo ziarno losowości daje powtarzalność, ale nie gwarantuje, że rozkład klas i cech będzie sensownie odwzorowany w każdym zbiorze. Przy zbalansowanych danych i dużej próbie losowy split zwykle jest wystarczający, ale w innych sytuacjach potrafi spowodować solidne przekłamania.

Przykład: w problemie binarnej klasyfikacji churn może się zdarzyć, że odsetek klientów, którzy odeszli, wynosi kilka procent. Czysty losowy podział potrafi dać zbiór testowy z odsetkiem churn dwa razy większym lub dwa razy mniejszym niż w całej populacji. Wtedy metryki typu AUC jeszcze jakoś się trzymają, ale już precision/recall potrafią wyglądać inaczej niż w rzeczywistej populacji.

Stratyfikacja względem klasy i innych kluczowych zmiennych

Stratyfikacja polega na tym, że podział na zbiory utrzymuje podobny rozkład klas lub innych ważnych kategorii jak w całości danych. Najczęściej stosuje się:

  • Stratyfikację względem etykiety – utrzymanie udziału klas w train, validation i test, szczególnie przy niezbalansowanych problemach.
  • Stratyfikację względem grup – np. kraj, produkt, kanał pozyskania, jeśli mają one istotny wpływ na zachowanie modelu.

W klasyfikacji binarnej większość bibliotek oferuje gotowe narzędzia (np. StratifiedKFold w scikit-learn). Dzięki nim odsetek klasy pozytywnej w każdym foldzie jest zbliżony do odsetka w całym zbiorze. Przy rzadkich zdarzeniach to często jedyny sposób, żeby każda część danych zawierała wystarczająco dużo pozytywnych przykładów do sensownej nauki i oceny.

Rzadkie zdarzenia i minimalny rozmiar zbioru testowego

Rzadkie zdarzenia i minimalny rozmiar zbioru testowego – podejście praktyczne

Przy klasyfikacji rzadkich zdarzeń (fraud, awarie, ciężkie powikłania medyczne) podstawowy problem brzmi: czy w ogóle będzie co mierzyć na teście. Jeśli pozytywna klasa stanowi np. promil danych, to prosty podział na 80/10/10 może zakończyć się zbiorem testowym z kilkoma (!) pozytywami – w takim układzie precision/recall będą bardziej loterią niż wiarygodną estymacją.

Rozsądny punkt wyjścia to patrzenie nie tylko na procentowy udział, ale także na bezwzględną liczbę pozytywów w każdym zbiorze. Kilkanaście przypadków to za mało, by cokolwiek stabilnie oceniać, kilkadziesiąt bywa dolną granicą, ale dopiero setki zaczynają dawać sensowną odporność na przypadek. Jeżeli takich przypadków po prostu nie ma, trzeba wprost zaakceptować, że dokładna ocena recall/precision na danych historycznych jest ograniczona i mocno przedyskutować ryzyka z biznesem.

W praktyce przy ekstremalnym niezbalansowaniu często:

  • zmniejsza się udział danych przeznaczonych na test, aby skupić więcej rzadkich przykładów w walidacji,
  • łączy się walidację z testem (przy zachowaniu ostrożności interpretacyjnej) i korzysta z walidacji krzyżowej na całym zbiorze,
  • stosuje się metryki bardziej odporne na małą liczbę pozytywów (np. AUC-ROC) jako wskaźniki pomocnicze, choć same nie rozwiązują problemu.

Żaden z tych manewrów nie „naprawi” sytuacji, w której w danych historycznych bądź operacyjnych jest kilka zdarzeń na krzyż. To raczej próba zminimalizowania szkód niż idealne rozwiązanie.

Podział według jednostek logicznych: użytkownik, sesja, transakcja

Losowanie obserwacji jedna po drugiej jest wygodne, ale często prowadzi do przecieków między zbiorami. Gdy wiele wierszy dotyczy tego samego klienta, urządzenia czy firmy, rozsądniejszy jest podział według jednostek logicznych, a nie pojedynczych rekordów. Przykładowo:

  • wszystkie sesje danego użytkownika trafiają do jednego zbioru (train lub validation lub test),
  • wszystkie transakcje danego sklepu internetowego są w jednym segmencie, jeśli sklep jest kluczową jednostką analizy,
  • wszystkie badania danego pacjenta (np. kolejne wizyty) lądują w jednym zbiorze.

Takie podejście ogranicza przeciek informacji o konkretnym obiekcie między zbiorem treningowym i testowym. Z punktu widzenia produkcji lepiej przypomina to rzeczywisty scenariusz: model będzie przewidywał dla nowych użytkowników lub przynajmniej dla nowych przypadków, których jeszcze nie widział.

Reprezentatywność domeny: regiony, kanały, sezony

Poza klasą i grupą jednostkową dobrze jest skontrolować także inne wymiary, które w praktyce zmieniają zachowanie modelu. Typowe wymiary to:

  • geografia – kraje, miasta, regiony sprzedaży,
  • kanał pozyskania – organic, płatne kampanie, partnerzy, call center,
  • czas – sezonowość, zmiany regulacyjne, nowe produkty.

Zbyt „czysty” test (np. tylko z jednego kanału marketingowego) może dawać złudnie wysokie wyniki. Jeżeli w danych widać dużą heterogeniczność, lepiej jawnie zadbać, by każdy zbiór zawierał reprezentantów kluczowych segmentów. Wyjątkiem są sytuacje, w których świadomie chcemy przetestować generalizację na nowy segment – wtedy można np. całe nowe państwo lub kanał zostawić jako osobny, trudniejszy zestaw testowy.

Kiedy zwykły split nie wystarcza: motywacja do walidacji krzyżowej

Ograniczenia pojedynczego podziału train/validation

Pojedynczy losowy podział ma tę wadę, że szczęście lub pech w samym podziale może znacząco wpłynąć na wyniki. Przy mniejszych datasetach różnica kilku nietypowych obserwacji w walidacji potrafi zmienić ranking modeli, choć w rzeczywistości są do siebie jakościowo zbliżone.

Widać to szczególnie, gdy:

  • rozmiar zbioru jest niewielki,
  • rozkład cech jest wielomodalny (wiele różnych „typów” przypadków),
  • interesuje nas nie tylko średni wynik, ale też stabilność między różnymi podzbiorami.

W takiej sytuacji pojedyncza liczba z walidacji bywa łaskawsza lub surowsza niż wynikałoby to z realnej jakości modelu. Trudno też odróżnić, czy poprawa o np. 0,01 AUC jest realna, czy to tylko losowe odchylenie.

Walidacja krzyżowa jako „uśredniony” test na wielu podziałach

Walidacja krzyżowa (cross-validation) rozwiązuje ten problem, powtarzając proces dzielenia danych i treningu wiele razy na różnych podzbiorach. Klasyczna postać to K-fold CV:

  1. Dane dzielone są na K (zwykle 3–10) rozłącznych części o zbliżonej wielkości.
  2. Dla każdego foldu:
    • trenuje się model na K−1 częściach,
    • ocenia się go na pozostałej części (fold walidacyjny).
  3. Na końcu uśrednia się metryki z wszystkich foldów, często także z odchyleniem standardowym.

Taki proces redukuje wrażliwość na konkretny, jednorazowy podział. Daje też informację o zmienności jakości – jeśli wyniki na foldach skaczą, to znak, że model jest mało stabilny lub dane są szczególnie niejednorodne.

Kiedy walidacja krzyżowa ma największy sens

Najwięcej zysku z walidacji krzyżowej pojawia się w sytuacjach, gdy:

  • dane nie są bardzo duże (czas trenowania jednego modelu jest akceptowalny),
  • liczba przykładów jest umiarkowana, a koszt błędnych decyzji wysoki (np. systemy medyczne, scoring kredytowy),
  • chcemy porównać kilka rodzin modeli i zależy nam na solidnej ocenie ich potencjału,
  • klasa pozytywna jest rzadka i trzeba „wycisnąć” maksimum informacji z każdego przykładu.

Przy bardzo dużych datasetach pełna walidacja krzyżowa staje się po prostu zbyt kosztowna obliczeniowo. Wtedy częściej stosuje się pojedynczy podział lub uproszczone warianty, np. monte-carlo CV (wielokrotne losowe podziały train/validation na podzbiorach danych).

Naukowiec w laboratorium obserwuje próbki pod mikroskopem
Źródło: Pexels | Autor: Mikhail Nilov

Klasyczne warianty walidacji krzyżowej i ich zastosowania

Standardowa K-fold i stratyfikowana K-fold

Podstawowa wersja K-fold zakłada, że każdy fold jest losową częścią danych. Przy klasyfikacji z klasami niezbalansowanymi zwykle lepszym wyborem jest stratyfikowana K-fold, która zapewnia podobny rozkład klas w każdym foldzie. Bez tego może dojść do sytuacji, w której jeden fold prawie nie zawiera klasy pozytywnej, co zniekształca wyniki.

W praktyce:

  • przy klasyfikacji binarnej niemal zawsze stosuje się stratyfikację,
  • przy regresji lub zadaniach bez wyraźnych klas korzysta się z klasycznej K-fold lub bardziej złożonych podejść (np. stratyfikacja po kwantylach zmiennej docelowej, jeśli jest to zasadne).

Leave-One-Out (LOO) i dlaczego rzadko ma sens

Leave-One-Out CV traktuje każdy pojedynczy przykład jako osobny fold walidacyjny: model trenuje się na wszystkich pozostałych danych i testuje na jednym punkcie. Cykl powtarza się dla każdej obserwacji. Formalnie to eleganckie rozwiązanie, jednak w praktyce:

  • jest bardzo kosztowne obliczeniowo – trzeba wytrenować tyle modeli, ile jest obserwacji,
  • daje metryki o wysokiej wariancji przy modelach silnie nieliniowych,
  • nie oddaje realistycznego scenariusza produkcyjnego, w którym model ma być używany na grupach przypadków, a nie pojedynczych punktach.

LOO ma sens głównie w bardzo małych datasetach, prostych modelach i zadaniach akademickich. W projektach biznesowych częściej jest ciekawostką niż narzędziem pierwszego wyboru.

Group K-fold: gdy jednostki się powtarzają

Group K-fold to wariant zaprojektowany pod sytuacje, gdy obserwacje można pogrupować po jakimś identyfikatorze (np. pacjent, klient, urządzenie) i nie chcemy, żeby ta sama grupa pojawiała się naraz w treningu i walidacji. Proces wygląda podobnie jak klasyczny K-fold, ale:

  • foldy są tworzone na poziomie grup, a nie pojedynczych rekordów,
  • cała grupa trafia w jednym przebiegu albo do train, albo do validation.

Group K-fold sprawdza się np. w systemach rekomendacyjnych, medycynie (wiele badań u jednego pacjenta) czy analizie dokumentów prawnych, gdzie jeden klient generuje liczne podobne sprawy. Bez takiej konstrukcji model może „pamiętać” indywidualne cechy grupy, co przeszacuje jego realną generalizację.

Repeated K-fold i bootstrap: gdy zależy na estymacji niepewności

Jeżeli zależy na lepszej ocenie niepewności metryk, można zastosować:

  • Repeated K-fold – wielokrotne losowanie foldów K-fold (z innym ziarnem) i uśrednianie wyników,
  • bootstrap – losowanie z powtórzeniami próbek treningowych i ocena na reszcie.

To rozwiązanie przydatne, gdy trzeba np. przedstawić zaufane przedziały dla AUC lub F1-score albo obronić model przed audytem. Z drugiej strony, koszt obliczeniowy takiej procedury jest wysoki, dlatego stosuje się ją zwykle na wybranym, już wstępnie ustalonym zestawie modeli, a nie na każdym pomyśle z hackathonu.

Dane sekwencyjne i szeregów czasowych: podział „do przodu”, nie wstecz

Dlaczego losowy podział niszczy założenia czasowe

W szeregach czasowych kluczowe jest to, że przyszłość nie może wpływać na przeszłość. Standardowy losowy split miesza obserwacje z różnych dat, przez co w zbiorze treningowym mogą znaleźć się dane z późniejszych okresów niż w walidacji. Model „widzi przyszłość”, ucząc się na nowszych danych niż te, na których jest oceniany.

Efekt jest subtelny, ale zabójczy: metryki na walidacji rosną, bo model dostaje informacje o zmianach trendów, sezonowości, nowych produktach z przeszłości walidacji. Po wdrożeniu, gdy taki komfort znika, jakość spada.

Forward chaining / walk-forward validation

Rozsądniejszy schemat dla danych czasowych opiera się na podziale w osi czasu. Przykładowo:

  • trenujemy na danych do końca Q1, walidujemy na Q2,
  • potem trenujemy na danych do końca Q2, walidujemy na Q3,
  • następnie do końca Q3 – walidacja na Q4 itd.

Taki schemat, znany jako forward chaining lub walk-forward validation, lepiej odzwierciedla rzeczywisty tryb pracy modelu w produkcji: najpierw uczymy się na historii, potem przewidujemy przyszły okres. Dodatkowo pozwala ocenić, jak stabilny jest model w kolejnych „oknach czasowych” – jeśli wyniki szybko się degradują, być może konieczna będzie częstsza retrenizacja.

Dobór okna treningowego i walidacyjnego

Warto jawnie zdefiniować:

  • długość okna treningowego – ile historii model widzi (np. 6 miesięcy, 2 lata),
  • horyzont prognozy – jak daleko w przyszłość przewiduje (dzień, tydzień, kwartał),
  • okno walidacyjne – jak długi okres wykorzystujemy do oceny (np. kolejny miesiąc).

Nadmiernie długie okno treningowe może wprowadzać nieaktualne relacje (np. sprzed istotnej zmiany regulacyjnej), natomiast zbyt krótkie okno zwiększa wariancję modelu i ryzyko przeuczenia na specyfice ostatnich tygodni. Nie ma uniwersalnej recepty – parametry trzeba dobrać eksperymentalnie i uzgodnić z kontekstem biznesowym (jak szybko zmienia się świat, w którym działa model).

Luki czasowe (gaps) dla uniknięcia przecieku

Przy wielu problemach sekwencyjnych dane zawierają cechy, które „wyglądają w przyszłość” w bardziej zawoalowany sposób. Informacje o finalnym statusie transakcji, sumy z końca miesiąca czy cechy liczone na przestrzeni długich okien potrafią zawierać wiedzę o okresie późniejszym niż moment, dla którego generujemy predykcję.

Jednym z podejść jest wprowadzenie luki czasowej między końcem okna treningowego a początkiem okna walidacyjnego. Przykład: model ma prognozować churn na bazie danych do końca miesiąca, ale nie powinien widzieć informacji wytworzonych po tej dacie (np. zaktualizowanych flag). W takiej sytuacji:

  • trenujemy na danych do końca miesiąca T,
  • pomijamy okres T+1 (luka bezpieczeństwa),
  • weryfikujemy model na okresie T+2.
Laptop z wykresami danych obok tabletu z kalendarzem planowania
Źródło: Pexels | Autor: Pixabay

Tuning hiperparametrów bez oszukiwania

Dlaczego „grzebanie” w ustawieniach na tym samym zbiorze jest mylące

Hiperparametry (głębokość drzewa, siła regularyzacji, liczba sąsiadów itd.) określają przestrzeń modeli. Jeśli dobiera się je „na oko” patrząc tylko na wyniki na jednym zbiorze walidacyjnym, bardzo łatwo dopasować się do przypadkowych fluktuacji w tym konkretnym podziale.

Typowy scenariusz: model jest wielokrotnie modyfikowany i oceniany na tej samej walidacji. Formalnie zbiór walidacyjny przestaje wtedy pełnić rolę niezależnego sędziego – staje się częścią procesu dopasowywania. Metryki z takiej walidacji będą optymistyczne i nie odtworzą się na nowych danych, nawet jeśli walidacja nie była nigdy mieszana z treningiem w sensie technicznym.

Nested CV: kiedy chcesz wiedzieć, jak „naprawdę” wypadasz

Rozdzielenie etapu wyboru hiperparametrów od oceny końcowej można osiągnąć przez tzw. nested cross-validation. Mechanizm:

  • zewnętrzna pętla: dzieli dane na foldy, służy wyłącznie do końcowej oceny jakości,
  • wewnętrzna pętla: w każdym foldzie zewnętrznym wykonuje się osobną walidację krzyżową do wyboru hiperparametrów.

W rezultacie każdy punkt danych występuje jako testowy tylko w zewnętrznej pętli, a wszystkie decyzje co do modelu (architektura, hiperparametry) są podejmowane wyłącznie na danych treningowych z tej pętli. To jeden z nielicznych sposobów, który w miarę uczciwie odpowiada na pytanie: „jak ten cały proces selekcji wypada na zupełnie nowych danych?”.

Minus – koszt obliczeniowy. Nested CV oznacza mnożenie liczby trenowań (K zewnętrzne × K wewnętrzne × liczba konfiguracji). W praktyce stosuje się je raczej w projektach o dużej odpowiedzialności (medycyna, compliance, audyty regulacyjne) albo na wczesnym etapie, by porównać rodziny modeli, a nie przy każdej drobnej iteracji.

Grid search, random search i optymalizacja bayesowska

Do przeszukiwania przestrzeni hiperparametrów używa się kilku popularnych strategii, z których każda ma swoje ograniczenia.

Grid search: proste nie znaczy skuteczne

Grid search testuje wszystkie kombinacje hiperparametrów z zadanego „rusztu”. Jest intuicyjny, ale:

  • słabo skaluje się z liczbą wymiarów – liczba kombinacji rośnie wykładniczo,
  • traktuje wszystkie parametry jednakowo, choć często tylko kilka z nich jest naprawdę czułych,
  • marnuje czas na testowanie wielu podobnych konfiguracji w „mniej istotnych” obszarach.

Ma sens przy bardzo małej liczbie hiperparametrów i w środowiskach, gdzie bardziej liczy się powtarzalność i prostota niż maksymalna efektywność (np. sztywne pipeline’y korporacyjne).

Random search: mniej uporządkowany, częściej lepszy

Random search losuje konfiguracje z zadanych rozkładów hiperparametrów. Działa zaskakująco dobrze, szczególnie gdy:

  • tylko kilka hiperparametrów istotnie wpływa na wynik,
  • można ograniczyć liczbę eksperymentów (np. budżet 50–100 trenowań).

Warunek sensownego działania to przemyślane rozkłady (np. skala logarytmiczna dla współczynnika regularyzacji, zamiast liniowej). Bez tego losujesz głównie nieprzydatne wartości w „złej” skali.

Optymalizacja bayesowska i algorytmy sekwencyjne

Bardziej zaawansowane podejścia (np. TPE, Gaussian Processes, SMAC) modelują zależność „hiperparametry → wynik walidacji” i na tej podstawie proponują kolejne konfiguracje do sprawdzenia. Dają przewagę, gdy:

  • trening pojedynczego modelu jest kosztowny,
  • przestrzeń hiperparametrów jest szeroka i nieliniowa,
  • liczba możliwych eksperymentów jest ograniczona.

Te metody same w sobie nie rozwiązują jednak problemu przecieku – trzeba je osadzić w poprawnym schemacie walidacyjnym (CV, nested CV) i jasno rozdzielić dane treningowe od testu.

Jak nie „przesterować” na walidację

Nawet z poprawnie skonfigurowaną walidacją krzyżową można dopasować się do szumu, jeśli liczba prób jest duża. Kilka prostych zasad ogranicza to ryzyko:

  • projektując eksperyment, narzuć z góry budżet (liczbę konfiguracji) i kryterium wyboru (np. średnia AUC z 5-fold CV),
  • nie zmieniaj schematu CV w trakcie porównywania modeli; każda zmiana to nowe źródło niereplikowalności,
  • po wyborze zwycięskiej konfiguracji oceń ją raz na trzymanym z boku zbiorze testowym i nie wracaj już do tuningu na podstawie wyniku z testu.

Próby „dokręcania” hiperparametrów po obejrzeniu wyniku na teście nie są niczym innym niż ręcznie sterowanym przeciekiem informacyjnym z testu do treningu.

Czym jest data leakage i dlaczego tak łatwo je przeoczyć

Data leakage jako ukryty kanał informacji

Data leakage to każdy przypadek, w którym model podczas trenowania korzysta – wprost lub pośrednio – z informacji, których nie będzie mieć w momencie podejmowania decyzji w produkcji. Nie chodzi wyłącznie o jawne mieszanie zbiorów. Często źródłem przecieku jest sposób przygotowania cech, kolejność operacji w pipeline’ie lub wybór okna czasowego.

Największy problem z przeciekiem polega na tym, że początkowo „działa” fenomenalnie. Metryki na walidacji są znakomite, eksperymenty wyglądają obiecująco, a w logach wszystko się zgadza. Dopiero po wdrożeniu okazuje się, że model był karmiony informacją, której nigdy nie zobaczy na żywo.

Dwa podstawowe typy przecieków

Uproszczając, przecieki można podzielić na dwie kategorie.

Przeciek przez czas (future leakage)

Występuje, gdy do przewidywania zdarzenia w czasie T używa się cech, które w rzeczywistości są znane dopiero po czasie T. Przykłady:

  • prognozowanie niewypłacalności klienta na podstawie pola „status kredytu po 6 miesiącach”,
  • model churn, który korzysta z flag liczących się raz w tygodniu i obejmujących zachowania z przyszłości względem daty predykcji,
  • agregaty miesięczne liczone na pełnym miesiącu, choć decyzja ma być podejmowana w połowie miesiąca.

Takie cechy zachowują się jak „wyrocznia z przyszłości” – świetnie tłumaczą przeszłe zdarzenia, lecz w produkcji są po prostu niedostępne.

Przeciek przez przetwarzanie (procedural leakage)

Pojawia się, gdy kroki przetwarzania danych naruszają izolację między train a validation/test. Do klasycznych przypadków należą:

  • skalowanie lub standaryzacja wykonana na całym zbiorze przed podziałem,
  • wybór cech (feature selection) oparty na statystykach liczonych na całości danych,
  • tuning hiperparametrów z użyciem informacji z testu (jawnie lub pośrednio),
  • deduplikacja, imputacja lub usuwanie outlierów zrobione „hurtowo” na wszystkich danych przed splitem.

W tych scenariuszach model nie widzi literalnie przykładów z walidacji, ale korzysta z ich statystyk. To wystarczy, aby podbić metryki i zaburzyć obraz generalizacji.

Najczęstsze źródła przecieku w praktyce

W codziennych projektach przeciek pojawia się często w banalnych miejscach. Kilka wzorców, które warto mieć z tyłu głowy.

Agregaty grupowe i target leakage

Kiedy buduje się cechy agregowane po kliencie, produkcie czy firmie (sumy, średnie, liczba zdarzeń), trzeba upewnić się, że agregacja nie wciąga informacji z przyszłości względem daty predykcji. Szczególnie zdradliwe są:

  • cechy „średnia wartość celu dla tego klienta / produktu” liczone na całym zbiorze,
  • globalne statystyki typu target encoding, jeżeli nie są liczone wyłącznie na danych treningowych w danym foldzie,
  • uśrednianie wskaźników w oknach, które obejmują okres po momencie, gdy model ma podjąć decyzję.

W skrajnej wersji powstaje bezpośredni target leakage, gdzie cecha jest funkcją zmiennej, którą próbujemy przewidzieć (np. zliczona liczba zwrotów produktu w okresie obejmującym sam zwrot).

Feature engineering robione „na skróty”

Przy złożonych pipeline’ach, gdzie wiele kroków jest niemal skopiowanych z notatnika do kodu produkcyjnego, łatwo przeoczyć kolejność działań. Przypadki typowe:

  • wypełnianie braków wartości medianą liczona na całości danych,
  • redukcja wymiaru (PCA, autoenkodery) trenowana na pełnym zbiorze, a następnie używana do transformacji w walidacji i testach,
  • klastrowanie (np. K-means) wykonane raz na wszystkich danych i używanie indeksu klastra jako cechy we wszystkich splitach.

Każda technika ucząca się z danych (nawet PCA czy K-means) powinna być trenowana wyłącznie na zbiorze treningowym, a dopiero potem stosowana do pozostałych części.

Przeciek przez nieszczelny identyfikator

Czasami pojedyncze pole, z pozoru niewinne, niesie w sobie bardzo silną informację związaną z celem. Przykładowo:

  • kod produktu, który jest w praktyce niemal równoważny kategorii ryzyka,
  • wewnętrzny identyfikator kampanii, który wskazuje, czy klient przeszedł już wcześniejszą selekcję,
  • ID użytkownika, kiedy w danych treningowych i walidacyjnych pojawia się ten sam użytkownik z powtarzalnym zachowaniem.

To nie zawsze jawny błąd – czasem taki identyfikator rzeczywiście będzie dostępny w produkcji. Problem pojawia się, gdy identyfikator odzwierciedla proces decyzyjny oparty na tej samej lub bardzo podobnej logice, którą próbujemy zbudować w modelu. Model nie tyle „uczy się świata”, co odtwarza istniejącą politykę, która w dodatkowych danych może wyglądać inaczej.

Jak projektować pipeline, żeby ograniczyć przecieki

Pełna eliminacja przecieków jest trudna, ale da się znacząco zmniejszyć ryzyko przez kilka konsekwentnych zasad konstrukcyjnych.

Strict train-only fitting

Każdy obiekt, który „uczy się” z danych – scaler, encoder, selektor cech, reduktor wymiaru, klastrowanie – musi być trenowany wyłącznie na zbiorze treningowym w danym podziale. Technicznie oznacza to:

  • wywołanie fit (lub odpowiednika) tylko na x_train,
  • używanie transform / predict na walidacji i teście bez fit na tych zbiorach,
  • unikanie funkcji, które w tle robią „fit na wszystkim”, gdy nie są osadzone w pipeline’ie (częsty problem przy ręcznym kodowaniu).

Frameworki typu scikit-learn czy tidymodels ułatwiają to, jeśli korzysta się z wbudowanych pipeline’ów, zamiast mieszać kroki w wielu niezależnych skryptach.

Walidacja i podział zaprojektowane jako pierwsze

Bezpieczniej jest zacząć od ustalenia strategii podziału (train/validation/test, CV, schemat czasowy) i zaimplementować ją jako pierwszą część pipeline’u. Dopiero potem budować kolejne kroki feature engineeringu w taki sposób, aby operowały na wejściu już podzielonym logicznie na zbiory.

Zupełnie odwrotny porządek – najpierw agresywne „czyszczenie i wzbogacanie całego datasetu”, a dopiero potem split – niemal gwarantuje mnóstwo drobnych nieszczelności, które trudno później zdiagnozować.

Kontrola cech względem dostępności w czasie

Przy danych czasowych i sekwencyjnych każdej nowej cesze można zadać dwa pytania:

  • „Czy ta informacja była już znana w momencie, kiedy model ma wydać decyzję?”
  • „Czy sposób jej obliczania nie korzysta z przyszłych rekordów (nawet pośrednio)?”

Jeśli odpowiedź na którekolwiek pytanie brzmi „nie” lub „nie jestem pewien”, ta cecha jest kandydatem do dokładniejszej analizy lub usunięcia. W praktycznych projektach przydaje się jawny słownik cech z opisem momentu ich powstania oraz źródeł danych – nie jest to biurokracja dla samej biurokracji, tylko narzędzie do kontroli nad przeciekami.

Wykrywanie przecieków po objawach

Nie każdy przeciek da się zauważyć z samej struktury kodu. Czasami łatwiej zidentyfikować problem po symptomach w wynikach.

  • Zbyt dobre metryki – jeżeli prosty model z ograniczoną liczbą cech osiąga rezultaty bliskie „perfekcji”, to zwykle nie jest dowód genialnego algorytmu, tylko sygnał, że w danych jest ukryta informacja o celu.
  • Duża luka między walidacją a testem – gdy wynik na walidacji jest stabilnie wysoki, a na teście znacznie niższy, warto sprawdzić, czy pipeline nie wyciekł do walidacji (np. inny sposób otrzymania cech na teście).
  • Najczęściej zadawane pytania (FAQ)

    Dlaczego muszę dzielić dane na trening i test w machine learning?

    Bez podziału na trening i test widzisz jedynie, jak model radzi sobie z przykładami, które już „zna”. To tak, jakby sprawdzać wiedzę ucznia wyłącznie na tych samych zadaniach, z których się uczył. Wynik będzie z definicji zbyt optymistyczny i nie powie nic o tym, jak model zachowa się na nowych danych.

    Osobny zbiór testowy symuluje „przyszłość” – dane, których model nie widział podczas nauki i strojenia. Dzięki temu można oszacować błąd generalizacji, czyli realną jakość po wdrożeniu. Brak takiego podziału prowadzi prosto do przeceniania własnych modeli i do niemiłych niespodzianek w produkcji.

    Czym różni się zbiór treningowy, walidacyjny i testowy?

    Trening służy wyłącznie do uczenia parametrów modelu. Algorytm minimalizuje na nim funkcję straty, „patrząc” na odpowiedzi. To tu model ma prawo się dopasowywać, a nawet trochę „przekombinować”, jeśli nie ma kontroli z zewnątrz.

    Walidacja to materiał do podejmowania decyzji projektowych: wybór typu modelu, hiperparametrów, sposobu przetwarzania cech, strategii radzenia sobie z niezrównoważonymi klasami. Test natomiast pełni rolę niezależnego audytora – jest używany tylko raz, do końcowej oceny wybranego pipeline’u. Jeśli na teście też „majstrujesz” przy modelu, to w praktyce przestaje on być testem, a staje się kolejną walidacją.

    Kiedy wystarczy prosty podział train/test, a kiedy potrzebna jest walidacja krzyżowa?

    Prosty podział train/test ma sens, gdy model jest relatywnie prosty, ma mało hiperparametrów i nie planujesz długiego tuningu (np. szybki baseline regresji liniowej na dużym zbiorze). W takiej sytuacji dodatkowy podział na walidację bywa nadmiarem, o ile nie „kręcisz” przesadnie przy architekturze.

    Walidacja krzyżowa przydaje się szczególnie wtedy, gdy danych jest mało lub gdy mocno „stroisz” model (np. wiele zestawów cech, dziesiątki kombinacji hiperparametrów). Dzięki k-kształtnej walidacji wykorzystujesz dane efektywniej, a wyniki są mniej zależne od jednego przypadkowego podziału. Minusem jest większy koszt obliczeniowy i ryzyko, że mimo wszystko przestroisz się pod konkretny schemat foldów.

    Jakie proporcje podziału danych na train/validation/test są najlepsze?

    Nie ma jednego „świętego” podziału. Typowe punkty startowe to 60/20/20 lub 70/15/15. Przy bardzo dużych datasetach częsty jest też schemat 80/10/10 albo 90/5/5, bo nawet kilka procent z milionów przykładów daje bardzo solidny test.

    Przy małych zbiorach każdy procent ma znaczenie. Zbyt duży test oznacza, że model uczy się na ubogim materiale i łatwo się rozjeżdża; zbyt mały test powoduje, że metryka mocno „skacze” i trudno ocenić, czy zmiana w modelu naprawdę coś poprawiła. W praktyce wybór proporcji to kompromis między stabilnym treningiem a wiarygodną oceną – czasem lepiej zmniejszyć test, ale uzupełnić go walidacją krzyżową.

    Co to jest przeciek danych (data leakage) i jak go uniknąć?

    Przeciek danych to każda sytuacja, w której model ma dostęp – bezpośrednio lub pośrednio – do informacji z przyszłości lub z części zbioru, która powinna być „niewidoczna” podczas treningu. Klasyczny przykład to liczenie statystyk (średnich, imputacji, encodera) na całym zbiorze przed podziałem albo użycie cechy, która powstaje już po zdarzeniu, które próbujemy przewidzieć.

    Aby ograniczyć przecieki, trzeba pilnować kilku zasad:

  • wszystkie transformacje (skalowanie, imputacja, encoding) dopasowuj wyłącznie na treningu, a potem stosuj na walidacji i teście,
  • przy danych czasowych dziel dane wzdłuż osi czasu, zamiast losowo mieszać wiersze,
  • unikać cech, które zawierają informację „spoza horyzontu prognozy” (np. wynik rozliczenia kredytu przy predykcji ryzyka).

Czy overfitting dotyczy tylko skomplikowanych modeli, jak sieci neuronowe?

Nie, prosty model też potrafi się przeuczyć. Regresja liniowa z dużą liczbą cech i silnymi współliniowościami, na małej próbce, może mieć niemal perfekcyjny wynik na treningu i słaby na nowych danych. Wystarczy, że dopasuje się nie tylko do sygnału, ale też do przypadkowego szumu.

Overfitting jest bardziej kwestią relacji: pojemność modelu vs ilość i jakość danych, a nie „modnego” algorytmu. Dlatego nawet przy prostych metodach trzeba oddzielać trening od walidacji/testu i traktować świetne wyniki na zbiorze uczącym z dużą rezerwą.

Co się stanie, jeśli cały czas oceniam model na tym samym zbiorze danych?

Na początku prawdopodobnie zobaczysz realne ulepszenia. Po kilku iteracjach zaczynasz jednak optymalizować pod konkretny zestaw przykładów, outlierów i przypadkowych korelacji obecnych tylko w tej próbce. Metryki rosną, ale to często iluzja postępu – model uczy się „pod ten plik CSV”, a nie pod rzeczywisty problem.

Typowy scenariusz: model wygląda świetnie „u siebie”, ale po wdrożeniu lub na nowym, niezależnym zbiorze jakościowo się rozsypuje. Oddzielny zbiór walidacyjny, ewentualnie walidacja krzyżowa, to minimalne zabezpieczenie przed takim samooszukiwaniem się.

Najważniejsze punkty

  • Ocena modelu wyłącznie na danych uczących prowadzi niemal zawsze do zbyt optymistycznych wniosków – parametry są dostrajane „pod ten zestaw”, więc błąd na treningu jest systematycznie zaniżony względem przyszłych danych.
  • Podział na trzy zbiory (trening, walidacja, test) rozdziela role: trening uczy parametry, walidacja służy do wyboru architektury i hiperparametrów, a test jest zewnętrznym audytorem jakości całego pipeline’u.
  • Brak niezależnego zbioru walidacyjnego lub walidacji krzyżowej sprzyja iluzji postępu: kolejne „ulepszenia” są dopasowane do tego samego zestawu obserwacji, więc na nowych danych często nie działają lepiej, a czasem wręcz gorzej.
  • Różnica między wynikami na treningu, walidacji i teście niesie informację diagnostyczną: duży spadek między treningiem a walidacją/testem sugeruje overfitting modelu, a przepaść między walidacją a testem – przeuczenie się pod konkretny podział walidacyjny.
  • Overfitting nie jest zarezerwowany dla głębokich sieci; może wystąpić nawet w prostej regresji liniowej, zwłaszcza przy wielu cechach względem liczby obserwacji, silnych korelacjach między cechami lub rzadkich, nietypowych przypadkach w danych.
  • Prosty podział train/test bywa wystarczający tylko w specyficznych warunkach: duży zbiór danych, mało hiperparametrów i ograniczony tuning; w bardziej złożonych projektach brak osobnej walidacji zwykle kończy się przecenieniem jakości modelu.