Probleme – ID3 de bout en bout
Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 26 — Apprentissage automatique
Énoncé
On reprend les dix jours de l'exercice 26.4.
- Écrire ID3 en OCaml : type de l'arbre, entropie, gain, construction.
- Dérouler l'algorithme et donner l'arbre complet.
- Cet arbre commet zéro erreur sur les dix jours. Est-ce une bonne nouvelle ?
- Que se passerait-il si l'un des attributs était le numéro du jour, traité par des tests de seuil ?
Corrigé
1. Le code. Il tient en trente lignes, et il s'exécute.
type arbre = Feuille of bool | Noeud of arbre * int * arbre
(* Noeud (branche « test faux », indice du test, branche « test vrai ») *)
type exemple = { nom : string; att : bool array; eti : bool }
(* Entropie des étiquettes de ex. Vaut 0 sur l'ensemble vide (convention
0 log 0 = 0). Complexité : Theta(|ex|). *)
let entropie ex =
let n = List.length ex in
if n = 0 then 0.0
else begin
let p = float_of_int (List.length (List.filter (fun e -> e.eti) ex))
/. float_of_int n in
let terme q = if q <= 0.0 then 0.0 else -. q *. (log q /. log 2.0) in
terme p +. terme (1.0 -. p)
end
let separer ex t = List.partition (fun e -> e.att.(t)) ex
(* Gain d'information du test t sur ex. Complexité : Theta(|ex|). *)
let gain ex t =
let (oui, non) = separer ex t in
let n = float_of_int (List.length ex) in
entropie ex
-. float_of_int (List.length oui) /. n *. entropie oui
-. float_of_int (List.length non) /. n *. entropie non
let majoritaire ex =
let o = List.length (List.filter (fun e -> e.eti) ex) in
2 * o >= List.length ex
let homogene ex = match ex with
| [] -> true
| e :: r -> List.for_all (fun f -> f.eti = e.eti) r
(* Arbre de décision d'ID3. Précondition : ex non vide.
Terminaison : la liste tests décroît strictement à chaque appel.
Complexité : O(|tests|² · |ex|) sur cette version. *)
let rec id3 ex tests =
if homogene ex || tests = [] then Feuille (majoritaire ex)
else begin
let meilleur = List.fold_left
(fun a t -> if gain ex t > gain ex a then t else a)
(List.hd tests) tests in
let (oui, non) = separer ex meilleur in
let reste = List.filter (fun t -> t <> meilleur) tests in
Noeud (id3 non reste, meilleur, id3 oui reste)
end
Terminaison : le variant est la longueur de tests, qui perd une unité à chaque appel récursif et reste positive ; la récursion a donc au plus niveaux. Correction partielle : par construction, chaque feuille porte l'étiquette majoritaire d'un sous-ensemble homogène — donc exacte — ou d'un sous-ensemble qu'aucun test ne peut plus séparer.
2. L'arbre. L'exécution donne , puis les gains (couvert), (vent), (humide) ; la racine est donc « couvert ? ». La branche « non » est homogène : feuille « oui ». La branche « oui » compte cinq jours ( oui, non, ), où « vent fort » a un gain de — il sépare parfaitement — contre pour « humide ». D'où :
couvert ?
oui : vent fort ?
oui : -> non
non : -> oui
non : -> oui
C'est exactement l'arbre de la figure du cours, et il commet erreur sur les dix jours.
3. Zéro erreur n'est pas une bonne nouvelle en soi. C'est le chiffre mesuré sur les données qui ont servi à construire l'arbre : par l'exercice 26.10, un modèle assez souple l'obtient toujours. Ici, l'arbre n'a que trois feuilles pour dix exemples, donc il résume vraiment — mais rien ne le prouve tant qu'on n'a pas testé sur des jours nouveaux. La seule mesure honnête est celle du problème 26.4.
4. Le numéro du jour, et le piège des seuils. Un attribut numérique se teste par « ? », avec pris parmi les milieux entre valeurs consécutives. Sur le numéro de jour, le test « numéro ? » sépare exactement les cinq premiers jours des cinq derniers, c'est-à-dire ici les « oui » des « non » sauf : son gain est très élevé, et ID3 le préférerait au bon test. L'arbre obtenu classerait parfaitement les dix jours et serait inutilisable sur le onzième, dont le numéro n'a jamais été vu.
La leçon, et elle vaut au-delà d'ID3 : un attribut qui identifie les exemples obtient toujours le meilleur gain. Il ne prédit rien, il mémorise. On l'écarte à la main, ou l'on pénalise les tests trop fins — mais aucune formule ne remplace la question « cet attribut sera-t-il disponible, et signifiant, sur une donnée nouvelle ? ».
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.