ML · Chapitre 3
Machine learning 3 — Classification
Après la régression, ce chapitre aborde la seconde grande famille de problèmes d'apprentissage supervisé : la classification, où la variable cible n'est plus une quantité continue mais une étiquette discrète. Survie d'un passager du Titanic, diagnostic d'une tumeur, détection de fraude : toutes ces tâches demandent au modèle de choisir parmi un nombre fini de classes plutôt que de prédire un nombre. Cette différence de nature change profondément la manière de mesurer la performance — l'erreur quadratique n'a plus aucun sens — et invite à un autre arsenal méthodologique.
Nous partirons d'un classifieur probabiliste très simple, le Naive Bayes, qui a le mérite de rendre explicite le rôle des probabilités conditionnelles. Nous en profiterons pour introduire la batterie de métriques de classification (accuracy, précision, rappel, F1) et la matrice de confusion. Nous verrons ensuite que la frontière entre « positif » et « négatif » dépend d'un seuil de décision, ce qui mène naturellement à la courbe ROC et à l'AUC. La seconde moitié du chapitre est consacrée aux arbres de décision et à leurs variantes d'ensemble : forêts aléatoires, gradient boosting, puis ses implémentations modernes LightGBM et XGBoost.
Les expérimentations s'appuient sur deux jeux de données mini : titanic_mini (variables binaires pour illustrer les notions) et cancer_mini (variables numériques continues, pour les arbres et les ensembles). Comme dans le chapitre précédent, on commence par les imports usuels.
import numpy as np import matplotlib.pyplot as plt import pandas as pd import plotly.express as px import seaborn as sns from sklearn.metrics import mean_absolute_error, mean_squared_error, mean_absolute_percentage_error, r2_score from sklearn.model_selection import train_test_split, cross_val_score
Le jeu de données titanic_mini
Le jeu de données titanic_mini est une version volontairement allégée du célèbre dataset du Titanic. Il a été conçu pour illustrer les bases de la classification supervisée sans complexité de prétraitement : toutes les variables sont déjà encodées en valeurs binaires 0/1. On y trouve Sex (0 homme, 1 femme), FirstClass (1 si passager en première classe), Children (1 si enfant) et la cible Survived (1 si survivant).
df = pd.read_csv('/kaggle/input/datasets/pyim59/mini-datasets/titanic_mini.csv') df.head()
Cette simplicité présente un double intérêt pédagogique : elle permet de calculer à la main certaines probabilités et certains indices d'impureté, et elle se prête bien à une lecture humaine de l'arbre de décision que nous construirons plus loin.
Filtrer un DataFrame avec des conditions booléennes
Avant d'entraîner un modèle, il est instructif d'extraire empiriquement quelques probabilités de survie. Pour cela, on utilise le filtrage booléen de pandas. Une condition logique appliquée à une colonne renvoie un masque booléen, qu'on peut utiliser pour sélectionner les lignes correspondantes.
df[df["Sex"] == 1]
Pour combiner plusieurs conditions, on emploie les opérateurs bit-à-bit & (et), | (ou) et ~ (non) — surtout pas les mots-clés Python and/or, qui ne sont pas vectorisés. Chaque condition doit en outre être placée entre parenthèses, sous peine d'erreur de précédence :
df[(df["Sex"] == 1) & (df["FirstClass"] == 1)]
Le résultat reste un DataFrame, sur lequel on peut chaîner les opérations habituelles : .shape, .value_counts(), .mean(). Pour une colonne binaire codée en 0/1, la moyenne correspond directement à une proportion — autrement dit, à une probabilité empirique. La survie d'une femme en première classe se calcule par exemple ainsi :
df[(df["Sex"] == 1) & (df["FirstClass"] == 1)]["Survived"].mean()
Astuce. Sur des variables binaires,
.mean()est l'estimateur le plus rapide d'une probabilité conditionnelle. C'est ce que nous utiliserons pour comparer empiriquement les chances de survie de différents profils.
Le classifieur Naive Bayes
Le classifieur Naive Bayes est un modèle probabiliste fondé sur le théorème de Bayes. On cherche à estimer la probabilité que la classe vaille sachant les variables explicatives :
Le théorème de Bayes nous donne, à un facteur de normalisation près :
Le qualificatif « naive » vient d'une hypothèse simplificatrice : on suppose que les variables explicatives sont indépendantes conditionnellement à la classe. Cette hypothèse, presque toujours fausse en pratique, simplifie radicalement le calcul puisqu'elle factorise la vraisemblance :
La règle de décision retenue est la classe qui maximise la probabilité a posteriori, appelée estimateur MAP (Maximum A Posteriori) :
Naive Bayes Bernoulli pour des variables binaires
Quand les variables explicatives sont binaires, comme dans titanic_mini, la variante adaptée est le Bernoulli Naive Bayes. On suppose que, conditionnellement à la classe , chaque variable suit une loi de Bernoulli de paramètre :
La vraisemblance d'une observation s'écrit alors comme un produit de termes de Bernoulli :
Les paramètres sont estimés directement à partir des fréquences observées dans l'échantillon d'entraînement, en pratique avec un lissage de Laplace pour éviter les probabilités strictement nulles lorsqu'une combinaison n'apparaît jamais dans les données :
où est l'hyperparamètre de lissage (alpha dans sklearn). La valeur correspond au lissage de Laplace classique.
Implémentation avec scikit-learn
L'usage suit le schéma désormais familier fit / predict. On charge les données, on sépare entraînement et test, on instancie le modèle, on l'entraîne et on prédit.
from sklearn.naive_bayes import BernoulliNB X = df.drop(columns=['Survived']).to_numpy() y = df['Survived'].to_numpy() X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) model = BernoulliNB(alpha=1.0) model.fit(X_train, y_train) y_hat = model.predict(X_test)
À ce stade se pose une question essentielle : comment évaluer la qualité de y_hat ? Les métriques de régression vues au chapitre précédent — MAE, RMSE, — ne sont plus adaptées. Comparer numériquement deux étiquettes de classe n'a aucun sens : la « distance » entre 0 et 1 ne traduit pas une erreur graduée mais une simple inégalité. Il nous faut donc d'autres outils.
Métriques d'évaluation en classification
Accuracy et matrice de confusion
La métrique la plus immédiate est l'accuracy, ou taux de bonne classification :
Pour des classes binaires 0/1, une réécriture astucieuse permet de retrouver l'accuracy à partir de la somme des écarts absolus :
L'accuracy donne une vue d'ensemble, mais elle masque la nature des erreurs. Un modèle qui se trompe systématiquement sur la classe minoritaire peut afficher une excellente accuracy s'il y a de gros déséquilibres. C'est pourquoi on lui adjoint presque toujours une matrice de confusion :
avec vrais positifs, vrais négatifs, faux positifs, faux négatifs. Toutes les métriques qui suivent s'expriment à partir de ces quatre quantités.
Précision, rappel, F1
La précision mesure la fiabilité des prédictions positives : parmi les observations prédites comme positives, combien le sont réellement ?
Elle est cruciale lorsque les faux positifs sont coûteux — par exemple, déclarer qu'une transaction bancaire est frauduleuse alors qu'elle ne l'est pas, et bloquer le client à tort.
Le rappel (ou recall, ou sensibilité) mesure au contraire la capacité du modèle à détecter les vrais positifs : parmi les positifs réels, combien le modèle a-t-il identifiés ?
Le rappel devient prioritaire lorsque les faux négatifs sont coûteux — manquer un cancer dans un dépistage, par exemple. Précision et rappel évoluent en sens contraire : améliorer l'un dégrade généralement l'autre.
Pour disposer d'une mesure unique qui combine les deux, on calcule le F1-score, leur moyenne harmonique :
La moyenne harmonique pénalise fortement les déséquilibres : un modèle avec une précision excellente mais un rappel médiocre obtiendra un F1 modeste, ce qui est exactement le comportement souhaité.
À retenir. Accuracy globale ; précision et rappel arbitrent la nature des erreurs ; F1 résume le compromis. Dans un problème déséquilibré, regarder uniquement l'accuracy est trompeur.
Le rapport de classification
Plutôt que d'appeler chaque métrique séparément, sklearn propose classification_report, qui affiche en un coup d'œil précision, rappel, F1 et support pour chacune des classes.
from sklearn.metrics import accuracy_score, classification_report print("Accuracy =", accuracy_score(y_test, y_hat)) print(classification_report(y_test, y_hat))
Le rapport donne aussi le support (nombre d'exemples par classe), précieux pour relativiser les scores : un rappel parfait sur une classe ne représentant que cinq exemples mérite moins de confiance que sur cinq cents.
Scores de probabilité et seuil de décision
La plupart des classifieurs ne prédisent pas directement une classe : ils estiment d'abord une probabilité d'appartenance à la classe positive, puis appliquent un seuil. Pour le Titanic, le modèle calcule
et la prédiction binaire s'obtient en comparant à un seuil :
La méthode predict_proba renvoie, pour chaque observation, les probabilités estimées des deux classes. La colonne d'indice 1 correspond à la classe positive.
probas = model.predict_proba(X_test) y_score = probas[:, 1]
Par défaut, sklearn utilise , mais ce seuil est un choix, pas une vérité universelle. Le modifier permet d'arbitrer explicitement entre les deux types d'erreur :
Un seuil élevé (proche de 1) produit peu de prédictions positives : peu de faux positifs mais beaucoup de faux négatifs. Un seuil faible (proche de 0) produit beaucoup de prédictions positives : peu de faux négatifs mais davantage de faux positifs.
Concrètement, on applique un seuil avec une simple comparaison vectorielle :
t = 0.7 y_hat = (y_score >= t).astype(int)
Ici, seules les observations dont la probabilité estimée de survie atteint au moins 70 % sont classées comme survivantes. Choisir revient à régler le compromis détection / fausses alertes en fonction du coût relatif des deux erreurs dans le contexte applicatif.
Courbe ROC et AUC
Le seuil étant un degré de liberté, il est utile de caractériser le modèle indépendamment d'un choix particulier. C'est le rôle de la courbe ROC (Receiver Operating Characteristic), qui balaie tous les seuils possibles et trace pour chacun le point avec :
Le n'est rien d'autre que le rappel ; le est la proportion d'exemples négatifs classés à tort comme positifs. Les deux varient entre 0 et 1.
Lecture de la courbe
Quand décroît de 1 vers 0, la courbe parcourt l'espace depuis — aucune prédiction positive — jusqu'à — tout est prédit positif. Un classifieur aléatoire se traduit par la diagonale : il identifie autant de positifs que de négatifs à tort. Plus la courbe se rapproche du coin supérieur gauche, meilleur est le modèle, puisqu'il atteint un fort taux de vrais positifs sans payer un taux de faux positifs élevé.
AUC : Area Under the Curve
L'AUC est l'aire sous la courbe ROC. Sa valeur appartient à et possède une interprétation probabiliste élégante :
L'AUC est la probabilité qu'un exemple positif tiré au hasard reçoive un score plus élevé qu'un exemple négatif tiré au hasard.
Comme repère mental, on retient qu'une AUC de 0,5 correspond à un modèle aléatoire, qu'une valeur autour de 0,7–0,8 est correcte, qu'au-dessus de 0,8 le modèle est jugé bon, et que dépasser 0,9 signale un très bon modèle (à condition que l'évaluation soit faite sur un ensemble de test indépendant).
Calcul avec scikit-learn
La courbe ROC et l'AUC se calculent à partir des probabilités, jamais des classes prédites — c'est précisément l'intérêt de la démarche.
from sklearn.metrics import roc_curve, roc_auc_score y_score = model.predict_proba(X_test)[:, 1] fpr, tpr, thresholds = roc_curve(y_test, y_score) auc = roc_auc_score(y_test, y_score)
Les tableaux fpr et tpr permettent ensuite de tracer la courbe avec Matplotlib et d'y placer, par exemple, le point correspondant au seuil pour visualiser le compromis associé.
Arbres de décision
Les arbres de décision adoptent une logique radicalement différente : au lieu de manipuler des probabilités explicites, ils enchaînent une succession de questions binaires sur les variables explicatives. À chaque nœud, l'arbre choisit une variable, sépare le dataset en sous-ensembles, et répète récursivement le procédé. L'objectif est de produire des feuilles aussi pures que possible vis-à-vis de la cible.
L'indice de Gini
Pour mesurer la pureté d'un nœud, on utilise une fonction d'impureté. Pour une cible binaire avec , l'indice de Gini est défini par
Cet indice atteint son maximum (0,5) quand — situation où le groupe est le plus mélangé — et s'annule lorsque le groupe est pur ( ou ). C'est donc une mesure naturelle de désordre.
Choix de la coupure
À un nœud donné, contenant un sous-dataset , on évalue chaque variable candidate . Pour chacune, on partitionne selon les valeurs de , on calcule l'impureté de chaque sous-groupe, et on en déduit l'impureté pondérée après coupure :
La variable retenue est celle qui minimise . Elle devient le test du nœud, et l'arbre poursuit récursivement sur chaque branche, jusqu'à ce qu'un critère d'arrêt soit rencontré : pureté parfaite (), absence de gain, profondeur maximale atteinte (max_depth), nombre minimum d'observations dans une feuille (min_samples_leaf)...
Décision dans une feuille
Une feuille contient un sous-dataset qu'on ne sépare plus. La classe prédite est la classe majoritaire de la feuille, et la probabilité prédite la fréquence empirique de la classe positive :
C'est exactement ce que renvoie predict_proba sur un arbre sklearn.
Arbres avec scikit-learn
L'implémentation de référence est DecisionTreeClassifier. Les hyperparamètres principaux sont criterion ("gini" ou "entropy"), max_depth, min_samples_split et min_samples_leaf.
from sklearn.tree import DecisionTreeClassifier X = df.drop(columns=['Survived']) y = df['Survived'].to_numpy() X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) model = DecisionTreeClassifier(max_depth=3) model.fit(X_train, y_train) y_hat = model.predict(X_test)
Limiter la profondeur à 3 force l'arbre à rester simple et favorise la généralisation : sans contrainte, l'arbre pousse jusqu'à atteindre la pureté parfaite sur l'ensemble d'entraînement, au prix d'un sur-apprentissage spectaculaire.
L'un des grands atouts des arbres est leur interprétabilité. On peut visualiser graphiquement la structure apprise :
from sklearn.tree import plot_tree plt.figure(figsize=(14, 6)) plot_tree( model, feature_names=X.columns, class_names=["0", "1"], filled=True, rounded=True, impurity=True ) plt.show()
Chaque nœud affiche la règle de séparation, l'indice de Gini, le nombre d'observations, la répartition des classes et la classe majoritaire (codée par la couleur). Une variante textuelle hiérarchique est également disponible :
from sklearn.tree import export_text rules = export_text(model, feature_names=list(X.columns)) print(rules)
Variables binaires et seuils. Même quand les variables sont déjà binaires,
sklearnles traite comme numériques. Les seuils affichés sont alors typiquement de la forme<= 0.5, ce qui sépare simplement les valeurs 0 et 1.
Coupure d'une variable numérique : exemple sur cancer_mini
Le dataset cancer_mini, issu du Breast Cancer Wisconsin Dataset, illustre le cas des variables continues. On y trouve quatre variables explicatives numériques (radius, perimeter, area, texture) et une cible diagnosis (0 bénin, 1 malin). La variable texture mesure l'écart-type des niveaux de gris dans l'image du noyau cellulaire ; les valeurs élevées signalent des surfaces irrégulières, plus fréquentes dans les tumeurs malignes.
Sur une variable numérique, l'arbre n'utilise plus une partition par modalités mais une règle de seuil : versus , où est appris automatiquement. Concrètement, on trie les valeurs observées, on examine des seuils candidats entre deux valeurs consécutives, on calcule pour chacun l'impureté pondérée après coupure, et on retient celui qui la minimise. Le procédé est alors strictement le même que pour les variables binaires, simplement appliqué à un grand nombre de seuils candidats par variable.
df = pd.read_csv('/kaggle/input/datasets/pyim59/mini-datasets/cancer_mini.csv') X = df.drop(columns=['diagnosis']) y = df['diagnosis'].to_numpy() X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) model = DecisionTreeClassifier(max_depth=3) model.fit(X_train, y_train) y_hat = model.predict(X_test)
L'arbre obtenu reste lisible : à chaque nœud, une règle du type texture <= 19.2 sépare les noyaux relativement homogènes (à gauche, majoritairement bénins) des noyaux plus hétérogènes (à droite, fréquemment malins). C'est une logique simple, mais cliniquement cohérente.
Effet de max_depth et validation croisée
La profondeur de l'arbre est l'hyperparamètre central. Trop faible, l'arbre sous-apprend et passe à côté de structures réelles ; trop élevée, il sur-apprend et apprend par cœur les particularités du jeu d'entraînement. Pour choisir une profondeur, on évalue le modèle sur plusieurs valeurs et on retient celle qui maximise la performance estimée.
L'évaluation sur un unique split entraînement / test est bruitée. Pour stabiliser la mesure, on utilise la validation croisée, déjà introduite au chapitre 2 : on découpe les données en plis, chaque pli joue successivement le rôle de test, et on moyenne les performances. Avec cross_val_score et l'option scoring="accuracy", le balayage de max_depth se fait en quelques lignes :
cv_scores = [] for d in range(1, 30): model = DecisionTreeClassifier(max_depth=d) scores = cross_val_score(model, X, y, cv=10, scoring="accuracy") cv_scores.append(scores.mean()) plt.plot(cv_scores)
La courbe obtenue présente typiquement une montée rapide, un plateau, puis une légère dégradation : c'est la signature classique du sur-apprentissage. La valeur optimale se lit au début du plateau, là où la performance est maximale sans complexité superflue.
Forêts aléatoires
Les arbres pris isolément souffrent d'une forte variance : un changement minime dans les données peut produire un arbre très différent. Les forêts aléatoires (Breiman, 2001) corrigent ce défaut en agrégeant un grand nombre d'arbres entraînés sur des versions légèrement différentes des données. Le principe statistique sous-jacent est intuitif : la moyenne de modèles instables mais peu biaisés est typiquement plus stable et plus performante qu'un modèle unique.
Deux sources d'aléa interviennent. D'une part, chaque arbre est entraîné sur un échantillon bootstrap : un tirage avec remise de la même taille que le dataset original, où certaines observations apparaissent en double et d'autres sont absentes. D'autre part, à chaque nœud de chaque arbre, seule une sous-partie aléatoire des variables est candidate à la coupure. Cette double randomisation empêche une variable très dominante de s'imposer à toutes les racines et garantit la diversité des arbres.
Pour produire la prédiction finale, en classification, la forêt agrège les votes des arbres : la classe prédite est celle qui obtient le vote majoritaire, et predict_proba renvoie la moyenne des probabilités des arbres. En régression, elle moyenne directement les prédictions.
from sklearn.ensemble import RandomForestClassifier model = RandomForestClassifier( n_estimators=200, max_depth=5 ) model.fit(X_train, y_train) y_hat = model.predict(X_test)
Les hyperparamètres clés sont n_estimators (nombre d'arbres), max_depth, max_features (nombre de variables candidates à chaque nœud), min_samples_leaf et bootstrap. En pratique, augmenter n_estimators stabilise le modèle sans risque majeur de sur-apprentissage — les arbres restent diversifiés grâce au bootstrap. C'est max_depth qui contrôle réellement la complexité.
Les forêts aléatoires constituent souvent un excellent point de référence : elles conservent le faible biais des arbres tout en réduisant fortement leur variance, demandent peu de réglage et tolèrent bien des données hétérogènes.
Gradient Boosting
Le gradient boosting repose sur une idée différente : plutôt que d'agréger des arbres indépendants, il les construit séquentiellement, chacun corrigeant les erreurs du modèle courant. Chaque arbre n'apporte donc pas une prédiction indépendante mais une correction appliquée à la prédiction agrégée des arbres précédents.
La construction procède par améliorations successives : un premier arbre fournit une estimation initiale, les erreurs résiduelles sont analysées, un nouvel arbre est entraîné spécifiquement pour corriger ces erreurs, et la prédiction globale est ajustée en conséquence. Le processus se répète plusieurs centaines de fois, chaque étape affinant la précédente. Plutôt que de parler de « somme d'arbres », on retient l'image d'une combinaison progressive de corrections, chacune apprise par un weak learner.
Un paramètre essentiel est le learning rate, qui contrôle l'intensité de chaque correction. Un learning rate élevé accélère la convergence mais expose au sur-apprentissage ; un learning rate faible avance plus lentement mais généralise mieux. La pratique privilégie systématiquement des corrections modestes sur un grand nombre d'arbres.
Cela distingue le gradient boosting des forêts aléatoires sur deux plans :
Dans une forêt aléatoire, les arbres sont indépendants et entraînés en parallèle ; les prédictions sont combinées par moyenne ou vote. Dans le gradient boosting, les arbres sont dépendants et entraînés séquentiellement ; chaque arbre améliore la prédiction existante.
L'implémentation de base dans sklearn est GradientBoostingClassifier.
from sklearn.ensemble import GradientBoostingClassifier model = GradientBoostingClassifier( n_estimators=200, learning_rate=0.05, max_depth=5 )
Pour comparer empiriquement forêts et boosting, on exécute une validation croisée sur les deux modèles avec le même nombre de plis, et on observe à la fois la moyenne et l'écart type des accuracies. Sur des jeux tabulaires de taille modérée, les deux familles tournent généralement très près l'une de l'autre, le boosting prenant souvent un léger avantage au prix d'un réglage plus délicat.
LightGBM et XGBoost
Deux bibliothèques modernes ont popularisé des implémentations très optimisées du gradient boosting : LightGBM (Microsoft) et XGBoost. Elles dominent depuis plusieurs années les compétitions Kaggle sur données tabulaires.
LightGBM
La spécificité de LightGBM est sa croissance des arbres par feuille (leaf-wise) plutôt que par niveau. À chaque étape, il développe en priorité la feuille qui permet la plus forte réduction de la perte. Cette stratégie permet une convergence plus rapide et de bonnes performances avec moins d'arbres, au prix d'un risque accru de sur-apprentissage si la complexité n'est pas contrôlée — d'où l'importance du paramètre num_leaves.
from lightgbm import LGBMClassifier model = LGBMClassifier( n_estimators=300, learning_rate=0.05, verbosity=-1 )
Les hyperparamètres principaux sont n_estimators, learning_rate, num_leaves, max_depth, subsample (sous-échantillonnage des observations) et colsample_bytree (sous-échantillonnage des variables).
XGBoost
XGBoost (eXtreme Gradient Boosting) conserve le principe du gradient boosting et ajoute plusieurs raffinements : une régularisation explicite des arbres (paramètres reg_alpha et reg_lambda pour les pénalisations L1 et L2), une optimisation plus précise des corrections, une gestion fine de la complexité et une implémentation hautement vectorisée. Il accepte aussi l'accélération GPU via tree_method="gpu_hist", particulièrement utile sur les gros volumes.
from xgboost import XGBClassifier model = XGBClassifier( n_estimators=300, learning_rate=0.05, max_depth=3 )
Quand choisir quoi ? Les forêts aléatoires offrent un excellent compromis simplicité / performance avec très peu de réglage. Le gradient boosting (LightGBM, XGBoost) tend à être plus performant sur données tabulaires structurées, en échange d'un réglage d'hyperparamètres plus exigeant.
Exercices
Exercice — Calcul empirique de probabilités de survie
On considère le jeu de données
titanic_mini, qui contient les variables suivantes :
Sex—0: homme,1: femmeFirstClass—1: passager en 1ʳᵉ classe,0: sinonSurvived—0: n'a pas survécu,1: a survécuToutes les variables sont binaires et déjà encodées.
L'objectif de cet exercice est de calculer empiriquement, à partir des données, la probabilité de survie :
- d'une femme en 1ʳᵉ classe
- d'un homme non en 1ʳᵉ classe
À l'aide d'un filtrage par conditions booléennes avec
pandas:
- sélectionnez les passagers correspondant à chacun des deux profils
- calculez, pour chaque sous-population, la proportion de passagers ayant survécu
- comparez les deux probabilités obtenues
Indication. La variable
Survivedétant binaire (0/1), sa moyenne correspond directement à une probabilité empirique.
Exercice — Naive Bayes sur le Titanic
Prédire la survie d'un passager avec
BernoulliNB.Est-ce que les métriques de régression comme
mean_absolute_erroront un sens ici ? Comment évaluer la performance de la prédiction ?Compléter ensuite l'exercice en affichant l'accuracy et le
classification_report.
Exercice — Calculer et tracer la courbe ROC (avec AUC)
On dispose :
- des vraies étiquettes
y_test(0/1)- des probabilités de la classe positive
y_score = model.predict_proba(X_test)[:, 1]On fixe un seuil et on définit la prédiction binaire :
Travail demandé :
- Calculer à partir de
y_testet de la prédiction au seuil 0.7- Calculer aussi (utile pour situer le point sur la ROC)
- Tracer la courbe ROC à partir de
y_testety_score- Afficher la valeur de l'AUC
Rappels.
Pour calculer le nombre de vrais positifs :
np.sum((y_test == 1) & (y_hat == 1))
Exercice — Indice de Gini et choix de la racine de l'arbre
- Calculer l'indice de Gini global du dataset
titanic_mini.- Calculer l'indice de Gini après coupure pour chacune des variables :
Sex,Children,FirstClass.- Comparer les valeurs obtenues.
- En déduire la variable choisie comme racine de l'arbre.
Indication — Calcul du Gini pour
FirstClass.# FirstClass = 0 df_fc0 = df[df["FirstClass"] == 0] p_fc0 = df_fc0["Survived"].mean() gini_fc0 = 2 * p_fc0 * (1 - p_fc0) # FirstClass = 1 df_fc1 = df[df["FirstClass"] == 1] p_fc1 = df_fc1["Survived"].mean() gini_fc1 = 2 * p_fc1 * (1 - p_fc1) # Pondérations w_fc0 = len(df_fc0) / len(df) w_fc1 = len(df_fc1) / len(df) # Gini après coupure gini_firstclass = w_fc0 * gini_fc0 + w_fc1 * gini_fc1 gini_firstclassReproduire le même calcul pour
SexetChildren, comparer les trois valeurs et identifier la variable correspondant au Gini minimal.
Exercice — Arbre de décision sur
titanic_miniUtiliser un
DecisionTreeClassifiersur le datasettitanic_mini. Afficher l'arbre de décision et les règles textuelles obtenues avecexport_text.
Exercice — Arbre sur
cancer_miniUtiliser un
DecisionTreeClassifiersur le datasetcancer_mini. Afficher l'arbre de décision et les règles.
Exercice — Effet de
max_depthTester plusieurs valeurs du paramètre
max_depthsurcancer_mini. Comment varie la performance ?
Exercice —
max_depthet validation croiséeTester plusieurs valeurs du paramètre
max_depthavec une validation croiséecross_val_score. On pourra utiliser une listecv_scorespour accumuler les résultats :cv_scores = [] for ...: ... cv_scores.append(scores.mean()) plt.plot(cv_scores)
Exercice — Forêts aléatoires
Tester les forêts aléatoires sur
cancer_miniet/outitanic_mini. Comparer les performances obtenues à celles de l'arbre simple.
Exercice — Random Forest vs Gradient Boosting
Comparer les forêts aléatoires et le gradient boosting avec une validation croisée à 10 plis sur
cancer_mini. Pour chaque modèle, afficher la moyenne et l'écart type des scores.
Exercice — Ajouter LightGBM et XGBoost
Compléter la comparaison précédente en ajoutant
LGBMClassifieretXGBClassifier, toujours avec une validation croisée à 10 plis. Discuter les résultats.
Pour aller plus loin
sklearn.naive_bayes.BernoulliNBsklearn.tree.DecisionTreeClassifiersklearn.ensemble.RandomForestClassifiersklearn.ensemble.GradientBoostingClassifier- Métriques de classification scikit-learn :
accuracy_score,precision_score,recall_score,f1_score,classification_report,roc_curve,roc_auc_score. - LightGBM — documentation officielle et
LGBMClassifier. - XGBoost — documentation officielle et
XGBClassifier.