23 KiB
Uczenie maszynowe UMZ 2017/2018
3. Naiwny klasyfikator bayesowski, drzewa decyzyjne
Część 2
3.2. Drzewa decyzyjne
Drzewa decyzyjne – przykład
# Przydatne importy
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
import pandas
%matplotlib inline
alldata = pandas.read_csv('tennis.tsv', sep='\t')
alldata
Day | Outlook | Humidity | Wind | Play | |
---|---|---|---|---|---|
0 | 1 | Sunny | High | Weak | No |
1 | 2 | Sunny | High | Strong | No |
2 | 3 | Overcast | High | Weak | Yes |
3 | 4 | Rain | High | Weak | Yes |
4 | 5 | Rain | Normal | Weak | Yes |
5 | 6 | Rain | Normal | Strong | No |
6 | 7 | Overcast | Normal | Strong | Yes |
7 | 8 | Sunny | High | Weak | No |
8 | 9 | Sunny | Normal | Weak | Yes |
9 | 10 | Rain | Normal | Weak | Yes |
10 | 11 | Sunny | Normal | Strong | Yes |
11 | 12 | Overcast | High | Strong | Yes |
12 | 13 | Overcast | Normal | Weak | Yes |
13 | 14 | Rain | High | Strong | No |
# Dane jako lista słowników
data = alldata.T.to_dict().values()
features = ['Outlook', 'Humidity', 'Wind']
# Możliwe wartości w poszczególnych kolumnach
values = {feature: set(row[feature] for row in data)
for feature in features}
values
{'Humidity': {'High', 'Normal'}, 'Outlook': {'Overcast', 'Rain', 'Sunny'}, 'Wind': {'Strong', 'Weak'}}
- Czy John zagra w tenisa, jeżeli będzie padać, przy wysokiej wilgotności i silnym wietrze?
- Algorytm drzew decyzyjnych spróbuje _zrozumieć „taktykę” Johna.
- Wykorzystamy metodę „dziel i zwyciężaj”.
# Podziel dane
def split(features, data):
values = {feature: list(set(row[feature]
for row in data))
for feature in features}
if not features:
return data
return {val: split(features[1:],
[row for row in data
if row[features[0]] == val])
for val in values[features[0]]}
split_data = split(['Outlook'], data)
for outlook in values['Outlook']:
print('\n\tOutlook\tHumid\tWind\tPlay')
for row in split_data[outlook]:
print('Day {Day}:\t{Outlook}\t{Humidity}\t{Wind}\t{Play}'
.format(**row))
Outlook Humid Wind Play Day 3: Overcast High Weak Yes Day 7: Overcast Normal Strong Yes Day 12: Overcast High Strong Yes Day 13: Overcast Normal Weak Yes Outlook Humid Wind Play Day 1: Sunny High Weak No Day 2: Sunny High Strong No Day 8: Sunny High Weak No Day 9: Sunny Normal Weak Yes Day 11: Sunny Normal Strong Yes Outlook Humid Wind Play Day 4: Rain High Weak Yes Day 5: Rain Normal Weak Yes Day 6: Rain Normal Strong No Day 10: Rain Normal Weak Yes Day 14: Rain High Strong No
Obserwacja: John lubi grać, gdy jest pochmurnie.
W pozostałych przypadkach podzielmy dane ponownie:
split_data_sunny = split(['Outlook', 'Humidity'], data)
for humidity in values['Humidity']:
print('\n\tOutlook\tHumid\tWind\tPlay')
for row in split_data_sunny['Sunny'][humidity]:
print('Day {Day}:\t{Outlook}\t{Humidity}\t{Wind}\t{Play}'
.format(**row))
Outlook Humid Wind Play Day 1: Sunny High Weak No Day 2: Sunny High Strong No Day 8: Sunny High Weak No Outlook Humid Wind Play Day 9: Sunny Normal Weak Yes Day 11: Sunny Normal Strong Yes
split_data_rain = split(['Outlook', 'Wind'], data)
for wind in values['Wind']:
print('\n\tOutlook\tHumid\tWind\tPlay')
for row in split_data_rain['Rain'][wind]:
print('Day {Day}:\t{Outlook}\t{Humidity}\t{Wind}\t{Play}'
.format(**row))
Outlook Humid Wind Play Day 6: Rain Normal Strong No Day 14: Rain High Strong No Outlook Humid Wind Play Day 4: Rain High Weak Yes Day 5: Rain Normal Weak Yes Day 10: Rain Normal Weak Yes
- Outlook=
- Overcast
- → Playing
- Sunny
- Humidity=
- High
- → Not playing
- Normal
- → Playing
- High
- Humidity=
- Rain
- Wind=
- Weak
- → Playing
- Strong
- → Not playing
- Weak
- Wind=
- Overcast
- (9/5)
- Outlook=Overcast (4/0)
- YES
- Outlook=Sunny (2/3)
- Humidity=High (0/3)
- NO
- Humidity=Normal (2/0)
- YES
- Humidity=High (0/3)
- Outlook=Rain (3/2)
- Wind=Weak (3/0)
- YES
- Wind=Strong (0/2)
- NO
- Wind=Weak (3/0)
- Outlook=Overcast (4/0)
Algorytm ID3
- podziel(węzeł, zbiór przykładów):
- A ← najlepszy atrybut do podziału zbioru przykładów
- Dla każdej wartości atrybutu A, utwórz nowy węzeł potomny
- Podziel zbiór przykładów na podzbiory według węzłów potomnych
- Dla każdego węzła potomnego i podzbioru:
- jeżeli podzbiór jest jednolity: zakończ
- w przeciwnym przypadku: podziel(węzeł potomny, podzbiór)
Jak wybrać „najlepszy atrybut”?
- powinien zawierać jednolity podzbiór
- albo przynajmniej „w miarę jednolity”
Skąd wziąć miarę „jednolitości” podzbioru?
- miara powinna być symetryczna (4/0 vs. 0/4)
Entropia
$$ H(S) = - p_{(+)} \log p_{(+)} - p_{(-)} \log p_{(-)} $$
- $S$ – podzbiór przykładów
- $p_{(+)}$, $p_{(-)}$ – procent pozytywnych/negatywnych przykładów w $S$
Entropię można traktować jako „liczbę bitów” potrzebną do sprawdzenia, czy losowo wybrany $x \in S$ jest pozytywnym, czy negatywnym przykładem.
Entropia – przykład
- (3 TAK / 3 NIE): $$ H(S) = -\frac{3}{6} \log\frac{3}{6} - \frac{3}{6} \log\frac{3}{6} = 1 \mbox{ bit} $$
- (4 TAK / 0 NIE): $$ H(S) = -\frac{4}{4} \log\frac{4}{4} - \frac{0}{4} \log\frac{0}{4} = 0 \mbox{ bitów} $$
_Information gain
_Information gain – różnica między entropią przed podziałem a entropią po podziale (podczas podziału entropia zmienia się):
$$ \mathop{\rm Gain}(S,A) = H(S) - \sum_{V \in \mathop{\rm Values(A)}} \frac{|S_V|}{|S|} H(S_V) $$
_Information gain – przykład
$$ \mathop{\rm Gain}(S, Wind) = H(S) - \frac{8}{14} H(S_{Wind={\rm Weak}}) - \frac{6}{14} H(S_{Wind={\rm Strong}}) = \\ = 0.94 - \frac{8}{14} \cdot 0.81 - \frac{6}{14} \cdot 1.0 = 0.049 $$
- _Information gain jest całkiem sensowną heurystyką wskazującą, który atrybut jest najlepszy do dokonania podziału.
- Ale: _information gain przeszacowuje użyteczność atrybutów, które mają dużo różnych wartości.
- Przykład: gdybyśmy wybrali jako atrybut _datę, otrzymalibyśmy bardzo duży information gain, ponieważ każdy podzbiór byłby jednolity, a nie byłoby to ani trochę użyteczne!
_Information gain ratio
$$ \mathop{\rm GainRatio}(S, A) = \frac{ \mathop{\rm Gain}(S, A) }{ -\sum_{V \in \mathop{\rm Values}(A)} \frac{|S_V|}{|S|} \log\frac{|S_V|}{|S|} } $$
- _Information gain ratio może być lepszym wyborem heurystyki wskazującej najużyteczniejszy atrybut, jeżeli atrybuty mają wiele różnych wartości.
Drzewa decyzyjne a formuły logiczne
Drzewo decyzyjne można pzekształcić na formułę logiczną w postaci normalnej (DNF):
$$ Play={\rm True} \Leftrightarrow \left( Outlook={\rm Overcast} \vee \\ ( Outlook={\rm Rain} \wedge Wind={\rm Weak} ) \vee \\ ( Outlook={\rm Sunny} \wedge Humidity={\rm Normal} ) \right) $$
Klasyfikacja wieloklasowa przy użyciu drzew decyzyjnych
Algorytm przebiega analogicznie, zmienia się jedynie wzór na entropię:
$$ H(S) = -\sum_{y \in Y} p_{(y)} \log p_{(y)} $$
Skuteczność algorytmu ID3
- Przyjmujemy, że wśród danych uczących nie ma duplikatów (tj. przykładów, które mają jednakowe cechy $x$, a mimo to należą do różnych klas $y$).
- Wówczas algorytm drzew decyzyjnych zawsze znajdzie rozwiązanie, ponieważ w ostateczności będziemy mieli węzły 1-elementowe na liściach drzewa.
Nadmierne dopasowanie drzew decyzyjnych
- Zauważmy, że w miarę postępowania algorytmu dokładność przewidywań drzewa (_accuracy) liczona na zbiorze uczącym dąży do 100% (i w ostateczności osiąga 100%, nawet kosztem jednoelementowych liści).
- Takie rozwiązanie niekoniecznie jest optymalne. Dokładność na zbiorze testowym może być dużo niższa, a to oznacza nadmierne dopasowanie.
Jak zapobiec nadmiernemu dopasowaniu?
Aby zapobiegać nadmiernemu dopasowaniu drzew decyzyjnych, należy je przycinać (_pruning).
Można tego dokonywać na kilka sposobów:
- Można zatrzymywać procedurę podziału w pewnym momencie (np. kiedy podzbiory staja się zbyt małe).
- Można najpierw wykonać algorytm ID3 w całości, a następnie przyciąć drzewo, np. kierując się wynikami uzyskanymi na zbiorze walidacyjnym.
- Algorytm _sub-tree replacement pruning (algorytm zachłanny).
_Sub-tree replacement pruning
- Dla każdego węzła:
- Udaj, że usuwasz węzeł wraz z całym zaczepionym w nim poddrzewem.
- Dokonaj ewaluacji na zbiorze walidacyjnym.
- Usuń węzeł, którego usunięcie daje największą poprawę wyniku.
- Powtarzaj, dopóki usuwanie węzłów poprawia wynik.
Zalety drzew decyzyjnych
- Zasadę działania drzew decyzyjnych łatwo zrozumieć człowiekowi.
- Atrybuty, które nie wpływają na wynik, mają _gain równy 0, zatem są od razu pomijane przez algorytm.
- Po zbudowaniu, drzewo decyzyjne jest bardzo szybkim klasyfikatorem (złożoność $O(d)$, gdzie $d$ jest głębokościa dzewa).
Wady drzew decyzyjnych
- ID3 jest algorytmem zachłannym – może nie wskazać najlepszego drzewa.
- Nie da się otrzymać granic klas (_decision boundaries), które nie są równoległe do osi wykresu.
3.3. Lasy losowe
Algorytm lasów losowych – idea
- Algorytm lasów losowych jest rozwinięciem algorytmu ID3.
- Jest to bardzo wydajny algorytm klasyfikacji.
- Zamiast jednego, będziemy budować $k$ drzew.
Algorytm lasów losowych – budowa lasu
- Weź losowy podzbiór $S_r$ zbioru uczącego.
- Zbuduj pełne (tj. bez przycinania) drzewo decyzyjne dla $S_r$, używając algorytmu ID3 z następującymi modyfikacjami:
- podczas podziału używaj losowego $d$-elementowego podzbioru atrybutów,
- obliczaj _gain względem $S_r$.
- Powyższą procedurę powtórz $k$-krotnie, otrzymując $k$ drzew ($T_1, T_2, \ldots, T_k$).
Algorytm lasów losowych – predykcja
- Sklasyfikuj $x$ według każdego z drzew $T_1, T_2, \ldots, T_k$ z osobna.
- Użyj głosowania większościowego: przypisz klasę przewidzianą przez najwięcej drzew.