Programmation pour l’IA
scikit-learn pour les arbres de décision :
Pour tester nos algorithmes, on va s’intéresser à un jeu de données classique pour les arbres de décision : le jeu de données IRIS. Ce jeu de données contient des exemples d’iris classés en trois espèces : les iris setosa, versicolor et virginica. Les attributs qui sont utilisés dans le jeu de données sont : la longueur et largeur des pétales, la longeur et la largeur des sépales.
On peut trouver une version du jeu de données au format ARFF sur OpenML, ou au format CSV. Il existe de nombreux formats de fichiers pour échanger des jeux de données (ARFF, CSV, svmlight etc.). La plupart des librairies pour l’apprentissage vous permettra d’importer sans trop de problème les données depuis différents formats dans leur format local. Il faut cependant garder à l’esprit que la constitution de tels jeux de données et leur normalisation est en général un travail fastidieux mais nécessaire pour l’apprentissage.
Assurez-vous d’avoir installé la librairie
scikit-learn.
iris.csv.numpy.loadtxt
ou pandas.read_csv
et pandas.DataFrame.to_numpy
pour charger le fichier. On créera les tableaux numpy
suivants :
feature_names de shape (4,) qui contiendra
le nom de chaque feature, data de shape (N,4)
où N est le nombre d’exemple dans le fichier et qui
contiendra les valeurs.classes de shape (N,)qui contiendra les
classes (dans le sens oĂą classes[i]est la classe du point
data[i]). Pour faciliter l’utilisation avec
scikit-learn, on renommera les classes avec des entiers :
{"Virginica" : 0, "Versicolor": 1, "Setosa": 2}.cname = ["Virginica", "Versicolor", "Setosa"].On pourra utiliser les paramètres skiprows,
usecols, max_rows de
numpy.loadtxt ou des manipulations classiques des
dataframes.
matplotlib, visualisez le jeu de données.
On pourra par exemple afficher, en nuage de points comme ci-dessous (on
pourra utiliser c=classes pour avoir des couleurs qui
dépendent des classes) :
Nous allons désormais trouver un arbre de décision pour expliquer les données. On importera les fonctions nécessaires avec :
from sklearn.tree import DecisionTreeClassifier, plot_treeOn crée un arbre de décision avec
clf = DecisionTreeClassifier()Et on l’entraînera sur les exemples avec fit. On lui
passera comme argument un tableau contenant les données et un tableau
contenant les classes :
clf.fit(donnees, classes)Enfin, on affichera l’arbre calculé avec la fonction
plot_tree. noms_attributs est un tableau
contenant le nom des attributs et noms_classes est un
tableau contenant le nom des classes, ce qui permet d’avoir un affichage
un peu plus lisible :
plt.figure(figsize=(20,20)) # zoom pour rendre l'arbre plus lisible
# changer en fonction de la taille de votre écran.
plot_tree(clf, feature_names=noms_attributs, class_names=noms_classes, filled=True, rounded=True)
plt.show()clf.predict(t) pour trouver
la classe de plusieurs point d’entrée (t est un tableau de vecteurs
d’attributs, par exemple [[10,4,5,0.1], [12,1,5,1]]).Scikit Learn nous permet facilement de créer ce genre de partition.
On utilisera pour cela la fonction train_test_split :
from sklearn.model_selection import train_test_split
donnees_train, donnees_test, classes_train, classes_test = train_test_split(donnees, classes, test_size=0.2, random_state=52)Cela permet de créer une partition des données et de leur classe en
une partie apprentissage et une partie entrainement. Dans l’exemple
ci-dessus, on prélève 20% des données pour faire nos tests.
random_state fixe la graine, ce qui permet de reproduire
les résultats.
Entraînez de nouveau un arbre de décision sur 80% des données et affichez-le.
Calculez la précision de votre modèle, c’est-à -dire le nombre
d’exemples tests bien classés divisé par le nombre total d’exemples
tests. On remarquera que clf.predict peut prendre un
tableau de données et renvoyer un tableau de décisions.
On peut calculer la précision d’un modèle avec la fonction suivante :
from sklearn import metrics
accuracy = metrics.accuracy_score(classe_test, classe_predites)Dans cet exercice, on va implémenter nous-mêmes un algorithme pour
trouver un bon arbre de décision. On va implémenter une version simple
de l’algorithme CART. On va se limiter au cas où les
données sont de la forme \((x_0,\dots,x_k)\) avec \(x_i\) un float et les
decisions prises dans chaque nœud sont de la forme \(x_i < v\) pour une certaine valeur \(v\).
decide(data: np.array, classes: np.array, i : int, v : float)
qui prend un ensemble de données data avec leur classe
classes, et renvoie deux tableaux numpy
(qdata, qclasses) tels que qdata[0] est le
sous-ensemble de data oĂą \(x_i
\geq v\) et qdata[1] est le sous-ensemble de
data oĂą \(x_i < v\).
Les classes de data[0] seront qclasses[0] et
celles de data[1] seront qclasses[1]. On
rappelle qu’on peut écrire data[:,i] qui est un tableau tel
que data[:,i][j] = data[j][i].On veut trouver la décision de la forme \(x_i < v\) la plus “clivante”, c’est-à -dire, celle qui sépare le mieux les classes. Pour cela, on utilise une fonction \(F\) qui estime le degré de mélange, comme l’entropie ou la fonction de Gini vu en cours. On estime le gain d’un test \(x_i < v\) comme
\[F(D) - |D_0| \cdot F(D_0) - |D_1| \cdot F(D_1)\]
où \(D_0\) est l’ensemble des éléments de \(D\) qui ne satisfont pas le test et \(D_1\) l’ensemble des éléments de \(D\) qui satisfont le test.
gini(data: np.array, classes: np.array) qui estime le degré
de mélange du jeu de donnéesgain(data: np.array, classes: np.array, i: int, v : float)
qui estime le gain du test \(x_i <
v\) pour la fonction de mélange gini.Écrire une fonction
maxgain(data: np.array, classes: np.array) qui renvoie un
couple (i,v) maximisant le gain possible parmis tous les
tests \(x_j < w\) possible. Pour
cela, on remarquera que pour chaque i, on a un nombre
restreint de valeurs Ă tester (bonus : rĂ©flĂ©chir Ă
différentes façons de tester cela).
On va représenter un arbre de décision comme suit:
class LeafNode(DecisionNode):
def __init__(self, value):
self.value = value
class InternalNode(DecisionNode):
def __init__(self, i, v, left, right):
self.i = feature_index
self.v = threshold
self.left = left
self.right = rightCréer une récursive fonction fit(data,classes) qui crée
un arbre de décision pour les données passées en paramètre et renvoie la
racine de cet arbre. Votre fonction devra créer l’arbre ainsi :
classes ne contient qu’une seule classe
c, alors on renverra un LeafNode qui renvoie
la classe c.InternalNode qui sépare le
jeu de données en fonction de la question maximisant le gain (selon
maxgain) et construire left et
right recursivement.iris.csv. Qu’observez-vous ? Comment pourriez-vous
améliorer la précision de votre modèle ?