Drzewo decyzyjne a hiperparametry

We wpisie o drzewach decyzyjnych poznaliśmy ich ideę oraz sposób budowania. Ale czy to już wszystko, co musimy wiedzieć o drzewach decyzyjnych? Nie, celowo pominąłem tam jeden element, który zasługuje na osobny wpis. Tym elementem jest, a raczej są, hiperparametry.

Hiper… co?

Hiperparametry to takie specjalne parametry. W programowaniu przyjęło się, że parametry to zmienne przekazywane do funkcji. Dzięki takim zmiennym funkcja może zachowywać się w różny sposób. No i właśnie w taki sposób będziemy używać tych specjalnych parametrów. Będziemy zmieniać zachowanie funkcji w taki sposób, żeby uzyskać lepsze wyniki.

Ale dlaczego Hiper – parametry?

No właśnie, dlaczego na te specjalne parametry mówimy hiperparametry, a nie po prostu parametry. Na pierwszy rzut oka faktycznie wydaje się to nie mieć za dużo sensu. Okazuje się jednak, że wyróżnienie tych parametrów może być przydatne. Zacznijmy więc od przyjrzenia się naszemu procesowi. Przygotowujemy dane – oczyszczamy je i przekształcamy. Następnie dzielimy je na dwie zmienne. Zmienną zawierającą wszystkie cechy (X) i na zmienną zawierającą informację, którą chcemy przewidzieć (y). Wrzucamy te dwie zmienne do funkcji modelującej i otrzymujemy model. Funkcją modelującą może być na przykład DecisionTreeClassifier, która w wyniku da nam wytrenowane drzewo decyzyjne. No i to drzewo będzie miało swoje parametry – cechy i wartości, które dokonują podziału w poszczególnych zbiorach. Nasza funkcja je wyznaczyła i wystarczy, że sobie je zapamiętamy i już znamy cały model.

A co jeśli chcemy zmienić sposób działania funkcji DecisionTreeClassifier? Po spojrzeniu do dokumentacji widzimy, że mamy tam około trzynastu parametrów. Każdym z nich możemy wpływać na to, jak ta funkcja się zachowuje. Niektóre są czysto techniczne (np. random_state) a niektóre nie. Niektóre z nich wpływają na cały proces na tyle znacząco, że spowodują stworzenie całkowicie odmiennych rezultatów, nawet przy małej zmianie. No i te parametry to właśnie wspomniane hiperparametry.

Drzewo decyzyjne – jakie hiperparametry tam mamy?

Drzewa decyzyjne to dość popularny typ modelu. Idea ta została użyta w wielu bibliotekach w wielu językach programowania. Ja jestem fanem Pythona i do tego artykułu wybrałem funkcje DecisionTreeClassifier i DecisionTreeRegressor z modułu Scikit-Learn. Tak jak wspomniałem wcześniej, mają one po kilkanaście (hiper)parametrów, które możemy swobodnie zmieniać. Wszystkie one mają ustawione wartości domyślne, możliwe jest więc używanie tych funkcji bez zaprzątania sobie głowy parametrami. Jednakże uzyskane w ten sposób wyniki prawdopodobnie będą znacznie gorsze, od tego, co możemy uzyskać dzięki zmianom wartości hiperparametrów. Przyjrzyjmy się więc jakie hiperparametry będą dla nas najważniejsze.

criterion

Hiperparametr criterion mówi nam o tym, jakie kryterium zostanie zastosowane przy dokonywaniu optymalnego podziału. W funkcji DecisionTreeClassifier domyślnie jest to gini (Gini impurity). Warto również sprawdzić entropy, czyli entropię, o której pisałem wcześniej. W funkcji DecisionTreeRegressor mamy natomiast do wyboru mse, friedman_mse i mae. Raczej nie ma sensu testowanie tutaj czegoś innego niż domyślne mse.

max_depth

Hiperparametr max_depth blokuje maksymalną głębokość drzewa. Domyślnie ustawiony jest na None, co oznacza, że budowane drzewo nie będzie miało ograniczenia co do głębokości. Nie ma to za wiele sensu, co pokażę w przyszłych artykułach. Okazuje się bowiem, że zablokowanie rozrostu drzewa może dawać bardziej precyzyjny model, który lepiej uogólnia rozwiązanie problemu. Parametr ten przyjmuje liczby naturalne i najmniejszą liczbą, jaką może przyjąć to 1. Oznacza to, że drzewo będzie miało tylko jeden poziom – zostanie dokonany tylko jeden podział. Trudno powiedzieć, jaka byłaby optymalna maksymalna liczba, którą można tutaj wstawić. Sugeruję sprawdzać kolejne liczby naturalne do 10. A jeśli okaże się, że optimum jest przy 10, to sugeruję nawet rozszerzyć to sprawdzenie o kolejne liczby. Możemy tak rozszerzać, aż okaże się, że większe liczby w tym hiperparametrze dają gorsze wyniki.

min_samples_split

Hiperparametr min_samples_split mówi nam o minimalnej liczbie obserwacji w liściu potrzebnej do dokonania podziału. Ustawiony jest domyślnie na 2 – potrzebujemy mieć co najmniej dwie obserwacje w liściu, żeby móc dokonać jego dalszego podziału. Jeśli mamy mniej obserwacji niż podana liczba to liść zostanie taki jaki jest. Nietrudno zauważyć, że wartość 2 jest minimalną możliwą ilością obserwacji, żeby w ogóle myśleć o podziale. Wartość ta nie jest więc wartością blokującą rozrost drzewa. Każda kolejna liczba naturalna będzie już wprowadzać ograniczenia. Podobnie jak w przypadku max_depth, wprowadzenie takiego ograniczenia może dawać lepsze wyniki. Sugeruję sprawdzić tutaj wartości do 20. Chyba że wartość krańcowa będzie dawać najlepsze wyniki – wtedy warto sprawdzić większe liczby.

min_samples_leaf

Hiperparametr min_samples_leaf odpowiada za minimalną wielkość liścia po podziale. Jest on bardzo podobny do hiperparametru min_samples_split. Jednakże chodzi w nim o minimalną liczność obserwacji po podziale. Możemy sobie na przykład ustawić w nim wartość 10. Co się wydarzy, jeśli w zbiorze mamy 30 elementów i podział zaproponuje utworzenie dwóch podzbiorów z 25 i 5 elementami? Taki podział nie nastąpi, mimo iż mogliśmy ustawić min_samples_split na 2. W tym wypadku nie ograniczyliśmy podziału licznością zbioru bazowego, ale licznością w nowych zbiorach. Domyślną wartością tego hiperparametru jest 1, co pozwala algorytmowi tworzyć nowe liście o liczności 1. Nie jest to więc ograniczeniem. Jeżeli zażądamy jednak, aby liście, które powstaną miały większą ilość obserwacji, to część podziałów może nie zaistnieć. Być może uda nam się w ten sposób stworzyć drzewo, które będzie lepiej uogólniać. Sugerowany zakres do sprawdzenia – do 20. Chyba że wynik przy wartości brzegowej będzie najlepszy, wtedy więcej.

Hiperparametry – druga właściwość

Jak pewnie zauważyłeś drogi czytelniku, przy doborze hiperparametrów nie sugerowałem posługiwania się jakimś algorytmem. Nie podałem też żadnych zasad dotyczących typowania tych wartości. Sugerowałem natomiast sprawdzenie różnych wartości w celu znalezienia tych, które dadzą najlepsze wyniki. I właśnie ten problem opisuje drugą właściwość hiperparametrów – nie da się ich wyznaczyć na bazie działania funkcji modelującej. Funkcja modelująca nie będzie próbowała ich wyznaczyć, zda się na wartości, które zostały podane przy jej wywołaniu.

Jak więc rozwiązać ten problem? Na samym początku musimy się zastanowić, które hiperparametry będą nas interesować. Musimy się zastanowić dobrze, gdyż do wyboru mamy wszystkie parametry funkcji. Jak już się zastanowimy, jakie hiperparametry będą nas interesować, to musimy zastanowić się nad ich wartościami. Gdy będziemy mieli już wybrane hiperparametry i ich możliwe wartości to tworzymy pętlę, która stworzy nam wszystkie możliwe kombinacje ich wartości. Wewnątrz tej pętli uruchomimy funkcję modelującą i oceniającą model zbudowany przy jej pomocy. Zapisujemy sobie wyniki dla poszczególnych kombinacji i gdy je wszystkie sprawdzimy, oceniamy która kombinacja była najlepsze. I tyle. A może, aż tyle?

Sprawdźmy, ile kombinacji musimy sprawdzić zgodnie z moimi sugestiami. W funkcji DecisionTreeRegressor:

  • max_depth – od 1 do 10 (10 wartości)
  • min_samples_split – od 2 do 20 (19 wartości)
  • min_samples_leaf – od 1 do 20 (20 wartości)

Daje nam to 10 * 19 * 20, czyli 3800 kombinacji. Czyli tyle modeli do sprawdzenia. Brzmi strasznie. A spójrzmy na funkcję DecisionTreeClassifier:

  • max_depth – od 1 do 10 (10 wartości)
  • min_samples_split – od 2 do 20 (19 wartości)
  • min_samples_leaf – od 1 do 20 (20 wartości)
  • criterion – entropy, gini (2 wartości)

Tutaj z kolei mamy 10 * 19 * 20 * 2, czyli 7600 modeli do sprawdzenia.

Każdy z tych modeli musi zostać wytrenowany. Niektóre będą się trenować krócej (np. mała wartość max_depth), niektóre dłużej (np. duża wartość max_depth). Widzimy więc, że dobre zastanowienie się nad potencjalnymi wartościami hiperparametrów może mieć znaczenie dla długości całego procesu modelowania. W przyszłych artykułach pokażę jaki wpływ na wyniki na konkretnym zbiorze danych mają te hiperparametry.

Tuning hiperparametrów

Koncepcja, którą tutaj zakreśliłem, jest częścią tak zwanego procesu tuningu hiperparametrów. Jest on o tyle ważny, że nie ma złotego środka, który mówi jak się za niego zabrać. Bardzo dużo w tym procesie zależy od wielkości zbioru danych. Ważny jest też czas, jaki możemy poświęcić na tuning. Istotne mogą też być funkcje modelujące, które są używane (różne wersje tego samego algorytmu mogą mieć więcej lub mniej hiperparametrów i możliwych ich wartości). Proces taki należy przeprowadzić możliwie rzetelnie, gdyż od niego będą zależały wyniki uzyskiwane przez modele. Różny może też być czas, który będziemy poświęcać na ich potencjalne ponowne trenowanie i używanie.

Dodatek

Opisany powyżej przykład jest szerzej przeze mnie omówiony w kursie Wystartuj z Data Science w Pythonie! który jest dostępny na platformie Udemy. Pod tym linkiem jest dostępny notebook którego tam użyłem. Jeśli chciałbyś zakupić dostęp do tego kursu sprawdź tę stronę – może akurat opublikowałem tam kupony zniżkowe 🙂

2 myśli na temat “Drzewo decyzyjne a hiperparametry

    1. I tutaj jest mały problem. W moim doświadczeniu nie znalazłem przykładu, w którym drzewa decyzyjne są najlepszym rozwiązaniem. Natomiast często zaczynam od drzew decyzyjnych, żeby zapoznać się ze zbiorem danych. Wiem, że chyba wszystkie funkcje używane w uczeniu maszynowym mają coś takiego jak feature importance, ale tutaj mam to w formie drzewa decyzyjnego. Łatwiej mi to sobie ułożyć w głowie. Są też sytuacje, gdzie zmierzone wartości mogą mieć niepewność pomiarową. I czasem może ona się różnić dla tej samej cechy w zależności czy jesteśmy np. w okolicach 0,0001 czy 1000. Wtedy też takie drzewo decyzyjne może nam wskazać miejsce, w którym warto by było zainwestować w proces, który będzie miał większą dokładność.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *