
Scikit-learn
La bibliothèque python pour le machine learning
Cours inspiré de la formation Machine learning in Python with scikit-learn de l'Inria sur Fun-Mooc suivie en 2024
Concepts
Voici quelques définitions utiles à connaître pour la suite du cours
- Modèle prédictif (predictive model): Agorithme d'apprentissage automatique entraîné sur des données historiques ou existantes afin de prédire des résultats futurs ou inconnus.
- Modèle supervisé (supervised learning): Modèle d'apprentissage automatique entraîné sur un ensemble de données étiquetées. Une fois entraîné, il permet de prédire les résultats de nouvelles données d'entrée qui n'ont pas encore été étiquetées.
- Modèle non supervisé (unsupervised learning): Modèle d'apprentissage automatique entraîné sur un ensemble de données non étiquetées, sans indication sur les résultats souhaités. Une fois entraîné, il permet de à regrouper les données en fonction de leurs similitudes.
- Problème de classification (classification problem): Problème supervisé ayant comme objectif d'affecter une étiquette à de nouvelles observations, en se basant sur un ensemble de données étiquetées pendant la phase d'entraînement, par exemple avec des étiquettes de chien ou de chat.
- Problème de régression (regression problem): Problème supervisé ayant comme objectif de prédire une valeur de sortie cible numérique à partir de variables d'entrée, par exemple pour prédire le prix d'une maison en fonction de ses caractéristiques.
- Matrice de données (data matrix ou feature matrix): Matrice X contenant l'ensemble des variables d'entrée utilisées pour entraîner un modèle.
- Caractéristique (feature): Propriété d'une variable d'entrée. C'est une colonne de la matrice de données X.
- Observation (record ou sample): Instance ou exemple individuel décrit par un ensemble de caractéristiques. Les termes enregistrement et échantillon sont aussi employés. C'est une ligne de la matrice de données X.
- Variable cible (target variable): Variable de sortie associée à chaque observation pendant la phase d'apprentissage puis variable que l'on cherche à prédire ensuite. On parle aussi de réponse, label et étiquette.
- Etiquette (label): Valeur ou catégorie associée à une donnée d'entrée. Les étiquettes sont utilisées pour entraîner des modèles supervisés. Par exemple, dans un ensemble de données d'images de chats et de chiens étiquetées, chaque image est associée à une étiquette indiquant si elle représente un chat ou un chien.
- Vecteur cible y (target vector ou label vector): Vecteur contenant l'ensemble des des variables cibles.
- Variable numérique: Elle représente des quantités ou des valeurs qui peuvent être mesurées ou calculées. et contient des données sous forme de chiffres. Ex: Âge, salaire, prix, distance, vitesse.
- Variable numérique continue: Elle Prend n'importe quelle valeur dans un intervalle donné. Ex: taille, surface, température.
- Variable numérique discrète: Elle prend des valeurs spécifiques et finies, souvent des entiers. Ex: nombre d'enfants, nombre d'années d'études.
- Variable catégorielle (qualitative): Elle représente une catégories ou un groupe distinct. Elle peut être représentée par des étiquettes ou des noms.
- Variable catégorielle nominale: Elle n'est pas ordonnée. Ex: couleur, sexe, espèce de fleurs.
- Variable catégorielle Ordinale: Elle est ordonnée mais les intervalles ne sont pas forcément égaux. Ex: classement d'une course, niveau d'éducation.
- Surapprentissage (overtitting): Le modèle est trop complexe car il s'ajuste aux données d'entraînement en capturant le bruit et et les fluctuations aléatoires ce qui conduit à une mauvaise généralisation sur de nouvelles données. L'erreur de test est beaucoup plus importante que l'erreur d'entraînement.
- Sous-apprentissage (underfiting): Le modèle est trop simple pour capturer les tendances des données d'entrainement puis pour faire des prédictions précises.
- Erreur d'entraînement (train error): Erreur d'un modèle appliqué aux données d'entraînement.
- Erreur de test (test error): Erreur d'un modèle appliqué aux données de test.
- Biais (bias): Mesure de la précision des prédictions par rapport à la vérité. Un modèle avec un biais élevé tend à sous-apprendre car il ne capture pas suffisamment la complexité des données. Il risque de faire des erreurs de prédiction systématiques.
- Variance: Mesure la sensibilité d'un modèle aux variations des données d'entraînement. Un modèle avec une variance élevée est sujet au surapprentissage, car il s'ajuste trop étroitement aux particularités des données d'entraînement.
- Hyperparamètres: Ils contrôlent l’apprentissage, sont spécifiques à chaque modèle et peuvent-être optimisés pour chaque dataset.
- Paramètres ajustés (fitted parameters): Ils sont issus de l'entraînement et se terminent par _. Pour une régression linéaire par exemple, la pente et l'ordonnée à l'origine.
- Modèle linéaire: La relation entre les valeurs des caractéristiques et la valeur cible est linéaire, représentée par une droite. Il y a risque de surapprentissage s'il n'y a pas assez d'échantillons par rapport aux caractéristiques.
- Régularisation: Technique pour prévenir le surapprentissage en ajoutant une pénalité à la complexité du modèle. Pour un problème de régression, dans le modèle Ridge, un paramètre alpha plus grand permet d'augmenter la régularisation. Le paramètre alpha=0 nous ramène à un modèle sans régularisation. Pour un problème de classification, dans le modèle logistic regression, c'est le paramètre C qu'il faut diminuer. Le paramètre C=1 par défaut implique de la régularisation par défaut. Le paramètre C très grand nous ramène à un modèle sans régularisation.
- Génération: Fait d'utiliser le modèle entrainé sur des données d'entrainement sur d'autres données.
- Extrapolation: Fait de prédire hors de pla plage d'entrainement.
- Modèle non linéaire: La relation entre les valeurs des caractéristiques et la valeur cible est non linéaire, représentée par une courbe.
- Feature engineering non linéaire: Approche utilisée pour créer des nouvelles caractéristiques à partir de données existantes, afin de capturer des relations complexes qui ne peuvent pas être modélisées par des relations linéaires simples.
- Arbre de décision (decision tree): Modèle constitué d'une séquence de règles de décision simples. Chaque nœud (split node) représente un test sur une caractéristique et un seuil, chaque branche correspond à un résultat du test, et chaque feuille (leaf) représente une sortie, une classe ou une valeur. Il est utilisés pour la classification et la régression. Il n'y a pas besoin de scaling pour caractéristiques numériques. Il ne permet pas d'extrapoler les prédictions hors du domaine d'entrainement.
- Seuil: Valeur spécifique utilisée pour diviser les données à un noeud. Par exemple, si la caractéristique est la taille, le seuil pourrait être 160 cm. Le seuil est choisi de sorte à minimiser l'erreur.
- Racine (root): Première règle de décision
- max_depth: Les modèles decision tree n'ont pas de paramètres mais on peut optimiser les hyperaramètres. Ainsi, l'hyperparamètre max_depth définit le nombre maximal de niveaux de l'arbre ie, le nombre de noeuds optimisés de l'arbre. Il contrôle la complexité de l'arbre. Il permet de trouver le meilleur compromis (trade_off) entre sous et sur apprentissage. Il est adapté à un jeu de données symétrique.
- min_samples_leaf: Hyperaramètre appliquant une contraine aux noeuds en définissant le nombre minimum d'échantillons requis à chaque noeud. Il permet d'augmenter l'assymétrie de l'arbre.
- Ensemble: Techniques combinant plusieurs modèles (estimateurs) pour améliorer les prédictions. Il y a le bagging et le boosting.
- Boostraping: Lors de l'entraînement, création de sous-échantillons aléatoires (boostrap samples) sans remplacement à partir de 100% des données d'origine. Il y a un modèle par sous-échantillon. Les modèles apprennent en parallèle.
- Aggregating: Lors de la prédiction, regroupement de données des différents modèles sur un nouvel échantillon à partir des résutlats du bootstraping les plus probables pour améliorer les résultats.
- Bagging: Boostraping + aggregating. Cette technique améliore les prédictions car elle moyenne les prédictions et donc limite le surapprentissage. En effet, l'entrainement des arbres indépendamment favorise leur surapprentissage. Pour les modèles d'arbres, on utilise les ensembles RandomForestClassifier ou RandomForestRegressor. Pour les autres on utiliser les ensembles BaggingClassifier ou BaggingRegressor. Bagging regressor a besoin de moins d'optimisation d'hyperparamètres qu'un simple decision tree modèle.
- Boosting: Technique utilisée sur les modèles d'arbre consistant à mettre plus de poids sur certains points pour en minimiser l'erreur. Les modèles apprennent en séquence l'un après l'autre. Chaque arbre est en sous apprentissage. Le traitement séquentiel réduit le sous apprentissage mais s'il y a trop d'arbres de décisions (predictors), il peut y avoir du surapprentissage. Le paramètre learning_rate permet de le limiter. Le boosting marche un peu mieux que bagging en termes de performance et de résultats. On pourra utiliser le GradientBoosting ou l'HistGradientBoosting.
- Gradient-boosting: Entraîne le modèle de decision tree sur les erreurs résiduelles de l'arbre précédent. GradientBoostingClassifier ou GradientBoostingClassifier sont utilisés pour les petits modèles d'arbres de décision de 3 à 5 niveaux appelés shallower trees.
- HistGradientBoosting: Technique utilisant l'histogramme de données pour améliorer l'efficacité. C'est plus rapide grâce à la discrétisation des caractéristiques (binning). HistGradientBoostingClassifier ou HistGradientBoostingClassifier sont utilisés pour les gros modèles d'arbres de décision.
Modèles
KNeighborsClassifier, LogisticRegression
KNeighborsClassifier
Modèle non linéaire simple de classification basé sur l'algorithme des k-plus proches voisins (k-NN)
-
>>> from sklearn.neighbors import KNeighborsClassifier >>> model = KNeighborsClassifier(n_neighbors=50)
LogisticRegression
Modèle linéaire basé sur l'algorithme de régression logistique
-
>>> from sklearn.linear_model import LogisticRegression >>> model = LogisticRegression()
LinearRegression
Modèle linéaire basé sur l'algorithme de régression linéaire
-
>>> from sklearn.linear_model import LinearRegression >>> linear_regression = LinearRegression()
HistGradientBoosting Classifier
Modèle non linéaire complexe construit avec un ensemble d'arbres de décision pour les problèmes de classification
-
>>> from sklearn.linear_model import LogisticRegression >>> model = HistGradientBoostingClassifier()
DecisionTreeClassifier
Modèle non linéaire complexe construit avec un ensemble d'arbres de décision pour les problèmes de classification
-
>>> from sklearn.tree import DecisionTreeClassifier >>> tree = DecisionTreeClassifier(max_depth=2)
DecisionTreeRegressor
Modèle non linéaire complexe construit avec un ensemble d'arbres de décision pour les problèmes de régression
-
>>> from sklearn.tree import DecisionTreeRegressor >>> regressor = DecisionTreeRegressor(random_state=0)
RandomForestClassifier
Modèle non linéaire complexe construit avec un ensemble d'arbres de décision pour les problèmes de classification avec un traitement des données aléatoires avec la méthode de bagging
-
>>> from sklearn.ensemble import RandomForestClassifier >>> model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=0)
RandomForestRegressor
Modèle non linéaire complexe construit avec un ensemble d'arbres de décision pour les problèmes de régression avec un traitement des données aléatoires
-
>>> from sklearn.ensemble import RandomForestRegressor >>> model = RandomForestRegressor(n_estimators=100, max_depth=5, random_state=0)
GradientBoostingClassifier
Modèle non linéaire complexe construit avec un ensemble d'arbres de décision séquentiel pour les problèmes de classification permettant d'améliorer la précision des prédictions car chaque arbre corrige les erreurs des arbres précédents.
-
>>> from sklearn.ensemble import GradientBoostingClassifier >>> model = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, max_depth=3, random_state=0)
GradientBoostingRegressor
Modèle non linéaire complexe construit avec un ensemble d'arbres de décision séquentiel pour les problèmes de régression permettant d'améliorer la précision des prédictions car chaque arbre corrige les erreurs des arbres précédents.
-
>>> from sklearn.ensemble import GradientBoostingRegressor >>> model = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1, max_depth=3, random_state=0)
Données numériques
Process avec les étapes préparation, partition, scaling, entrainement, évaluation et prédiction
Préparation
Après avoir chargé le jeu de données, séparation en données et cible puis conservation des données numériques
Chargement du dataset
-
>>> import pandas as pd >>> df = pd.read_csv('my_data.csv')
Séparation en données et cible
-
>>> target_name = 'class' >>> target = df[target_name] >>> data = df.drop(columns=[target_name])
Conservation des données numériques
-
>>> from sklearn.compose import make_column_selector as selector >>> numerical_columns_selector = selector(dtype_include='number') >>> numerical_columns = numerical_columns_selector(data) >>> data_numerical = data[numerical_columns]
Partition
Partitionner le jeu de données en un jeu de données d'entraînement et de test avec la méthode train_test_split.
-
>>> from sklearn.model_selection import train_test_split >>> data_train, data_test, target_train, target_test = train_test_split(data_numerical, target, random_state=42, test_size=0.25) >>> print(f"Number of samples in testing = {data_test.shape[0]} = {data_test.shape[0] / data_numerical.shape[0] * 100:.1f}%")
Mise à l'échelle
Normalisation du jeu de données d'entraînement et de test avec la méthode fit_transform de la classe StandardScaler
La mise à l'échelle permet d'obtenir une moyenne nulle (données sont centrées autour de zéro) et une variance unitaire (dispersion des valeurs de chaque variable par rapport à sa moyenne de taille égale).
Cela garantit que chaque variable a la même importance dans l'ensemble des données et que les variables qui ont des valeurs initialement plus grandes ou plus petites ne dominent pas le processus d'apprentissage ou l'analyse des données simplement en raison de leur échelle.
Si les données d'origine suivent une distribution normale, alors la plupart des valeurs normalisées seront comprises entre -3 et 3.
-
>>> from sklearn.preprocessing import StandardScaler >>> scaler = StandardScaler().set_output(transform="pandas") >>> data_train_scaled = scaler.fit_transform(data_train) >>> data_test_scaled = scaler.transform(data_test)
Entrainement
Avec la méthode fit
-
>>> model.fit(data_train, target_train)
Evaluation
Voici 2 méthodes pour évaluer le modèle
Performance
Calcul d'un score de performance du modèle en fonction des données fournies en arguments avec la méthode score.
-
>>> accuracy = model.score(data_test, target_test) >>> print(round(accuracy, 3)) >>> print(f"Accuracy : {accuracy:.3f}") 0.818
Validation croisée
Dans l'exemple suivant, les données seront divisées en 5 parties égales (cv=5 nombre de folds ou plis) et évaluées grâce à la fonction cross_validate.
fit_time : Temps en secondes d'entraînement sur chaque itération
score_time : Temps en secondes d'évaluation sur chaque itération
test_score : Scores de performance du modèle sur chaque itération
-
>>> from sklearn.model_selection import cross_validate >>> cv_result = cross_validate(model, data_numerical, target, cv=5, return_estimator=True) >>> print(cv_result) {'fit_time': array([0.06839132, 0.06863546, 0.07640576, 0.06505561, 0.06649685]), 'score_time': array([0.01212072, 0.01221037, 0.01803184, 0.01288223, 0.01441002]), 'test_score': array([0.81216092, 0.8096018 , 0.81337019, 0.81326781, 0.82207207])} >>> scores = cv_results["test_score"] >>> print(f"Score R2 score obtenu avec validation croisée: {scores.mean():.3f} ± {scores.std():.3f}")
Prédiction
Avec la méthode predict
-
>>> target_predicted = model.predict(new_data)
Pipeline
Le pipeline permet de combiner les étapes en un flux de travail séquentiel.
Mise à l'échelle et création du modèle LogisticRegression avec la méthode make_pipeline
-
>>> from sklearn.preprocessing import StandardScaler >>> from sklearn.linear_model import LogisticRegression >>> from sklearn.model_selection import train_test_split >>> from sklearn.pipeline import make_pipeline >>> data_train, data_test, target_train, target_test = train_test_split(data_numerical, target, random_state=42) >>> model = make_pipeline(StandardScaler(), LogisticRegression()) >>> model.fit(data_train, target_train) >>> model Pipeline | StandardScaler | LogisticRegression >>> accuracy = model.score(data_test, target_test) >>> print(f"Accuracy of logistic regression: {accuracy:.2f}") 0.818
Utilisation des colonnes numériques du dataset et validation avec cross_validate
-
>>> from sklearn.preprocessing import StandardScaler >>> from sklearn.linear_model import LogisticRegression >>> from sklearn.pipeline import make_pipeline >>> from sklearn.model_selection import cross_validate >>> data_numerical = data[numerical_columns] >>> model = make_pipeline(StandardScaler(), LogisticRegression()) >>> cv_results = cross_validate(model, data_numerical, target, cv=10) >>> scores = cv_results['test_score'] >>> print(f"The mean accuracy is: {scores.mean():.3f} ± {scores.std():.3f}") The mean accuracy is: 0.892 ± 0.013
Données catégorielles
Process avec les étapes préparation, encodage
Préparation
Après avoir chargé le jeu de données, séparation en données et cible puis conservation des données catégorielles
Chargement du dataset
-
>>> import pandas as pd >>> df = pd.read_csv('my_data.csv')
Séparation en données et cible
-
>>> target_name = 'class' >>> target = df[target_name] >>> data = df.drop(columns=[target_name])
Conservation des données catégorielles
-
>>> from sklearn.compose import make_column_selector as selector >>> categorical_columns_selector = selector(dtype_include=object) >>> categorical_columns = categorical_columns_selector(data) >>> data_categorical = data[categorical_columns]
Encodage
Choix du type d'encodage en fonction des catégories ordinales ou nominales
Catégories ordinales
La classe OrdinalEncoder permet de transformer chaque catégorie en une valeur numérique basées selon son ordre.
-
>>> from sklearn.preprocessing import OrdinalEncoder >>> encoder = OrdinalEncoder().set_output(transform="pandas") >>> data_encoded = encoder.fit_transform(data_categorical)
Catégories nominales
La classe OneHotEncoder permet de transformer chaque valeur de chaque catégorie en une nouvelle catégorie avec un format binaire.
Si une ligne de données appartient à une catégorie, la colonne associée aura un 1, tandis que les autres colonnes auront des 0.
-
>>> from sklearn.preprocessing import OneHotEncoder >>> encoder = OneHotEncoder(sparse_output=False).set_output(transform="pandas") >>> data_encoded = encoder.fit_transform(data_categorical)
Pipeline
Augmentation du nombre d'itérations car pas de de scaling.
Catégories ordinales
-
>>> from sklearn.pipeline import make_pipeline >>> from sklearn.linear_model import LogisticRegression >>> model = make_pipeline(OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1), LogisticRegression(max_iter=500),)
Catégories nominales
-
>>> from sklearn.pipeline import make_pipeline >>> from sklearn.linear_model import LogisticRegression >>> model = make_pipeline(OneHotEncoder(handle_unknown="ignore"), LogisticRegression(max_iter=500))
Evaluation
-
>>> from sklearn.model_selection import cross_validate >>> cv_results = cross_validate(model, data_categorical, target) >>> scores = cv_results['test_score'] >>> print(f"The accuracy is: {scores.mean():.2f} ± {scores.std():.2f}")
Données combinées
Données numériques et catégorielles
Préparation
Après avoir chargé le jeu de données, séparation en données et cible puis conservation des données catégorielles
Chargement du dataset
-
>>> import pandas as pd >>> df = pd.read_csv('my_data.csv')
Séparation en données et cible
-
>>> target_name = 'class' >>> target = df[target_name] >>> data = df.drop(columns=[target_name])
Conservation des données numériques et des données catégorielles
-
>>> from sklearn.compose import make_column_selector as selector >>> numerical_columns_selector = selector(dtype_include='number') >>> categorical_columns_selector = selector(dtype_include=object) >>> numerical_columns = numerical_columns_selector(data) >>> categorical_columns = categorical_columns_selector(data) >>> data = data[numerical_columns + categorical_columns]
Encodage
Choix du type d'encodage
Modèle | Ordonné | Non ordonné |
---|---|---|
Tree-based | OrdinalEncoder | OrdinalEncoder |
Linéaire | OrdinalEncoder (avec prudence) | OneHotEncoder |
Pipeline
Encodage puis transformation et concaténation des colonnes avec ColumnTransformer et enfin pipeline
Modèle linéaire: Encodages StandardScaler et OneHotEncoder et modèle LogisticRegression
-
>>> from sklearn.preprocessing import OneHotEncoder, StandardScaler >>> from sklearn.compose import ColumnTransformer >>> from sklearn.pipeline import make_pipeline >>> from sklearn.linear_model import LogisticRegression >>> categorical_preprocessor = OneHotEncoder(handle_unknown="ignore") >>> numerical_preprocessor = StandardScaler() >>> preprocessor = ColumnTransformer([("one-hot-encoder", categorical_preprocessor, categorical_columns), ("standard_scaler", numerical_preprocessor, numerical_columns),]) >>> model = make_pipeline(preprocessor, LogisticRegression(max_iter=500))
Tree based model: Encodage OrdinalEncoder et modèle HistGradientBoostingClassifier
Il n'est pas utile de scaler les données numériques dans ColumnTransformer.
De plus, l'option remainder="passthrough" signifie que les colonnes non spécifiées dans la tranformation sont laissées inchangées.
Les variables catégorielles peuvent être traitées avec un OrdinalEncoder même si elles ne sont pas ordonnées.
-
>>> from sklearn.preprocessing import OrdinalEncoder >>> from sklearn.compose import ColumnTransformer >>> from sklearn.pipeline import make_pipeline >>> from sklearn.ensemble import HistGradientBoostingClassifier >>> categorical_preprocessor = OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1) >>> preprocessor = ColumnTransformer([("categorical", categorical_preprocessor, categorical_columns)], remainder="passthrough",) >>> model = make_pipeline(preprocessor, HistGradientBoostingClassifier())
Choix de l'encodeur
OrdinalEncoder pour le Tree-based model
Evaluation
-
>>> from sklearn.model_selection import cross_validate >>> cv_results = cross_validate(model, data, target, cv=5) >>> scores = cv_results['test_score'] >>> print(f"The mean accuracy is: {scores.mean():.3f} ± {scores.std():.3f}") The mean cross-validation accuracy is: 0.851 ± 0.003
Modèle de régression
Prédiction d'une valeur cible
Préparation
Utilisation du jeu de données fetch_california_housing de sickit-learn
-
>>> import pandas as pd >>> from sklearn.datasets import fetch_california_housing >>> housing = fetch_california_housing(as_frame=True) >>> data, target = housing.data, housing.target >>> target *= 100 >>> from sklearn.tree import DecisionTreeRegressor >>> regressor = DecisionTreeRegressor(random_state=0)
Entraînement
Partition en donnés de test et entrainement avec train_test_split
-
>>> from sklearn.model_selection import train_test_split >>> data_train, data_test, target_train, target_test = train_test_split(data, target, random_state=0) >>> regressor.fit(data_train, target_train)
Evaluation
Calcul de l'erreur d'entraînement et de l'erreur des tests
Utilisation de mean_absolute_error pour calculer le score
-
>>> from sklearn.metrics import mean_absolute_error >>> target_predicted = regressor.predict(data_train) >>> score = mean_absolute_error(target_train, target_predicted) >>> print(f"The training error of our model is {score:.2f} k$") >>> target_predicted = regressor.predict(data_test) >>> score = mean_absolute_error(target_test, target_predicted) >>> print(f"The testing error of our model is {score:.2f} k$")
Utilisation de ShuffleSplit et de cross_validate pour calculer le score des tests
-
>>> from sklearn.model_selection import cross_validate >>> from sklearn.model_selection import ShuffleSplit >>> import pandas as pd >>> cv = ShuffleSplit(n_splits=40, test_size=0.3, random_state=0) >>> cv_results = cross_validate(regressor, data, target, cv=cv, scoring="neg_mean_absolute_error", return_train_score=True, n_jobs=2) >>> cv_results = pd.DataFrame(cv_results) >>> cv_results["test_error"] = -cv_results["test_score"] >>> scores = pd.DataFrame() >>> scores[["train_error", "test_error"]] = -cv_results[["train_score", "test_score"]] >>> print(f"The mean cross-validated train error is:{scores['train_error'].mean():.2f} k$ ± {scores['train_error'].std():.2f} k$") >>> print(f"The mean cross-validated test error is:{scores['test_error'].mean():.2f} k$ ± {scores['test_error'].std():.2f} k$")
Graphique en diagramme des résultats de tests avec pyplot
Diagramme de l'erreur d'apprentissage et diagramme de l'erreur de test.
-
>>> import matplotlib.pyplot as plt >>> scores.plot.hist(bins=50, edgecolor="black") >>> plt.xlabel("Mean absolute error (k$)")
Courbe de validation avec ValidationCurveDisplay et variation de hyperparamètre
Pour le modèle DecisionTreeRegressor, on fait varier l'hyperparamètre max_depth.
-
>>> import numpy as np >>> max_depth = np.array([1, 5, 10, 15, 20, 25]) >>> from sklearn.model_selection import ValidationCurveDisplay >>> disp = ValidationCurveDisplay.from_estimator(regressor, data, target, param_name="max_depth", param_range=max_depth, cv=cv, scoring="neg_mean_absolute_error", negate_score=True, std_display_style="errorbar", n_jobs=2) >>> _ = disp.ax_.set(xlabel="Maximum depth of decision tree", ylabel="Mean absolute error (k$)", title="Validate curve for decision tree")
Courbe d'apprentissage avec LearningCurveDisplay et variation la taille des données d'entraînement
On utilise numpy pour faire varier la taille des des données d'entraînement de 0.1 à 1.
-
>>> import numpy as np >>> train_sizes = np.linspace(0.1, 1.0, num=5, endpoint=True) >>> from sklearn.model_selection import LearningCurveDisplay >>> display = LearningCurveDisplay.from_estimator(regressor, data, target, train_sizes=train_sizes, cv=cv, score_type='both', scoring='neg_mean_absolute_error', negate_score=True, score_name='Mean absolute error (k$)', std_display_style='errorbar', n_jobs=2) >>> _ = display.ax_.set(xscale='log', title='Learning curve for decision tree')
Validation croisée imbriquée (nested cross validation)
Avec la boucle de validation externe habituelle cv_outer (KFold ou n_splits=5 pour cv=5) puis une boucle interne cv_inner (autre KFold n_splits utilisé)
-
>>> from sklearn.model_selection import cross_validate >>> cv_results = cross_validate(model_grid_search, data, target, cv=5, n_jobs=2, return_estimator=True) >>> cv_results = pd.DataFrame(cv_results) >>> cv_test_scores = cv_results["test_score"] >>> print(f"Mean score : {cv_test_scores.mean():.3f} ± {cv_test_scores.std():.3f}") >>> for cv_fold, estimator_in_fold in enumerate(cv_results["estimator"]): print(f"Best hyperparameters for fold #{cv_fold + 1} : {estimator_in_fold.best_params_}")
Tuning
Réglage, personnalisation et optimisation des hyperparamètres
Préparation
Utilisation de données numériques du jeu adult-census avec le modèle LogisticRegression et validation croisée
-
>>> import pandas as pd >>> from sklearn.pipeline import Pipeline >>> from sklearn.preprocessing import StandardScaler >>> from sklearn.linear_model import LogisticRegression >>> from sklearn.model_selection import cross_validate >>> adult_census = pd.read_csv('adult-census.csv') >>> target_name = 'class' >>> numerical_columns = ['age', 'capital-gain', 'capital-loss', 'hours-per-week'] >>> target = adult_census[target_name] >>> data = adult_census[numerical_columns] >>> model = Pipeline(steps=[("preprocessor", StandardScaler()), ("classifier", LogisticRegression()),]) >>> cv_results = cross_validate(model, data, target) >>> scores = cv_results['test_score'] >>> print(f"The mean accuracy is: {scores.mean():.3f} ± {scores.std():.3f}") The mean accuracy is: 0.800 ± 0.003
Hyperparamètres
Lister les hyperparamètres, afficher et modifier leur valeur
Lister les hyperparamètres avec la méthode get_params
Dans notre cas, les hyperparamètres s'écrivent classifier__hyperparametre
-
>>> for parameter in model.get_params(): print(parameter) ... preprocessor__with_std classifier__C classifier__class_weight ...
Afficher la valeur d'un hyperparamètre
hyperparamètre C
-
>>> model.get_params()["classifier__C"] 1.0
Modifier la valeur d'un hyperparamètre avec la méthode set_params
Dans l'exemple suivant, on fixe la valeur de C à 0.001
-
>>> model.set_params(classifier__C=1e-3) >>> cv_results = cross_validate(model, data, target) >>> scores = cv_results['test_score'] >>> print(f"The mean accuracy is: {scores.mean():.3f} ± {scores.std():.3f}") The mean accuracy is: 0.787 ± 0.002
Boucler sur une liste de valeurs d'un hyperparamètre
Dans l'exemple suivant, on fait varier C de 0.001 à 10
-
>>> for C in [1e-3, 1e-2, 1e-1, 1, 10]: model.set_params(classifier__C=C) cv_results = cross_validate(model, data, target) scores = cv_results['test_score'] print(f"The mean accuracy with C={C} is: {scores.mean():.3f} ± {scores.std():.3f}") The mean accuracy with C=0.001 is: 0.787 ± 0.002 The mean accuracy with C=0.01 is: 0.799 ± 0.003 The mean accuracy with C=0.1 is: 0.800 ± 0.003 The mean accuracy with C=1 is: 0.800 ± 0.003 The mean accuracy with C=10 is: 0.800 ± 0.003
Réglage avec Grid-Search CV
Evaluation de toutes les combinaisons possibles d'hyperparamètres contenus dans une grille à l'aide de la validation croisée et sélection de celle qui donne les meilleures résultats. Cette méthode est utilisée pour 2 hyperparametres maximum.
Remarque: Dans l'exemple suivant, nous considérons des données combinées et nous utilisons le modèle HistGradientBoostingClassifier (arbres de décision) donc nous n'avons pas besoin de faire de scaling pour les valeurs numériques et nous choisissons OrdinalEncoder pour les valeurs catégorielles même si elles sont non ordonnées.
Chargement du dataset adult-census.csv
-
>>> import pandas as pd >>> adult_census = pd.read_csv('adult-census.csv')
Séparation en données et cible
-
>>> target_name = 'class' >>> target = adult_census[target_name] >>> data = adult_census.drop(columns=[target_name, 'education-num'])
Partition en donnés de test et d'entrainement
Avec la fonction train_test_split
-
>>> from sklearn.model_selection import train_test_split >>> data_train, data_test, target_train, target_test = train_test_split(data, target, random_state=42)
Sélections des données catégorielles
Avec selector
-
>>> from sklearn.compose import make_column_selector as selector >>> categorical_columns_selector = selector(dtype_include=object) >>> categorical_columns = categorical_columns_selector(data)
Encodage des données catégorielles et création du preprocessor
Avec OrdinalEncoder et ColumnTransformer
-
>>> from sklearn.preprocessing import OrdinalEncoder >>> from sklearn.compose import ColumnTransformer >>> categorical_preprocessor = OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1) >>> preprocessor = ColumnTransformer([("cat_preprocessor", categorical_preprocessor, categorical_columns)], remainder="passthrough")
Pipeline
Avec le modèle HistGradientBoostingClassifier
-
>>> from sklearn.ensemble import HistGradientBoostingClassifier >>> from sklearn.pipeline import Pipeline >>> model = Pipeline([("preprocessor", preprocessor), ("classifier", HistGradientBoostingClassifier(random_state=42, max_leaf_nodes=4))])
Réglage (Tuning) et entraînement
Avec GridSearchCV pour les hyperparamètres learning_rate et max_leaf_nodes
D'abord, création de la grille param_grid avec les valeurs des hyperparamètres learning_rate et max_leaf_nodes. Le nombre de combinaisons d'hyperparamètres testées égale 4 learning_rate x 3 max_leaf_nodes = 12.
Ensuite, création de model_grid_search avec GridSearchCV. L'argument n_jobs=2 définit 2 threads et l'argument cv=2 définit 2 plis. Cela signifie qu'il y a 2 itérations, l'itération 1 avec 50% des échantillons et l'itération 2 avec les 50% d'échantillons restants.
Enfin, entraînement appliqué sur le model_grid_search en utilisant le jeu de données d'entraînement et affichage des valeurs d'hyperparamètres optimums.
-
>>> from sklearn.model_selection import GridSearchCV >>> param_grid = {"classifier__learning_rate": (0.01, 0.1, 1, 10), "classifier__max_leaf_nodes": (3, 10, 30)} >>> model_grid_search = GridSearchCV(model, param_grid=param_grid, n_jobs=2, cv=2) >>> model_grid_search.fit(data_train, target_train) >>> print(f"The best set of parameters is: {model_grid_search.best_params_}") The best set of parameters is: {'classifier__learning_rate': 0.1, 'classifier__max_leaf_nodes': 30}
Calcul de la précision (Accuracy) de model_grid_search
Avec le jeu de données de test
-
>>> accuracy = model_grid_search.score(data_test, target_test) >>> print(f"The test accuracy score of the grid-searched pipeline is: {accuracy:.3f}") The test accuracy score of the grid-searched pipeline is: 0.879
Prédiction de model_grid_search
Pour le jeu de données de test
-
>>> model_grid_search.predict(data_test) array([' <=50K', ' <=50K', ' >50K', ..., ' <=50K', ' <=50K', ' >50K'], dtype=object)
Graphique heatmap
Avec seaborn
-
>>> ax = sns.heatmap(pivoted_cv_results, annot=True, cmap="YlGnBu", vmin=0.7, vmax=0.9) >>> ax.invert_yaxis()
Réglage avec Randomized-search CV
Itération sur des valeurs aléatoires d'hyperparamètres dans un intervalle et mis à l'échelle logarithmique à l'aide de la validation croisée et sélection de celle qui donne les meilleures résultats. Cette méthode est conseillée pour plus de 2 hyperparamètres
Remarque: Nous reprenons l'exemple précédant mais avec 5 hyperparamètres: l2_regularization, min_samples_leaf, max_bins, learning_rate, max_leaf_nodes.
Création du modèle(idem GridSearchCV)
-
>>> import pandas as pd >>> from sklearn.model_selection import train_test_split >>> from sklearn.compose import ColumnTransformer >>> from sklearn.preprocessing import OrdinalEncoder >>> from sklearn.compose import make_column_selector as selector >>> from sklearn.ensemble import HistGradientBoostingClassifier >>> from sklearn.pipeline import Pipeline >>> adult_census = pd.read_csv("adult-census.csv") >>> target_name = "class" >>> target = adult_census[target_name] >>> data = adult_census.drop(columns=[target_name, "education-num"]) >>> data_train, data_test, target_train, target_test = train_test_split(data, target, random_state=42) >>> categorical_columns_selector = selector(dtype_include=object) >>> categorical_columns = categorical_columns_selector(data) >>> categorical_preprocessor = OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1) >>> preprocessor = ColumnTransformer([("cat_preprocessor", categorical_preprocessor, categorical_columns)], remainder="passthrough") >>> model = Pipeline([("preprocessor", preprocessor), ("classifier", HistGradientBoostingClassifier(random_state=42, max_leaf_nodes=4))])
Génération de valeurs entières aléatoires
Avec scipy.stats.loguniform
-
>>> from scipy.stats import loguniform >>> class loguniform_int: def __init__(self, a, b): self._distribution = loguniform(a, b) def rvs(self, *args, **kwargs): """Random variable sample""" return self._distribution.rvs(*args, **kwargs).astype(int)
Réglage et entraînement avec RandomizedSearchCV pour les 5 hyperparamètres
Remarque: Les valeurs d'hyperparamètres trouvées changent si on relance RandomizedSearchCVs
-
>>> from sklearn.model_selection import RandomizedSearchCV >>> from pprint import pprint >>> param_distributions = { "classifier__l2_regularization": loguniform(1e-6, 1e3), "classifier__learning_rate": loguniform(0.001, 10), "classifier__max_leaf_nodes": loguniform_int(2, 256), "classifier__min_samples_leaf": loguniform_int(1, 100), "classifier__max_bins": loguniform_int(2, 255)} >>> model_random_search = RandomizedSearchCV(model, param_distributions=param_distributions, n_iter=10, cv=5, verbose=1) >>> model_random_search.fit(data_train, target_train) >>> pprint(model_random_search.best_params_) Fitting 5 folds for each of 10 candidates, totalling 50 fits {'classifier__l2_regularization': 6.481909581367688, 'classifier__learning_rate': 0.08677948901984608, 'classifier__max_bins': 59, 'classifier__max_leaf_nodes': 51, 'classifier__min_samples_leaf': 3}
Calcul de la précision (Accuracy) de model_random_search
Avec le jeu de données de test
-
>>> accuracy = model_random_search.score(data_test, target_test) >>> print(f"The test accuracy score of the best model is {accuracy:.3f}") The test accuracy score of the best model is 0.869
Analyse des résultats avec cv_results_
Simplication du nom avec rsplit (on enlève classifier__ du nom des hyperparamètres)
On voit qu'on a des valeurs d'hyperparamètres très différentes pour chaque itération
-
>>> column_results = [f"param_{name}" for name in param_distributions.keys()] >>> column_results += ["mean_test_score", "std_test_score", "rank_test_score"] >>> cv_results = pd.DataFrame(model_random_search.cv_results_) >>> cv_results = cv_results[column_results].sort_values("mean_test_score", ascending=False) >>> def shorten_param(param_name): if "__" in param_name: return param_name.rsplit("__", 1)[1] return param_name >>> cv_results = cv_results.rename(shorten_param, axis=1)
Récupération des résultats pour n_iter = 500 et analyse des résultats
Remarque: Le paramètre n_iter=500 montre des valeurs d'hyperparamètres très variées pour de bon résultats mais il nécessite beaucoup de ressouces de calcul. Il est donc recommandé d'utiliser n_iter = 500 si les ressources le permettent.
-
>>> cv_results = pd.read_csv("randomized_search_results.csv", index_col=0) >>> cv_results[column_results].rename(shorten_param, axis=1).sort_values("mean_test_score", ascending=False)
Graphique en courbes savec tous les hyperparametres
Avec plotly.express
-
>>> import numpy as np >>> import plotly.express as px >>> pd.DataFrame.iteritems = pd.DataFrame.items >>> fig = px.parallel_coordinates(cv_results.rename(shorten_param, axis=1).apply( { "learning_rate": np.log10, "max_leaf_nodes": np.log2, "max_bins": np.log2, "min_samples_leaf": np.log10, "l2_regularization": np.log10, "mean_test_score": lambda x: x, }), color="mean_test_score", color_continuous_scale=px.colors.sequential.Viridis) >>> fig.show()
Modèles linéaires
Problème de régression et problème de classification
Problème de régression linéaire
Modèle LinearRegression
Chargement du dataset et séparation en données et cible
Jeu de données penguins_regression
-
>>> import pandas as pd >>> penguins = pd.read_csv("penguins_regression.csv") >>> feature_name = "Flipper Length (mm)" >>> target_name = "Body Mass (g)" >>> data, target = penguins[[feature_name]], penguins[target_name]
Entrainement du modèle LinearRegression
Avec les données data et target
-
>>> from sklearn.linear_model import LinearRegression >>> linear_regression = LinearRegression() >>> linear_regression.fit(data, target)
Equation trouvée avec weight et intercept optimums
Dans l'équation linéaire y = a x + b, x est flipper_length, y est body_mass,
la pente de la droite ou coefficient directeur a est le poids (weight)
et l’ordonnée à l’origine b est l'intersection (intercept).
-
>>> weight_flipper_length = linear_regression.coef_[0] >>> intercept_body_mass = linear_regression.intercept_ >>> print(f"weight_flipper_length = {weight_flipper_length:.2f}") >>> print(f"intercept_body_mass = {intercept_body_mass:.2f}") weight_flipper_length = 49.68 intercept_body_mass = -5780.83
Graphiques
Graphiques des points avec scatterplot de seaborn et courbe affine avec pyplot de matplotlib
Flipper_length_range représente l'intervalle des valeurs de x
et predicted_body_mass représente l'intervalle des valeurs de y calculées avec l'équation de droite.
-
>>> import numpy as np >>> import matplotlib.pyplot as plt >>> import seaborn as sns >>> flipper_length_range = np.linspace(data.min(), data.max(), num=300)") >>> predicted_body_mass = (weight_flipper_length * flipper_length_range + intercept_body_mass) >>> sns.scatterplot(x=data[feature_name], y=target, color="black", alpha=0.5) >>> plt.plot(flipper_length_range, predicted_body_mass) >>> _ = plt.title("Model using LinearRegression from scikit-learn")
Erreur quadratique moyenne (mean_squared_error)
Prédiction de la masse avec les mêmes données data puis calcul de l'erreur
-
>>> from sklearn.metrics import mean_squared_error >>> inferred_body_mass = linear_regression.predict(data) >>> model_error = mean_squared_error(target, inferred_body_mass) >>> print(f"Mean squared error : {model_error:.2f}") Mean squared error : 154546.19
Erreur absolue moyenne (mean_absolute_error)
Calcul de l'erreur à partir de target et inferred_body_mass
-
>>> from sklearn.metrics import mean_absolute_error >>> model_error = mean_absolute_error(target, inferred_body_mass) >>> print(f"Mean absolute error : {model_error:.2f}") Mean absolute error : 313.00
Problème de classification linéaire
Modèle LogisticRegression
Chargement du dataset, séparation en données et cible et partition en donnés de test et d'entrainement
Sélection des échantillons des catégories Adelie et Chinstrap,
Sélection des colonnes avec les caractéristiques Culmen Length et Depth pour les données,
Sélection de la colonne Species pour la cible.
-
>>> import pandas as pd >>> from sklearn.model_selection import train_test_split >>> penguins = pd.read_csv("penguins_regression.csv") >>> penguins = (penguins.set_index("Species").loc[["Adelie", "Chinstrap"]].reset_index()) >>> culmen_columns = ["Culmen Length (mm)", "Culmen Depth (mm)"] >>> target_column = "Species" >>> data = penguins[culmen_columns] >>> target = penguins[target_column] >>> data_train, data_test, target_train, target_test = train_test_split(data, target, random_state=0) >>> penguins_test = pd.concat([data_test, target_test], axis=1)
Histogrammes avec les 2 catégories sélectionnées
Pour chaque caractéristique, Culment Length et Culmen Depth
On remarque que la longueur du culmen permet de prédire l'espèce de manchot contrairement à la profondeur du culmen.
-
>>> import matplotlib.pyplot as plt >>> for feature_name in culmen_columns: plt.figure() penguins.groupby("Species")[feature_name].plot.hist(alpha=0.5, legend=True) plt.xlabel(feature_name)
Graphique des points et graphique de la droite frontière de décision
Avec seaborn et avec la classe DecisionBoundaryDisplay de sickitlearn
-
>>> import seaborn as sns >>> from sklearn.inspection import DecisionBoundaryDisplay >>> DecisionBoundaryDisplay.from_estimator(logistic_regression, data_test, response_method="predict", cmap="RdBu_r", alpha=0.5) >>> sns.scatterplot(data=penguins_test, x=culmen_columns[0], y=culmen_columns[1], hue="Species", palette=["tab:red", "tab:blue"]) >>> _ = plt.title("Decision boundary of the trained\n LogisticRegression")
Calcul des coefficients de la droite frontière de décision
Son équation est coef0 * x0 + coef1 * x1 + intercept = 0
-
>>> coefs = logistic_regression[-1].coef_[0] >>> weights = pd.Series(coefs, index=[f"Weight for '{c}'" for c in culmen_columns]) >>> print(weights) Weight for 'Culmen Length (mm)' 3.724988 Weight for 'Culmen Depth (mm)' -1.096500 dtype: float64
Graphique des poids (weight) en diagramme barre horizontal
Pour les caractéristiques Culment Length et Culmen Depth
-
>>> weights.plot.barh() >>> _ = plt.title("Weights of the logistic regression")
Prédiction (estimation)
Pour un couple de valeurs de Culmen Length et de Culmen Depth (binary classification)
Pour un scénario de classification binaire, la régression logistique effectue des prédictions basées sur la fonction logistique en forme de S sigmoïde entre 0 et 1.
-
>>> test_penguin = pd.DataFrame({"Culmen Length (mm)": [45], "Culmen Depth (mm)": [17]}) >>> logistic_regression.predict(test_penguin) array(['Chinstrap'], dtype=object)
Tableau de probabilité
Ce tableau (array) est de la forme (n_samples, n_classes).
Dans notre exemple, on a 1 échantillon et 2 classes.
-
>>> y_pred_proba = logistic_regression.predict_proba(test_penguin) >>> print(y_pred_proba) >>> print(y_pred_proba.shape) [[0.17145312 0.82854688]] (1, 2)
Diagramme en barre des probalités
Pour le jeu de test
-
>>> y_proba_sample = pd.Series(y_pred_proba.ravel(), index=logistic_regression.classes_)) >>> y_proba_sample.plot.bar() >>> plt.ylabel("Estimated probability") >>> _ = plt.title("Probability of the sample belonging to a penguin class")
Graphique des points et graphique des probabilités de prédiction pour l'ensemble du dataset d'entrainement
Avec seaborn et avec sickitlearn
-
>>> DecisionBoundaryDisplay.from_estimator(logistic_regression, data_test, response_method="predict_proba", cmap="RdBu_r", alpha=0.5) >>> sns.scatterplot(data=penguins_test, x=culmen_columns[0], y=culmen_columns[1], hue=target_column, palette=["tab:red", "tab:blue"]) >>> _ = plt.title("Predicted probability of the trained\n LogisticRegression")
Problème de régression linéaire avec un modèle non linéaire
Comparaison entre un modèle linéaire et plusieurs modèles non linéaires
Création du dataset
Avec Pandas à partir de données aléatoires générées avec Numpy
-
>>> import numpy as np >>> import pandas as pd >>> rng = np.random.RandomState(0) >>> n_sample = 100 >>> data_max, data_min = 1.4, -1.4 >>> len_data = data_max - data_min >>> data = np.sort(rng.rand(n_sample) * len_data - len_data / 2) >>> noise = rng.randn(n_sample) * 0.3 >>> target = data**3 - 0.5 * data**2 + noise >>> full_data = pd.DataFrame({"input_feature": data, "target": target})
Gaphique des points
Avec seaborn
-
>>> import seaborn as sns >>> _ = sns.scatterplot(data=full_data, x="input_feature", y="target", color="black", alpha=0.5)
Transformation du tableau de données 1D en un tableau 2D
Avec une seule colonne de la forme (n_samples, n_features) pour sickitlearn avec reshape
-
>>> data = data.reshape((-1, 1)) >>> data.shape
Création d'une fonction fit_score_plot_regression
Avec mse (mean_squared_error)affiché dans le titre du graphique
-
>>> def fit_score_plot_regression(model, title=None): model.fit(data, target) target_predicted = model.predict(data) mse = mean_squared_error(target, target_predicted) ax = sns.scatterplot(data=full_data, x="input_feature", y="target", color="black", alpha=0.5) ax.plot(data, target_predicted) if title is not None: _ = ax.set_title(title + f" (MSE = {mse:.2f})") else: _ = ax.set_title(f"Mean squared error = {mse:.2f}")
Modèle linéaire LinearRegression
Appel de la fonction fit_score_plot_regression et affichage du poids et de l'intersection
On trouve MSE = 0.36.
-
>>> from sklearn.linear_model import LinearRegression >>> from sklearn.metrics import mean_squared_error >>> linear_regression = LinearRegression() >>> linear_regression >>> fit_score_plot_regression(linear_regression, title="Simple linear regression") >>> print(f"weight: {linear_regression.coef_[0]:.2f}, intercept: {linear_regression.intercept_:.2f}") weight: 1.18, intercept: -0.29
Modèle non linéaire DecisionTreeRegressor
Avec linéarité par paliers
On trouve MSE = 0.08.
-
>>> from sklearn.tree import DecisionTreeRegressor >>> tree = DecisionTreeRegressor(max_depth=3).fit(data, target) >>> display(tree) >>> fit_score_plot_regression(tree, title="Decision tree regression")
Modèle non linéaire PolynomialFeatures
Avec une dernière étape de régression linéaire
On trouve MSE = 0.09.
-
>>> from sklearn.pipeline import make_pipeline >>> from sklearn.preprocessing import PolynomialFeatures >>> polynomial_regression = make_pipeline(PolynomialFeatures(degree=3, include_bias=False), LinearRegression()) >>> display(polynomial_regression) >>> fit_score_plot_regression(polynomial_regression, title="Polynomial regression")
Modèle non linéaire SVR (Support Vector Machine)
Avec un noyau (kernel) linéaire
On trouve MSE = 0.37.
-
>>> from sklearn.svm import SVR >>> svr = SVR(kernel="linear") >>> display(svr) >>> fit_score_plot_regression(svr, title="Linear support vector machine")
Modèle non linéaire KBinsDiscretizer
Avec une dernière étape de régression linéaire
On trouve MSE = 0.15.
-
>>> from sklearn.preprocessing import KBinsDiscretizer >>> binned_regression = make_pipeline(KBinsDiscretizer(n_bins=8), LinearRegression()) >>> display(binned_regression) >>> fit_score_plot_regression(binned_regression, title="Binned regression")
Modèle non linéaire SplineTransformer
Avec une dernière étape de régression linéaire
On trouve MSE = 0.08.
-
>>> from sklearn.preprocessing import SplineTransformer >>> spline_regression = make_pipeline(SplineTransformer(degree=3, include_bias=False), LinearRegression(),) >>> display(spline_regression) >>> fit_score_plot_regression(spline_regression, title="Spline regression")
Modèle non linéaire Nystroem
Avec une dernière étape de régression linéaire
On trouve MSE = 0.09.
-
>>> from sklearn.kernel_approximation import Nystroem >>> nystroem_regression = make_pipeline(Nystroem(kernel="poly", degree=3, n_components=5, random_state=0), LinearRegression()) >>> display(nystroem_regression) >>> fit_score_plot_regression(nystroem_regression, title="Polynomial Nystroem regression")
Problème de classification linéaire avec un modèle non linéaire
Comparaison entre un modèle linéaire et plusieurs modèles non linéaires
Création de 3 datasets moons, Gaussian quantiles et XOR
De 100 échantillons avec 2 colonnes de caractéristiques et une colonne class
-
>>> import numpy as np >>> import pandas as pd >>> from sklearn.datasets import make_moons >>> from sklearn.datasets import make_gaussian_quantiles >>> feature_names = ["Feature #0", "Feature #1"] >>> target_name = "class" >>> X, y = make_moons(n_samples=100, noise=0.13, random_state=42) >>> moons = pd.DataFrame(np.concatenate([X, y[:, np.newaxis]], axis=1), columns=feature_names + [target_name]) >>> data_moons, target_moons = moons[feature_names], moons[target_name] >>> X, y = make_gaussian_quantiles(n_samples=100, n_features=2, n_classes=2, random_state=42) >>> gauss = pd.DataFrame(np.concatenate([X, y[:, np.newaxis]], axis=1), columns=feature_names + [target_name]) >>> data_gauss, target_gauss = gauss[feature_names], gauss[target_name] >>> xor = pd.DataFrame(np.random.RandomState(0).uniform(low=-1, high=1, size=(200, 2)), columns=feature_names) >>> target_xor = np.logical_xor(xor["Feature #0"] > 0, xor["Feature #1"] > 0) >>> target_xor = target_xor.astype(np.int32) >>> xor["class"] = target_xor >>> data_xor = xor[feature_names]
Graphique de points pour les 3 datasets
Avec pyplot de matplotlib
-
>>> import matplotlib.pyplot as plt >>> from matplotlib.colors import ListedColormap >>> _, axs = plt.subplots(ncols=3, figsize=(14, 4), constrained_layout=True) >>> common_scatter_plot_params = dict(cmap=ListedColormap(["tab:red", "tab:blue"]), edgecolor="white", linewidth=1) >>> axs[0].scatter(data_moons[feature_names[0]], data_moons[feature_names[1]], c=target_moons, **common_scatter_plot_params) >>> axs[1].scatter(data_gauss[feature_names[0]], data_gauss[feature_names[1]], c=target_gauss, **common_scatter_plot_params) >>> axs[2].scatter( data_xor[feature_names[0]], data_xor[feature_names[1]], c=target_xor, **common_scatter_plot_params) >>> axs[0].set(title="The moons dataset", xlabel=feature_names[0], ylabel=feature_names[1]) >>> axs[1].set(title="The Gaussian quantiles dataset", xlabel=feature_names[0]) >>> axs[2].set( title="The XOR dataset", xlabel=feature_names[0])
Création de la fonction plot_decision_boundary
Pour tracer les graphiques de la droite frontière de décision pour les 3 datasets
-
>>> from sklearn.inspection import DecisionBoundaryDisplay >>> def plot_decision_boundary(model, title=None): datasets = [(data_moons, target_moons), (data_gauss, target_gauss), (data_xor, target_xor)] fig, axs = plt.subplots(ncols=3, figsize=(14, 4), constrained_layout=True) for i, ax, (data, target) in zip(range(len(datasets)), axs, datasets): model.fit(data, target) DecisionBoundaryDisplay.from_estimator(model, data, response_method="predict_proba", plot_method="pcolormesh", cmap="RdBu", alpha=0.8, vmin=0, vmax=1, ax=ax) DecisionBoundaryDisplay.from_estimator(model, data, response_method="predict_proba", plot_method="contour", alpha=0.8, levels=[0.5], linestyles="--", linewidths=2, ax=ax ) ax.scatter(data[feature_names[0]], data[feature_names[1]], c=target, **common_scatter_plot_params) if i > 0: ax.set_ylabel(None) if title is not None: fig.suptitle(title)
Modèle linéaire LogisticRegression
Dans un pipeline avec StandardScaler
Graphiques de la droite frontière de décision pour les 3 datasets avec la fonction plot_decision_boundary
-
>>> from sklearn.pipeline import make_pipeline >>> from sklearn.preprocessing import StandardScaler >>> from sklearn.linear_model import LogisticRegression >>> logistic_regression = make_pipeline(StandardScaler(), LogisticRegression()) >>> display(logistic_regression) >>> plot_decision_boundary(logistic_regression, title="Linear classifier")
Modèle non linéaire KBinsDiscretizer
Dans un pipeline avec LogisticRegression
Graphiques de la droite frontière de décision pour les 3 datasets avec la fonction plot_decision_boundary
-
>>> from sklearn.preprocessing import KBinsDiscretizer >>> classifier = make_pipeline(KBinsDiscretizer(n_bins=5, encode="onehot"), LogisticRegression()) >>> display(classifier) >>> plot_decision_boundary(classifier, title="Binning classifier")
Modèle non linéaire SplineTransformer
Dans un pipeline avec LogisticRegression
Graphiques de la droite frontière de décision pour les 3 datasets avec la fonction plot_decision_boundary
-
>>> from sklearn.preprocessing import SplineTransformer >>> classifier = make_pipeline(SplineTransformer(degree=3, n_knots=5), LogisticRegression()) >>> dosplay(classifier) >>> plot_decision_boundary(classifier, title="Spline classifier")
Modèle non linéaire PolynomialFeatures
Dans un pipeline avec LogisticRegression
Graphiques de la droite frontière de décision pour les 3 datasets avec la fonction plot_decision_boundary
-
>>> from sklearn.preprocessing import PolynomialFeatures >>> classifier = make_pipeline(StandardScaler(), PolynomialFeatures(degree=3, include_bias=False), LogisticRegression(C=10)) >>> display(classifier) >>> plot_decision_boundary(classifier, title="Polynomial classifier")
Modèle non linéaire Nystroem
Dans un pipeline avec LogisticRegression
Graphiques de la droite frontière de décision pour les 3 datasets avec la fonction plot_decision_boundary
-
>>> from sklearn.kernel_approximation import Nystroem >>> classifier = make_pipeline(StandardScaler(), Nystroem(kernel="poly", degree=3, coef0=1, n_components=100), LogisticRegression(C=10)) >>> display(classifier) >>> plot_decision_boundary(classifier, title="Polynomial classifier")
Combinaison des modèles KBinsDiscretizer et Nystroem
Dans un pipeline avec LogisticRegression
Graphiques de la droite frontière de décision pour les 3 datasets avec la fonction plot_decision_boundary
-
>>> classifier = make_pipeline(KBinsDiscretizer(n_bins=5), Nystroem(kernel="rbf", gamma=1.0, n_components=100), LogisticRegression()) >>> display(classifier) >>> plot_decision_boundary(classifier, title="Binning + Nystroem classifier")
Combinaison des modèles SplineTransformer et Nystroem
Dans un pipeline avec LogisticRegression
Graphiques de la droite frontière de décision pour les 3 datasets avec la fonction plot_decision_boundary
-
>>> classifier = make_pipeline(SplineTransformer(n_knots=5), Nystroem(kernel="rbf", gamma=1.0, n_components=100), LogisticRegression()) >>> display(classifier) >>> plot_decision_boundary(classifier, title="Spline + RBF Nystroem classifier")
Régularisation
Modèles de régression linéaire, de régression linéaire avec optimisation et de régression logistique
Modèle de régression linéaire
Avec le modèle ridge
Préparation
Chargement du dataset ames_housing_no_missing et séparation en données et cible
-
>>> import pandas as pd >>> ames_housing = pd.read_csv('ames_housing_no_missing.csv') >>> features_of_interest = ['LotFrontage', 'LotArea', 'PoolArea', 'YearBuilt', 'YrSold'] >>> target_name = 'SalePrice' >>> data, target = (ames_housing[features_of_interest], ames_housing[target_name])
Pipeline
Mise à l'échelle (normalisation) avec avec MinMaxScaler
Puis transformation (feature engineering) avec le modèle non linéaire PolynomialFeatures
Et enfin régularisation et modélisation avec le modèle de régularisation linéaire ridge en choisissant le paramètre alpha=10 et le solver cholesky
-
>>> from sklearn.pipeline import make_pipeline >>> from sklearn.preprocessing import MinMaxScaler >>> from sklearn.preprocessing import PolynomialFeatures >>> from sklearn.linear_model import Ridge >>> scaled_ridge = make_pipeline(MinMaxScaler(), PolynomialFeatures(degree=2, include_bias=False), Ridge(alpha=10, solver='cholesky'))
Configuration du pipeline afin qu'il retourne des DataFrames pandas après transformation
Avec la méthode set_output. Ce sera utile pour l'analyse des résultats par la suite.
-
>>> scaled_ridge.set_output(transform='pandas')
Validation croisée
Avec cross_validate et neg_mean_squared_error
-
>>> from sklearn.model_selection import cross_validate >>> cv_results = cross_validate(scaled_ridge, data, target, cv=10, scoring='neg_mean_squared_error', return_train_score=True, return_estimator=True)
Erreur quadratique moyenne
Sur le jeu d'entrainement et de test
-
>>> train_error = -cv_results['train_score'] >>> print(f"Mean squared error on the train set : {train_error.mean():.2e} ± {train_error.std():.2e}") >>> test_error = -cv_results["test_score"] >>> print(f"Mean squared error on the test set : {test_error.mean():.2e} ± {test_error.std():.2e}") Mean squared error on the train set : 3.78e+09 ± 1.21e+08 Mean squared error on the test set : 3.83e+09 ± 1.17e+09
Création du dataframe weights_ridge_scaled_data
Avec en colonnes les caractéristiques du modèle polynomial de degré 2 grâce à méthode feature_names_in_
et en lignes les coefficients pour chaque pli de la Validation croisée
Affichage du datagrame et dees valeur max et min.
-
>>> import pandas as pd >>> model_first_fold = cv_results['estimator'][0] >>> feature_names = model_first_fold[-1].feature_names_in_ >>> coefs = [est[-1].coef_ for est in cv_results["estimator"]] >>> # Autre méthode >>> coefs = [pipeline[-1].coef_ for pipeline in cv_results["estimator"]] >>> weights_ridge_scaled_data = pd.DataFrame(coefs, columns=feature_names) >>> display(weights_ridge_scaled_data) >>> display(weights_ridge_scaled_data.describe().loc[["min", "max"]])
Graphique des coefficients
Avec pyplot de matplotlib
-
>>> import matplotlib.pyplot as plt >>> fig, ax = plt.subplots(figsize=(8, 10)) >>> color = {'whiskers': 'black', 'medians': 'black', 'caps': 'black'} >>> weights_ridge_scaled_data.plot.box(color=color, vert=False, ax=ax) >>> _ = ax.set(title="Ridge regression weights with data scaling")
Optimisation du paramètre alpha de régularisation linéaire
Avec le modèle RidgeCV
Pipeline
Reprenons le même jeu de données précédant et faisons varier le paramètre alpha grâce à Numpy.
Création du pipeline
-
>>> from sklearn.linear_model import RidgeCV >>> import numpy as np >>> alphas = np.logspace(-7, 5, num=100) >>> ridge = make_pipeline(MinMaxScaler(), PolynomialFeatures(degree=2, include_bias=False), RidgeCV(alphas=alphas, store_cv_values=True)) >>> ridge.set_output(transform='pandas')
Validation croisée aléatoire
Gâce à ShuffleSplit et avec neg_mean_squared_error
Création du pipeline
-
>>> from sklearn.model_selection import ShuffleSplit >>> cv = ShuffleSplit(n_splits=50, random_state=0) >>> cv_results = cross_validate(ridge, data, target, cv=cv, scoring="neg_mean_squared_error", return_train_score=True, return_estimator=True, n_jobs=2)
Erreur quadratique moyenne
Sur le jeu d'entrainement et de test
Les résultats avec ridgecv sont meilleurs qu'avec ridge.
-
>>> train_error = -cv_results['train_score'] >>> print(f"Mean squared error on the train set : {train_error.mean():.2e} ± {train_error.std():.2e}") >>> test_error = -cv_results["test_score"] >>> print(f"Mean squared error on the test set : {test_error.mean():.2e} ± {test_error.std():.2e}") Mean squared error on the train set : 3.12e+09 ± 1.25e+08 Mean squared error on the test set : 3.50e+09 ± 1.40e+09
Graphique de l'erreur quadratique moyenne en fonction de alpha
A partir du dataframe et avec pyplot
-
>>> mse_alphas = [est[-1].cv_values_.mean(axis=0) for est in cv_results['estimator']] >>> cv_alphas = pd.DataFrame(mse_alphas, columns=alphas) >>> cv_alphas = cv_alphas.aggregate(['mean', 'std']).T >>> fig, ax = plt.subplots(figsize=(8, 6)) >>> ax.errorbar(cv_alphas.index, cv_alphas['mean'], yerr=cv_alphas['std']) >>> _ = ax.set(xscale="log", xlabel='alpha', yscale='log', ylabel='Mean squared error', title='Testing error')
Choix du meilleur alpha
Création de la liste des meilleurs alphas trouvés avec l'attribut .alpha_ pour chacun des 50 plis
Affichage de l'intervalle du meilleur alpha optimisé
-
>>> best_alphas = [est[-1].alpha_ for est in cv_results["estimator"]] >>> print(f"Best alpha is between {np.min(best_alphas):.2f} and {np.max(best_alphas):.2f}") Best alpha is between 0.09 and 0.61
Jeu de données combinées
Numériques et catégorielles
Préparation
Reprenons l'exemple précédant en ajoutant des données catégorielles.
-
>>> data = ames_housing.drop(columns=target_name) >>> categorical_features = ['LotConfig', 'SaleType'] >>> data_categorical = data[categorical_features] >>> data_numerical = data[numerical_features] >>> data = pd.concat([data_categorical, data_numerical], axis=1)
Pipeline
Avec les mêmes paramètres que l'exemple précédant
-
>>> categorical_preprocessor = OneHotEncoder(handle_unknown="ignore") >>> numerical_preprocessor = MinMaxScaler() >>> preprocessor = ColumnTransformer([("one-hot-encoder", categorical_preprocessor, categorical_features), ("min-max-encoder", numerical_preprocessor, numerical_features)]) >>> ridge = make_pipeline(preprocessor, PolynomialFeatures(degree=2, include_bias=False), RidgeCV(alphas=alphas, store_cv_values=True)) >>> cv_results = cross_validate(ridge, data, target, cv=cv, scoring="neg_mean_squared_error", return_train_score=True, return_estimator=True, n_jobs=2)
Erreur quadratique moyenne
Sur le jeu d'entrainement et de test
Les résultats sont encore meilleurs sur le jeu de test.
-
>>> train_error = -cv_results['train_score'] >>> print(f"Mean squared error on the train set : {train_error.mean():.2e} ± {train_error.std():.2e}") >>> test_error = -cv_results["test_score"] >>> print(f"Mean squared error on the test set : {test_error.mean():.2e} ± {test_error.std():.2e}") Mean squared error on the train set : 2.95e+09 ± 1.60e+08 Mean squared error on the test set : 3.52e+09 ± 1.21e+09
Modèle de régression logistique
C'est le même modèle avec et sans régression. Par défaut, le paramètre C=1 inclus de la régression.
Il faut diminuer le paramètre C pour qu'il se rapproche de 0 pour augmenter la régularisation.
Dans l'exemple suivant, nous utilisation le modèle le régularisation logistique avec régression pour un jeu de données combinées numériques et catégorielles.
Préparation
Chargement du dataset penguins_classification et séparation en données et cible puis partition en un jeu de données d'entraînement et de test
-
>>> import pandas as pd >>> from sklearn.model_selection import train_test_split, GridSearchCV >>> from sklearn.pipeline import make_pipeline >>> from sklearn.preprocessing import StandardScaler >>> from sklearn.linear_model import LogisticRegression >>> from sklearn.kernel_approximation import Nystroem >>> penguins = pd.read_csv("penguins_classification.csv") >>> penguins = penguins.set_index("Species").loc[["Adelie", "Chinstrap"]].reset_index() >>> culmen_columns = ["Culmen Length (mm)", "Culmen Depth (mm)"] >>> target_column = "Species" >>> penguins_train, penguins_test = train_test_split(penguins, random_state=0, test_size=0.4) >>> data_train = penguins_train[culmen_columns] >>> data_test = penguins_test[culmen_columns] >>> target_train = penguins_train[target_column] >>> target_test = penguins_test[target_column]
Pipeline
Avec les étapes StandardScaler, Nystroem et LogisticRegression
-
>>> pipeline = make_pipeline(StandardScaler(), Nystroem(kernel="rbf", gamma=1, n_components=100), LogisticRegression(max_iter=1000))
Entrainement et recherche du meilleur paramètre C
Avec GridSearchCV et la liste Cs contenant les valeurs possibles pour le paramètre C
-
>>> Cs = [1e-6, 0.01, 0.1, 1, 10, 100, 1e6] >>> param_grid = {'logisticregression__C': Cs} >>> grid_search = GridSearchCV(pipeline, param_grid, cv=5, scoring='accuracy') >>> grid_search.fit(data_train, target_train) >>> print(f"best C: {grid_search.best_params_['logisticregression__C']}") >>> print(f"Cross validation accuracy : {grid_search.best_score_:.3f}") >>> best_model = grid_search.best_estimator_ >>> test_accuracy = best_model.score(data_test, target_test) >>> print(f"Test accuracy score: {test_accuracy:.3f}") best C: 1 Cross validation accuracy : 0.947 Test accuracy score: 0.977
Modèle d'arbre de décision
Utilisations pour un problème de classification et pour un problème de régression
Problème de classification
Avec le modèle DecisionTreeClassifier
Chargement du dataset penguins_classification
Problème multiclasses avec 2 caractéristiques culmen depth et culmen length et 3 classes Adelie, Gentoo et Chinstrap
Séparation en données et cible puis partition en un jeu de données d'entraînement et de test
-
>>> import pandas as pd >>> from sklearn.model_selection import train_test_split >>> penguins = pd.read_csv("penguins_classification.csv") >>> culmen_columns = ["Culmen Length (mm)", "Culmen Depth (mm)"] >>> target_column = "Species" >>> data, target = penguins[culmen_columns], penguins[target_column] >>> data_train, data_test, target_train, target_test = train_test_split(data, target, random_state=0)
Création du modèle DecisionTreeClassifier puis entrainement
Exemple avec l'hyperparamètre max_depth fixé à 2
-
>>> from sklearn.tree import DecisionTreeClassifier >>> tree = DecisionTreeClassifier(max_depth=2) >>> tree.fit(data_train, target_train)
Optimisation de max_depth avec GridSearchCV
Dans un intervalle de 1 à 10
GridSearchCV permet de trouver le paramètre max_depth optimal.
Les hyperaramètres min_samples_leaf, min_samples_split, max_leaf_nodes et min_impurity_decrease permettent d'augmenter l'assymétrie et d'appliquer une contraine aux feuilles ou aux noeuds.
Dans l'exemple suivant, on fixe min_samples_leaf à 2.
-
>>> from sklearn.model_selection import GridSearchCV >>> import numpy as np >>> param_grid = {"max_depth": np.arange(1, 10)} >>> tree = GridSearchCV(DecisionTreeClassifier(min_samples_leaf=2), param_grid=param_grid) >>> tree.fit(data_train, target_train) >>> print(f"max_depth = {tree.best_params_['max_depth']}") >>> display(tree) max_depth optimal = 5
Graphique des points et graphique de la droite frontière de décision
Avec seaborn, avec la classe DecisionBoundaryDisplay de sickitlearn et avec plt et mpl de matplotlib
-
>>> from sklearn.inspection import DecisionBoundaryDisplay >>> import matplotlib.pyplot as plt >>> import matplotlib as mpl >>> import seaborn as sns >>> tab10_norm = mpl.colors.Normalize(vmin=-0.5, vmax=8.5) >>> palette = ["tab:blue", "tab:green", "tab:orange"] >>> DecisionBoundaryDisplay.from_estimator(tree, data_train, response_method="predict", cmap="tab10", norm=tab10_norm, alpha=0.5) >>> sns.scatterplot(data=penguins, x=culmen_columns[0], y=culmen_columns[1], hue=target_column, palette=palette) >>> plt.legend(bbox_to_anchor=(1, 1), loc="upper left") >>> _ = plt.title("Decision boundary using a decision tree")
Grqphique de l'arbre de décision avec plot_tree
Dans notre exemple, au premier noeud on a culmen depth <= 16.45 (criterion).
Les valeurs entre crochets indiquent [107 = nb of Adelie, 53 = nb of Chinstrap, 96 = nb of Gentoo].
-
>>> from sklearn.tree import plot_tree >>> _, ax = plt.subplots(figsize=(40, 30)) >>> _ = plot_tree(tree.best_estimator_, feature_names=culmen_columns, class_names=tree.classes_.tolist(), impurity=False, ax=ax)
Score de précision
Sur le jeu de test avec la méthode score
-
>>> test_score = tree.score(data_test, target_test) >>> print(f"Précision du modèle DecisionTreeClassifier: {test_score:.3f}") Précision du modèle DecisionTreeClassifier: 1.000
Prédiction
D'un dataframe contenant un exemple d'échantillon
-
>>> example = pd.DataFrame({'Culmen Length (mm)': [10.0], 'Culmen Depth (mm)': [15.0]}) >>> predicted_species = tree.predict(example) >>> print(f"Espèce prédite: {predicted_species[0]}") Espèce prédite: Adelie
Problème de régression
Avec le modèle DecisionTreeRegressor
Chargement du dataset penguins_regression
Séparation en donnée avec la colonne Flipper Length (mm) et en cible avec la colonne Body Mass (g)
-
>>> import pandas as pd >>> from sklearn.model_selection import train_test_split >>> penguins = pd.read_csv('penguins_regression.csv') >>> feature_name = "Flipper Length (mm)" >>> target_name = "Body Mass (g)" >>> data, target = penguins[[feature_name]], penguins[target_name]
Création du modèle DecisionTreeClassifier puis entrainement
Exemple avec l'hyperparamètre max_depth fixé à 2
-
>>> from sklearn.tree import DecisionTreeRegressor >>> tree = DecisionTreeRegressor(max_depth=2) >>> tree.fit(data, target)
Optimisation de max_depth avec GridSearchCV
Dans un intervalle de 1 à 10
-
>>> param_grid = {"max_depth": np.arange(1, 10)} >>> tree = GridSearchCV(DecisionTreeRegressor(), param_grid=param_grid) >>> tree.fit(data, target) >>> print(f"max_depth = {tree.best_params_['max_depth']}") >>> display(tree) max_depth = 3
Création d'un dataset de test
Avec les valeurs entière dans l'intervalle [min Flipper Length, max Flipper Length] de data_train
-
>>> data_test = pd.DataFrame(np.arange(data.iloc[:, 0].min(), data.iloc[:, 0].max()), columns=data[[feature_name]].columns)
Prédictions
Sur ce jeu de données data_test
-
>>> target_predicted_tree = tree.predict(data_test)
Graphique des points et graphique de la droite frontière de décision
Les points pour le dataset penguins
La droite frontière de décision pour le dataset target_predicted_tree
La droite frontière de décision est un ensemble de courbes constantes (en escalier) des prédiction avec le modèle decision tree
-
>>> import matplotlib.pyplot as plt >>> import seaborn as sns >>> sns.scatterplot(data=penguins, x=feature_name, y=target_name, color="black", alpha=0.5) >>> plt.plot(data_test[feature_name], target_predicted_tree, label="Decision tree") >>> plt.legend() >>> _ = plt.title("Prédiction avec le modèle decision tree") >>> _ = plt.title(f"Optimal depth found via CV: {tree.best_params_['max_depth']}") >>> _ = plt.title(f"Modèle d'arbre de décision avec max_depth optimal = {tree.best_params_['max_depth']}")
Ensemble de modèles
Bootstrapping et boosting puis optimisation des hyperparamètres
Bootstrapping
Avec les ensembles BaggingRegressor et RandomForestRegressor
Chargement du dataset fetch_california_housing
Séparation en données et cible
Conversion de la masse cible
Puis partition en un jeu de données d'entraînement et de test
-
>>> from sklearn.datasets import fetch_california_housing >>> from sklearn.model_selection import train_test_split >>> data, target = fetch_california_housing(as_frame=True, return_X_y=True) >>> target *= 100 >>> data_train, data_test, target_train, target_test = train_test_split(data, target, random_state=0)
Ensemble de modèles DecisionTreeRegressor avec BaggingRegressor
Hyperaramètre max_depth=3 pour DecisionTreeRegressor
Nombre d'arbres n_estimators=20 pour BaggingRegressor
Valisation croisée avec cross_validate et affichage du score R2 (R-carré)
Et entraînement et prédiction
-
>>> from sklearn.tree import DecisionTreeRegressor >>> from sklearn.ensemble import BaggingRegressor >>> from sklearn.model_selection import cross_validate >>> from sklearn.metrics import mean_absolute_error >>> estimator = DecisionTreeRegressor(random_state=0, max_depth=3) >>> bagged_trees = BaggingRegressor(estimator=estimator, n_estimators=20, random_state=0) >>> cv_results = cross_validate(bagged_trees, data_train, target_train) >>> scores = cv_results["test_score"] >>> print(f"Score R2 score obtenu avec validation croisée: {scores.mean():.3f} ± {scores.std():.3f}") >>> bagged_trees.fit(data_train, target_train) >>> bagged_trees_predictions = bagged_trees.predict(data_test) >>> print(f"Erreur absolue moyenne : {mean_absolute_error(target_test, bagged_trees_predictions):.2f} k$") Score R2 score obtenu avec validation croisée: 0.562 ± 0.011 Erreur absolue moyenne : 58.21 k$
Bagging complex pipelines avec le modèle de régression polynomial Ridge (ce n'est pas un modèle tree)
Puis ensemble de modèles Ridge avec BaggingRegressor (n_estimators=20)
-
>>> from sklearn.linear_model import Ridge >>> from sklearn.preprocessing import PolynomialFeatures >>> from sklearn.preprocessing import MinMaxScaler >>> from sklearn.pipeline import make_pipeline >>> polynomial_regressor = make_pipeline(MinMaxScaler(), PolynomialFeatures(degree=2), Ridge(alpha=1.0)) >>> bagging_ridge = BaggingRegressor(estimator=polynomial_regressor, n_estimators=20, random_state=0) >>> cv_results = cross_validate(bagging_ridge, data_train, target_train) >>> scores = cv_results["test_score"] >>> print(f"Score R2 score obtenu avec validation croisée: {scores.mean():.3f} ± {scores.std():.3f}") >>> bagging_ridge.fit(data_train, target_train) >>> bagged_ridge_predictions = bagging_ridge.predict(data_test) >>> print(f"Erreur absolue moyenne : {mean_absolute_error(target_test, bagged_ridge_predictions):.2f} k$") Score R2 score obtenu avec validation croisée: -3.376 ± 7.874 Erreur absolue moyenne : 52.32 k$
Modèle random forests regressor
Ensemble de modèles de régression
-
>>> from sklearn.ensemble import RandomForestRegressor >>> forest = RandomForestRegressor(n_estimators=3) >>> cv_results = cross_validate(forest, data_train, target_train, n_jobs=2) >>> scores = cv_results["test_score"] >>> print(f"Score R2 score obtenu avec validation croisée: {scores.mean():.2f} ± {scores.std():.2f}") >>> forest.fit(data_train, target_train) >>> forest_predictions = forest.predict(data_test) >>> print(f"Erreur absolue moyenne : {mean_absolute_error(target_test, forest_predictions):.2f} k$") Score R2 score obtenu avec validation croisée: 0.72 ± 0.01 Erreur absolue moyenne : 41.02 k$
Boosting
Avec les modèle GradientBoostingRegressor et HistGradientBoostingRegressor
Modèle gradient-boosting decision tree (GBDT)
Même jeu de données que précédemment
-
>>> from sklearn.ensemble import GradientBoostingRegressor >>> gbdt = GradientBoostingRegressor(max_depth=5, learning_rate=0.5, n_estimators=200, n_iter_no_change=5) >>> cv_results = cross_validate(gbdt, data_train, target_train, n_jobs=2) >>> scores = cv_results["test_score"] >>> print(f"Score R2 score obtenu avec validation croisée: {scores.mean():.2f} ± {scores.std():.2f}") >>> gbdt.fit(data_train, target_train) >>> print(f"n_estimators_ = {gbdt.n_estimators_}") >>> gdbt_predictions = gbdt.predict(data_test) >>> print(f"Erreur absolue moyenne : {mean_absolute_error(target_test, gdbt_predictions):.2f} k$") Score R2 score obtenu avec validation croisée: 0.80 ± 0.01 n_estimators_ = 26 Erreur absolue moyenne : 35.00 k$
Modèle HistGradientBoostingRegressor (HGBDT)
Le paramètre early_stopping permet d'arrêter l'entraînement avant que toutes les itérations soient complètes, si le modèle ne s'améliore plus.
%%time permet d'afficher les temps de calcul
-
>>> %%time >>> from sklearn.ensemble import HistGradientBoostingRegressor >>> hgbdt = HistGradientBoostingRegressor(max_iter=1000, early_stopping=True, random_state=0) >>> cv_results = cross_validate(hgbdt, data_train, target_train, n_jobs=2) >>> scores = cv_results["test_score"] >>> print(f"Score R2 score obtenu avec validation croisée: {scores.mean():.2f} ± {scores.std():.2f}")) >>> hgbdt.fit(data_train, target_train) >>> hgbdt_predictions = hgbdt.predict(data_test) >>> print(f"Erreur absolue moyenne : {mean_absolute_error(target_test, hgbdt_predictions):.2f} k$") Score R2 score obtenu avec validation croisée: 0.84 ± 0.01 Erreur absolue moyenne : 29.68 k$ CPU times: total: 1.14 s Wall time: 4.65 s
Hyperparamètres
Optimisation pour les modèle RandomForestRegressor et HistGradientBoostingRegressor
Modèle RandomForestRegressor
Optimisation des hyperparamètres avec RandomizedSearchCV (n_iter=5) pour les hyperparamètres suivants:
max_features : nombre maximum de caractéristiques à considérer pour chaque division d'un arbre
max_leaf_nodes: nombre maximum de nœuds feuilles dans chaque arbre
min_samples_leaf: nombre minimum d'échantillons qu'un nœud feuille doit contenir
-
>>> import pandas as pd >>> from sklearn.model_selection import RandomizedSearchCV >>> from sklearn.ensemble import RandomForestRegressor >>> param_distributions = {'max_features': [1, 2, None], 'max_leaf_nodes': [10, 100, None], 'min_samples_leaf': [1, 10]} >>> search_cv = RandomizedSearchCV(RandomForestRegressor(n_jobs=2), param_distributions=param_distributions, scoring="neg_mean_absolute_error", n_iter=5, random_state=0, n_jobs=2) >>> search_cv.fit(data_train, target_train) >>> columns = [f"param_{name}" for name in param_distributions.keys()] >>> columns += ["mean_test_error", "std_test_error"] >>> cv_results = pd.DataFrame(search_cv.cv_results_) >>> cv_results["mean_test_error"] = -cv_results["mean_test_score"] >>> cv_results["std_test_error"] = cv_results["std_test_score"] >>> display(cv_results[columns].sort_values(by="mean_test_error")) >>> error = -search_cv.score(data_test, target_test) >>> print(f"Erreur moyenne : {error:.2f} k$") Erreur moyenne : 33.33 k$
Modèle HistGradientBoostingRegressor
Optimisation des mêmes hyperparamètres mais avec des valeurs différentes
-
>>> from sklearn.ensemble import HistGradientBoostingRegressor >>> param_distributions = {'max_iter': [100, 200, 300], 'max_leaf_nodes': [10, 100], 'learning_rate': [0.01, 0.02, 0.02]} >>> search_cv = RandomizedSearchCV(HistGradientBoostingRegressor(), param_distributions=param_distributions, scoring="neg_mean_absolute_error", n_iter=5, random_state=0, n_jobs=2) >>> search_cv.fit(data_train, target_train) >>> columns = [f"param_{name}" for name in param_distributions.keys()] >>> columns += ["mean_test_error", "std_test_error"] >>> cv_results = pd.DataFrame(search_cv.cv_results_) >>> cv_results["mean_test_error"] = -cv_results["mean_test_score"] >>> cv_results["std_test_error"] = cv_results["std_test_score"] >>> display(cv_results[columns].sort_values(by="mean_test_error")) >>> error = -search_cv.score(data_test, target_test) >>> print(f"Erreur moyenne : {error:.2f} k$") Erreur moyenne : 36.75 k$