12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
Zeste d…
BIBLIOTHÈQUE
BIBLIOTHÈQUE
TRIBUNE
TRIBUNE
FORUM
FORUM
Connexion
Rechercher
+
Une première version : ID3
Auteur :
Bermudes
Catégorie : Programmation et algorithmique
Dernière mise à jour jeudi 15 mars 2018 à 17h22
Inscription
Licence CC BY-SA
python mathématiques
algorithmique
Comprendre le concept
Une amélioration : C 4.5
Dans cette partie, nous allons voir le principe de l’algorithme ID3 à l’aide d’un exemple. Ensuite je vous donnerai le pseudo-
code an de nous lancer dans une implémentation en Python3.
L'algorithme ID3
Une implémentation
L'algorithme ID3
C’est, pour commencer, cet algorithme qui va être expliqué. ID3 veut dire Iterative Dichotomiser 3. Il va être expliqué à l’aide
d’un premier exemple, puis l’algorithme sera explicité, et enn quelques améliorations possibles seront expliquées.
1. Exemple de playTennis
Voici l’exemple utilisé par Quinlan en personne pour expliquer son algorithme dans le magazine Machine Learning.
L’algorithme ID3 part d’un tableau (de manière plus générale, d’un set, ensemble en anglais) d’exemples étiquetés duquel va
découler un arbre qui pourra prédire les étiquettes de nouveaux exemples non étiquetés donnés par après. Comme vous
pouvez le voir, ID3 est bien un algorithme d’apprentissage automatique vu qu’il est bien question d’exemples, d’étiquettes,
d’attributs, de classication, etc. et c’est bien un arbre de décision parce que… parce que c’est un arbre pardi !
1.1. Tableau de valeurs
Voici le tableau contenant les informations nécessaires à l’explication de l’algorithme tel qu’expliqué par Ross Quinlan.
Jour
Attributs des exemples
Classe
Prévisions
Température Humidité
Vent
1
2
3
4
Ensoleillé
Chaud
Élevée
Faible
Non
Ensoleillé
Chaud
Élevée
Fort
Non
Nuageux
Chaud
Élevée
Faible
Oui
Pluvieux
Moyen
Élevée
Faible
Oui
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
1/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
Jour
Attributs des exemples
Classe
5
6
7
8
9
10
11
12
13
14
Prévisions
Température Humidité
Vent
Pluvieux
Frais
Normale
Faible
Oui
Pluvieux
Frais
Normale
Fort
Non
Nuageux
Frais
Normale
Fort
Oui
Ensoleillé
Moyen
Élevée
Faible
Non
Ensoleillé
Frais
Normale
Faible
Oui
Pluvieux
Moyen
Normale
Faible
Oui
Ensoleillé
Moyen
Normale
Fort
Nuageux
Moyen
Élevée
Fort
Oui
Oui
Nuageux
Chaud
Normale
Faible
Oui
Pluvieux
Moyen
Élevée
Fort
Non
Ensemble dʼexemples pour playTennis
Prévisions, Température, Humidité et Vent sont les quatre attributs qui déterminent chacun des exemples qui vont être
fournis. On peut voir qu’il existe uniquement 36 exemples diérents pour cette conguration d’attributs :
#{Ensoleillé, Nuageux, Pluvieux} × #{Chaud, Moyen, Frais} × #{Élevée, Normale} × #{Faible, Fort} = 3 × 3 × 2 × 2 = 9 × 4 = 36
1.2. Arbre complet et optimisé généré par lʼalgorithme
Voici ce à quoi on devrait arriver à la n de ce chapitre : un arbre tout beau tout propre généré sur base du tableau que je viens
de vous donner. Si vous testez chaque branche avec la méthodologie expliquée au-dessus (§1.2.), vous vous apercevrez que
l’arbre classe tous les exemples sans faute.
lʼarbre généré par ID3 de lʼexemple playTennis (Fig. 3)
2. Explication de lʼalgorithme
L’algorithme ID3 se base sur le concept d’attributs et de classe de l’apprentissage automatique (sur classication discrète ).
3
Cet algorithme recherche l’attribut le plus pertinent à tester pour que l’arbre soit le plus court et optimisé possible.
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
2/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
Pour trouver l’attribut à tester, Quinlan parle d’entropie. Pour dénir l’entropie, il faut d’abord rappeler que la quantité
minimale de données redondantes à ajouter pour qu’un message ayant une probabilité p d’arriver sans être corrompu est
− log2(p). Une telle nécessité de stocker des données redondantes est assez évidente dans le cas de codes correcteurs d’erreurs
par exemple . Ne vous laissez pas avoir par le - devant le log : le logarithme d’un nombre compris entre 0 et 1 est toujours
1
négatif. Il faut donc prendre son opposé avec le -. Rappelons également que l’entropie est la longueur minimale nécessaire
pour coder la classe d’un membre pris au hasard dans le set d’exemples S. C’est en réalité de l’entropie de Shannon qu’il est
question. On l’appelle ainsi car c’est une notion trouvée par l’ingénieur américain Claude Shannon.
2
Entropie(S) =
∑
c ∈ classes ( S )
− pc × log2(pc)
Tel que pc est la proportion d’exemples de S ayant pour classe résultante c. Dans l’exemple d’au-dessus, les seules classes
possibles sont Oui et Non. donc l’entropie vaut
− pOui × log2(pOui) − pNon × log2(pNon)
D’ailleurs voici à quoi ressemble la fonction d’entropie pour un ensemble à deux classes possibles (Fig. 4)
le graphique dʼentropie de Shannon pour un ensemble nʼayant que deux étiquettes (Fig. 4)
Remarquons quelque chose d’intéressant : cette fonction est parfaitement symétrique en prenant l’axe x =
1
2
. Et pour cause : la
fonction d’entropie représente le "désordre" au sein du set (dans notre cas, ou du message dans le cas du code correcteur
d’erreurs). Donc qu’il y ait 5 éléments d’une classe et 3 d’une autre ou inversement, le "désordre" reste le même car les
proportions restent inchangées. On peut également voir trois points intéressants sur ce graphique (il y a en réalité une
innité de points intéressants pour ceux comme moi à qui ce graphe parle mais bon mon but est de ne pas vous erayer donc
nous allons nous intéresser qu’à ces trois là maintenant
). Ces trois points sont les points d’abscisse 0, 0.5 et 1. Vous voyez
où je veux en venir ? Si l’entropie représente le désordre du set, c’est logique que le set ait l’entropie la plus élevée quand il est
le plus désordonné. Et quand est-ce qu’il est le plus désordonné ? Quand il a autant d’objets de la première classe que d’objets
de la deuxième classe. Logique non ? Et inversement : quand le set a une entropie nulle (qui vaut 0), c’est parce que c’est à ce
moment que le désordre est le moins élevé. Et quand est-ce que le désordre est le moins élevé ? Quand tous les objets sont de
la même classe. Donc quand la probabilité est soit minimum (0) soit maximum (1).
}
Initialement, l’algorithme prend tout le set S = J1, J2, J3, . . . , J14 . Et comme 9 des 14 exemples donnent la réponse (ou classe)
{
Oui et 5 sur 14 donnent la réponse (ou classe) Non,
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
3/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
pOui =
9
14
pNon =
5
14
On peut donc calculer que
4
Entropie(S) = −
(
Publicité
9
14
)
× log2
(
9
14
)
−
(
5
14
)
× log2
(
5
14
)
= 0.94
Il faut savoir que, comme on peut le voir sur la Fig. 4,
∀S : 0 ≤ Entropie(S) ≤ 1
Ce qui veut dire que pour tout ensemble S, le désordre de S est toujours compris entre 0 et 1.
Maintenant que nous savons que l’entropie initiale du set est de 0.94, il nous faut savoir quel attribut tester en premier, puis
en second, …, puis en n-ième.
Pour savoir quel attribut tester, il faut connaître la notion de gain d’entropie. Le gain est déni par un set d’exemples et par
un attribut. Cette formule va donc servir à calculer ce que cet attribut apporte au désordre du set. Plus un attribut contribue au
désordre, plus il est important de le tester pour séparer le set en plus petits sets ayant une entropie moins élevée.
Gain(S, A) = Entropie(S) −
∑
v ∈ valeurs ( A )
| Sv |
| S |
× Entropie(Sv)
L’attribut qui va être testé à ce nœud de l’arbre est le nœud qui va le plus réduire l’entropie. C’est logique : quand l’entropie
vaut zéro, c’est qu’il n’y a qu’une seule classe représentée :
− 1 × log2(1) − n × (0 × log2(0)) = 0 − n × 0 = 0
Ici, j’ai mis un n en évidence pour dire qu’il peut y avoir autant de classes que l’on veut. Mais si la probabilité d’une
classe est 1, on a beau avoir 150 classes, leur probabilité sera nulle à chaque fois. On peut donc mettre le nombre de
classes en évidence. Mais attention, si je mets n en évidence, c’est parce qu’il y a n classes de probabilité nulle, mais
il y a aussi une classe de probabilité 1. Il y a donc n+1 classes diérentes.
Toujours dans notre exemple du point 2, en considérant S comme le set initial, pour déterminer l’attribut à tester, il faut
calculer le gain de tous les attributs :
2.1. Calcul de gain dʼentropie
2.1.1. Prévisions
L’attribut Prévisions a trois valeurs possibles : {Ensoleillé, Nuageux, Pluvieux}.
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
4/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
Gain(S, Prévisions) = Entropie(S)
= 0.94
−
−
−
−
−
−
5
14
4
14
5
14
5
14
4
14
5
14
× Entropie(SEnsoleillé)
× Entropie(SNuageux)
× Entropie(SPluvieux)
× −
× −
× −
(
(
(
3
5
0
4
3
5
× log2
× log2
× log2
3
5
0
( )
( )
( )
3
5
4
−
−
−
2
5
4
4
2
5
× log2
× log2
× log2
5
4
2
( ))
( ))
( ))
5
2
4
= 0.94
= 0.24742
− 0.357 × (0.97)
− 0.286 × (0)
− 0.357 × (0.97)
2.1.2. Vent
L’attribut Vent a quant à lui deux valeurs possibles : {Faible, Fort}.
Gain(S, Vent) = Entropie(S)
−
−
8
14
6
14
× −
(
(
6
8
3
6
× log2
× log2
8
6
( )
( )
6
3
−
−
2
8
3
6
× log2
× log2
2
8
( ))
( ))
3
6
× −
= 0.94
= 0.048
− 0.571 × (0.811)
− 0.428 × 1
2.1.3. Humidité
Humidité a également deux valeurs possibles : {Élevée, Normale}.
Gain(S, Humidité) = Entropie(S)
−
−
7
14
7
14
× −
(
(
4
7
6
7
× log2
× log2
4
7
( )
( )
6
7
−
−
3
7
1
7
× log2
× log2
3
7
( ))
( ))
1
7
× −
= 0.94
= 0.153
Publicité
− 0.49
− 0.296
2.1.4. Température
Température a trois valeurs diérentes possibles : {Chaud, Chaud, Frais}.
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
5/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
Gain(S, Température) = Entropie(S)
−
−
−
4
14
6
14
4
14
× −
× −
× −
(
(
(
2
4
4
6
3
4
× log2
× log2
× log2
2
4
4
( )
( )
( )
6
4
3
−
−
−
2
4
2
6
1
4
× log2
× log2
× log2
2
4
2
( ))
( ))
( ))
4
6
1
= 0.94
= 0.028
− 0.286
− 0.394
− 0.232
Récapitulons : Gain(S, Température) < Gain(S, Vent) < Gain(S, Humidité) < Gain(S, Prévisions). On peut voir que le plus grand gain est pour
Prévisions. C’est donc Prévisions qui est le premier attribut testé dans l’arbre. Si on regarde chaque nœud ls, on remarque
que pour le nœud Nuageux, tous les résultats sont positifs. Il n’y a donc pas d’attribut à tester ici, on peut directement
étiqueter Oui. Voici à quoi ressemble notre arbre.
arbre à la première itération de sa réalisation (Fig. 5)
Image perdue
J’ai utilisé une notation entre crochets de la forme [n+, m-]. Cette notation veut dire que dans l’ensemble ayant n+m
éléments, n sont positifs et m sont négatifs. Dans notre cas, les positifs sont les exemples étiquetés Oui et les
négatifs sont ceux étiquetés Non bien entendu.
Il faut maintenant continuer à mettre des nœuds après Ensoleillé et Pluvieux car tous les exemples ne donnent pas le même
résultat. Déterminons donc pour Ensoleillé quel est le meilleur attribut à tester en utilisant à nouveau le gain. Cependant il
n’est plus utile de tester le gain de Prévisions étant donné qu’il vient d’être utilisé. Je vous donne juste les résultats, je vous
laisse faire les calculs vous-même pour vous entrainer.
Rappel : pour ceux n’ayant pas de fonctionnalité log2 sur leur calculatrice :
log2(f(x)) =
log(f(x))
log(2)
Voici donc ce que vous devez normalement obtenir :
Gain(SEnsoleillé, Température) = 0.571
Gain(SEnsoleillé, Humidité) = 0.971
Gain(SEnsoleillé, Vent) = 0.019
Donc récapitulons à nouveau :
Gain(SEnsoleillé, Vent) < Gain(SEnsoleillé, Température) < Gain(SEnsoleillé, Humidité)
Le plus grand gain est pour Humidité. D’ailleurs on peut voir que le gain est égal à l’entropie de SEnsoleillé. Ça veut dire que tous
les ls de Humidité donneront une étiquette. Voilà notre arbre jusqu’à présent. Il nous reste à continuer l’arbre du côté de
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
6/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
Pluvieux. Voici les gains pour les diérents attributs (à nouveau, il n’est pas nécessaire de tester Prévisions qui vient de l’être
mais il faut tester Humidité qui n’a pas été testé de ce côté-là de la branche) :
Gain(SPluvieux, Température) = 0.019
Gain(SPluvieux, Humidité) = 0.019
Gain(SPluvieux, Vent) = 0.971
Récapitulons une fois de plus :
Gain(SPluvieux, Température) ≤ Gain(SPluvieux, Humidité) < Gain(SPluvieux, Vent)
Le plus grand gain est à nouveau de 0.971 et est pour Vent. Il nous faut donc tester Vent et vu que le gain est égal à l’entropie
de SPluvieux, chaque nœud ls de Vent sera une étiquette.
Voici donc notre arbre. Il n’y a plus rien à faire étant donné qu’il ne reste aucun nœud qui n’est pas étiqueté. Le travail est
donc accompli et voilà l’arbre que nous avons généré.
lʼarbre généré par ID3 de lʼexemple playTennis (Fig. 6)
Si vous comparez l’arbre que nous venons de générer avec l’arbre que je vous ai donné en début de tutoriel, vous pouvez
constater que c’est exactement le même. L’arbre est optimisé car il donne une étiquette en maximum deux tests alors qu’il y a
4 attributs diérents.
Vous ne voyez peut-être pas l’utilité de cet arbre parce qu’ici il y a maximum 36 combinaisons des attributs et 14 nous sont
déjà données. Mais imaginez maintenant que vous voulez faire un arbre qui va déterminer un diagnostic pour des malades.
Comme il existe des milliers de maladies et encore plus de symptômes, l’arbre sera plus dicile à faire à la main. Ceci dit, tant
que tous les exemples possibles n’ont pas été donnés il est possible que l’arbre contienne des erreurs. Mais nous y
reviendrons plus tard.
3. Algorithme et pseudo-code de lʼalgorithme ID3
Voici l’algorithme de génération d’un arbre de décision selon ID3 sous forme de pseudo-code. Ce chapitre est pour les
programmeurs. Si vous êtes uniquement venus pour les maths, ce chapitre ne vous concerne pas directement et vous n’êtes
pas obligés de lire ce qui suit.
Je ne vais pas détailler le pseudo-code, ça devrait sembler relativement clair. Et si vous avez tout de même besoin d’aide à la
compréhension, regardez dans les commentaires, peut-être trouverez-vous votre réponse. Et si vous ne l’y trouvez pas,
posez votre question sur les forums, c’est à ça qu’ils servent.
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
7/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
FONCTION ID3
Entrée :
- exemples = liste d'exemples étiquetés
- questions = liste des attributs non utilisés jusqu'à présent
Retour :
- un nœud
DEBUT
SI exemples est vide, alors
Finir la fonction sans construire de nœud
FIN SI
SI tous les exemples sont la même classe, alors
Retourner une feuille ayant cette classe
FIN SI
SI questions est vide, alors
Retourner une feuille avec la classe la plus fréquente
FIN SI
q = attribut optimal (avec le plus grand gain d'entropie)
n = nouveau nœud créé qui testera l'attribut q
POUR CHAQUE v = valeur possible de q, FAIRE
e = l'ensemble des éléments de exemples ayant v comme valeur à l'attribut q
chaque nœud fils de n est créé par ID3(e, questions\{q})
FIN POUR CHAQUE
Retourner n
FIN
Notez que si la condition SI questions est vide est remplie, ça veut dire que deux exemples identiques mais étiquetés
diéremment ont été mis dans le set d’exemples de test.
4. Améliorations
Ici sont présentées d’abord les améliorations que l’on peut apporter aux notions vues au (§2.).
5
Commençons par la fonction d’Entropie. Une petite étude mathématique nous montre que la probabilité d’une classe dans un
set n’est autre que la longueur du sous-set de cette classe divisée par la longueur du set initial. Donc
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
8/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
Entropie(S) = ∑
− pc × log2(pc)
c ∈ classes ( S )
= ∑
c ∈ classes ( S )
−
| c |
| S |
× log2
(
| c |
| S |
)
= −
= −
Publicité
= −
= −
= −
= −
1
| S |
1
| S |
1
| S |
1
| S |
1
| S |
1
| S |
∑
c ∈ classes ( S )
∑
c ∈ classes ( S )
| c | × log2
(
| c |
| S |
)
| c | × (log2 | c | − log2 | S | )
(
(
(
∑
c ∈ classes ( S )
∑
c ∈ classes ( S )
∑
c ∈ classes ( S )
| c | × log2 | c | − ∑
| c | × log2 | S |
c ∈ classes ( S )
)
| c | × log2 | c | − log2 | S | ∑
c ∈ classes ( S )
| c |
)
| c | × log2 | c | − | S | × log2 | S |
)
∑
c ∈ classes ( S )
| c | × log2 | c | +
| S |
| S |
× log2 | S |
= 1 × log2 | S | −
= log2 | S | −
| c | × log2 | c |
1
| S |
∑
c ∈ classes ( S )
∑c ∈ classes ( S ) | c | × log2 | c |
| S |
Alors cette petite démonstration peut vous paraître rude, mais elle est très bien car elle nous permet de faire beaucoup moins
de calculs. C’est très pratique tant pour programmer (car le programme sera plus rapide) que pour faire les calculs de tête
(vous risquez beaucoup moins de faire des erreurs). On a donc simplié notre splendide fonction d’entropie et on a gardé un
indice sommatoire tout de même. Ouf !
Quid de la fonction de \text{Gain}(S, A) ?
Cette fonction varie entre 0 et \text{Entropie}(S) vu que :
0 < \displaystyle \sum_{v\ \in\ \text{valeurs}(A)}\frac{|S_v|}{|S|} \times \text{Entropie}(S_v) < \text{Entropie}(S)
Quand la somme vaut 0, \text{Gain}(S, A) vaut \text{Entropie}(S) ce qui est son maximum. C’est donc cet attribut qui va être
choisi. Alors que si la somme vaut \text{Entropie}(S), gain vaut 0 et donc cet attribut n’est pas intéressant à tester à ce
moment vu qu’il ne réduit pas l’entropie (qui est, rappelons-le, le désordre des classications du set). La seule chose qui nous
intéresse dans cette fonction est donc la somme qui doit être la plus petite possible (il nous faut avoir le moins de désordre).
On peut donc changer la fonction comme ceci :
\text{Perte}(S, A) = \sum_{v\ \in\ \text{valeurs}(A)}\frac{|S_v|}{|S|} \times \text{Entropie}(S_v) = \frac{1}{|S|} \times \displaystyle
\sum_{v\ \in\ \text{valeurs}(A)}|S_v| \times \text{Entropie}(S_v)
Sauf que je viens de vous montrer comment améliorer la fonction d’entropie. On peut donc encore simplier la fonction de
gain !
\begin{aligned} \text{Perte}(S, A) &= \frac{1}{|S|} \times \sum_{v \, \in \, \text{valeurs}(A)}|S_v| \times Entropie(S_v) \\ &= \frac{1}{|S|}
\times \sum_{v \, \in \, \text{valeurs}(A)}|S_v| \times \le(\log_2|S_v| - \frac{1}{|S_v|} \times \sum_{c \, \in \, \text{classes}(S_v)}|c|
\times \log_2|c|\right) \\ &= \frac{1}{|S|} \times \le(\sum_{v \, \in \, \text{valeurs}(A)}|S_v| \times \log_2|S_v| - \sum_{v \, \in \,
\text{valeurs}(A)}\frac{|S_v|}{|S_v|} \times \sum_{c \, \in \, \text{classes}(S_v)}|c| \times \log_2|c|\right) \\ &= \frac{1}{|S|} \times
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
9/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
\le(\sum_{v \, \in \, \text{valeurs}(A)} |S_v|\times \log_2|S_v| - \sum_{v \in \text{valeurs}(A)} \sum_{c \, \in \, \text{classes}(S_v)} |c|
\times \log_2|c|\right) \end{aligned}
Je ne pense pas que cette amélioration-ci ait réellement une inuence en termes de performances lors d’un programme mais
c’est plus facile de faire les calculs à la main avec cette formule. La double somme peut faire un petit peu peur, mais ce n’est
pas bien compliqué : ça veut dire qu’on fait la somme sur tous les v, et que pour chaque v, on ajoute la somme sur tous les c.
PS : faites bien attention, ici c’est bien un indice sommatoire sur c dans les classes de S_v et plus juste dans S. La raison est
bien simple, c’est ce qui est expliqué juste au-dessus, c’est lié à la double somme.
Ces améliorations ne changent rien à l’algorithme. Ce sont juste de légères optimisations pour éviter de trop calculer. C’est
pour ceux qui programment leur algorithme mais également pour ceux qui font leurs calculs sur papier et à la calculette (ou
pour les plus balèzes, qui font les calculs de logarithmes avec leurs tables) et qui ne veulent pas tout le temps calculer les
mêmes choses ce qui augmente la probabilité d’erreurs.
Une implémentation
5. Implémentation
Je vais vous proposer une implémentation détaillée en python3. Je vas vous guider étape par étape dans la réalisation de ce
programme. Et pour ceux qui ne savent pas vraiment programmer ce n’est pas grave, le code complet sera mis à la n.
Le code que je vous propose est une implémentation que j’ai choisie et que j’ai développée. Elle n’est pas la plus
performante mais les choix sont avant tout pédagogiques.
Le code qui suit est pseudo-orienté objet (dans la mesures de ce que Python permet), il vous faut être relativement à
l’aise avec cette notion pour pouvoir le comprendre !
5.1. Les types de données nécessaires
Commençons par déterminer de quelles structures de données nous allons avoir besoin. Voici ce que je propose, je vous laisse
le lire, puis j’expliquerai.
1
2
3
classe Nœud:
- enfants: dict
- attribut_testé: str
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
10/24
1. Pour plus d’informations, c’est ici et plus largement ici ou encore par ici. ↩2. Plus de détails : ici. ↩3. ici, la classication discrète s’oppose à la classication continue. Les mathématiques discrètes concernent les opérationssur des ensembles nis (ex : les possibilités d’un jet de dé : \left\{1, 2, 3, 4, 5, 6\right\}) alors que les mathématiquescontinues concernent les opérations sur des ensembles innis (ex : \mathbb R) ↩4. j’ai tendance à arrondir à la seconde décimale histoire que tout le monde ait une idée de l’ordre de grandeur pour pouvoirfaire des comparaisons. Mais pour une question de lisibilité, je préfère mettre le symbole = que le symbole \simeq. Lavaleur ici n’est donc pas précisément 0.94 mais s’en rapproche assez fort. On peut donc se permettre uneapproximation. ↩5. ce chapitre n’est pas indispensable à la lecture. C’est un développement mathématique sur les logarithmes qui permet desimplier l’écriture et/ou le sens des formules. Vous pouvez soit l’ignorer et passer à la suite, soit regarder à quellesformules on arrive sans regarder le reste, soit suivre toute la pseudo-démonstration (qui s’apparente plus à undéveloppement et à des substitutions). Je vous conseille de faire ce que votre cerveau pourra tolérer en terme de maths, jene vous en voudrai pas si vous ne le lisez même pas ;) ↩12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
classe Feuille:
- étiquette: str
classe Exemple:
- étiquette: str
- attributs: dict
classe Ensemble:
- noms_attributs: list de str
- liste_exemples: list de Exemple
classe Arbre_ID3:
- arbre: Nœud/Feuille
- ensemble: Ensemble
Voilà le moment où je dois vous l’expliquer. J’ai donc fait un découpage en classes. Peut-être auriez-vous fait autrement mais
voilà, c’est ce que j’ai choisi.
Pourquoi ces classes là ?
Bon pour commencer, laissez-moi expliquer la classe Arbre_ID3. Cette classe contient deux informations importantes :
l’arbre en lui-même ;
et l’ensemble de données sur lequel il est construit.
Cet ensemble, il est de classe Ensemble (oui je sais c’est pas très imaginatif mais au moins, c’est clair
). La classe Ensemble
quant à elle contient également deux informations mais ce ne sont pas du tout les mêmes :
le nom des attributs qui sont à utiliser ;
et les exemples.
Le nom des attributs est stocké dans une liste (qui est un type interne à Python ) et les exemples sont également stockés dans
1
une liste. Mais que contiennent ces listes me direz-vous !
… Mais que contiennent ces listes ?
Merci de poser la question, ça me permet de vous expliquer la classe Exemple ! La première liste, le nom des attributs ne
contient que des chaines de caractères, ou des Strings. Ce type s’appelle str en Python. Par contre, la seconde liste, celle qui
contient les exemples contient des… Exemples ! donc des objets de la classe Exemple.
Cette classe Exemple contient deux informations importantes :
l’étiquette de l’exemple ;
et ses attributs.
L’étiquette est une str alors que les attributs sont un dictionnaire ayant pour clef le nom de l’attribut et comme valeur la
valeur de l’attribut. Logique, non ?
Je crois qu’on en a ni pour vous expliquer ce qui était contenu dans la variable ensemble de la classe Arbre_ID3… Mais il
nous reste la variable arbre !
Cette variable arbre est soit un objet de type Feuille soit un objet de type Nœud. Étant donné que la notion d’arbre est un
prérequis pour ce cours, je considère que vous connaissez ces deux termes et je ne m’attarderai pas dessus.
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
11/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
La classe Feuille ne contient qu’un élément de type str . C’est vrai que ce n’était pas obligatoire d’en faire une classe, mais je
préférais pour une raison de clarté.
La classe Nœud quant à elle contient la valeur de l’attribut testé dans un str et un dictionnaire de Nœuds. Eectivement,
c’est bien le principe : pour faire un arbre n-aire, il nous faut pouvoir mettre autant de ls que possible à chaque nœud.
Nous avons maintenant fait le tour des variables, nous pouvons passer à la phase de développement du code, du vrai !
5.2. Le code le vrai
La première chose que doit faire notre code, c’est savoir sur quelles données il va travailler. On pourrait soit rentrer toutes nos
données de la classe Ensemble à la main, mais j’ai le sentiment que ça va être long, fastidieux et inutile… Je vous propose donc
de créer une fonction qui va récupérer des données dans un chier !
5.2.1. implémentation des types de données basiques
Cette fonction est une méthode de la classe Ensemble. Pourquoi ? C’est simple : on veut faire une fonction qui va remplir notre
classe Ensemble et donc créer et modier ses éléments. Cette méthode est donc dans notre classe Ensemble. Je propose même
que ça fasse partie de notre constructeur ! je vais tout de même laisser la possibilité de créer un Ensemble simple, vous
comprendrez plus tard pourquoi.
Voici donc le début de notre classe Ensemble qui contient un constructeur et une fonction qui nous permet d’alléger le code
du constructeur.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Publicité
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Ensemble:
"""
Un ensemble contient deux valeurs :
- les noms des attributs (list)
- les exemples (list)
"""
def __init__(self, chemin=""):
"""
chemin est l'emplacement du fichier contenant les données.
Cette variable peut être non précisée en quel cas les variables
seront initialisées comme des listes vides.
"""
#Python est un langage à typage dynamique fort,
#il faut donc vérifier que l'utilisateur ne fait pas n'importe quoi
#en passant autre chose qu'un str
if not isinstance(chemin, str):
raise TypeError("chemin doit être un str et non {}" \
.format(type(chemin)))
if chemin == "":
#initialisation en listes vides
self.liste_attributs = list()
self.liste_exemples = list()
else:
with open(chemin, 'r') as fichier:
#on stocke chaque mot de la première ligne dans liste_attributs
self.liste_attributs = \
fichier.readline().lower().strip().split(' ')
#ensuite on stocke la liste d'exemples dans liste_exemples
self.liste_exemples = self.liste_en_exemples(
fichier.read().strip().lower().split('\n'),
self.liste_attributs
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
12/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
)
@staticmethod #fonction statique car ne dépend pas de l'objet mais est commune à toute instan
def liste_en_exemples(exemples, noms_attributs):
"""
retourne une liste d'exemples sur base d'une liste de str contenant
les valeurs et d'une liste de str contenant les noms des attributs
"""
#on initialise la liste à retourner
ret = list()
for ligne in exemples:
#on stocke chaque mot de la ligne dans une liste attributs
attributs = ligne.lower().strip().split(' ')
#met l'étiquette par défaut si elle n'est pas dans la ligne
etiquette = attributs[-1] if len(attributs) != len(noms_attributs) \
#on ajoute un objet de type Exemple contenant la ligne
ret.append(Exemple(noms_attributs,
else ""
attributs[:len(noms_attributs)],
etiquette))
return ret
Voici comment nous allons coder le constructeur d’Exemple : cette fonction doit prendre en paramètres la liste contenant le
nom des attributs, puis la liste contenant la valeur de chaque attribut, puis l’étiquette.
Voilà donc le début de notre classe Exemple :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Exemple:
"""
Un exemple contient 2 valeurs :
- un dictionnaire d'attributs (dict)
- une étiquette (str)
"""
def __init__(self, noms_attributs, valeurs_attributs, etiquette=""):
"""
etiquette peut être non précisée en quel cas on aurait
un exemple non étiqueté
"""
#si on a un problème de types
if not isinstance(noms_attributs, list) \
or not isinstance(valeurs_attributs, list):
raise TypeError("noms_attributs et valeurs_attributs doivent être" \
" des listes et pas des {0} et {1}" \
.format(type(noms_attributs),
type(valeurs_attributs)))
if not isinstance(etiquette, str):
raise TypeError("etiquette doit être un str et pas un {}" \
.format(type(etiquette)))
#si les deux listes n'ont pas le même nombre d'éléments
if len(valeurs_attributs) != len(noms_attributs):
raise ValueError("noms_attributs et valeurs_attributs doivent " \
"avoir le même nombre d'éléments")
self.etiquette = etiquette
https://zestedesavoir.com/tutoriels/962/les-arbres-de-decisions/premiere-version-id13/#1-10802_lalgorithme-id3
13/24
12/11/21, 5:46 PM
Une première version : ID3 - Les arbres de décisions • Tutoriels • Zeste de Savoir
28
29
30
31
self.dict_attributs = dict()
#on ajoute chaque attribut au dictionnaire
for i in range(len(noms_attributs)):
self.dict_attributs[noms_attributs[i]] = valeurs_attributs[i]
Lançons-nous maintenant dans la création de l’arbre en lui-même. Il nous faut donc manipuler la classe Arbre_ID3, et donc,
la créer ! Le constructeur doit prendre une variable, à savoir le chemin vers le chier de données à envoyer au constructeur de
sa variable ensemble :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Arbre_ID3:
"""
Un arbre ID3 contient deux valeurs :
- un ensemble d'exemples (Ensemble)
- un arbre (Noeud)
"""
def __init__(self, chemin=""):
"""
chemin est l'emplacement du fichier contenant les données
"""
#initialisation de l'ensemble avec le fichier dans chemin
self.ensemble = Ensemble(chemin)
#initialisation du noeud principal de l'arbre
self.arbre = None
Voilà la base de notre classe. Sauf que jusqu’ici, on ne sait qu’initialiser les données, nous ce qu’on veut faire, c’est bien plus !
Il nous faut donc pouvoir créer l’arbre. Voici la méthode de Arbre_ID3 que je vous propose :
1
2
3
4
5
def construire(self):
"""
génère l'arbre sur base de l'ensemble pré-chargé
"""
self.arbre = self.__construire_arbre(self.ensemble)
Alors, vous aimez ?
Bon d’accord, je le reconnais, je n’ai pas codé la réalisation de l’arbre… Mais c’est fait exprès ! J’ai créé une petite fonction
toute simple à appeler par l’utilisateur pour que l’arbre se crée. Cette fonction appelle la fonction privée
Publicité
__construi...