Luokittelu - päätöspuu

Klassinen esimerkki luokittelusta on kurjenmiekkojen (iris) luokittelu kolmeen lajiin (setosa, versicolor, virginica) terä- (petal) ja verholehtien (sepal) koon mukaan. Seuraavassa käytän luokitteluun päätöspuuta.

Päästöspuu-menetelmän idea

Päätöspuumenetelmälle annetaan parametrina puun syvyys (kuinka monta haarautumista enimmillään?).

Jokaisessa haarautumisessa algoritmi valitsee parhaiten erottelevan selittävän muuttujan ja siihen liittyvän rajakohdan.

Gini = millä todennäköisyydellä tehdään väärä luokittelu? Toimivassa päätöspuussa on haarautumisten jälkeen vain pieniä gini-arvoja.

In [1]:
import pandas as pd
import matplotlib as plt
import seaborn as sns
%matplotlib inline

#Vaikuttaa kaavioiden ulkoasuun:
sns.set()
In [2]:
#Esimerkkiaineisto löytyy seaborn-kirjastosta:
iris = sns.load_dataset('iris')
iris.head()
Out[2]:
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa
In [3]:
#Seaborn-kirjaston pairplot havainnollistaa hyvin lajin (species) riippuvuutta petal- ja sepal-mitoista:
sns.pairplot(iris, hue='species')
C:\Users\Aki\Anaconda3\lib\site-packages\scipy\stats\stats.py:1713: FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result.
  return np.add.reduce(sorted[indexer] * weights, axis=axis) / sumval
Out[3]:
<seaborn.axisgrid.PairGrid at 0x29b1c9816d8>
In [4]:
#Feature-matriisi on iris-data ilman species-muuttujaa:
X = iris.drop('species', axis=1)

#Target on species (laji)
y = iris['species']
In [5]:
#train_test_split jakaa datan opetusdataan ja testidataan (25 % datasta, jolloin toisin määrätä).
#random_state määrittää satunnaislukugeneraattorin siemenluvun. Sama siemenluku takaa saman jaottelun
#eri suorituskerroilla.

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test=train_test_split(X, y, random_state=4)
In [6]:
#Päätöspuumallin tuonti:
from sklearn import tree

#Parametrilla max_depth määrätään päätöspuun maksimi syvyys.
malli = tree.DecisionTreeClassifier(max_depth=4)
malli.fit(X_train, y_train)

#Mallin mukaisten ennusteiden laskeminen opetusdatalle ja testidatalle:
y_train_malli = malli.predict(X_train)
y_test_malli = malli.predict(X_test)
In [7]:
#Oikeaan osuneiden ennusteiden osuus opetusdatassa:

from sklearn.metrics import accuracy_score

print(accuracy_score(y_train, y_train_malli))
1.0
In [8]:
#Oikeaan osuneiden ennusteiden osuus testidatassa:

print(accuracy_score(y_test, y_test_malli))
0.9736842105263158
In [9]:
#Confusion-matriisi opetusdatalle:

from sklearn.metrics import confusion_matrix

print(confusion_matrix(y_train, y_train_malli))
[[32  0  0]
 [ 0 42  0]
 [ 0  0 38]]
In [10]:
#Confusion-matriisi testidatalle:

print(confusion_matrix(y_test, y_test_malli))
[[18  0  0]
 [ 0  7  1]
 [ 0  0 12]]

Päätäspuun graafista esittämistä varten tarvitset kahta pakettia, joita ei ole Anacondan vakioasennuksessa:

  • pydotplus
  • python-graphviz

Asennuksen voit tehdä AnacondaPrompt-komentorivillä:

  • asenna pydotplus kirjoittamalla komento conda install pydotplus (odota asentumista rauhassa ja vastaa esitettyihin kysymyksiin)
  • asenna python-graphviz kirjoittamalla komento conda install python-graphviz (odota asentumista rauhassa ja vastaa esitettyihin kysymyksiin)

Ainakin minä jouduin tämän jälkeen vielä lisäämään Windowsin path määrittelyihin C:\Users\Aki\Anaconda3\Library\bin\graphviz

Lisätietoa https://github.com/ContinuumIO/anaconda-issues/issues/1666

In [11]:
#Seuraava edellyttää pydotplus-kirjaston asentamista ja python-graphviz -apuohjelman asentamista:
import pydotplus 
from IPython.display import Image  

dot_data = tree.export_graphviz(malli, out_file=None, 
                         feature_names=X.columns,
                         class_names=['setosa', 'versicolor', 'virginica'],
                         filled=True, rounded=True,  
                         special_characters=True)  
graph = pydotplus.graph_from_dot_data(dot_data)  
Image(graph.create_png())
Out[11]:

Ensimmäinen haarautuminen tehdään petal_length-muuttujan perusteella. Jos petal_length on pienempi tai yhtäsuuri kuin 2,45, niin havainto luokitellaan setosaksi (32 kpl, gini=0). Jäljelle jää 42 versicoloria ja 38 virginicaa (gini=0,499).

Seuraava jako tehdään petal_width-muuttujan arvon 1,75 kohdalta. Toiseen haaraan jää 41 versicoloria ja 4 virginicaa (gini=0,162) ja toiseen haaraan 1 versicolor ja 34 virginicaa (gini=0,056). Neljännen jaon jälkeen päästään täydelliseen erotteluun versicolorin ja virginican välillä.

In [12]:
#Päätöspuun kuvan voi tallentaa pdf-muodossa:
graph.write_pdf("iris.pdf")
Out[12]:
True
In [13]:
#Uusi data, jota ei ole valmiiksi luokiteltu:
Xnew = pd.read_excel('http://taanila.fi/irisnew.xlsx')
Xnew
Out[13]:
sepal_length sepal_width petal_length petal_width
0 5.0 3.5 1.5 0.3
1 8.1 3.3 6.5 1.9
2 6.0 3.0 3.0 0.5
In [14]:
#Luokittelu:
malli.predict(Xnew)
Out[14]:
array(['setosa', 'virginica', 'setosa'], dtype=object)