Notebook pour le projet : Prédiction des prix de maisons
Projet : Prédiction du prix des maisons¶
Nom : ETSE Kossivi
Formation : Master 2 Mathématiques Appliquées, Statistiques, Parcours Data Science
Université : Université d'Aix-Marseille, Faculté des Sciences, Site de Saint-Charles
Cours : Mathématiques pour les sciences de données $1$
Année Universitaire : 2024–2025
Date : 20 Décembre 2024
$I$. Introduction¶
La prédiction du prix des maisons représente un défi majeur dans le domaine de la data science et trouve de nombreuses applications pratiques, notamment pour les agents immobiliers, les acheteurs, les vendeurs, et les investisseurs. Ce projet a pour ambition d’analyser et de prédire la valeur marchande de biens immobiliers en se basant sur une série de caractéristiques spécifiques. Le modèle de prédiction des prix immobiliers peut être vu comme un modèle de régression où la variable cible (le prix) dépend d’un ensemble de variables explicatives qui décrivent les caractéristiques des propriétés.
La particularité de cette problématique réside dans la nécessité de bien comprendre l’influence de chaque caractéristique (ou feature) sur la valeur d’un bien. En effet, des attributs comme la superficie, le nombre de chambres, la localisation, l’année de construction, ou la qualité de la finition peuvent avoir un impact direct ou indirect sur le prix final. En data science, cette tâche consiste à trouver une fonction de prédiction optimisée qui puisse capturer ces relations complexes entre les variables et fournir une estimation aussi précise que possible du prix.
$a$. Contexte et Objectifs¶
L’objectif principal de ce projet est de construire plusieurs modèles de régression pour estimer les prix de maisons. Cela comprend le développement de modèles simples, tels que des modèles de base (baseline) pour une première estimation, ainsi que des modèles plus sophistiqués capables de capturer des relations non linéaires entre les variables. À travers cette étude, nous explorerons des techniques de régression linéaire ainsi que des modèles ensemblistes (par exemple, Random Forest ou Gradient Boosting), reconnus pour leur capacité à réduire le surapprentissage et à améliorer les performances prédictives.
$b$. Étapes du Projet¶
Le projet est structuré en plusieurs étapes clés :
Exploration et Préparation des Données :
- La première étape consiste à explorer et analyser le jeu de données en profondeur, avec une analyse univariée et multivariée des caractéristiques. Cela permet d’identifier les relations existantes entre les variables explicatives et la variable cible (le prix).
- Ensuite, un nettoyage des données est effectué pour traiter les valeurs manquantes, les erreurs de saisie, et les incohérences. La préparation des données inclut également la transformation et le typage des variables (par exemple, transformation des variables catégorielles en variables numériques), étape cruciale pour assurer la bonne performance des modèles de machine learning.
Développement des Modèles de Régression :
Modèle Baseline : En guise de référence, un modèle de base est d’abord construit. Ce modèle simple se base sur des moyennes ou des règles statistiques élémentaires pour fournir une première estimation des prix. Bien qu’il soit rudimentaire, il sert de point de comparaison pour évaluer l’amélioration obtenue avec les modèles plus avancés.
Régression Linéaire : La régression linéaire est ensuite utilisée pour prédire les prix en fonction des variables explicatives. Ce modèle est idéal pour comprendre les contributions individuelles de chaque variable, car il attribue un coefficient spécifique à chacune, permettant ainsi une interprétation directe de leur influence sur le prix.
Modèle Ensembliste (Random Forest ou Gradient Boosting) : Enfin, nous mettons en œuvre un modèle ensembliste, qui combine les prédictions de plusieurs sous-modèles pour réduire l’erreur et améliorer la précision des prédictions. Ces modèles sont particulièrement performants pour capturer des relations non linéaires et complexes entre les variables explicatives et le prix des maisons.
Évaluation et Analyse des Performances :
Pour évaluer la précision des prédictions, nous utiliserons des métriques de performance comme l’erreur absolue moyenne (Mean Absolute Error - MAE), le Root Mean Squared Error (RMSE) et le coefficient de détermination ($R^2$). Ces indicateurs permettent d’évaluer la qualité des prédictions et de comparer les performances des différents modèles.
Une analyse des coefficients et des « feature importances » (importance des caractéristiques) est également effectuée pour comprendre quelles variables influencent le plus la valeur d’un bien immobilier. Cette analyse permet d’identifier les caractéristiques les plus significatives, ce qui est particulièrement utile pour des applications pratiques de prise de décision dans le secteur immobilier.
L’ensemble du projet sera réalisé dans un environnement Python, en utilisant des bibliothèques comme NumPy, pandas, seaborn et scikit-learn.
$II$. Importation des bibliothèques et du jeu de données¶
# Importation des bibliothèques nécessaires
import numpy as np
import pandas as pd
import seaborn as sns
from scipy import stats
import statsmodels.api as sm
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder
from sklearn.linear_model import LinearRegression
from scipy.stats import chi2_contingency, ttest_ind
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.metrics import mean_absolute_error, r2_score, mean_squared_error
from sklearn.model_selection import train_test_split, GridSearchCV, RandomizedSearchCV
# Chargement des données
def load_data(filepath):
"""
Charge le jeu de données et retourne un DataFrame.
Parameters:
filepath (str): Le chemin vers le fichier CSV.
Returns:
pd.DataFrame: Le DataFrame contenant les données.
"""
data = pd.read_csv(filepath, sep=",")
return data
# Analyse des données
df = load_data("house_prices.csv") # Chargement du jeu de données
df.info() # Affiche les informations sur les types de colonnes et les valeurs manquantes
# print(df.head(10))
Unexpected exception formatting exception. Falling back to standard exception
Traceback (most recent call last):
File "/Users/del_son_as/Dropbox/Mac/Desktop/Predictions_Prix_Maison_Mathias_Kossivi/test/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 3508, in run_code
File "/var/folders/7n/_9qklb4s31s31j53dyk8hvf00000gn/T/ipykernel_75238/581928038.py", line 2, in <module>
import numpy as np
ModuleNotFoundError: No module named 'numpy'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/del_son_as/Dropbox/Mac/Desktop/Predictions_Prix_Maison_Mathias_Kossivi/test/lib/python3.10/site-packages/pygments/styles/__init__.py", line 89, in get_style_by_name
ModuleNotFoundError: No module named 'pygments.styles.default'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/del_son_as/Dropbox/Mac/Desktop/Predictions_Prix_Maison_Mathias_Kossivi/test/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 2105, in showtraceback
File "/Users/del_son_as/Dropbox/Mac/Desktop/Predictions_Prix_Maison_Mathias_Kossivi/test/lib/python3.10/site-packages/IPython/core/ultratb.py", line 1428, in structured_traceback
File "/Users/del_son_as/Dropbox/Mac/Desktop/Predictions_Prix_Maison_Mathias_Kossivi/test/lib/python3.10/site-packages/IPython/core/ultratb.py", line 1319, in structured_traceback
File "/Users/del_son_as/Dropbox/Mac/Desktop/Predictions_Prix_Maison_Mathias_Kossivi/test/lib/python3.10/site-packages/IPython/core/ultratb.py", line 1172, in structured_traceback
File "/Users/del_son_as/Dropbox/Mac/Desktop/Predictions_Prix_Maison_Mathias_Kossivi/test/lib/python3.10/site-packages/IPython/core/ultratb.py", line 1062, in format_exception_as_a_whole
File "/Users/del_son_as/Dropbox/Mac/Desktop/Predictions_Prix_Maison_Mathias_Kossivi/test/lib/python3.10/site-packages/IPython/core/ultratb.py", line 1113, in get_records
File "/Users/del_son_as/Dropbox/Mac/Desktop/Predictions_Prix_Maison_Mathias_Kossivi/test/lib/python3.10/site-packages/pygments/styles/__init__.py", line 91, in get_style_by_name
pygments.util.ClassNotFound: Could not find style module 'default', though it should be builtin.
$a$. Description de la Base de Données¶
La base de données utilisée pour ce projet contient des informations détaillées sur les caractéristiques des maisons et leur prix. Elle est constituée de $14$ variables, dont une variable cible (Price) et $13$ variables explicatives décrivant divers aspects des maisons. Voici la description des variables :
Price: Le prix de la maison (en euros), qui constitue la variable cible pour la prédiction.Area: La superficie totale de la maison en pieds carrés.Bedrooms: Le nombre de chambres dans la maison.Bathrooms: Le nombre de salles de bain dans la maison.Stories: Le nombre d'étages dans la maison.Mainroad: Indique si la maison est connectée à une route principale (Yes/No).Guestroom: Indique si la maison dispose d'une chambre d'amis (Yes/No).Basement: Indique si la maison dispose d'un sous-sol (Yes/No).Hotwaterheating: Indique si la maison est équipée d'un système de chauffage à eau chaude (Yes/No).Airconditioning: Indique si la maison est équipée d'un système de climatisation (Yes/No).Parking: Le nombre d'emplacements de stationnement disponibles dans la maison.Prefarea: Indique si la maison est située dans une zone préférée (Yes/No).Furnishing status: L'état d'ameublement de la maison (entièrement meublée, semi-meublée ou non meublée).House Age: L'âge de la maison, exprimé en années.
$b$. Structure de la Base de Données¶
La base de données comprend des variables numériques continues (comme Price, Area, House Age, et Parking), ainsi que des variables catégoriques binaires ou multinomiales (par exemple, Mainroad, Guestroom, Furnishing status). Cette diversité de variables permet d'explorer divers aspects des caractéristiques des maisons et de construire des modèles de prédiction robustes.
$c$. Analyse des variables et des Valeurs Manquantes¶
Les variables quantitatives présentent des valeurs manquantes modérées pour la plupart, à l'exception de l'âge des maisons, quasiment inexploitable en raison de son taux élevé de valeurs manquantes. Les moyennes et écart-types des variables comme price et area indiquent une large dispersion, reflet de la diversité des biens immobiliers en termes de prix et de superficie.
Les variables catégorielles, telles que mainroad, guestroom, et basement, contiennent des valeurs textuelles (oui/non) et comportent peu de valeurs manquantes, rendant leur gestion plus simple. Une transformation en variables binaires $(0/1)$ faciliterait leur utilisation dans les modèles prédictifs.
$d$. Conclusion¶
En conclusion, le jeu de données dispose d’informations suffisantes et variées pour entraîner des modèles de prédiction des prix des maisons. Néanmoins, certaines étapes de prétraitement sont essentielles, notamment la gestion des valeurs manquantes (les colonnes avec peu de valeurs manquantes peuvent être imputées statistiquement, tandis que la colonne houseage est quasi vide et pourrait être exclue), le formatage des variables, et l’encodage des caractéristiques catégorielles. Une fois ces transformations effectuées, les données seront prêtes pour des analyses et des modélisations plus poussées.
$III$. Exploration et Préparation des Données¶
L’exploration des données est une étape cruciale dans tout projet de data science, car elle permet de mieux comprendre la structure et les particularités du jeu de données. Cette phase vise à examiner les caractéristiques des variables, à identifier les tendances générales et les relations éventuelles entre les variables explicatives et la variable cible. Pour le jeu de données de prédiction des prix des maisons, l’exploration des données inclura des analyses univariées et multivariées, permettant de visualiser la distribution des variables, de détecter des anomalies ou valeurs extrêmes, et de vérifier la présence de valeurs manquantes. Cette analyse approfondie fournit ainsi les bases pour décider des étapes de prétraitement nécessaires, telles que le nettoyage des données, le formatage des variables et la transformation des caractéristiques. Une bonne compréhension des données dès le départ est essentielle pour construire des modèles prédictifs robustes et efficaces.
Nettoyage et formatage¶
La phase de traitement des données est essentielle dans tout projet de machine learning, car elle garantit la qualité et la cohérence des données en entrée. L’une des étapes fondamentales de cette phase est la gestion des valeurs manquantes. Les valeurs manquantes, si elles sont ignorées, peuvent nuire aux performances des modèles en introduisant des biais ou en réduisant la représentativité des données. Dans ce contexte, différentes méthodes d’imputation ont été choisies pour remplir les valeurs manquantes, en fonction de la nature des données et de leur distribution.
Pour les variables quantitatives, nous avons opté pour l’imputation par la moyenne ou la médiane. La moyenne est utilisée lorsqu’une distribution est relativement symétrique, tandis que la médiane est privilégiée pour les variables susceptibles de contenir des valeurs extrêmes, car elle est moins influencée par celles-ci. En ce qui concerne les variables catégorielles, l’imputation par le mode (valeur la plus fréquente) est retenue pour maintenir la distribution représentative des catégories. Enfin, les colonnes contenant un nombre excessif de valeurs manquantes sont supprimées pour éviter des biais trop importants.
Ces choix méthodologiques permettent d’obtenir un jeu de données complet et équilibré, prêt pour la modélisation.
Pour assurer la qualité et la cohérence des données utilisées dans notre modélisation, nous avons développé une fonction de nettoyage et d’imputation. Cette fonction gère les valeurs manquantes en appliquant des méthodes adaptées citées ci-haut.
# Nettoyage et imputation des valeurs manquantes
def clean_data(data):
"""
Nettoie, formate et gère les valeurs manquantes dans les données pour la modélisation.
Parameters:
data (pd.DataFrame): Le DataFrame contenant les données brutes.
Returns:
pd.DataFrame: Le DataFrame nettoyé et prêt pour la modélisation.
"""
# Formatage des noms de colonnes
data.columns = [col.lower().replace(" ", "_") for col in data.columns]
# Imputation pour les colonnes quantitatives (moyenne ou médiane)
numeric_imputations = {
"price": "mean",
"area": "mean",
"bedrooms": "median",
"bathrooms": "median",
"stories": "median",
"parking": "median",
}
for col, method in numeric_imputations.items():
if col in data.columns:
if method == "mean":
data[col] = data[col].fillna(data[col].mean())
elif method == "median":
data[col] = data[col].fillna(data[col].median())
# Suppression de la colonne `houseage` qui a trop de valeurs manquantes
if "houseage" in data.columns:
data = data.drop(columns=["houseage"])
# Imputation pour les colonnes catégorielles (mode)
categorical_cols = [
"mainroad", "guestroom", "basement",
"hotwaterheating", "air_conditioning",
"prefarea", "furnishing_status",
]
for col in categorical_cols:
if col in data.columns:
data[col] = data[col].fillna(data[col].mode()[0])
return data
# Application du nettoyage et de l'imputation au DataFrame
df_clean = clean_data(df)
#print(df_clean)
print(df_clean.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 809 entries, 0 to 808 Data columns (total 13 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 price 809 non-null float64 1 area 809 non-null float64 2 bedrooms 809 non-null float64 3 bathrooms 809 non-null float64 4 stories 809 non-null float64 5 mainroad 809 non-null object 6 guestroom 809 non-null object 7 basement 809 non-null object 8 hotwaterheating 809 non-null object 9 air_conditioning 809 non-null object 10 parking 809 non-null float64 11 prefarea 809 non-null object 12 furnishing_status 809 non-null object dtypes: float64(6), object(7) memory usage: 82.3+ KB None
Les données sont maintenant complètes et bien formatées, comme le montrent les $809$ valeurs non nulles présentes pour chacune des $13$ variables (colonnes). Toutes les valeurs manquantes ont été imputées ou les variables non exploitables ont été supprimées, ce qui garantit la qualité et la cohérence du jeu de données. Avec un format standardisé pour les colonnes numériques et catégorielles, les données sont prêtes à être utilisées pour l’analyse et la modélisation de nos données.
$IV$. Analyse descriptive des variables de différents modèles à estimer à partir du jeux de données recueillis.¶
Pour mieux comprendre les caractéristiques du jeu de données et les relations potentielles entre les variables explicatives et la variable cible, nous effectuons une analyse descriptive comprenant une visualisation univariée et multivariée. L’étude univariée permet d’explorer la distribution de chaque variable individuellement, tandis que l’analyse multivariée aide à identifier les corrélations ou interactions entre les variables. Ces visualisations offrent une première vue d’ensemble des données et des patterns sous-jacents, facilitant ainsi les décisions futures pour la modélisation et l’interprétation des résultats.
$1$. Étude univariée¶
1.1 Variable dépendante (Variable Cible)
Dans cette section, nous analysons la variable cible de notre projet : le prix des maisons (price). Cette variable quantitative représente la valeur estimée des propriétés dans notre jeu de données. Comprendre la distribution et les caractéristiques de cette variable est essentiel, car elle détermine l'objectif de notre modèle de prédiction. L'analyse univariée de la variable cible permettra d'identifier des éventuelles anomalies, des valeurs extrêmes, ainsi que la tendance générale des prix des maisons. Cela facilitera également l'interprétation des résultats et la construction de modèles robustes.
def univariate_statistics(data):
"""
Affiche les statistiques descriptives pour les variables quantitatives et catégorielles.
Parameters:
data (DataFrame): DataFrame contenant les variables.
Returns:
None
"""
# Vérification des variables quantitatives
numerical_columns = data.select_dtypes(include=['float64', 'int64']).columns
if len(numerical_columns) > 0:
print("Statistiques univariées pour les variables quantitatives :")
print(data[numerical_columns].describe())
else:
print("Aucune variable quantitative trouvée dans le DataFrame.")
# Vérification des variables catégorielles
categorical_columns = data.select_dtypes(include='object').columns
if len(categorical_columns) > 0:
print("\nStatistiques univariées pour les variables catégorielles :")
for col in categorical_columns:
print(f"\n{col} :")
print(data[col].value_counts())
else:
print("Aucune variable catégorielle trouvée dans le DataFrame.")
def univariate_visualization(data, numerical_columns, categorical_columns=None):
"""
Affiche les visualisations univariées pour chaque variable quantitative et catégorielle.
Parameters:
data (DataFrame): DataFrame contenant les données.
numerical_columns (list): Liste des colonnes numériques à visualiser.
categorical_columns (list, optional): Liste des colonnes catégorielles à visualiser. Par défaut, None.
Returns:
None
"""
if categorical_columns is None:
categorical_columns = []
# Définition des différentes palettes de couleurs
color_palettes = ['viridis', 'plasma', 'inferno', 'magma', 'cividis']
categorical_palette = "Set2"
# Visualisation des variables quantitatives
if len(numerical_columns) > 0:
plt.figure(figsize=(15, 10))
for i, col in enumerate(numerical_columns):
color = sns.color_palette(color_palettes[i % len(color_palettes)])[2]
# Histogramme
plt.subplot(len(numerical_columns), 2, 2 * i + 1)
sns.histplot(data[col], kde=True, color=color)
plt.title(f"Distribution de {col}", fontsize=12)
plt.xlabel(col, fontsize=10)
plt.ylabel("Count", fontsize=10)
# Boxplot
plt.subplot(len(numerical_columns), 2, 2 * i + 2)
sns.boxplot(x=data[col], color=color)
plt.title(f"Boxplot de {col}", fontsize=12)
plt.xlabel(col, fontsize=10)
plt.tight_layout()
plt.show()
else:
print("Aucune variable quantitative à visualiser.")
# Visualisation des variables catégorielles
if len(categorical_columns) > 0:
plt.figure(figsize=(15, 10))
for i, col in enumerate(categorical_columns):
plt.subplot(len(categorical_columns), 1, i + 1)
sns.countplot(data=data, x=col, hue=col, palette=categorical_palette, legend=False)
plt.title(f"Distribution de {col}", fontsize=12)
plt.xlabel(col, fontsize=10)
plt.ylabel("Count", fontsize=10)
plt.tight_layout()
plt.show()
else:
print("Aucune variable catégorielle à visualiser.")
univariate_statistics(df_clean[['price']])
univariate_visualization(df_clean, ['price'], [])
Statistiques univariées pour les variables quantitatives :
price
count 8.090000e+02
mean 5.406437e+06
std 2.190206e+06
min 1.750000e+06
25% 3.640000e+06
50% 4.900000e+06
75% 7.140000e+06
max 1.330000e+07
Aucune variable catégorielle trouvée dans le DataFrame.
Aucune variable catégorielle à visualiser.
La distribution des prix est asymétrique, avec une forte concentration autour de la moyenne ($5,4$ millions), mais plusieurs valeurs extrêmes au-delà de $10$ millions. Le boxplot confirme cette asymétrie et montre des valeurs aberrantes, ce qui suggère une variabilité significative dans les prix des maisons. Cette dispersion importante pourrait être due à des propriétés de différentes tailles ou dans des emplacements variés.
1.2. Variables explicatives
Les variables explicatives sont au nombre de $12$ : Cinq variables continues ('area', 'bedrooms', 'bathrooms', 'stories', 'parking') et $7$ variables catégorielles ('mainroad', 'guestroom', 'basement', 'hotwaterheating', 'air_conditioning', 'prefarea', 'furnishing_status').
Le tableau suivant présente leurs statistiques descriptives :
# Liste des colonnes numériques et catégorielles
numerical_columns = ['area', 'bedrooms', 'bathrooms', 'stories', 'parking']
categorical_columns = ['mainroad', 'guestroom', 'basement', 'hotwaterheating', 'air_conditioning', 'prefarea', 'furnishing_status']
# Affichage des statistiques et visualisations
univariate_statistics(df_clean[numerical_columns + categorical_columns])
univariate_visualization(df_clean, numerical_columns, categorical_columns)
#df.describe() # Affiche des statistiques descriptives pour chaque colonne.
Statistiques univariées pour les variables quantitatives :
area bedrooms bathrooms stories parking
count 809.000000 809.000000 809.000000 809.000000 809.000000
mean 5482.997481 3.046972 1.457355 1.936959 0.829419
std 2182.793075 0.731382 0.664498 0.942522 0.866354
min 1650.000000 1.000000 1.000000 1.000000 0.000000
25% 3700.000000 3.000000 1.000000 1.000000 0.000000
50% 5400.000000 3.000000 1.000000 2.000000 1.000000
75% 6600.000000 3.000000 2.000000 2.000000 2.000000
max 16200.000000 6.000000 4.000000 4.000000 3.000000
Statistiques univariées pour les variables catégorielles :
mainroad :
mainroad
yes 719
no 90
Name: count, dtype: int64
guestroom :
guestroom
no 644
yes 165
Name: count, dtype: int64
basement :
basement
no 529
yes 280
Name: count, dtype: int64
hotwaterheating :
hotwaterheating
no 759
yes 50
Name: count, dtype: int64
air_conditioning :
air_conditioning
no 496
yes 313
Name: count, dtype: int64
prefarea :
prefarea
no 605
yes 204
Name: count, dtype: int64
furnishing_status :
furnishing_status
semi-furnished 339
unfurnished 238
furnished 224
FURNISHED 4
Furnished 4
Name: count, dtype: int64
$a$. Analyse et Interprétation des Sorties¶
Variables Quantitatives :
- Superficie (area) : La distribution de la superficie présente également une asymétrie, avec un pic autour de $5 000$ pieds carrés et quelques valeurs très élevées allant jusqu'à $16 200$ pieds carrés. Le boxplot révèle plusieurs valeurs extrêmes, indiquant une hétérogénéité marquée dans la taille des propriétés. Cette variabilité est typique des marchés immobiliers où les biens peuvent varier de manière significative en fonction de leur emplacement et de leurs caractéristiques.
- Chambres (bedrooms) : La distribution est principalement concentrée autour de $3$ chambres, avec quelques maisons ayant jusqu'à $6$ chambres, marquées comme valeurs extrêmes dans le boxplot. Cela montre que la plupart des maisons sont de taille moyenne, mais quelques-unes sont de grande taille avec plus de chambres.
- Salles de bain (bathrooms) : La majorité des maisons ont $1$ ou $2$ salles de bain, avec des cas rares ayant jusqu'à $4$ salles de bain. Cette répartition est similaire à celle des chambres, montrant une tendance vers des maisons de taille moyenne.
- Étages (stories) : La distribution indique que la majorité des maisons ont $1$ ou $2$ étages, avec quelques valeurs extrêmes pour les maisons de $4$ étages, visibles dans le boxplot. Cela peut indiquer des styles de maisons différents, par exemple, des maisons de ville avec plusieurs étages.
- Parking : La plupart des maisons disposent d'une place de parking, bien que certaines en possèdent jusqu'à 3. La répartition montre une majorité avec peu de places, suggérant une variation en fonction des caractéristiques ou de la localisation des maisons.
Variables Catégorielles :
- Mainroad, Guestroom, Basement, Hotwaterheating, Air Conditioning, Prefarea : La majorité des maisons sont situées sur des routes principales, sans chambre d’invités, sans sous-sol, ni chauffage à eau chaude. Peu de maisons sont équipées de climatisation. Les barres de distribution montrent un déséquilibre important pour ces caractéristiques, avec la plupart des valeurs concentrées sur "yes" ou "no". Cela peut influencer les caractéristiques principales de l’échantillon, notamment sur les commodités et l’accessibilité.
- Furnishing Status : La majorité des maisons sont semi-meublées ou non meublées. Il existe cependant plusieurs variations d'orthographe (
furnished,FURNISHED,Furnished), indiquant un besoin de standardisation. Les barres montrent une distribution variée pour le statut d’ameublement, suggérant une diversité dans l’aménagement des biens.
$b$. Conclusion¶
Les analyses révèlent une diversité marquée dans les caractéristiques des maisons, en particulier pour les variables quantitatives comme le prix et la superficie, avec des valeurs extrêmes pour certaines d'entre elles. Les distributions et boxplots montrent des asymétries et des valeurs aberrantes, qui pourraient influencer les analyses futures. Les variables catégorielles révèlent des déséquilibres dans certaines caractéristiques (comme la présence de climatisation, de sous-sol, etc.), ce qui pourrait refléter les préférences ou les contraintes du marché. Enfin, des corrections pour uniformiser les valeurs de certaines catégories (furnishing_status) seront nécessaires pour garantir la cohérence des données avant la modélisation.
$2$. Étude bivariée¶
Vues les natures de nos variables (qualitatives et quantitatives), les tests de dépendance
de Khi-deux et de comparaison des moyennes nous permettront de vérifier l’existence ou non d’une liaison entre nos variables explicatives et la variable expliquée price.
$2.1$. Test de dépendance de Khi-deux
Ce type de test n’est valable que pour les variables explicatives et la variable cible toutes qualitatives. Pour ce test, considérons les hypothèses de test suivantes : $$ \begin{cases} H_0 : \text{la variable } X_i \text{ et le prix de la maison ne sont pas significativement liés} \\ H_1 : \text{la variable } X_i \text{ et le prix de la maison sont significativement liés} \end{cases} $$
Où $X_i$ désigne la $i^{ème}$ variable explicative qualitative $(i = 1, 2, · · · , 7)$ de notre liste de variables explicatives.
Le seuil de significativité retenu pour ce test est de $5\%$. Le seuil de significativité retenu pour ce test est de $5\%$. Ainsi, si la p-value associée à la statistique de Khi-deux est supérieure au seuil retenu, alors on rejettera l’hypothèse $H_1$ et on conclura que les deux variables ne sont pas liées ; dans le cas contraire, on rejettera l’hypothèse $H_0$ et on dira qu’elles sont liées.
L'autre critère de significativité est de comparer les Statistiques de Chi-$2$ . Ainsi,
- Plus la valeur de la statistique de Chi-$2$ est élevée, plus il est probable qu'il existe une association significative entre la variable catégorielle et la variable cible.
- Une valeur de Chi-$2$ élevée indique une forte divergence entre les fréquences observées et les fréquences attendues, ce qui suggère que la variable catégorielle influence la variable cible.
Dans cette étude, il s’agit de faire des tableaux croisés et des tests d’indépendance pour les variables qualitatives suivantes :
- mainroad (route principale)
- guestroom (chambre d’invités)
- basement (sous-sol)
- hotwaterheating (chauffage à eau chaude)
- air_conditioning (climatisation)
- prefarea (zone préférée)
- furnishing_status (statut d’ameublement)
En appliquant le test de Khi-deux pour chacune de ces variables explicatives qualitatives, nous pourrons déterminer celles qui sont significativement corrélées avec le prix de la maison. Si la p-value est inférieure à $5 \%$, cela signifie que la variable explicative est significativement liée à la variable cible, et elle sera considérée comme pertinente pour notre modèle de prédiction.
Le code suivant permet de faire un tel test.
# Test de Khi-deux pour les variables catégorielles
categorical_columns = ['mainroad', 'guestroom', 'basement', 'hotwaterheating', 'air_conditioning', 'prefarea', 'furnishing_status']
for col in categorical_columns:
contingency_table = pd.crosstab(df_clean[col], df_clean['price'] > df_clean['price'].median())
chi2, p, dof, ex = chi2_contingency(contingency_table)
#print(f"Test de Khi-deux pour {col}:")
#print(f"Chi2 = {chi2}, p-value = {p}\n")
Affichage et Analyse des Résultats :
| Variable | Chi-2 | p-value | Significativité |
|---|---|---|---|
| mainroad | $60.88$ | $6.07e-15$ | Significatif |
| guestroom | $76.52$ | $2.18e-18$ | Significatif |
| basement | $24.38$ | $7.90e-07$ | Significatif |
| hotwaterheating | $11.96$ | $5.45e-04$ | Significatif |
| air_conditioning | $158.26$ | $2.71e-36$ | Significatif |
| prefarea | $87.87$ | $6.97e-21$ | Significatif |
| furnishing_status | $45.65$ | $2.92e-09$ | Significatif |
Après analyse de nos résultats, on retient que :
- Toutes les variables catégorielles analysées sont significativement corrélées avec la variable cible, car leurs p-values sont toutes inférieures à $0.05$.
- Les variables avec des valeurs de Chi-$2$ très élevées, comme
air_conditioning,prefarea, etguestroom, montrent une association très forte avec la variable cible. Cela indique que ces variables sont particulièrement importantes pour l’étude et devraient être incluses par défaut dans notre modèle de prédiction.
En résumé, les résultats des tests de Khi-deux confirment que les variables catégorielles que nous avons analysées sont toutes pertinentes pour notre étude de prédiction des prix des maisons.
Une autre façon de tester la significativité entre variables catégorielles est d'utiliser le test ANOVA (Analyse of variance).
Le test ANOVA, tout comme le test de Khi-deux, vise à évaluer l’association entre une variable catégorielle et la variable cible. Alors que le test de Khi-deux analyse la dépendance en comparant les fréquences observées et attendues, le test ANOVA se concentre sur la comparaison des moyennes entre différentes catégories. Ce test repose sur les critères suivants :
Statistique F de Fisher :
- La statistique F mesure la proportion de la variance expliquée par la variable catégorielle par rapport à la variance non expliquée.
- Plus la valeur de F est élevée, plus il est probable qu'il existe une différence significative entre les moyennes des groupes. Cela indique que la variable catégorielle influence fortement la variable cible.
p-value :
- La p-value indique la probabilité d'observer un résultat aussi extrême que celui obtenu, en supposant que l'hypothèse nulle soit vraie.
- Si la p-value est inférieure à $0.05$, nous rejetons l’hypothèse nulle. Cela signifie qu'il existe une différence significative entre les moyennes des groupes, et la variable catégorielle est significativement corrélée avec la variable cible.
- Si la p-value est supérieure à $0.05$, nous n’avons pas suffisamment de preuves pour rejeter l’hypothèse nulle, et nous considérons que la variable catégorielle n’est pas significativement corrélée avec la variable cible.
La fonction suivante permet de réaliser un tel test.
def anova_test(data, target, categorical_columns):
"""
Effectue un test ANOVA pour évaluer la corrélation entre les variables catégorielles et la variable cible.
Parameters:
data (DataFrame): DataFrame contenant les données.
target (str): Nom de la variable cible.
categorical_columns (list): Liste des variables catégorielles.
Returns:
None
"""
for col in categorical_columns:
groups = [data[target][data[col] == level] for level in data[col].unique()]
f_stat, p_value = stats.f_oneway(*groups)
print(f"ANOVA pour {col} : F-stat = {f_stat:.2f}, p-value = {p_value:.4f}")
if p_value < 0.05:
print(f"La variable catégorielle {col} est significativement corrélée avec la variable cible {target}.\n")
else:
print(f"Aucune corrélation significative entre {col} et {target}.\n")
# Exemple d'utilisation
categorical_columns = ['mainroad', 'guestroom', 'basement', 'hotwaterheating', 'air_conditioning', 'prefarea', 'furnishing_status']
#anova_test(df_clean, 'price', categorical_columns)
Les résultats et analyses de ce test sont consignés dans le tableau suivant et confirment toutes les analyses faites précédemment.
| Variable | F-stat | p-value | Significativité |
|---|---|---|---|
| mainroad | $92.93$ | $0.0000$ | Significatif |
| guestroom | $59.10$ | $0.0000$ | Significatif |
| basement | $26.04$ | $0.0000$ | Significatif |
| hotwaterheating | $23.31$ | $0.0000$ | Significatif |
| air_conditioning | $240.53$ | $0.0000$ | Significatif |
| prefarea | $78.62$ | $0.0000$ | Significatif |
| furnishing_status | $17.63$ | $0.0000$ | Significatif |
$2.2$. Test de comparaison des Moyennes
Dans notre projet de prédiction des prix des maisons, nous avons des variables explicatives quantitatives telles que la superficie de la maison (area), le nombre de chambres (bedrooms), le nombre de salles de bain (bathrooms), le nombre d'étages (stories), et le nombre de places de parking (parking). Ces types de tests sont donc plus adaptés pour étudier une dépendance entre chacune de ces variables avec la variable dépendante price.
Pour cela, considérons les hypothèses de test suivantes :
$$ \begin{cases} H_0 : \text{la variable } X_i \text{ et le prix de la maison ne sont pas significativement liés} \\ H_1 : \text{la variable } X_i \text{ et le prix de la maison sont significativement liés} \end{cases} $$
Où $X_i$ désigne la $i^{ème}$ variable explicative quantitative $(i = 1, 2, · · · , 5)$ de notre liste de variables explicatives.
Le seuil de significativité retenu pour ce test est de $5\%$. Ainsi, si la p-value associée à la statistique de comparaison des moyennes est supérieure au seuil retenu, alors nous rejetons l’hypothèse $H_1$ et concluons que les deux variables ne sont pas liées. Dans le cas contraire, nous rejetons l’hypothèse $H_0$ et affirmons qu’elles sont liées.
Dans cette étude, nous utiliserons des tests de comparaison des moyennes pour analyser la relation entre les variables explicatives quantitatives suivantes et la variable cible price :
- area (Superficie de la maison)
- bedrooms (Nombre de chambres)
- bathrooms (Nombre de salles de bain)
- stories (Nombre d'étages)
- parking (Nombre de places de parking)
En résumé, l’objectif est de vérifier l’association entre chaque variable explicative quantitative et la variable cible price afin de déterminer celles qui sont significativement corrélées au prix des maisons et qui doivent être incluses dans notre modèle prédictif.
Le code suivant permet de faire un tel test.
# Test t pour comparer les moyennes
numerical_columns = ['area', 'bedrooms', 'bathrooms', 'stories', 'parking']
for col in numerical_columns:
group1 = df_clean[df_clean['price'] > df_clean['price'].median()][col]
group2 = df_clean[df_clean['price'] <= df_clean['price'].median()][col]
t_stat, p_val = ttest_ind(group1, group2)
#print(f"Test t pour {col}:")
#print(f"t-stat = {t_stat}, p-value = {p_val}\n")
Affichage et nalyse des Résultats :
| Variable | t-stat | p-value | Significativité |
|---|---|---|---|
| area | $17.07$ | $5.45e-56$ | Significatif |
| bedrooms | $11.54$ | $1.28e-28$ | Significatif |
| bathrooms | $17.44$ | $4.94e-58$ | Significatif |
| stories | $12.67$ | $1.14e-33$ | Significatif |
| parking | $10.47$ | $4.00e-24$ | Significatif |
Après analyse de nos résultats, on retient que :
- Toutes les variables quantitatives analysées (
area,bedrooms,bathrooms,stories,parking) sont significativement liées à la variable cible (price), car leurs p-values sont toutes inférieures à $0.05$. - Les variables avec des valeurs de t-stat élevées, comme
areaetbathrooms, montrent une différence plus prononcée entre les groupes et peuvent être considérées comme ayant une association plus forte avec le prix des maisons.
En résumé, les résultats des tests confirment que toutes les variables quantitatives considérées sont pertinentes pour l'analyse et doivent être incluses dans le modèle de prédiction.
Vue la nature quantitatives de ces variables, une autre façon de voir leur significativité avec la variable cible, est de visualiser leur matrice de corrélation.
Le code ci-dessous permet d'avoir une telle matrice.
def plot_correlation_matrix(data):
"""
Affiche la matrice de corrélation pour toutes les variables numériques du DataFrame.
Parameters:
data (DataFrame): DataFrame contenant les données.
Returns:
DataFrame: Matrice de corrélation.
"""
# Sélection des colonnes numériques
numerical_columns = data.select_dtypes(include=['float64', 'int64']).columns
# Calcul de la matrice de corrélation
corr_matrix = data[numerical_columns].corr()
# Affichage de la heatmap de la matrice de corrélation
plt.figure(figsize=(12, 10))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', vmin=-1, vmax=1, linewidths=0.5)
plt.title("Matrice de corrélation des variables numériques")
plt.show()
return corr_matrix
# Application
correlation_matrix = plot_correlation_matrix(df_clean)
print(correlation_matrix)
price area bedrooms bathrooms stories parking price 1.000000 0.529523 0.397980 0.615856 0.429652 0.451830 area 0.529523 1.000000 0.182052 0.259114 0.139188 0.369594 bedrooms 0.397980 0.182052 1.000000 0.375922 0.357988 0.198216 bathrooms 0.615856 0.259114 0.375922 1.000000 0.344479 0.277572 stories 0.429652 0.139188 0.357988 0.344479 1.000000 0.150506 parking 0.451830 0.369594 0.198216 0.277572 0.150506 1.000000
De l'analyse de cette matrice de corélation, il ressort que :
- Les variables
bathrooms,area, etparkingmontrent une forte corrélation avec la variable cibleprice. Elles sont donc des candidates importantes à inclure dans le modèle de prédiction. - Les autres variables, comme
storiesetbedrooms, ont des corrélations modérées mais restent pertinentes pour l’analyse. - Il n’y a pas de corrélations extrêmement élevées entre les variables explicatives, ce qui suggère qu'il n'y a pas de multicolinéarité problématique entre elles.
Ces résultats indiquent que la majorité des variables explicatives sont significativement liées au prix des maisons et qu’elles devraient être prises en compte dans la modélisation, ce que confirme notre test de comparaison de moyennes.
$V$. Typage des variables pour la Modélisation¶
Après avoir exploré les données et identifié les variables importantes, il est essentiel de s'assurer que toutes les variables sont prêtes pour l'étape de modélisation. Cette section se concentre sur le typage et la transformation des variables pour garantir une compatibilité optimale avec les algorithmes de machine learning.
Pour préparer les données au modèle, il est nécessaire de s'assurer que toutes les variables sont numériques. Voici les étapes clés que nous suivons :
$1$. Conversion des variables catégorielles :
- Les variables catégorielles doivent être transformées en variables numériques . Cela peut être effectué de plusieurs manières mais dans ce projet, nous utiliserons la méthode de One-Hot Encoding pour attribuer un entier à chaque catégorie.
La méthode de One-Hot Encoding est une technique de transformation des variables catégorielles en représentations numériques sans introduire d’ordre implicite entre les catégories. Contrairement au Label Encoding, où chaque catégorie est attribuée à un entier unique, le One-Hot Encoding crée une nouvelle colonne pour chaque catégorie, contenant une valeur binaire (1 ou 0) indiquant la présence ou l’absence de cette catégorie. Par exemple, une variable couleur avec les catégories ["rouge", "bleu", "vert"] sera transformée en trois colonnes : couleur_rouge, couleur_bleu, et couleur_vert. Cette méthode est particulièrement utile pour les modèles sensibles à l’échelle ou à l’ordre des données, comme la régression linéaire, car elle évite d’attribuer une hiérarchie arbitraire aux catégories. Cependant, elle peut augmenter significativement la dimensionnalité des données si le nombre de catégories est élevé.
$2$. Conversion des variables booléennes :
- Les variables booléennes doivent être converties en entiers (
0ou1) au lieu de leurs formatsYesouNo;TrueouFalse.
Nous utilisons le code suivant pour faire la conversion de nos variables booléennes en entier.
# Étape 1 : Nettoyage et encodage One-Hot de 'furnishing_status'
df_clean_copy = df_clean.copy() # Création d'une copie du DataFrame original
df_clean_copy['furnishing_status'] = df_clean_copy['furnishing_status'].str.strip().str.lower() # Nettoyage des valeurs : tout en minuscules et suppression des espaces
# Encodage fictif (one-hot encoding) pour la colonne furnishing_status
df_clean_copy = pd.get_dummies(df_clean_copy, columns=['furnishing_status'], prefix='furnishing_status', drop_first=False)
# Conversion explicite des colonnes fictives en entier
furnishing_status_columns = [col for col in df_clean_copy.columns if 'furnishing_status_' in col]
df_clean_copy[furnishing_status_columns] = df_clean_copy[furnishing_status_columns].astype(int)
# Étape 2 : Conversion des colonnes booléennes contenant 'yes'/'no' ou True/False
columns_to_map = ['mainroad', 'guestroom', 'basement', 'hotwaterheating', 'air_conditioning', 'prefarea']
# Conversion des valeurs booléennes ('yes' -> 1, 'no' -> 0) pour chaque colonne spécifiée
for col in columns_to_map:
df_clean_copy[col] = df_clean_copy[col].map({'yes': 1, 'no': 0, True: 1, False: 0}).fillna(0).astype(int)
# Étape 3 : Vérification des résultats
# Affichage des 5 premières lignes après les transformations
print(f"Aperçu des données après encodage fictif et conversion des variables booléennes : \n\n", df_clean_copy.head())
# Comptage des occurrences de 1 et 0 pour les colonnes booléennes
#for col in columns_to_map:
# counts = df_clean_copy[col].value_counts()
# print(f"Occurrences de 1 et 0 dans la colonne '{col}' :")
# print(counts)
#print("-" * 30)
# Vérification des colonnes encodées fictivement
#print(f"Aperçu des colonnes encodées pour 'furnishing_status': \n\n", df_clean_copy.filter(like='furnishing_status').head())
#print(df_clean_copy)
#print(df_clean)
Aperçu des données après encodage fictif et conversion des variables booléennes :
price area bedrooms bathrooms stories mainroad guestroom \
0 4543000.0 4990.0 4.0 2.0 2.0 1 1
1 8080940.0 7000.0 3.0 2.0 4.0 1 0
2 8750000.0 4321.0 3.0 2.0 2.0 1 0
3 1890000.0 1700.0 3.0 1.0 2.0 1 0
4 12215000.0 7500.0 4.0 2.0 2.0 1 0
basement hotwaterheating air_conditioning parking prefarea \
0 1 0 0 0.0 1
1 0 0 1 2.0 0
2 1 1 0 2.0 0
3 0 0 0 0.0 0
4 1 0 1 3.0 1
furnishing_status_furnished furnishing_status_semi-furnished \
0 1 0
1 1 0
2 1 0
3 0 0
4 1 0
furnishing_status_unfurnished
0 0
1 0
2 0
3 1
4 0
$3$. Validation des Types :
- Une fois les conversions effectuées, vérifions que toutes les colonnes ont des types numériques (
int64oufloat64).
data_prix = df_clean_copy.copy()
print(data_prix.dtypes)
price float64 area float64 bedrooms float64 bathrooms float64 stories float64 mainroad int64 guestroom int64 basement int64 hotwaterheating int64 air_conditioning int64 parking float64 prefarea int64 furnishing_status_furnished int64 furnishing_status_semi-furnished int64 furnishing_status_unfurnished int64 dtype: object
En conclusion, le typage des variables a permis de transformer efficacement toutes les colonnes catégorielles et booléennes en données numériques adaptées aux modèles de machine learning. La méthode One-Hot Encoding a été utilisée pour encoder les catégories non ordonnées, comme furnishing_status, tandis que les colonnes booléennes contenant des valeurs telles que yes/no ou True/False ont été converties en valeurs binaires ($1$ et $0$). Ces transformations garantissent une table uniformisée, exclusivement composée de données numériques, prête pour l’entraînement des modèles. La table finale, qui servira de base pour les modélisations et les analyses, a été nommée data_prix pour refléter son rôle central dans la prédiction des prix des maisons.
$VI$. Modélisation¶
La phase de modélisation est une étape clé dans le projet de prédiction des prix des maisons. Elle consiste à construire et évaluer différents modèles de régression, en adoptant une approche progressive allant des modèles simples à des modèles plus complexes et sophistiqués. L’objectif principal est de comprendre comment les différentes méthodes permettent de capturer les relations entre les caractéristiques explicatives et la variable cible (price) tout en améliorant les performances de prédiction. Nous commencerons par un modèle baseline pour établir un point de référence, puis nous développerons des modèles de régression linéaire pour exploiter les relations linéaires entre les variables. Enfin, nous explorerons des modèles ensemblistes, tels que les Random Forests et Gradient Boosting, qui permettent de capter des relations plus complexes et non linéaires. Les performances des modèles seront évaluées à l’aide de métriques comme l’erreur absolue moyenne (MAE) et le coefficient de détermination $(R^2)$, tout en examinant l’impact de la taille des données d’entraînement sur leurs performances. Les résultats seront analysés à l’aide de graphiques, tels que la comparaison des distributions des valeurs réelles et prédites, ainsi que des scatter plots pour visualiser la précision des prédictions. Cette approche permet non seulement de comparer les modèles, mais également de mieux comprendre leur comportement et leur adéquation au problème étudié.
$a$. Validation croisée¶
La validation croisée est une méthode essentielle permettant d'estimer les performances de prédiction d'un modèle à partir des données disponibles. Elle repose sur un protocole simple en trois étapes principales :
- Diviser les données en deux ensembles : un ensemble d’apprentissage (train) et un ensemble de test (test), tout en s'assurant d'une répartition cohérente de la variable cible.
- Entraîner le modèle sur l’ensemble d’apprentissage (train).
- Estimer les performances du modèle sur l’ensemble de test (test), avec des exemples non utilisés lors de l'apprentissage.
De cette façon, les données utilisées pour estimer la performance sont des exemples nouveaux qui n’ont pas été utilisés lors de l’apprentissage.
Dans ce projet, le jeu de données data_prix a été divisé en deux sous-ensembles distincts afin de garantir une évaluation fiable des performances des modèles. L’ensemble d’entraînement, représentant $80\%$ des données avec $14$ variables explicatives, sera utilisé pour construire et ajuster les modèles. L’ensemble de test, représentant les $20\%$ restants et comprenant les mêmes variables explicatives, servira à évaluer les performances des modèles sur des données nouvelles, non utilisées lors de l’apprentissage. Cette division permet de simuler un scénario réaliste où le modèle doit faire des prédictions sur des données inconnues. À noter que la stratification a été désactivée dans cette division en raison de la présence de classes rares dans la variable cible, garantissant ainsi une répartition aléatoire des données entre les deux ensembles.
Dans le cadre de cette validation croisée, nous avons implémenté une fonction appelée split_data qui permet de diviser efficacement notre jeu de données en ensembles d’entraînement et de test, garantissant ainsi une évaluation rigoureuse des performances de nos modèles tout en limitant les biais liés à la répartition des données.
def split_data(dataframe, target_column, test_ratio=0.2, random_seed=42):
"""
Divise les données en ensembles d'entraînement et de test tout en garantissant
une répartition similaire de la variable cible, si possible.
Paramètres :
-----------
dataframe : pd.DataFrame
Le DataFrame contenant les données étiquetées.
target_column : str
Le nom de la colonne cible (la variable à prédire).
test_ratio : float, optionnel (par défaut = 0.2)
La proportion des exemples à inclure dans le set de test.
random_seed : int, optionnel (par défaut = 42)
La graine pour la randomisation.
Retourne :
--------
X_train : pd.DataFrame
Variables explicatives pour l'ensemble d'entraînement.
y_train : pd.Series
Variable cible pour l'ensemble d'entraînement.
X_test : pd.DataFrame
Variables explicatives pour l'ensemble de test.
y_test : pd.Series
Variable cible pour l'ensemble de test.
"""
# Séparation des variables explicatives (X) et la variable cible (y)
X = dataframe.drop(columns=[target_column]) # Supprime la colonne cible
y = dataframe[target_column] # Colonne cible
# Vérification si stratification est nécessaire et applicable
if y.nunique() > 10 or y.value_counts().min() < 2:
# Désactiver la stratification pour une cible continue ou avec classes rares
stratify_option = None
print("Attention !!!! : Stratification désactivée en raison de classes rares ou d'une variable continue.")
else:
# Sinon, appliquer la stratification
stratify_option = y
# Division des données en ensembles d'entraînement et de test
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=test_ratio, random_state=random_seed, stratify=stratify_option
)
return X_train, y_train, X_test, y_test
# Chargement des données (data_prix représente votre DataFrame final)
data_prix_train, price_train, data_prix_test, price_test = split_data(data_prix, target_column='price', test_ratio=0.2, random_seed=42)
# Préparation des données pour le tableau
data_summary = {
"Nom de l'ensemble": ["data_prix_train", "data_prix_test", "price_train", "price_test"],
"Taille (lignes)": [data_prix_train.shape[0], data_prix_test.shape[0], price_train.shape[0], price_test.shape[0]],
"Taille (variables explicatives)": [data_prix_train.shape[1], data_prix_test.shape[1], "-", "-"]
}
# Créeation d'un DataFrame pour afficher sous forme de tableau
summary_table = pd.DataFrame(data_summary)
# Affichage du tableau
print("\nTailles des ensembles :")
print(summary_table)
Attention !!!! : Stratification désactivée en raison de classes rares ou d'une variable continue. Tailles des ensembles : Nom de l'ensemble Taille (lignes) Taille (variables explicatives) 0 data_prix_train 647 14 1 data_prix_test 162 14 2 price_train 647 - 3 price_test 162 -
$b$. Métriques et Visualisations pour l'Évaluation des Modèles¶
Avant de passer à la mise en place des différents modèles, il est important de définir un cadre commun pour l’évaluation et l’analyse des performances. Ce cadre repose sur l’utilisation de métriques quantitatives et de visualisations graphiques, applicables à tous les modèles que nous allons développer.
$b_1$. Métriques d’Évaluation¶
Nous utiliserons trois métriques principales pour évaluer les performances des modèles :
MAE (Mean Absolute Error) :
- Cette métrique mesure l’erreur moyenne absolue entre les valeurs réelles et prédites.
- Elle est intuitive et permet de quantifier directement la précision moyenne des prédictions.
RMSE (Root Mean Squared Error) :
- Contrairement au MAE, le RMSE pénalise davantage les grandes erreurs en élevant les écarts au carré avant de les moyenner.
- Il est particulièrement utile pour détecter l’impact des valeurs aberrantes et fournit une mesure dans la même unité que la variable cible, ce qui le rend facile à interpréter et permet de quantifier directement l’échelle de l’erreur moyenne. Un écart significatif entre RMSE et MAE indique la présence de valeurs aberrantes (outliers).
$R^2$ (Coefficient de détermination) :
- Le $R^2$ indique la proportion de variance des valeurs réelles expliquée par le modèle.
- Il est utile pour évaluer globalement dans quelle mesure un modèle capture les variations des données.
Ces trois métriques offrent une évaluation complète des performances en combinant précision absolue (MAE), robustesse aux grandes erreurs (RMSE), et capacité explicative globale $(R^2)$.
$b_2$. Visualisations pour l’Analyse des Modèles¶
En complément des métriques, des visualisations graphiques seront utilisées pour analyser les performances des modèles et identifier leurs limites :
Histogramme des distributions des prix réels et prédits :
- Cet histogramme compare la distribution des valeurs réelles avec celle des valeurs prédites.
- Il permet de visualiser si le modèle capture correctement la dispersion des prix. Une forte divergence entre les deux distributions reflète les limites du modèle.
Scatter plot (Prix réels vs Prix prédits) :
- Ce graphique montre la relation entre les valeurs réelles et leurs prédictions.
- Idéalement, les points devraient être proches de la ligne idéale
y=x, indiquant des prédictions précises. Toute dispersion autour de cette ligne reflète les erreurs du modèle.
Distribution des résidus :
- Les résidus sont les différences entre les valeurs réelles et prédites.
- Ce graphique permet d’analyser les biais potentiels du modèle. Une distribution centrée autour de $0$ et symétrique indique un modèle équilibré, tandis qu'une asymétrie ou une concentration des résidus signale des problèmes de sous-prédiction ou sur-prédiction.
Ces visualisations fournissent une compréhension qualitative des performances des modèles et complètent les métriques quantitatives pour une analyse plus approfondie.
En adoptant ces métriques et ces graphiques comme base commune, nous pouvons comparer de manière cohérente les performances des différents modèles, qu’il s’agisse du modèle baseline ou des modèles plus complexes à venir.
$c$. Modèle Baseline¶
Le modèle baseline est une étape fondamentale dans tout projet de machine learning. Il s’agit de construire un modèle simple qui sert de point de référence pour évaluer les performances des modèles plus complexes développés ultérieurement. Dans le contexte de ce projet, le modèle baseline consiste à estimer le prix des maisons en utilisant une mesure centralisée, comme la moyenne des prix dans l’ensemble d’entraînement, en fonction du nombre de chambres.
Pour mettre en place ce modèle baseline, nous implémentons un code permettant de calculer les prédictions basées sur la moyenne des prix pour chaque nombre de chambres dans l’ensemble d’entraînement.
# Étape 1 : Calcul du prix moyen par nombre de chambres (baseline)
baseline_predictions = price_train.groupby(data_prix_train['bedrooms']).mean()
# Étape 2 : Prédiction sur l'ensemble de test en utilisant les moyennes de l'ensemble train
price_test_predicted = data_prix_test['bedrooms'].map(baseline_predictions)
# Étape 3 : Évaluation des performances du modèle baseline
mae_baseline = mean_absolute_error(price_test, price_test_predicted)
r2_baseline = r2_score(price_test, price_test_predicted)
rmse_baseline = np.sqrt(mean_squared_error(price_test, price_test_predicted))
print(f"Évaluation du modèle baseline :")
print(f"MAE (Mean Absolute Error) : {mae_baseline:.2f}")
print(f"RMSE (Root Mean Squared Error) : {rmse_baseline:.2f}")
print(f"R² (Coefficient de détermination) : {r2_baseline:.2f}")
Évaluation du modèle baseline : MAE (Mean Absolute Error) : 1632309.57 RMSE (Root Mean Squared Error) : 2043163.18 R² (Coefficient de détermination) : 0.21
Le modèle baseline affiche des performances limitées avec un MAE de $1,632,309.57$, indiquant une erreur moyenne absolue importante dans les prédictions des prix des maisons. Le RMSE de $2,043,163.18$, supérieur au MAE, montre que des écarts significatifs (grandes erreurs) existent dans les prédictions, ces derniers étant amplifiés par la pénalisation quadratique du RMSE. Enfin, le $R^2$ de $0.21$ révèle que le modèle explique seulement $21\%$ de la variance des prix réels, ce qui souligne sa faible capacité à capturer les relations sous-jacentes entre les variables explicatives et la cible.
Conclusion : Ces résultats, bien qu’attendus pour un modèle simple basé sur des moyennes, confirment les limites du modèle baseline. Il ne capture pas la complexité des données et sert uniquement de point de référence minimal pour comparer les performances des modèles plus avancés.
Pour mieux comprendre les performances du modèle baseline et identifier ses limites, nous analysons ses prédictions à l’aide de visualisations comparant les prix réels et prédits, ainsi que la distribution des résidus à travers le code suivant.
# Comparaison des distributions des valeurs réelles et prédites
plt.figure(figsize=(10, 6))
plt.hist(price_test, bins=20, alpha=0.5, label='Valeurs réelles')
plt.hist(price_test_predicted.dropna(), bins=20, alpha=0.5, label='Valeurs prédites (baseline)')
plt.xlabel('Prix des maisons')
plt.ylabel('Fréquence')
plt.title('Comparaison des distributions des prix (réels vs prédits)')
plt.legend()
plt.show()
# Scatter plot des valeurs réelles vs prédites
plt.figure(figsize=(10, 6))
plt.scatter(price_test, price_test_predicted, alpha=0.5, label="Prédictions baseline")
plt.plot([price_test.min(), price_test.max()], [price_test.min(), price_test.max()], 'r--', label="Ligne idéale (y=x)")
plt.xlabel("Prix réels")
plt.ylabel("Prix prédits")
plt.title("Scatter plot : Prix réels vs Prix prédits (Baseline)")
plt.legend()
plt.show()
# Distribution des résidus
residuals = price_test - price_test_predicted
plt.figure(figsize=(10, 6))
plt.hist(residuals.dropna(), bins=20, alpha=0.7, label="Résidus")
plt.axvline(x=0, color='r', linestyle='--', label="Moyenne des résidus = 0")
plt.xlabel("Résidus (Prix réels - Prix prédits)")
plt.ylabel("Fréquence")
plt.title("Distribution des résidus")
plt.legend()
plt.show()
Analyse des graphiques¶
- Histogramme des distributions des prix réels et prédits
Cet histogramme met en évidence une grande divergence entre la distribution des prix réels et celle des prix prédits par le modèle baseline. Les prix réels sont bien répartis sur une large plage de valeurs, montrant une forte variabilité, tandis que les prix prédits se concentrent autour de quelques moyennes spécifiques. Ces pics reflètent la nature simpliste du modèle baseline, qui calcule une moyenne par nombre de chambres sans prendre en compte d'autres caractéristiques pertinentes, comme la superficie, la localisation, ou les équipements. Cette différence indique que le modèle ne capture pas la complexité des données et qu'il simplifie excessivement la relation entre les variables explicatives et la cible.
- Scatter plot (Prix réels vs Prix prédits)
Le scatter plot montre une faible correspondance entre les prix réels et leurs prédictions. La majorité des points sont éloignés de la ligne idéale y=x, ce qui indique des erreurs importantes dans les prédictions. Les prédictions du modèle baseline sont constantes pour chaque groupe de chambres, ce qui se traduit par des lignes horizontales. Cela confirme l'incapacité du modèle à capturer la variabilité des prix réels et à fournir des prédictions précises.
- Distribution des résidus
La distribution des résidus (différences entre les prix réels et les prix prédits) met en évidence une large dispersion autour de $0$, avec des écarts importants dans les résidus positifs et négatifs. Cela indique que le modèle sous-prédit et sur-prédit de manière significative dans certains cas, sans tendance claire. Bien que la moyenne des résidus soit proche de $0$, leur dispersion montre une variabilité importante que le modèle ne parvient pas à expliquer.
Conclusion¶
Les trois graphiques montrent clairement que le modèle baseline est très limité dans sa capacité à capturer les relations complexes entre les caractéristiques des maisons et leurs prix. Il simplifie excessivement la tâche en se basant uniquement sur des moyennes par nombre de chambres, ce qui entraîne des prédictions imprécises et des erreurs significatives. Ces résultats confirment que le modèle baseline, bien qu'utile comme point de départ, doit être amélioré avec des approches plus sophistiquées pour obtenir de meilleures performances.
Pour aller au-delà des limitations du modèle baseline, nous introduisons un modèle de régression linéaire, qui vise à établir une relation linéaire entre les caractéristiques explicatives des maisons et leurs prix, permettant ainsi de mieux capturer les variations et d’améliorer la précision des prédictions.
$d$. Modèle de regression linéaire¶
Le modèle de régression linéaire est une méthode statistique et de machine learning utilisée pour modéliser la relation entre une variable cible continue et une ou plusieurs variables explicatives. Il repose sur l'hypothèse que cette relation peut être exprimée sous la forme d'une équation linéaire :
$y = \beta_0 + \beta_1x_1 + \beta_2x_2 + \ldots + \beta_nx_n + \epsilon$
où
- $ \beta_0 $ est l'ordonnée à l'origine (intercept),
- $ \beta_i $ sont les coefficients associés aux variables explicatives $ x_i $,
- et $ \epsilon $ représente l'erreur résiduelle.
La régression linéaire ajuste ces coefficients pour minimiser l'erreur entre les valeurs prédites et les valeurs réelles, généralement en utilisant la méthode des moindres carrés. Ce modèle est apprécié pour sa simplicité, sa capacité d'interprétation et sa performance dans les contextes où les relations entre les variables sont approximativement linéaires. Cependant, il est sensible aux hypothèses sous-jacentes (comme la normalité des résidus et l'absence de colinéarité) et peut avoir des performances limitées dans des problèmes non linéaires ou avec des données complexes.
Le code suivant permet de construire ce modèle dans le cadre de notre projet en utilisant toutes les variables explicatives de notre jeu de donnée traité.
# Ajout d'une constante pour le biais (intercept) dans le modèle
X_train_with_const = sm.add_constant(data_prix_train)
# Construire le modèle de régression linéaire avec statsmodels
linear_model_stats = sm.OLS(price_train, X_train_with_const).fit()
# Afficher le résumé statistique
print(linear_model_stats.summary())
OLS Regression Results
==============================================================================
Dep. Variable: price R-squared: 0.702
Model: OLS Adj. R-squared: 0.695
Method: Least Squares F-statistic: 114.4
Date: Mon, 02 Dec 2024 Prob (F-statistic): 1.92e-156
Time: 21:06:26 Log-Likelihood: -9963.8
No. Observations: 647 AIC: 1.996e+04
Df Residuals: 633 BIC: 2.002e+04
Df Model: 13
Covariance Type: nonrobust
====================================================================================================
coef std err t P>|t| [0.025 0.975]
----------------------------------------------------------------------------------------------------
const -2.297e+05 1.92e+05 -1.194 0.233 -6.08e+05 1.48e+05
area 236.7044 24.856 9.523 0.000 187.894 285.515
bedrooms 2.055e+05 7.36e+04 2.793 0.005 6.1e+04 3.5e+05
bathrooms 9.687e+05 8.42e+04 11.508 0.000 8.03e+05 1.13e+06
stories 3.767e+05 6.06e+04 6.216 0.000 2.58e+05 4.96e+05
mainroad 6.077e+05 1.63e+05 3.722 0.000 2.87e+05 9.28e+05
guestroom 3.441e+05 1.3e+05 2.657 0.008 8.98e+04 5.98e+05
basement 3.157e+05 1.12e+05 2.821 0.005 9.6e+04 5.35e+05
hotwaterheating 1.15e+06 2.08e+05 5.540 0.000 7.42e+05 1.56e+06
air_conditioning 9.607e+05 1.12e+05 8.574 0.000 7.41e+05 1.18e+06
parking 3.647e+05 6.15e+04 5.931 0.000 2.44e+05 4.85e+05
prefarea 5.969e+05 1.16e+05 5.152 0.000 3.69e+05 8.24e+05
furnishing_status_furnished -3.154e+04 1.05e+05 -0.301 0.764 -2.37e+05 1.74e+05
furnishing_status_semi-furnished 1.066e+05 8.99e+04 1.185 0.236 -7e+04 2.83e+05
furnishing_status_unfurnished -3.048e+05 8.82e+04 -3.454 0.001 -4.78e+05 -1.31e+05
==============================================================================
Omnibus: 49.090 Durbin-Watson: 1.970
Prob(Omnibus): 0.000 Jarque-Bera (JB): 92.859
Skew: 0.487 Prob(JB): 6.85e-21
Kurtosis: 4.580 Cond. No. 4.87e+19
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The smallest eigenvalue is 9.37e-30. This might indicate that there are
strong multicollinearity problems or that the design matrix is singular.
Analyse des coefficients attribués à chaque caractéristique¶
Les coefficients estimés par le modèle de régression linéaire indiquent l'impact de chaque caractéristique sur le prix des maisons, tout en tenant compte des autres variables. Voici une analyse des coefficients principaux lorsque nous considérons euros comme unité monnétaire :
area ($236.7044\,€$) :
- Ce coefficient positif montre que chaque mètre carré supplémentaire de surface habitable augmente le prix de la maison de $236.70 \,€$ en moyenne, toutes les autres variables étant égales. Ceci reflète l'importance de la superficie dans la valorisation immobilière.
bedrooms ($205,500\,€$) :
- Un nombre supplémentaire de chambres contribue à une augmentation moyenne de $205,500 \,€$ dans le prix de la maison, toutes les autres variables étant égales. Ce qui reflète l'importance des chambres comme critère clé dans l'évaluation des maisons.
bathrooms ($968,700\,€$) :
- Ce coefficient positif montre que, toute chose étant égale par ailleurs, chaque salle de bain supplémentaire ajoute en moyenne $968,700 \,€$ au prix de la maison. C'est l'une des variables les plus influentes, probablement en raison de la valeur perçue des salles de bain modernes ou multiples.
stories ($376,700\,€$) :
- Avec ce coefficient positif, toutes les autres variables étant égales, une maison avec un étage supplémentaire voit son prix augmenter en moyenne de $376.700 \,€$. Cela peut s’expliquer par une préférence pour les maisons à plusieurs niveaux, offrant plus d’espace sans augmenter l’empreinte au sol.
mainroad ($607,700\,€$) :
- Une maison située près d'une route principale voit son prix augmenter significativement, de $607,700 \,€$ en moyenne. Cette proximité peut être perçue comme un avantage en termes d'accessibilité, bien que cela puisse également avoir des inconvénients (comme le bruit).
air_conditioning ($960,700\,€$) :
- La présence de la climatisation ajoute en moyenne $960,700 \,€$ au prix, soulignant son rôle important dans le confort des maisons.
guestroom ($344,100 \,€$) :
- La présence d’une chambre d’amis augmente le prix moyen de la maison de $344,100 \,€$. Cela reflète l'importance accordée à des espaces supplémentaires pour accueillir des invités ou pour un usage personnel.
basement ($315,700 \,€$) :
- Une maison avec un sous-sol voit son prix augmenter en moyenne de $315,700 \,€$. Les sous-sols sont souvent considérés comme des espaces polyvalents, ajoutant de la valeur à la maison.
hotwaterheating ($1.150.000 \,€$) :
- La présence d'un système de chauffage à eau chaude est associée à une augmentation moyenne de $1,150,000 \,€$. Ce coefficient positif élevé montre l'importance perçue des équipements modernes pour le confort et l'efficacité énergétique.
air_conditioning ($960,700 \,€$) :
- La présence de climatisation contribue à une augmentation moyenne de $960,700 \,€$ du prix. Cela reflète son rôle dans le confort, notamment dans les régions où les températures sont élevées.
parking ($364,700 \,€$) :
- Chaque place de parking supplémentaire augmente le prix de la maison de $364,700 \,€$ en moyenne. Les places de parking sont souvent perçues comme un élément de commodité majeur, en particulier dans les zones urbaines.
prefarea ($596,900 \,€$) :
- Vivre dans une zone privilégiée augmente le prix moyen de la maison de $596,900 \,€$. Cela met en évidence l'importance de l'emplacement dans l'évaluation immobilière.
furnishing_status_furnished ($-31.540 \,€$) :
- Toute chose étant égale par ailleurs, ce coeeficient négatif montre que une maison meublée voit son prix diminué de $31.540 \,€$ par rapport à une maison non meublée ou semi-meublée. Toutefois, cette variable n'est pas significative ($p = 0.764$), donc son impact est négligeable dans ce modèle.
furnishing_status_semi-furnished ($106.600 \,€$) :
- Une maison semi-meublée a un prix supérieur de $106.600 \,€$ par rapport à une maison meublée. Cependant, cette variable n'est pas significative ($p = 0.236$), ce qui suggère que son influence est incertaine.
furnishing_status_unfurnished ($-304,800\,€$) :
- Un statut de maison non meublée réduit significativement son prix en moyenne de $304,800 \,€$ par rapport à d'autres statuts, reflétant l'importance des meubles dans la valorisation d'une maison.
Variables significatives¶
Les variables significatives sont identifiées grâce à la valeur $p$ ($P>|t|$). Une variable est considérée comme statistiquement significative si sa valeur $p$ est inférieure à $0.05$. Voici les variables significatives dans ce modèle :
Les variables telles que area, bedrooms, bathrooms, stories, mainroad, guestroom, basement, hotwaterheating, air_conditioning, parking, prefarea et furnishing_status_unfurnished sont les variables significatives du modèle car elles ont des $p$-values inférieures à $0.05$.
Les variables comme furnishing_status_furnished et furnishing_status_semi-furnished ont des $p$-values élevées ($> 0.05$), indiquant qu'elles ne sont pas significatives pour le modèle.
Commentaire sur $R^2$¶
Le coefficient de détermination $R^2$ du modèle est $0.702$, ce qui signifie que $70.2 \, \%$ de la variance des prix des maisons est expliquée par les caractéristiques incluses dans le modèle, contrairement au modèle Baseline qui ne fourni que que $21\%$ . C'est un bon indicateur que le modèle capture une grande partie des variations des prix, mais il reste $29.8 \, \%$ de la variance non expliquée, ce qui pourrait être attribué à des facteurs non inclus dans les données ou à des relations non linéaires.
Conclusion¶
- Les variables comme la superficie (
area), le nombre de salles de bain (bathrooms), et la présence de climatisation (air_conditioning) sont les plus influentes et significatives pour prédire le prix des maisons. - Les variables avec des coefficients non significatifs (comme
furnishing_status_furnished) pourraient être exclues dans les futurs modèles pour réduire la complexité. - Le $R^2$ montre que le modèle est performant, mais il reste des variations inexpliquées, ce qui pourrait nécessiter des approches plus complexes comme des modèles non linéaires ou ensemblistes pour améliorer les prédictions.
Passons maintenant à l’évaluation du modèle de régression linéaire en utilisant nos métriques prédéfinies, à savoir le MAE, le RMSE, et le $R^2$, afin de quantifier ses performances sur les ensembles d’entraînement et de test.
# Étape 1 : Initialisation et entraînement du modèle de régression linéaire
linear_model = LinearRegression()
linear_model.fit(data_prix_train, price_train)
# Étape 2 : Prédictions sur les ensembles d'entraînement et de test
price_train_predicted = linear_model.predict(data_prix_train)
price_test_predicted = linear_model.predict(data_prix_test)
# Étape 3 : Évaluation des performances du modèle sur l'ensemble d'entraînement
mae_train = mean_absolute_error(price_train, price_train_predicted)
rmse_train = np.sqrt(mean_squared_error(price_train, price_train_predicted))
r2_train = r2_score(price_train, price_train_predicted)
print("Performance sur l'ensemble d'entraînement :")
print(f"MAE (Mean Absolute Error) : {mae_train:.2f}")
print(f"RMSE (Root Mean Squared Error) : {rmse_train:.2f}")
print(f"R² (Coefficient de détermination) : {r2_train:.2f}")
# Étape 4 : Évaluation des performances sur l'ensemble de test
mae_test = mean_absolute_error(price_test, price_test_predicted)
rmse_test = np.sqrt(mean_squared_error(price_test, price_test_predicted))
r2_test = r2_score(price_test, price_test_predicted)
print("\nPerformance sur l'ensemble de test :")
print(f"MAE (Mean Absolute Error) : {mae_test:.2f}")
print(f"RMSE (Root Mean Squared Error) : {rmse_test:.2f}")
print(f"R² (Coefficient de détermination) : {r2_test:.2f}")
Performance sur l'ensemble d'entraînement : MAE (Mean Absolute Error) : 886258.35 RMSE (Root Mean Squared Error) : 1179995.26 R² (Coefficient de détermination) : 0.70 Performance sur l'ensemble de test : MAE (Mean Absolute Error) : 901765.43 RMSE (Root Mean Squared Error) : 1199669.34 R² (Coefficient de détermination) : 0.73
Le modèle de régression linéaire présente des performances similaires sur les ensembles d’entraînement et de test, ce qui est un bon indicateur de sa capacité de généralisation. Sur l’ensemble d’entraînement, le MAE est de $886,258.35 \,€$, indiquant une erreur moyenne absolue modérée, tandis que le RMSE est légèrement plus élevé à $1,179,995.26 \,€$, reflétant l’impact des grandes erreurs sur les prédictions. Le $R^2$ de $0.70$ montre que le modèle explique $70 \, \%$ de la variance des prix dans les données d’entraînement, ce qui est une performance acceptable pour un modèle simple comme la régression linéaire.
Sur l’ensemble de test, les résultats sont très similaires, avec un MAE de $901,765.43 \,€$, un RMSE de $1,199,669.34 \,€$, et un $R^2$ de $0.73$, indiquant que le modèle est capable de maintenir ses performances sur des données qu’il n’a jamais vues. Le faible écart entre les métriques des ensembles d’entraînement et de test suggère que le modèle n’est ni sous-entraîné ni surentraîné, ce qui est un point positif.
Conclusion
Le modèle de régression linéaire est relativement performant pour expliquer la variance des prix des maisons et présente une bonne capacité de généralisation. Cependant, les erreurs (MAE et RMSE) restent élevées en valeur absolue, indiquant qu’il pourrait ne pas capturer toute la complexité des données. Ces résultats suggèrent qu’il est nécessaire d’explorer des modèles plus sophistiqués, comme des modèles non linéaires ou ensemblistes, pour améliorer les prédictions.
Pour compléter l’évaluation du modèle, nous analysons les prédictions à l’aide de visualisations, notamment la comparaison des distributions des prix réels et prédits, le scatter plot des valeurs réelles contre prédictions, ainsi que la distribution des résidus, afin de mieux comprendre les forces et les limites du modèle.
# Comparaison des distributions des valeurs réelles et prédites
plt.figure(figsize=(10, 6))
plt.hist(price_test, bins=20, alpha=0.5, label='Valeurs réelles')
plt.hist(price_test_predicted, bins=20, alpha=0.5, label='Valeurs prédites (Régression Linéaire)')
plt.xlabel('Prix des maisons')
plt.ylabel('Fréquence')
plt.title('Comparaison des distributions des prix (réels vs prédits - Régression Linéaire)')
plt.legend()
plt.show()
# Scatter plot des valeurs réelles vs prédites
plt.figure(figsize=(10, 6))
plt.scatter(price_test, price_test_predicted, alpha=0.5, label="Prédictions Régression Linéaire")
plt.plot([price_test.min(), price_test.max()], [price_test.min(), price_test.max()], 'r--', label="Ligne idéale (y=x)")
plt.xlabel("Prix réels")
plt.ylabel("Prix prédits")
plt.title("Scatter plot : Prix réels vs Prix prédits (Régression Linéaire)")
plt.legend()
plt.show()
# Distribution des résidus
residuals = price_test - price_test_predicted
plt.figure(figsize=(10, 6))
plt.hist(residuals, bins=20, alpha=0.7, label="Résidus")
plt.axvline(x=0, color='r', linestyle='--', label="Moyenne des résidus = 0")
plt.xlabel("Résidus (Prix réels - Prix prédits)")
plt.ylabel("Fréquence")
plt.title("Distribution des résidus (Régression Linéaire)")
plt.legend()
plt.show()
Analyse des graphiques¶
1. Histogramme des distributions des prix réels et prédits¶
Cet histogramme montre une correspondance relativement bonne entre les distributions des prix réels et ceux prédits par la régression linéaire. Les valeurs réelles (en bleu) et les prédictions (en orange) présentent une similarité dans leurs formes générales, bien que quelques différences subsistent dans certaines plages de prix, notamment autour des pics de distribution. Cela suggère que le modèle capture une partie importante des tendances des données, mais qu'il reste des erreurs non négligeables dans certaines plages de prix.
2. Scatter plot (Prix réels vs Prix prédits)¶
Le scatter plot montre une forte concentration de points autour de la ligne idéale $y = x$, ce qui indique que les prédictions du modèle sont proches des prix réels pour une grande partie des données. Cependant, on observe une dispersion plus marquée pour les prix plus élevés, ce qui indique que le modèle a plus de difficulté à prédire correctement les valeurs élevées. Cela pourrait être dû à une sous-représentation de ces cas dans les données d'entraînement ou à une limitation de la linéarité du modèle.
3. Distribution des résidus¶
La distribution des résidus (différences entre les prix réels et prédits) est centrée autour de $0$, ce qui est un bon signe d'équilibre des prédictions. Cependant, la présence de résidus plus importants dans les plages positives et négatives indique que certaines prédictions divergent significativement des valeurs réelles. Cette distribution symétrique, mais légèrement étalée, suggère que le modèle fonctionne bien dans la majorité des cas, mais qu'il est sensible aux écarts extrêmes, probablement liés aux valeurs aberrantes.
Conclusion¶
Les visualisations confirment que le modèle de régression linéaire est capable de capturer les tendances principales des données, avec une correspondance acceptable entre les prix réels et prédits. Cependant, les graphiques mettent également en évidence certaines limitations, notamment dans la prédiction des prix élevés et la présence de résidus significatifs dans ces cas. Bien que ce modèle offre une base solide, des approches plus complexes pourraient être nécessaires pour améliorer la précision globale, notamment dans la gestion des cas extrêmes.
Pour améliorer les performances de la prédiction des prix des maisons, nous introduisons le modèle de Random Forest, une méthode ensembliste qui combine plusieurs arbres de décision pour capturer les relations complexes entre les variables explicatives et la cible, tout en réduisant le risque de surapprentissage.
$e$. Modèle ensembliste (Random Forest)¶
Le modèle Random Forest est une méthode ensembliste puissante qui repose sur la combinaison de plusieurs arbres de décision pour effectuer des prédictions plus robustes et précises. Chaque arbre est construit sur un échantillon aléatoire des données (avec remise), et à chaque nœud, un sous-ensemble aléatoire des variables explicatives est utilisé pour déterminer la meilleure division. Cette approche introduit de la diversité entre les arbres, réduisant ainsi le risque de surapprentissage. Les prédictions finales sont obtenues en agrégeant les résultats de tous les arbres, soit par une moyenne (régression) soit par un vote majoritaire (classification). Le Random Forest est apprécié pour sa capacité à gérer des données complexes et bruitées, à capturer des interactions non linéaires, et à fournir une estimation de l’importance des variables, tout en nécessitant peu de réglages par rapport à d’autres modèles avancés. Cependant, il peut être coûteux en termes de calcul, surtout pour des ensembles de données volumineux.
L'algorithme suivant permet de mettre en place ce modèle.
# Étape 1 : Impact du nombre d’arbres (n_estimators)
n_estimators_list = [10, 50, 100, 200, 300, 500, 1000]
results_n_estimators = []
train_scores_n = []
test_scores_n = []
train_rmse_n = []
test_rmse_n = []
train_mae_n = []
test_mae_n = []
for n in n_estimators_list:
rf_model = RandomForestRegressor(n_estimators=n, max_depth=10, random_state=42)
rf_model.fit(data_prix_train, price_train)
# Prédictions
price_train_pred = rf_model.predict(data_prix_train)
price_test_pred = rf_model.predict(data_prix_test)
# Calcul des métriques
mae_train = mean_absolute_error(price_train, price_train_pred)
rmse_train = np.sqrt(mean_squared_error(price_train, price_train_pred))
r2_train = r2_score(price_train, price_train_pred)
mae_test = mean_absolute_error(price_test, price_test_pred)
rmse_test = np.sqrt(mean_squared_error(price_test, price_test_pred))
r2_test = r2_score(price_test, price_test_pred)
train_scores_n.append(r2_train)
test_scores_n.append(r2_test)
train_rmse_n.append(rmse_train)
test_rmse_n.append(rmse_test)
train_mae_n.append(mae_train)
test_mae_n.append(mae_test)
results_n_estimators.append({
'n_estimators': n,
'MAE_train': mae_train,
'RMSE_train': rmse_train,
'R2_train': r2_train,
'MAE_test': mae_test,
'RMSE_test': rmse_test,
'R2_test': r2_test
})
# Convertion des résultats en DataFrame
results_df_n_estimators = pd.DataFrame(results_n_estimators)
# Affichage les résultats
print("Performances des modèles en fonction du nombre d’arbres (n_estimators) :")
print(results_df_n_estimators)
# Étape 2 : Impact de la profondeur maximale des arbres (max_depth)
max_depth_list = [2, 5, 10, 15, 20, None]
results_max_depth = []
train_scores_d = []
test_scores_d = []
train_rmse_d = []
test_rmse_d = []
train_mae_d = []
test_mae_d = []
for depth in max_depth_list:
rf_model = RandomForestRegressor(n_estimators=100, max_depth=depth, random_state=42)
rf_model.fit(data_prix_train, price_train)
# Prédictions
price_train_pred = rf_model.predict(data_prix_train)
price_test_pred = rf_model.predict(data_prix_test)
# Calcul des métriques
mae_train = mean_absolute_error(price_train, price_train_pred)
rmse_train = np.sqrt(mean_squared_error(price_train, price_train_pred))
r2_train = r2_score(price_train, price_train_pred)
mae_test = mean_absolute_error(price_test, price_test_pred)
rmse_test = np.sqrt(mean_squared_error(price_test, price_test_pred))
r2_test = r2_score(price_test, price_test_pred)
train_scores_d.append(r2_train)
test_scores_d.append(r2_test)
train_rmse_d.append(rmse_train)
test_rmse_d.append(rmse_test)
train_mae_d.append(mae_train)
test_mae_d.append(mae_test)
results_max_depth.append({
'max_depth': depth,
'MAE_train': mae_train,
'RMSE_train': rmse_train,
'R2_train': r2_train,
'MAE_test': mae_test,
'RMSE_test': rmse_test,
'R2_test': r2_test
})
# Convertion les résultats en DataFrame
results_df_max_depth = pd.DataFrame(results_max_depth)
# Affichage des résultats
print("\n Performances des modèles en fonction de la profondeur maximale des arbres (max_depth) :")
print(results_df_max_depth)
Performances des modèles en fonction du nombre d’arbres (n_estimators) :
n_estimators MAE_train RMSE_train R2_train MAE_test \
0 10 372522.413065 531375.934013 0.939472 750439.549188
1 50 350494.623957 497011.431189 0.947048 729130.957683
2 100 338743.983533 486015.855147 0.949365 718917.344601
3 200 336862.027552 482783.660158 0.950036 714212.186444
4 300 335124.297200 477211.632495 0.951183 713476.288612
5 500 334231.273081 475965.560184 0.951438 710632.918176
6 1000 335104.654314 478700.232915 0.950878 709256.288929
RMSE_test R2_test
0 1.055299e+06 0.789215
1 1.011001e+06 0.806540
2 1.004118e+06 0.809165
3 1.001003e+06 0.810347
4 9.913788e+05 0.813977
5 9.892763e+05 0.814765
6 9.917088e+05 0.813853
Performances des modèles en fonction de la profondeur maximale des arbres (max_depth) :
max_depth MAE_train RMSE_train R2_train MAE_test \
0 2.0 1.111435e+06 1.432988e+06 0.559815 1.240652e+06
1 5.0 7.331432e+05 9.764724e+05 0.795605 9.007263e+05
2 10.0 3.387440e+05 4.860159e+05 0.949365 7.189173e+05
3 15.0 2.755026e+05 4.248214e+05 0.961313 7.090370e+05
4 20.0 2.728510e+05 4.233635e+05 0.961578 7.011220e+05
5 NaN 2.726488e+05 4.233523e+05 0.961580 7.007916e+05
RMSE_test R2_test
0 1.590881e+06 0.520969
1 1.184427e+06 0.734475
2 1.004118e+06 0.809165
3 1.007267e+06 0.807966
4 9.988796e+05 0.811151
5 9.990770e+05 0.811076
Analyse des performances du modèle Random Forest¶
Les résultats montrent que le modèle Random Forest ajuste progressivement ses performances en fonction du nombre d’arbres (n_estimators) et de la profondeur maximale des arbres (max_depth). Voici une analyse des deux paramètres.
1. Impact du nombre d’arbres (n_estimators):¶
Les performances du modèle s’améliorent avec l’augmentation du nombre d’arbres, jusqu’à atteindre une stabilisation au-delà de $300$ arbres. Par exemple :
- Sur l’ensemble d’entraînement, le $R^2$ passe de $0.94$ avec 10 arbres à $0.95$ avec $500$ arbres. Les erreurs moyennes se réduisent également, avec un MAE diminuant de $372,522.41\,€$ à $334,231.27\,€$.
- Sur l’ensemble de test, le $R^2$ augmente également, passant de $0.79$ avec $10$ arbres à $0.81$ avec 500 arbres. Le MAE diminue de $750,439.55\,€$ à $710,632.92\,€$, indiquant une meilleure précision des prédictions. Cependant, le gain marginal entre $300$ et $500$ arbres est minime, suggérant que l’ajout d’arbres au-delà de $300$ n’apporte que peu d’amélioration.
2. Impact de la profondeur maximale des arbres (max_depth) :¶
La profondeur des arbres joue un rôle crucial dans la capacité du modèle à capturer les relations complexes dans les données :
- Avec une profondeur faible (max_depth=2), le modèle est sous-ajusté, comme le montre un $R^2$ de $0.56$ sur l’ensemble d’entraînement et de $0.52$ sur l’ensemble de test. Les erreurs, comme le MAE de $1,111,435.00\,€$ sur l’ensemble d’entraînement, sont élevées, indiquant que le modèle est trop simplifié.
- Une profondeur plus élevée améliore considérablement les performances. À max_depth=10, le modèle atteint un $R^2$ de $0.95$ sur l’ensemble d’entraînement et de $0.81$ sur l’ensemble de test, avec un MAE de $709,173.00\,€$ sur le test.
- Une profondeur excessive (au-delà de $15$) offre peu d’amélioration sur l’ensemble de test, avec un $R^2$ stable autour de $0.81$. Cependant, le MAE et le RMSE de l’ensemble d’entraînement continuent de diminuer, signalant un possible surajustement.
Conclusion¶
Le modèle Random Forest, contrairement aux modèles Baseline et Linéaires, montre des performances solides avec une configuration de $300$ arbres et une profondeur maximale de $10$. Ces paramètres permettent d’équilibrer le compromis entre précision et généralisation. L’ajout de plus d’arbres ou l’augmentation excessive de la profondeur apporte des gains marginaux sur l’ensemble de test, tout en augmentant les risques de surajustement. Ces résultats confirment la capacité du modèle à capturer des relations complexes dans les données, tout en maintenant une bonne capacité de généralisation, ce qui en fait une méthode robuste pour ce projet contrairement à un modèle plus simple comme la régression linéaire.
Nous pouvons analyser les perofrmances de ce modèle à travers les graphiques suivants.
# Comparaison des distributions des valeurs réelles et prédites
plt.figure(figsize=(10, 6))
plt.hist(price_test, bins=20, alpha=0.5, label='Valeurs réelles')
plt.hist(price_test_pred, bins=20, alpha=0.5, label='Valeurs prédites (Random Forest)')
plt.xlabel('Prix des maisons')
plt.ylabel('Fréquence')
plt.title('Comparaison des distributions des prix (réels vs prédits - Random Forest)')
plt.legend()
plt.show()
# Scatter plot des valeurs réelles vs prédites
plt.figure(figsize=(10, 6))
plt.scatter(price_test, price_test_pred, alpha=0.5, label="Prédictions Random Forest")
plt.plot([price_test.min(), price_test.max()], [price_test.min(), price_test.max()], 'r--', label="Ligne idéale (y=x)")
plt.xlabel("Prix réels")
plt.ylabel("Prix prédits")
plt.title("Scatter plot : Prix réels vs Prix prédits (Random Forest)")
plt.legend()
plt.show()
# Distribution des résidus
residuals_rf = price_test - price_test_pred
plt.figure(figsize=(10, 6))
plt.hist(residuals_rf, bins=20, alpha=0.7, label="Résidus")
plt.axvline(x=0, color='r', linestyle='--', label="Moyenne des résidus = 0")
plt.xlabel("Résidus (Prix réels - Prix prédits)")
plt.ylabel("Fréquence")
plt.title("Distribution des résidus (Random Forest)")
plt.legend()
plt.show()
# Visualisation de l'impact du nombre d'arbres
plt.figure(figsize=(10, 6))
plt.plot(n_estimators_list, train_scores_n, label="R² - Train", marker='o')
plt.plot(n_estimators_list, test_scores_n, label="R² - Test", marker='o')
plt.xlabel("Nombre d'arbres (n_estimators)")
plt.ylabel("R²")
plt.title("Impact du nombre d'arbres sur les performances du Random Forest")
plt.legend()
plt.show()
plt.figure(figsize=(10, 6))
plt.plot(n_estimators_list, train_mae_n, label="MAE - Train", marker='o')
plt.plot(n_estimators_list, test_mae_n, label="MAE - Test", marker='o')
plt.xlabel("Nombre d'arbres (n_estimators)")
plt.ylabel("MAE")
plt.title("Impact du nombre d'arbres sur MAE (Random Forest)")
plt.legend()
plt.show()
plt.figure(figsize=(10, 6))
plt.plot(n_estimators_list, train_rmse_n, label="RMSE - Train", marker='o')
plt.plot(n_estimators_list, test_rmse_n, label="RMSE - Test", marker='o')
plt.xlabel("Nombre d'arbres (n_estimators)")
plt.ylabel("RMSE")
plt.title("Impact du nombre d'arbres sur RMSE (Random Forest)")
plt.legend()
plt.show()
# Visualisation de l'impact de la profondeur
plt.figure(figsize=(10, 6))
plt.plot([str(d) for d in max_depth_list], train_scores_d, label="R² - Train", marker='o')
plt.plot([str(d) for d in max_depth_list], test_scores_d, label="R² - Test", marker='o')
plt.xlabel("Profondeur maximale des arbres (max_depth)")
plt.ylabel("R²")
plt.title("Impact de la profondeur maximale sur les performances du Random Forest")
plt.legend()
plt.show()
plt.figure(figsize=(10, 6))
plt.plot([str(d) for d in max_depth_list], train_mae_d, label="MAE - Train", marker='o')
plt.plot([str(d) for d in max_depth_list], test_mae_d, label="MAE - Test", marker='o')
plt.xlabel("Profondeur maximale des arbres (max_depth)")
plt.ylabel("MAE")
plt.title("Impact de la profondeur maximale sur MAE (Random Forest)")
plt.legend()
plt.show()
plt.figure(figsize=(10, 6))
plt.plot([str(d) for d in max_depth_list], train_rmse_d, label="RMSE - Train", marker='o')
plt.plot([str(d) for d in max_depth_list], test_rmse_d, label="RMSE - Test", marker='o')
plt.xlabel("Profondeur maximale des arbres (max_depth)")
plt.ylabel("RMSE")
plt.title("Impact de la profondeur maximale sur RMSE (Random Forest)")
plt.legend()
plt.show()
Commentaires des graphiques¶
Comparaison des distributions des prix (réels vs prédits - Random Forest)
Ce graphique montre que la distribution des prix prédites par le modèle Random Forest suit globalement celle des prix réels, mais il existe quelques écarts. La courbe des prédictions semble légèrement moins étendue que celle des prix réels, indiquant que le modèle tend à lisser certaines variations. Ces écarts pourraient refléter des limitations du modèle dans la capture de certains comportements extrêmes.
Scatter plot : Prix réels vs Prix prédits (Random Forest)
Le nuage de points est bien aligné avec la ligne idéale $y=x$, ce qui démontre une bonne capacité du modèle à prédire les prix. Toutefois, quelques écarts importants sont visibles, surtout pour les valeurs les plus élevées, indiquant des prédictions moins précises pour les maisons les plus chères. Globalement, le modèle Random Forest est performant, avec une grande majorité des prédictions proches des valeurs réelles.
Distribution des résidus (Random Forest)
La distribution des résidus est centrée autour de zéro, ce qui est un bon signe d'un modèle bien ajusté. Toutefois, des queues légèrement plus épaisses montrent que certaines prédictions présentent des erreurs significatives, notamment dans les deux extrémités. Une distribution symétrique indique que le modèle ne montre pas de biais systématique dans ses prédictions.
Impact du nombre d'arbres sur les performances du Random Forest
L'analyse des graphes montre que l'augmentation du nombre d'arbres améliore les performances sur les ensembles d'entraînement et de test, mais les gains deviennent marginaux au-delà de $300$ arbres, ce qui suggère que l'ajout d'arbres supplémentaires n'apporte pas de valeur significative :
- Sur l'ensemble d'entraînement, le MAE diminue pour se stabiliser autour de $335,000€$, tandis que le RMSE atteint environ $475,000€$. Cela montre une meilleure précision des prédictions à mesure que le modèle devient plus robuste.
- Sur l'ensemble de test, le MAE et le RMSE diminuent également avec l'augmentation du nombre d'arbres, mais les gains deviennent minimes au-delà de 300 arbres. Le MAE reste légèrement supérieur à $700,000€$, et le RMSE est stable autour de $990,000€$, indiquant une bonne généralisation avec un compromis entre complexité et précision.
- En termes de $R^2$, les performances se stabilisent à environ $0.96$ pour l'entraînement et $0.81$ pour le test. Ce comportement indique que l'ajout d'arbres supplémentaires au-delà de 300 n'améliore pas significativement les performances globales.
Impact de la profondeur maximale des arbres sur les performances du Random Forest
L'augmentation de la profondeur maximale des arbres montre une tendance au surapprentissage à des valeurs plus élevées :
- Sur l'ensemble d'entraînement, le MAE et le RMSE diminuent fortement à mesure que la profondeur augmente, atteignant environ $275,000€$ pour le MAE et $420,000€$ pour le RMSE lorsque la profondeur est fixée à 15 ou plus. Cela reflète une capture accrue des relations dans les données d'entraînement.
- Sur l'ensemble de test, les performances se stabilisent pour des profondeurs supérieures à $10$, avec un MAE d'environ $710,000€$ et un RMSE autour de $1,000,000€$. Ces résultats suggèrent que des profondeurs plus élevées n'améliorent pas les performances sur des données non vues.
- Le $R^2$ pour l'entraînement atteint près de $0.96$, tandis que sur le test, il reste stable à $0.81$ au-delà d'une profondeur de 10, renforçant l'idée qu'une profondeur excessive mène à un surajustement.
Conclusion¶
Pour le modèle Random Forest, les paramètres optimaux semblent être $300$ arbres et une profondeur maximale de $10$, offrant un équilibre entre précision et généralisation. L'augmentation des hyperparamètres au-delà de ces valeurs n'apporte que des améliorations négligeables, tout en augmentant les risques de surapprentissage et le temps de calcul. Ces observations confirment la robustesse et la flexibilité de Random Forest pour ce type de problème de régression complexe.
$f$. Modèle ensembliste (Gradient Boosting)¶
Le Gradient Boosting est un modèle ensembliste qui construit une série d'arbres de décision faibles, généralement peu profonds, en les combinant de manière séquentielle pour améliorer les prédictions. Contrairement à la méthode Random Forest qui crée des arbres indépendants, le Gradient Boosting ajuste chaque arbre pour corriger les erreurs des arbres précédents. Ce processus repose sur l'optimisation d'une fonction de perte (comme l'erreur quadratique moyenne pour la régression), où chaque nouvel arbre est formé pour minimiser cette perte sur les résidus. L'algorithme attribue des poids plus élevés aux observations mal prédites, ce qui lui permet de se concentrer sur les erreurs les plus importantes. Bien qu'il offre une grande flexibilité et des performances élevées, le Gradient Boosting peut être sensible au surapprentissage, mais cela peut être contrôlé grâce à des hyperparamètres tels que n_estimators (nombre d'arbres), learning_rate (taux d'apprentissage), et max_depth (profondeur des arbres). Ce modèle est largement utilisé pour les tâches de régression et de classification grâce à son équilibre entre précision et interprétabilité.
L'algorithme suivant permet de mettre en place ce modèle.
# Étape 1 : Impact du nombre d'arbres (n_estimators)
n_estimators_list = [10, 50, 100, 200, 300, 500, 1000]
results_n_estimators = []
for n in n_estimators_list:
# Initialisation du modèle avec une profondeur fixe
gb_model = GradientBoostingRegressor(n_estimators=n, max_depth=3, learning_rate=0.1, random_state=42)
gb_model.fit(data_prix_train, price_train)
# Prédictions
price_train_pred = gb_model.predict(data_prix_train)
price_test_pred = gb_model.predict(data_prix_test)
# Calcul des métriques
mae_train = mean_absolute_error(price_train, price_train_pred)
rmse_train = np.sqrt(mean_squared_error(price_train, price_train_pred))
r2_train = r2_score(price_train, price_train_pred)
mae_test = mean_absolute_error(price_test, price_test_pred)
rmse_test = np.sqrt(mean_squared_error(price_test, price_test_pred))
r2_test = r2_score(price_test, price_test_pred)
# Enregistrement des résultats
results_n_estimators.append({
'n_estimators': n,
'MAE_train': mae_train,
'RMSE_train': rmse_train,
'R2_train': r2_train,
'MAE_test': mae_test,
'RMSE_test': rmse_test,
'R2_test': r2_test
})
# Convertion des résultats en DataFrame
results_df_n_estimators = pd.DataFrame(results_n_estimators)
# Affichage des résultats
print("Résultats en fonction du nombre d'arbres (n_estimators) :")
print(results_df_n_estimators)
# Étape 2 : Impact de la profondeur maximale des arbres (max_depth)
max_depth_list = [2, 5, 10, 15, 20, None]
results_max_depth = []
for depth in max_depth_list:
# Initialisation le modèle avec un nombre d'arbres fixe
gb_model = GradientBoostingRegressor(n_estimators=100, max_depth=depth, learning_rate=0.1, random_state=42)
gb_model.fit(data_prix_train, price_train)
# Prédictions
price_train_pred = gb_model.predict(data_prix_train)
price_test_pred = gb_model.predict(data_prix_test)
# Calcul des métriques
mae_train = mean_absolute_error(price_train, price_train_pred)
rmse_train = np.sqrt(mean_squared_error(price_train, price_train_pred))
r2_train = r2_score(price_train, price_train_pred)
mae_test = mean_absolute_error(price_test, price_test_pred)
rmse_test = np.sqrt(mean_squared_error(price_test, price_test_pred))
r2_test = r2_score(price_test, price_test_pred)
# Enregistrement des résultats
results_max_depth.append({
'max_depth': depth,
'MAE_train': mae_train,
'RMSE_train': rmse_train,
'R2_train': r2_train,
'MAE_test': mae_test,
'RMSE_test': rmse_test,
'R2_test': r2_test
})
# Convertion des résultats en DataFrame
results_df_max_depth = pd.DataFrame(results_max_depth)
# Affichage les résultats
print("Résultats en fonction de la profondeur maximale des arbres (max_depth) :")
print(results_df_max_depth)
Résultats en fonction du nombre d'arbres (n_estimators) :
n_estimators MAE_train RMSE_train R2_train MAE_test \
0 10 1.131274e+06 1.406440e+06 0.575974 1.234479e+06
1 50 6.984965e+05 9.212736e+05 0.818061 8.062376e+05
2 100 6.003094e+05 7.959148e+05 0.864205 7.373581e+05
3 200 4.943381e+05 6.559758e+05 0.907759 7.186480e+05
4 300 4.300068e+05 5.790954e+05 0.928113 7.301346e+05
5 500 3.392868e+05 4.721814e+05 0.952207 7.607541e+05
6 1000 2.308326e+05 3.646794e+05 0.971492 7.854549e+05
RMSE_test R2_test
0 1.546353e+06 0.547409
1 1.083638e+06 0.777742
2 1.023251e+06 0.801823
3 1.000902e+06 0.810386
4 1.018912e+06 0.803500
5 1.071355e+06 0.782752
6 1.134345e+06 0.756455
Résultats en fonction de la profondeur maximale des arbres (max_depth) :
max_depth MAE_train RMSE_train R2_train MAE_test \
0 2.0 736973.409451 980881.089672 0.793756 832480.712247
1 5.0 310506.122631 445429.141218 0.957469 681867.115748
2 10.0 34157.078994 214736.209018 0.990115 666880.233550
3 15.0 28143.356627 214491.360720 0.990138 688409.439325
4 20.0 28140.842550 214491.359122 0.990138 693845.800798
5 NaN 28140.842550 214491.359122 0.990138 694480.194657
RMSE_test R2_test
0 1.126974e+06 0.759610
1 9.870517e+05 0.815597
2 1.114974e+06 0.764702
3 1.184855e+06 0.734283
4 1.199702e+06 0.727582
5 1.199370e+06 0.727733
Analyse des performances du modèle Gradient Boosting¶
Les résultats obtenus pour le modèle Gradient Boosting mettent en évidence l'impact significatif des hyperparamètres n_estimators (nombre d'arbres) et max_depth (profondeur maximale des arbres) sur les performances du modèle. Voici une analyse détaillée de ces deux paramètres :
1. Impact du nombre d’arbres (n_estimators) :¶
L'augmentation du nombre d'arbres améliore les performances du modèle, tant sur l'ensemble d'entraînement que sur l'ensemble de test, jusqu'à un certain point où les gains deviennent marginaux, voire se dégradent :
- Sur l'ensemble d’entraînement, le $R^2$ passe de $0.58$ avec 10 arbres à $0.97$ avec $1000$ arbres, montrant que le modèle devient de plus en plus performant pour ajuster les données. Les erreurs moyennes diminuent significativement, avec un MAE passant de $1,131,274.00€$ à $230,832.60€$, et un RMSE passant de $1,406,440.00€$ à $364,679.40€$.
- Sur l'ensemble de test, les performances augmentent initialement, avec un $R^2$ passant de $0.55$ avec 10 arbres à un pic de $0.81$ avec 200 arbres. Les erreurs, mesurées par le MAE et le RMSE, diminuent initialement (MAE passant de $1,234,479.00€$ à $718,648.00€$, RMSE passant de $1,546,353.00€$ à $1,000,902.00€$). Cependant, au-delà de 300 arbres, le modèle commence à montrer des signes de surajustement, avec des performances légèrement dégradées sur l'ensemble de test ($R^2=0.75$, MAE=$785,454.90€$, RMSE=$1,134,345.00€$ avec 1000 arbres).
Ces observations soulignent qu'un nombre optimal d'arbres se situe autour de $200-300$ pour maintenir un bon équilibre entre ajustement et généralisation.
2. Impact de la profondeur maximale des arbres (max_depth) :¶
La profondeur maximale des arbres joue un rôle crucial dans la capacité du modèle à capturer des relations complexes dans les données. Cependant, des profondeurs excessives entraînent un surajustement :
- Avec une profondeur faible (max_depth=2), le modèle est sous-ajusté, avec un $R^2$ de $0.79$ sur l'entraînement et de $0.76$ sur le test. Les erreurs restent élevées, avec un MAE de $736,973.40€$ sur l'entraînement et 832,480.71€ sur le test.
- Une profondeur moyenne (max_depth=5) offre un bon compromis, avec un $R^2$ de $0.96$ sur l'entraînement et de $0.81$ sur le test. Les erreurs sont significativement réduites (MAE=$310,506.12€$ sur l'entraînement et $681,867.12€$ sur le test, RMSE=$445,429.14€$ sur l'entraînement et $987,051.70€$ sur le test).
- Avec des profondeurs élevées (max_depth=10 ou plus), le modèle montre des signes de surajustement. Par exemple, le $R^2$ sur l'entraînement atteint $0.99$, mais il diminue légèrement sur le test ($R^2=0.76$). Les erreurs sur le test augmentent également, avec un MAE=$666,880.23€$ et un RMSE=$1,114,974.00€$ pour max_depth=10.
Ces résultats indiquent qu'une profondeur modérée (max_depth=5) est optimale pour ce modèle, permettant un bon compromis entre la précision sur l'entraînement et la généralisation sur le test.
Conclusion¶
Le modèle Gradient Boosting montre des performances solides et une capacité à généraliser sur des données non vues lorsque les hyperparamètres sont bien calibrés contrairement aux modèles précédents :
- Nombre optimal d'arbres : Environ $200-300$, où le modèle atteint un bon compromis entre généralisation et précision, avec un $R^2$ stable autour de $0.81$ sur le test.
- Profondeur optimale des arbres : max_depth=5, permettant au modèle de capturer les relations complexes dans les données sans tomber dans le surajustement.
Toutefois, au-delà de ces valeurs, le modèle commence à surapprendre les données d'entraînement, ce qui se traduit par une dégradation des performances sur l'ensemble de test. Cela met en évidence l'importance de calibrer soigneusement les hyperparamètres pour maintenir un bon équilibre entre ajustement et généralisation.
Nous pouvons également visualiser les performances de ce modèle à travers les graphiques suivants.
# Comparaison des distributions des valeurs réelles et prédites
plt.figure(figsize=(10, 6))
plt.hist(price_test, bins=20, alpha=0.5, label='Valeurs réelles')
plt.hist(price_test_pred, bins=20, alpha=0.5, label='Valeurs prédites (Gradient Boosting)')
plt.xlabel('Prix des maisons')
plt.ylabel('Fréquence')
plt.title('Comparaison des distributions des prix (réels vs prédits - Gradient Boosting)')
plt.legend()
plt.show()
# Scatter plot des valeurs réelles vs prédites
plt.figure(figsize=(10, 6))
plt.scatter(price_test, price_test_pred, alpha=0.5, label="Prédictions Gradient Boosting")
plt.plot([price_test.min(), price_test.max()], [price_test.min(), price_test.max()], 'r--', label="Ligne idéale (y=x)")
plt.xlabel("Prix réels")
plt.ylabel("Prix prédits")
plt.title("Scatter plot : Prix réels vs Prix prédits (Gradient Boosting)")
plt.legend()
plt.show()
# Distribution des résidus
residuals_gb = price_test - price_test_pred
plt.figure(figsize=(10, 6))
plt.hist(residuals_gb, bins=20, alpha=0.7, label="Résidus")
plt.axvline(x=0, color='r', linestyle='--', label="Moyenne des résidus = 0")
plt.xlabel("Résidus (Prix réels - Prix prédits)")
plt.ylabel("Fréquence")
plt.title("Distribution des résidus (Gradient Boosting)")
plt.legend()
plt.show()
# Visualisation de l'impact du nombre d'arbres
plt.figure(figsize=(10, 6))
plt.plot(n_estimators_list, results_df_n_estimators['R2_train'], label="R² - Train", marker='o')
plt.plot(n_estimators_list, results_df_n_estimators['R2_test'], label="R² - Test", marker='o')
plt.xlabel("Nombre d'arbres (n_estimators)")
plt.ylabel("R²")
plt.title("Impact du nombre d'arbres sur les performances (Gradient Boosting)")
plt.legend()
plt.show()
plt.figure(figsize=(10, 6))
plt.plot(n_estimators_list, results_df_n_estimators['MAE_train'], label="MAE - Train", marker='o')
plt.plot(n_estimators_list, results_df_n_estimators['MAE_test'], label="MAE - Test", marker='o')
plt.xlabel("Nombre d'arbres (n_estimators)")
plt.ylabel("MAE")
plt.title("Impact du nombre d'arbres sur les performances (Gradient Boosting)")
plt.legend()
plt.show()
plt.figure(figsize=(10, 6))
plt.plot(n_estimators_list, results_df_n_estimators['RMSE_train'], label="RMSE - Train", marker='o')
plt.plot(n_estimators_list, results_df_n_estimators['RMSE_test'], label="RMSE - Test", marker='o')
plt.xlabel("Nombre d'arbres (n_estimators)")
plt.ylabel("RMSE")
plt.title("Impact du nombre d'arbres sur les performances (Gradient Boosting)")
plt.legend()
plt.show()
# Étape 2 : Impact de la profondeur maximale des arbres (max_depth)
max_depth_list = [2, 3, 5, 7, 10]
results_max_depth = []
for depth in max_depth_list:
# Initialisation du modèle avec un nombre d’arbres fixe
gb_model = GradientBoostingRegressor(n_estimators=100, max_depth=depth, learning_rate=0.1, random_state=42)
gb_model.fit(data_prix_train, price_train)
# Prédictions
price_train_pred = gb_model.predict(data_prix_train)
price_test_pred = gb_model.predict(data_prix_test)
# Calcul des métriques
mae_train = mean_absolute_error(price_train, price_train_pred)
rmse_train = np.sqrt(mean_squared_error(price_train, price_train_pred))
r2_train = r2_score(price_train, price_train_pred)
mae_test = mean_absolute_error(price_test, price_test_pred)
rmse_test = np.sqrt(mean_squared_error(price_test, price_test_pred))
r2_test = r2_score(price_test, price_test_pred)
# Enregistrement des résultats
results_max_depth.append({
'max_depth': depth,
'MAE_train': mae_train,
'RMSE_train': rmse_train,
'R2_train': r2_train,
'MAE_test': mae_test,
'RMSE_test': rmse_test,
'R2_test': r2_test
})
# Convertion des résultats en DataFrame
results_df_max_depth = pd.DataFrame(results_max_depth)
# Visualisation de l'impact de la profondeur maximale
plt.figure(figsize=(10, 6))
plt.plot(max_depth_list, results_df_max_depth['R2_train'], label="R² - Train", marker='o')
plt.plot(max_depth_list, results_df_max_depth['R2_test'], label="R² - Test", marker='o')
plt.xlabel("Profondeur maximale des arbres (max_depth)")
plt.ylabel("R²")
plt.title("Impact de la profondeur maximale des arbres (Gradient Boosting)")
plt.legend()
plt.show()
plt.figure(figsize=(10, 6))
plt.plot(max_depth_list, results_df_max_depth['MAE_train'], label="MAE - Train", marker='o')
plt.plot(max_depth_list, results_df_max_depth['MAE_test'], label="MAE - Test", marker='o')
plt.xlabel("Profondeur maximale des arbres (max_depth)")
plt.ylabel("MAE")
plt.title("Impact de la profondeur maximale des arbres (Gradient Boosting)")
plt.legend()
plt.show()
plt.figure(figsize=(10, 6))
plt.plot(max_depth_list, results_df_max_depth['RMSE_train'], label="RMSE - Train", marker='o')
plt.plot(max_depth_list, results_df_max_depth['RMSE_test'], label="RMSE - Test", marker='o')
plt.xlabel("Profondeur maximale des arbres (max_depth)")
plt.ylabel("RMSE")
plt.title("Impact de la profondeur maximale des arbres (Gradient Boosting)")
plt.legend()
plt.show()
Commentaires des graphiques¶
Comparaison des distributions des prix (réels vs prédits - Gradient Boosting)
Le graphique montre que la distribution des prix prédits par le modèle Gradient Boosting suit bien celle des prix réels. Cependant, on observe quelques écarts, notamment dans les queues de distribution, où le modèle semble avoir des difficultés à capturer certaines valeurs extrêmes. Globalement, le modèle parvient à reproduire les tendances principales, mais une légère amélioration pourrait être apportée pour mieux capturer les prix les plus élevés et les plus bas.Scatter plot : Prix réels vs Prix prédits (Gradient Boosting)
Ce graphique illustre la corrélation entre les prix réels et prédits. La majorité des points se situe près de la ligne idéale $y=x$, indiquant une bonne précision du modèle. Toutefois, quelques points éloignés, particulièrement pour les valeurs les plus élevées, signalent des prédictions moins précises dans ces cas. Ces écarts pourraient refléter des variations complexes ou des limites dans la capacité du modèle à généraliser pour ces observations.Distribution des résidus (Gradient Boosting)
La distribution des résidus est centrée autour de zéro, suggérant un modèle équilibré sans biais systématique. Cependant, des résidus légèrement plus importants aux extrémités montrent que certaines prédictions comportent des erreurs plus significatives. Une distribution symétrique, comme celle observée ici, est un indicateur positif pour un modèle de régression.Impact du nombre d'arbres sur les performances du Gradient Boosting
Les graphiques montrent que l'augmentation du nombre d'arbres améliore les performances, mais seulement jusqu'à un certain seuil :
- Sur l'ensemble d'entraînement, le MAE et le RMSE diminuent constamment avec l'ajout d'arbres, atteignant respectivement environ $230,000€$ et $360,000€$ avec 1000 arbres. Le $R^2$ progresse également pour atteindre près de $0.97$, indiquant une excellente adéquation aux données d'entraînement.
- Sur l'ensemble de test, les métriques (MAE et RMSE) diminuent jusqu'à environ $200$ arbres, où elles atteignent un minimum de $720,000€$ et $1,000,000€$ respectivement, avant de remonter légèrement avec un grand nombre d'arbres. Cela peut indiquer un début de surapprentissage. Le $R^2$ pour le test suit une tendance similaire, atteignant un maximum de $0.81$ pour environ $200$ arbres avant de diminuer légèrement.
- Impact de la profondeur maximale des arbres sur les performances du Gradient Boosting
L'augmentation de la profondeur des arbres a un impact notable sur les performances, mais des profondeurs trop importantes peuvent entraîner un surapprentissage :
- Sur l'ensemble d'entraînement, le MAE et le RMSE diminuent fortement avec une profondeur croissante, atteignant environ $28,000€$ pour le MAE et $210,000€$ pour le RMSE à des profondeurs élevées (10 ou plus). Cela montre une forte capacité du modèle à s'adapter aux données d'entraînement.
- Sur l'ensemble de test, les performances sont meilleures avec une profondeur modérée (entre 5 et 7), où le MAE et le RMSE atteignent respectivement environ $680,000€$ et $980,000€$. Une profondeur excessive (10 ou plus) entraîne une dégradation des performances, avec une augmentation du RMSE au-delà de $1,100,000€$. Le $R^2$ reste stable autour de $0.81$ pour des profondeurs entre $5$ et $7$, mais diminue pour des profondeurs plus importantes.
Conclusion¶
Le modèle Gradient Boosting montre des performances solides et la capacité de capturer les tendances des prix des maisons. Cependant, il est essentiel de trouver un équilibre dans le choix des hyperparamètres pour éviter le surajustement. Une configuration de $300$ arbres et une profondeur maximale entre $5$ et $7$ semble offrir le meilleur compromis entre précision et généralisation, comme le montrent les métriques ($R^2$, MAE, RMSE) sur les ensembles d'entraînement et de test.
$g$. Importance des Features pour un Modèle Ensembliste¶
- Extraction des “Features Importances”
Les modèles ensemblistes comme Random Forest et Gradient Boosting incluent des fonctionnalités intégrées dans Scikit-learn pour mesurer l’importance des variables explicatives. Cette importance est calculée automatiquement au cours de l’entraînement du modèle et est accessible via l’attribut feature_importances_. Cet attribut retourne un score pour chaque feature, représentant son rôle relatif dans les prédictions globales du modèle.
- Méthodes d'interprétation des Résultats
- Features importantes : Les features ayant une importance élevée contribuent fortement à la prédiction du prix des maisons. Ces variables sont essentielles pour le modèle et devraient être conservées pour maintenir de bonnes performances.
- Features moins importantes : Les features avec une importance faible ou nulle peuvent être considérées comme peu pertinentes ou redondantes. Leur suppression peut améliorer l’efficacité du modèle sans affecter ses performances, tout en réduisant le bruit et le risque de surapprentissage.
- Calcul de l’Importance des Features
L’importance des features dans les modèles ensemblistes est calculée à l’aide de la réduction moyenne de l’impureté (Mean Decrease in Impurity - MDI). Chaque fois qu’une feature est utilisée pour effectuer une division dans un arbre de décision, elle réduit l’impureté des sous-ensembles créés (par exemple, la réduction de l’entropie ou de l’indice de Gini). Les contributions de cette réduction pour chaque feature sont additionnées sur tous les arbres et normalisées pour obtenir un score.
Formellement :
- La réduction de l’impureté pour une feature $f_i$ est calculée comme :
$$ \text{Importance}(f_i) = \frac{1}{T} \sum_{t=1}^{T} \sum_{s \in \text{splits}(t, f_i)} \Delta \text{Impureté}(s) $$
où :
- $T$ est le nombre total d’arbres.
- $\Delta \text{Impureté}(s)$ représente la réduction de l’impureté pour un split $s$ impliquant la feature $f_i$.
Dans un modèle Random Forest, ces scores sont moyennés sur tous les arbres.
Dans un modèle Gradient Boosting, la même logique est utilisée, mais les scores sont pondérés par l’importance de chaque arbre dans le processus de boosting.
$a$. Importance des Features pour le Modèle Random Forest¶
Pour le modèle Random Forest de $300$ arbres avec $10$ profondeurs, nous avons mis en place le code suivant pour trouver les features clés pour optimiser les performances et réduire la complexité.
# Entraînement du modèle Random Forest
random_forest_model = RandomForestRegressor(n_estimators=300, max_depth=10, random_state=42)
random_forest_model.fit(data_prix_train, price_train)
# Extraction des importances des features
feature_importances = random_forest_model.feature_importances_
features = data_prix_train.columns
# Création d'un DataFrame pour organiser les résultats
importance_df = pd.DataFrame({'Feature': features, 'Importance': feature_importances})
importance_df = importance_df.sort_values(by='Importance', ascending=False)
# Visualisation des importances des features
plt.figure(figsize=(12, 8))
plt.barh(importance_df['Feature'], importance_df['Importance'], color='skyblue')
plt.xlabel("Importance des Features")
plt.ylabel("Features")
plt.title("Importance des Features selon Random Forest")
plt.gca().invert_yaxis() # Inverser l'ordre des features pour une meilleure lisibilité
plt.show()
# Affichage des résultats sous forme de tableau
print(importance_df)
Feature Importance 0 area 0.407431 2 bathrooms 0.268716 9 parking 0.055335 3 stories 0.048622 8 air_conditioning 0.037482 7 hotwaterheating 0.029921 13 furnishing_status_unfurnished 0.028110 1 bedrooms 0.026771 10 prefarea 0.025609 6 basement 0.022000 5 guestroom 0.017345 11 furnishing_status_furnished 0.013665 12 furnishing_status_semi-furnished 0.012710 4 mainroad 0.006283
Analyse des Importances des Features - Random Forest¶
L’analyse des importances des features révèle que area (superficie) est la variable la plus influente dans la prédiction du prix des maisons, avec une contribution de $40.74\%$, suivie par bathrooms ($26.87\%$), ce qui souligne leur rôle crucial dans la détermination du prix. Des variables comme parking ($5.53\%$) et stories ($4.86\%$) jouent également un rôle important, bien qu’avec une influence moindre. Les fonctionnalités liées à l’équipement et au confort, telles que air_conditioning ($3.75\%$) et hotwaterheating ($3\%$), montrent une contribution significative mais moins centrale.
En revanche, des variables comme mainroad ($0.06\%$) et furnishing_status_furnished ($1.37\%$) ont une importance négligeable, suggérant qu’elles contribuent peu à la prédiction globale et pourraient être candidates à une élimination dans un processus de simplification du modèle.
Conclusion¶
Ces résultats confirment que les caractéristiques liées à la superficie, au nombre de salles de bain et aux commodités principales sont essentielles pour expliquer les variations des prix. À l’inverse, les variables avec des importances faibles ou nulles, telles que mainroad, pourraient être supprimées pour améliorer l’efficacité et la simplicité du modèle sans nuire à ses performances.
$b$. Importance des Features pour le Modèle Gradient Boosting¶
Pour le modèle Gradient Boosting avec $300$ itérations et une profondeur maximale de $6$, nous avons mis en place le code suivant pour identifier les features clés afin d’optimiser les performances et réduire la complexité.
# Entraînement du modèle Gradient Boosting
gradient_boosting_model = GradientBoostingRegressor(n_estimators=300, max_depth=6, random_state=42, learning_rate=0.1)
gradient_boosting_model.fit(data_prix_train, price_train)
# Extraction des importances des features
feature_importances_gb = gradient_boosting_model.feature_importances_
features = data_prix_train.columns
# Création d'un DataFrame pour organiser les résultats
importance_df_gb = pd.DataFrame({'Feature': features, 'Importance': feature_importances_gb})
importance_df_gb = importance_df_gb.sort_values(by='Importance', ascending=False)
# Visualisation des importances des features
plt.figure(figsize=(12, 8))
plt.barh(importance_df_gb['Feature'], importance_df_gb['Importance'], color='salmon')
plt.xlabel("Importance des Features")
plt.ylabel("Features")
plt.title("Importance des Features selon Gradient Boosting")
plt.gca().invert_yaxis() # Inverser l'ordre des features pour une meilleure lisibilité
plt.show()
# Affichage des résultats sous forme de tableau
print(importance_df_gb)
Feature Importance 0 area 0.397286 2 bathrooms 0.284809 9 parking 0.054499 8 air_conditioning 0.043056 3 stories 0.041321 7 hotwaterheating 0.031287 13 furnishing_status_unfurnished 0.028950 10 prefarea 0.023740 5 guestroom 0.023711 1 bedrooms 0.023130 6 basement 0.019422 11 furnishing_status_furnished 0.011295 12 furnishing_status_semi-furnished 0.010833 4 mainroad 0.006661
Analyse des Importances des Features - Gradient Boosting¶
L’analyse des importances des features selon le modèle Gradient Boosting révèle des résultats cohérents et pertinents pour expliquer la prédiction des prix des maisons. La variable area est clairement la plus influente, avec une importance de $39.7\%$, ce qui confirme que la superficie de la maison est un facteur déterminant pour estimer son prix comme nous l'avions constaté lors de l'étude bivariée. La deuxième variable la plus contributive est bathrooms, avec une importance de $28.5\%$, soulignant l’impact significatif du nombre de salles de bain sur la valeur d’une maison. Ces deux variables dominent largement le modèle, représentant ensemble environ $68\%$ de l’importance totale.
Les autres variables comme parking, air_conditioning, et stories ont une contribution modérée, ce qui suggère qu’elles jouent également un rôle non négligeable dans les prédictions. En revanche, certaines variables comme mainroad ($0.67\%$) et les différents états de mobilier (furnishing_status_furnished et furnishing_status_semi-furnished) ont une très faible importance, indiquant qu’elles sont moins pertinentes pour ce modèle. Cela peut être dû à une faible variation dans ces variables ou à un impact négligeable sur la valeur finale.
Conclusion¶
Pour optimiser le modèle, les variables comme mainroad, furnishing_status_furnished, et furnishing_status_semi-furnished pourraient être envisagées pour suppression ou révision, car elles apportent peu à la précision du modèle. En revanche, des efforts doivent être concentrés sur les variables clés telles que area et bathrooms, qui sont les moteurs principaux de la prédiction et justifient leur rôle essentiel dans le modèle. Cela permettra de réduire le bruit, améliorer l’interprétabilité et potentiellement renforcer la robustesse du modèle.
$h$. Fine-tuning des Modèles Ensemblistes : RandomizedSearchCV et GridSearchCV¶
La performance des modèles ensemblistes comme Random Forest et Gradient Boosting dépend fortement du choix des hyperparamètres. Les méthodes RandomizedSearchCV et GridSearchCV fournies par la bibliothèque Scikit-learn permettent d’optimiser ces hyperparamètres en testant différentes combinaisons et en identifiant celle qui maximise les performances du modèle.
$a$. Différences entre RandomizedSearchCV et GridSearchCV¶
1. GridSearchCV¶
- Principe : GridSearchCV effectue une recherche exhaustive en testant toutes les combinaisons possibles d’hyperparamètres spécifiés.
- Avantages : Permet d’explorer systématiquement tout l’espace des hyperparamètres.
- Inconvénients : Très coûteux en termes de temps de calcul, surtout lorsque le nombre de combinaisons est élevé.
2. RandomizedSearchCV¶
- Principe : RandomizedSearchCV sélectionne un nombre fixe de combinaisons aléatoires à partir de l’espace des hyperparamètres spécifié.
- Avantages : Moins coûteux en temps de calcul. Permet d’explorer un espace plus large si le nombre d’itérations est bien défini.
- Inconvénients : Risque de passer à côté de la meilleure combinaison si le nombre d’itérations est trop faible.
En résumé, GridSearchCV est préférable lorsque l’espace des hyperparamètres est petit et que le temps de calcul n’est pas un facteur limitant, tandis que RandomizedSearchCV est plus adapté lorsque l’espace des hyperparamètres est vaste ou que les ressources sont limitées.
$b$. Fine-tuning pour le Modèle Random Forest¶
Pour Random Forest, les hyperparamètres importants à optimiser sont :
- n_estimators : Nombre d’arbres dans la forêt.
- max_depth : Profondeur maximale des arbres.
- min_samples_split : Nombre minimal d’échantillons requis pour diviser un nœud.
- min_samples_leaf : Nombre minimal d’échantillons dans une feuille terminale.
Mise en Œuvre de RandomizedSearchCV¶
# Définition de l’espace des hyperparamètres
param_distributions = {
'n_estimators': [100, 200, 300, 500, 1000],
'max_depth': [5, 10, 15, 20, 50],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4]
}
# Initialisation du modèle
rf_model = RandomForestRegressor(random_state=42)
# Configuration du RandomizedSearchCV
random_search_rf = RandomizedSearchCV(
estimator=rf_model,
param_distributions=param_distributions,
n_iter=50, # Nombre d’itérations
scoring='neg_mean_squared_error',
cv=3, # Validation croisée
random_state=42,
verbose=2,
n_jobs=-1
)
# Exécution de la recherche
random_search_rf.fit(data_prix_train, price_train)
# affichages des meilleurs hyperparamètres
print("\nMeilleurs hyperparamètres (RandomizedSearchCV - Random Forest) :", random_search_rf.best_params_)
Fitting 3 folds for each of 50 candidates, totalling 150 fits
[CV] END max_depth=15, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=15, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=15, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=10, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=10, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=10, n_estimators=300; total time= 0.4s
[CV] END max_depth=5, min_samples_leaf=1, min_samples_split=5, n_estimators=1000; total time= 1.1s
[CV] END max_depth=5, min_samples_leaf=1, min_samples_split=5, n_estimators=1000; total time= 1.1s
[CV] END max_depth=5, min_samples_leaf=1, min_samples_split=5, n_estimators=1000; total time= 1.1s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=10, n_estimators=500; total time= 0.7s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=1000; total time= 1.6s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=1000; total time= 1.6s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=10, n_estimators=500; total time= 0.6s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=1000; total time= 1.6s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=10, n_estimators=500; total time= 0.6s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=10, n_estimators=200; total time= 0.2s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=10, n_estimators=200; total time= 0.2s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=10, n_estimators=200; total time= 0.2s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=10, n_estimators=100; total time= 0.1s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=10, n_estimators=100; total time= 0.1s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=2, n_estimators=1000; total time= 1.1s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=10, n_estimators=100; total time= 0.1s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=2, n_estimators=1000; total time= 1.2s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=10, n_estimators=500; total time= 0.6s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=2, n_estimators=1000; total time= 1.2s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=10, n_estimators=500; total time= 0.6s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=100; total time= 0.1s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=10, n_estimators=500; total time= 0.6s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=100; total time= 0.1s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=10, n_estimators=100; total time= 0.1s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=100; total time= 0.1s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=10, n_estimators=100; total time= 0.1s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=10, n_estimators=100; total time= 0.1s
[CV] END max_depth=5, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=5, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=5, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.7s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.7s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=100; total time= 0.2s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=100; total time= 0.2s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=100; total time= 0.2s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.7s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=100; total time= 0.2s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=100; total time= 0.2s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=100; total time= 0.2s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=10, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=10, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=10, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=10, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=10, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=10, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=2, n_estimators=300; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.7s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.7s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.7s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=2, n_estimators=300; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.4s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=2, n_estimators=1000; total time= 1.4s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=2, n_estimators=1000; total time= 1.4s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=2, n_estimators=1000; total time= 1.4s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=10, n_estimators=100; total time= 0.1s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=10, n_estimators=100; total time= 0.1s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=10, n_estimators=100; total time= 0.1s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=4, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=10, n_estimators=500; total time= 0.7s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=10, n_estimators=500; total time= 0.7s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=10, n_estimators=500; total time= 0.7s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.2s
[CV] END max_depth=10, min_samples_leaf=4, min_samples_split=2, n_estimators=100; total time= 0.1s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.2s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=10, n_estimators=500; total time= 0.7s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=10, n_estimators=500; total time= 0.7s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.2s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=10, n_estimators=500; total time= 0.6s
[CV] END max_depth=5, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.2s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=10, n_estimators=1000; total time= 1.4s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=10, n_estimators=1000; total time= 1.4s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=10, n_estimators=1000; total time= 1.4s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.8s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.8s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.7s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=100; total time= 0.2s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=10, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=100; total time= 0.2s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=100; total time= 0.2s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=10, n_estimators=300; total time= 0.4s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=10, n_estimators=300; total time= 0.4s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=1000; total time= 1.5s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=2, n_estimators=500; total time= 0.7s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=1000; total time= 1.5s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=1000; total time= 1.5s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=2, n_estimators=500; total time= 0.7s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=1000; total time= 1.5s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=1000; total time= 1.6s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=1000; total time= 1.5s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=2, n_estimators=500; total time= 0.7s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=10, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=10, n_estimators=300; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.7s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=50, min_samples_leaf=4, min_samples_split=10, n_estimators=300; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.6s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=10, n_estimators=100; total time= 0.2s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=10, n_estimators=100; total time= 0.2s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.6s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=10, n_estimators=100; total time= 0.2s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=10, n_estimators=500; total time= 0.8s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=10, n_estimators=500; total time= 0.8s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=10, n_estimators=500; total time= 0.8s
[CV] END max_depth=15, min_samples_leaf=4, min_samples_split=5, n_estimators=300; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=4, min_samples_split=5, n_estimators=300; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=4, min_samples_split=5, n_estimators=300; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=4, min_samples_split=2, n_estimators=1000; total time= 1.0s
[CV] END max_depth=15, min_samples_leaf=4, min_samples_split=2, n_estimators=1000; total time= 0.9s
[CV] END max_depth=15, min_samples_leaf=4, min_samples_split=2, n_estimators=1000; total time= 0.9s
Meilleurs hyperparamètres (RandomizedSearchCV - Random Forest) : {'n_estimators': 1000, 'min_samples_split': 2, 'min_samples_leaf': 1, 'max_depth': 50}
Analyse des Résultats de RandomizedSearchCV pour Random Forest
Les résultats de la recherche RandomizedSearchCV pour le modèle Random Forest indiquent que les meilleurs hyperparamètres sont :
- n_estimators = 1000 : le modèle utilise un nombre élevé d’arbres pour maximiser la robustesse des prédictions.
- min_samples_split = 2 : le critère minimal pour diviser un nœud est fixé à la valeur minimale, favorisant une granularité fine.
- min_samples_leaf = 1 : chaque feuille terminale contient au moins un échantillon, permettant une segmentation fine des données.
- max_depth = 50 : une profondeur maximale importante est choisie, offrant une capacité élevée pour capturer des relations complexes.
Ces choix d’hyperparamètres permettent d’optimiser les performances sur les données d’entraînement en augmentant la complexité et la flexibilité du modèle. Cependant, l’utilisation d’un grand nombre d’arbres (n_estimators = $1000$) et d’une profondeur maximale importante (max_depth = 50) pourrait accroître le risque de surajustement et allonger considérablement le temps d’entraînement. De plus, les valeurs minimales pour min_samples_split et min_samples_leaf indiquent que le modèle favorise une forte granularité dans la division des données, ce qui peut être utile pour des structures de données complexes.
En conclusion, bien que ces hyperparamètres maximisent les performances sur les données d’entraînement, il est essentiel de vérifier les performances sur l’ensemble de test pour garantir une bonne généralisation et éviter un surajustement excessif.
Mise en Œuvre de GridSearchCV¶
# Définition d'une grille restreinte pour les hyperparamètres
param_grid = {
'n_estimators': [200, 300, 500],
'max_depth': [10, 15, 20,50],
'min_samples_split': [2, 5],
'min_samples_leaf': [1, 2]
}
# Configuration GridSearchCV
grid_search_rf = GridSearchCV(
estimator=rf_model,
param_grid=param_grid,
scoring='neg_mean_squared_error',
cv=3,
verbose=2,
n_jobs=-1
)
# Exécution de la recherche
grid_search_rf.fit(data_prix_train, price_train)
# Meilleurs hyperparamètres
print("\nMeilleurs hyperparamètres (GridSearchCV - Random Forest) :", grid_search_rf.best_params_)
Fitting 3 folds for each of 48 candidates, totalling 144 fits
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.4s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.5s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.5s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.6s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.6s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 0.9s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 0.9s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 0.9s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.4s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.4s
[CV] END max_depth=10, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.7s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.4s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.4s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.8s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.8s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.8s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.4s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.4s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.7s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.7s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.6s
[CV] END max_depth=10, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.7s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.6s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.6s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 0.9s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 0.9s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 0.9s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.4s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.4s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.7s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 1.0s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.6s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.7s
[CV] END max_depth=15, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 1.0s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.7s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.4s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 1.0s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 1.0s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 1.1s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.5s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.9s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.6s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.6s
[CV] END max_depth=15, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.6s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.6s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 1.0s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 1.0s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 1.0s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.6s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.9s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.9s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.9s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.9s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.9s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.9s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=200; total time= 0.4s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.6s
[CV] END max_depth=20, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=5, n_estimators=200; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 1.0s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 1.0s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=2, n_estimators=500; total time= 1.0s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.8s
[CV] END max_depth=50, min_samples_leaf=1, min_samples_split=5, n_estimators=500; total time= 0.7s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=300; total time= 0.5s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=200; total time= 0.3s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.8s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.8s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=2, n_estimators=500; total time= 0.7s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.5s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=300; total time= 0.4s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.6s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.6s
[CV] END max_depth=50, min_samples_leaf=2, min_samples_split=5, n_estimators=500; total time= 0.5s
Meilleurs hyperparamètres (GridSearchCV - Random Forest) : {'max_depth': 10, 'min_samples_leaf': 1, 'min_samples_split': 2, 'n_estimators': 300}
Analyse des Résultats de GridSearchCV pour Random Forest
Les résultats de la recherche exhaustive GridSearchCV pour le modèle Random Forest indiquent que les meilleurs hyperparamètres sont :
- max_depth = 10 : une profondeur modérée est choisie, favorisant un bon équilibre entre complexité et généralisation.
- min_samples_leaf = 1 : chaque feuille terminale contient au moins un échantillon, permettant une segmentation fine.
- min_samples_split = 2 : le critère minimal pour diviser un nœud est fixé à la valeur minimale, permettant des arbres plus détaillés.
- n_estimators = 300 : un nombre optimal d’arbres est utilisé, assurant des performances sans surcharger le calcul.
Ces hyperparamètres optimaux permettent au modèle de capturer les relations essentielles dans les données tout en limitant le risque de surajustement grâce à une profondeur maximale contrôlée (max_depth = 10) et un nombre raisonnable d’arbres (n_estimators = 300). Contrairement à RandomizedSearchCV, GridSearchCV explore systématiquement toutes les combinaisons, ce qui permet de s'assurer que la meilleure configuration possible a été identifiée. Cependant, cela peut être coûteux en termes de temps de calcul, comme en témoigne le total de 144 fits nécessaires dans cet exemple.
En conclusion, la configuration optimale identifiée par GridSearchCV montre un modèle bien équilibré, apte à généraliser sur des données non vues, tout en conservant une complexité modérée pour limiter les risques de surapprentissage.
$c$. Fine-tuning pour le Modèle Gradient Boosting¶
Pour Gradient Boosting, les hyperparamètres importants à optimiser sont :
- n_estimators : Nombre d’étapes de boosting.
- max_depth : Profondeur maximale des arbres.
- learning_rate : Taux d’apprentissage (impact de chaque arbre sur le modèle final).
- subsample : Proportion des échantillons utilisés pour former chaque arbre.
Mise en Œuvre de RandomizedSearchCV¶
# Définition de l’espace des hyperparamètres
param_distributions = {
'n_estimators': [100, 200, 300, 500],
'max_depth': [3, 5, 7, 10],
'learning_rate': [0.01, 0.05, 0.1, 0.2],
'subsample': [0.6, 0.8, 1.0]
}
# Initialisation du modèle
gb_model = GradientBoostingRegressor(random_state=42)
# Configuration RandomizedSearchCV
random_search_gb = RandomizedSearchCV(
estimator=gb_model,
param_distributions=param_distributions,
n_iter=50,
scoring='neg_mean_squared_error',
cv=3,
random_state=42,
verbose=2,
n_jobs=-1
)
# Exécution de la recherche
random_search_gb.fit(data_prix_train, price_train)
# Affichage des meilleurs hyperparamètres
print("\nMeilleurs hyperparamètres (RandomizedSearchCV - Gradient Boosting) :", random_search_gb.best_params_)
Fitting 3 folds for each of 50 candidates, totalling 150 fits
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=200, subsample=0.8; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=200, subsample=0.8; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=200, subsample=0.8; total time= 0.4s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.01, max_depth=10, n_estimators=500, subsample=0.6; total time= 0.7s
[CV] END learning_rate=0.01, max_depth=10, n_estimators=500, subsample=0.6; total time= 0.7s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.01, max_depth=10, n_estimators=500, subsample=0.6; total time= 0.7s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=500, subsample=1.0; total time= 0.8s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=500, subsample=1.0; total time= 0.8s
[CV] END learning_rate=0.2, max_depth=7, n_estimators=500, subsample=0.6; total time= 0.6s
[CV] END learning_rate=0.2, max_depth=7, n_estimators=500, subsample=0.6; total time= 0.5s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=500, subsample=1.0; total time= 0.9s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.2, max_depth=7, n_estimators=500, subsample=0.6; total time= 0.6s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.01, max_depth=3, n_estimators=500, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.01, max_depth=3, n_estimators=500, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.01, max_depth=3, n_estimators=500, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.2, max_depth=3, n_estimators=100, subsample=0.8; total time= 0.1s
[CV] END learning_rate=0.2, max_depth=3, n_estimators=100, subsample=0.8; total time= 0.1s
[CV] END learning_rate=0.2, max_depth=3, n_estimators=100, subsample=0.8; total time= 0.1s
[CV] END learning_rate=0.2, max_depth=3, n_estimators=500, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.2, max_depth=3, n_estimators=500, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.2, max_depth=3, n_estimators=500, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.01, max_depth=7, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.01, max_depth=7, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.01, max_depth=7, n_estimators=300, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=500, subsample=0.6; total time= 0.5s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=500, subsample=0.6; total time= 0.5s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=1.0; total time= 0.4s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=500, subsample=0.6; total time= 0.5s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.2, max_depth=7, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.2, max_depth=7, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.2, max_depth=7, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=3, n_estimators=100, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.1, max_depth=3, n_estimators=100, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.1, max_depth=3, n_estimators=100, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.2, max_depth=3, n_estimators=500, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.2, max_depth=3, n_estimators=500, subsample=0.8; total time= 0.4s
[CV] END learning_rate=0.2, max_depth=3, n_estimators=500, subsample=0.8; total time= 0.4s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=500, subsample=0.8; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=500, subsample=0.8; total time= 0.5s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=500, subsample=0.8; total time= 0.5s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.01, max_depth=7, n_estimators=100, subsample=0.6; total time= 0.1s
[CV] END learning_rate=0.01, max_depth=7, n_estimators=100, subsample=0.6; total time= 0.1s
[CV] END learning_rate=0.01, max_depth=7, n_estimators=100, subsample=0.6; total time= 0.1s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=100, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=100, subsample=0.6; total time= 0.1s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=100, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=100, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=100, subsample=0.6; total time= 0.1s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=100, subsample=0.6; total time= 0.1s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=500, subsample=0.6; total time= 0.6s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=500, subsample=0.8; total time= 0.4s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=500, subsample=0.8; total time= 0.5s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=500, subsample=0.6; total time= 0.6s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=500, subsample=0.6; total time= 0.6s
[CV] END learning_rate=0.2, max_depth=7, n_estimators=100, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=500, subsample=0.8; total time= 0.5s
[CV] END learning_rate=0.2, max_depth=7, n_estimators=100, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.2, max_depth=7, n_estimators=100, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.01, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.01, max_depth=3, n_estimators=200, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.01, max_depth=3, n_estimators=200, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.01, max_depth=3, n_estimators=200, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.05, max_depth=10, n_estimators=300, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.05, max_depth=10, n_estimators=300, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=100, subsample=0.8; total time= 0.1s
[CV] END learning_rate=0.05, max_depth=10, n_estimators=300, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=100, subsample=0.8; total time= 0.1s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=100, subsample=0.8; total time= 0.1s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=500, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=500, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=500, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=100, subsample=0.8; total time= 0.1s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=100, subsample=0.8; total time= 0.1s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=500, subsample=0.8; total time= 0.5s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=100, subsample=0.8; total time= 0.1s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=500, subsample=0.8; total time= 0.5s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=500, subsample=0.8; total time= 0.7s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=500, subsample=0.8; total time= 0.7s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=500, subsample=0.8; total time= 0.7s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.2, max_depth=5, n_estimators=300, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=500, subsample=0.8; total time= 0.6s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=200, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=10, n_estimators=200, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.2, max_depth=10, n_estimators=100, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.01, max_depth=10, n_estimators=300, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.2, max_depth=10, n_estimators=100, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.01, max_depth=7, n_estimators=500, subsample=1.0; total time= 0.6s
[CV] END learning_rate=0.01, max_depth=7, n_estimators=500, subsample=1.0; total time= 0.6s
[CV] END learning_rate=0.01, max_depth=7, n_estimators=500, subsample=1.0; total time= 0.6s
[CV] END learning_rate=0.01, max_depth=10, n_estimators=300, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.2, max_depth=10, n_estimators=100, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=3, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=3, n_estimators=200, subsample=1.0; total time= 0.1s
[CV] END learning_rate=0.01, max_depth=10, n_estimators=300, subsample=0.6; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=3, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=3, n_estimators=300, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=3, n_estimators=300, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=3, n_estimators=300, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=0.6; total time= 0.2s[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=0.6; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=300, subsample=0.6; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=300, subsample=0.6; total time= 0.2s
Meilleurs hyperparamètres (RandomizedSearchCV - Gradient Boosting) : {'subsample': 0.6, 'n_estimators': 100, 'max_depth': 5, 'learning_rate': 0.05}
Analyse des Résultats de RandomizedSearchCV pour Gradient Boosting
Les résultats de RandomizedSearchCV pour le modèle Gradient Boosting montrent que les meilleurs hyperparamètres sont :
- subsample = 0.6 : seulement 60 % des échantillons sont utilisés pour entraîner chaque arbre, ce qui aide à réduire le surapprentissage.
- n_estimators = 100 : un nombre modéré d'arbres est suffisant pour obtenir des performances optimales.
- max_depth = 5 : une profondeur d'arbre modérée permet de capturer des relations complexes tout en limitant la complexité du modèle.
- learning_rate = 0.05 : un taux d’apprentissage relativement faible assure une convergence stable et robuste.
Ces paramètres indiquent que le modèle Gradient Boosting est bien ajusté pour les données avec une approche prudente, limitant les risques de surajustement grâce à un sous-échantillonnage contrôlé (subsample = 0.6) et un taux d’apprentissage faible (learning_rate = 0.05). En comparaison avec GridSearchCV, RandomizedSearchCV explore aléatoirement un sous-ensemble d’hyperparamètres, ce qui est plus rapide tout en conservant une bonne chance d’identifier des paramètres performants.
La combinaison optimale d’hyperparamètres identifiée ici montre une orientation vers une régularisation contrôlée et une complexité modérée, idéale pour garantir un bon compromis entre performance et généralisation.
Mise en Œuvre de GridSearchCV¶
# Définition d'une grille restreinte pour les hyperparamètres
param_grid = {
'n_estimators': [200, 300],
'max_depth': [5, 7],
'learning_rate': [0.05, 0.1],
'subsample': [0.8, 1.0]
}
# Configuration GridSearchCV
grid_search_gb = GridSearchCV(
estimator=gb_model,
param_grid=param_grid,
scoring='neg_mean_squared_error',
cv=3,
verbose=2,
n_jobs=-1
)
# Exécution de la recherche
grid_search_gb.fit(data_prix_train, price_train)
# Affichage des meilleurs hyperparamètres
print("\nMeilleurs hyperparamètres (GridSearchCV - Gradient Boosting) :", grid_search_gb.best_params_)
Fitting 3 folds for each of 16 candidates, totalling 48 fits
[CV] END learning_rate=0.05, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=5, n_estimators=300, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=300, subsample=0.8; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=300, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=300, subsample=0.8; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=200, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=200, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=300, subsample=1.0; total time= 0.4s
[CV] END learning_rate=0.05, max_depth=7, n_estimators=300, subsample=1.0; total time= 0.4s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=200, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=200, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=200, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=5, n_estimators=300, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=300, subsample=0.8; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=300, subsample=1.0; total time= 0.3s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=200, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=300, subsample=0.8; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=300, subsample=1.0; total time= 0.2s
[CV] END learning_rate=0.1, max_depth=7, n_estimators=300, subsample=1.0; total time= 0.2s
Meilleurs hyperparamètres (GridSearchCV - Gradient Boosting) : {'learning_rate': 0.05, 'max_depth': 5, 'n_estimators': 200, 'subsample': 0.8}
Analyse des Résultats de GridSearchCV pour Gradient Boosting
Les résultats de GridSearchCV pour le modèle Gradient Boosting montrent que les meilleurs hyperparamètres sont :
- learning_rate = 0.05 : un taux d’apprentissage faible qui assure une convergence plus stable et réduit le risque de surajustement.
- max_depth = 5 : une profondeur d’arbre modérée qui équilibre la capacité du modèle à capturer les relations complexes tout en limitant la complexité et le surajustement.
- n_estimators = 200 : un nombre d’arbres suffisant pour garantir des performances optimales sans surcharger le modèle.
- subsample = 0.8 : 80 % des échantillons sont utilisés pour entraîner chaque arbre, ce qui ajoute une régularisation supplémentaire en limitant les corrélations entre les arbres.
Ces hyperparamètres montrent une orientation vers un modèle bien régularisé, capable de généraliser efficacement sur de nouvelles données. Comparé à RandomizedSearchCV, GridSearchCV explore de manière exhaustive toutes les combinaisons possibles, assurant ainsi une optimisation plus complète, mais au prix d’un temps de calcul plus élevé. Les hyperparamètres optimaux trouvés ici sont similaires à ceux obtenus par RandomizedSearchCV, ce qui renforce la fiabilité de cette configuration pour ce dataset.
La combinaison optimale identifiée ici met en évidence l’importance de trouver un compromis entre une complexité suffisante pour capturer des relations pertinentes et une régularisation pour éviter le surajustement.
Conclusion¶
Les fonctions RandomizedSearchCV et GridSearchCV sont toutes deux des méthodes d’optimisation des hyperparamètres fournies par Scikit-learn, mais elles diffèrent par leur approche. GridSearchCV effectue une recherche exhaustive sur toutes les combinaisons possibles d’hyperparamètres dans un espace défini. Cela garantit que chaque combinaison est testée, ce qui permet de trouver l’ensemble optimal avec certitude, à condition que l’espace soit bien défini. Cependant, cette méthode est coûteuse en temps et en ressources, surtout lorsque le nombre de combinaisons est élevé ou lorsque le modèle est complexe. À l’inverse, RandomizedSearchCV échantillonne aléatoirement un nombre défini de combinaisons d’hyperparamètres. Cela permet d’explorer rapidement un espace de recherche potentiellement plus large tout en réduisant considérablement les coûts de calcul, mais au risque de passer à côté de l’ensemble optimal si le nombre d’itérations est insuffisant.
En analysant les résultats, les deux méthodes ont montré des performances similaires dans l’identification des hyperparamètres optimaux pour les modèles Random Forest et Gradient Boosting. Cependant, RandomizedSearchCV s’est révélé plus rapide pour les tests, tout en obtenant des configurations compétitives (par exemple, profondeur modérée des arbres et taux d’apprentissage réduit pour Gradient Boosting). En revanche, GridSearchCV, bien que plus lent, a confirmé l’efficacité des hyperparamètres identifiés par RandomizedSearchCV en explorant toutes les combinaisons. Ainsi, pour des problèmes complexes avec un large espace d’hyperparamètres, RandomizedSearchCV est préférable pour une optimisation rapide. En revanche, pour un espace restreint ou des contraintes moins strictes sur le temps de calcul, GridSearchCV reste une solution plus exhaustive et fiable.
$i$. Analyse de l’impact de la taille des données d’entraînement¶
Dans un projet de prédiction, la taille des données d’entraînement joue un rôle crucial dans les performances des modèles. Une base d’apprentissage trop petite peut limiter la capacité du modèle à généraliser sur de nouvelles données, tandis qu’une base plus grande permet souvent une meilleure capture des patterns complexes, bien que cela puisse augmenter le temps de calcul. Cette partie vise à explorer comment la performance des modèles évolue en fonction de la quantité de données d’entraînement disponibles. Pour ce faire, nous analysons les performances des modèles développés dans le projet (régression linéaire, Random Forest, Gradient Boosting, etc.) en variant progressivement la taille des données d’entraînement (10, 50, 100, 250, …).
Parmi les modèles développés, Gradient Boosting a été retenu pour cette analyse détaillée. Ce choix s’explique par ses performances globales élevées lors des phases précédentes, ainsi que par sa sensibilité aux variations dans les données d’entraînement. Contrairement à des modèles comme Random Forest, qui sont moins sensibles à la taille des données grâce à leur approche de bagging, Gradient Boosting construit ses prédictions de manière incrémentale, ce qui le rend particulièrement adapté pour observer l’influence des données sur la précision des prédictions. Ainsi, cette approche nous permettra de mieux comprendre l’effet de la quantité de données sur un modèle puissant et flexible tout en conservant un point de comparaison avec les autres modèles du projet.
# Fonction pour évaluer les performances des modèles en fonction de la taille des données
def evaluate_models(data_train, target_train, data_test, target_test, model, sizes):
"""
Évalue les performances d'un modèle sur des tailles croissantes de données d'entraînement.
Parameters:
data_train (pd.DataFrame or np.ndarray): Les données d'entraînement.
target_train (pd.Series or np.ndarray): Les cibles associées aux données d'entraînement.
data_test (pd.DataFrame or np.ndarray): Les données de test.
target_test (pd.Series or np.ndarray): Les cibles associées aux données de test.
model: Le modèle à entraîner (doit implémenter `.fit()` et `.predict()`).
sizes (list[int]): Une liste de tailles pour limiter les données d'entraînement.
Returns:
tuple: Trois listes contenant respectivement les scores MAE, RMSE et R².
"""
mae_scores = []
rmse_scores = []
r2_scores = []
for size in sizes:
# Limitation des données d'entraînement à la taille définie
data_subset = data_train[:size]
target_subset = target_train[:size]
# Entraînement du modèle
model.fit(data_subset, target_subset)
# Prédiction sur les données de test
predictions = model.predict(data_test)
# Calcul des métriques
mae_scores.append(mean_absolute_error(target_test, predictions))
rmse_scores.append(np.sqrt(mean_squared_error(target_test, predictions)))
r2_scores.append(r2_score(target_test, predictions))
return mae_scores, rmse_scores, r2_scores
# Configuration des tailles des données d'entraînement
sizes = [10, 50, 100, 250, 500, len(data_prix_train)]
# Modèles à comparer
models = {
"Régression Linéaire": LinearRegression(),
"Random Forest": RandomForestRegressor(n_estimators=100, max_depth=10, random_state=42),
"Gradient Boosting": GradientBoostingRegressor(n_estimators=100, max_depth=5, learning_rate=0.05, random_state=42)
}
# Initialisation des résultats
results = {}
# Évaluation de chaque modèle
for name, model in models.items():
mae_scores, rmse_scores, r2_scores = evaluate_models(data_prix_train, price_train, data_prix_test, price_test, model, sizes)
results[name] = {"MAE": mae_scores, "RMSE": rmse_scores, "R2": r2_scores}
# Visualisation des résultats
plt.figure(figsize=(18, 6))
# Graphique MAE
plt.subplot(1, 3, 1)
for name in results:
plt.plot(sizes, results[name]["MAE"], label=name)
plt.title("Performance en fonction de la taille des données - MAE")
plt.xlabel("Taille des données d'entraînement")
plt.ylabel("MAE")
plt.legend()
# Graphique RMSE
plt.subplot(1, 3, 2)
for name in results:
plt.plot(sizes, results[name]["RMSE"], label=name)
plt.title("Performance en fonction de la taille des données - RMSE")
plt.xlabel("Taille des données d'entraînement")
plt.ylabel("RMSE")
plt.legend()
# Graphique R²
plt.subplot(1, 3, 3)
for name in results:
plt.plot(sizes, results[name]["R2"], label=name)
plt.title("Performance en fonction de la taille des données - R2")
plt.xlabel("Taille des données d'entraînement")
plt.ylabel("R2")
plt.legend()
plt.tight_layout()
plt.show()
Analyse des graphiques
L’analyse des performances en fonction de la taille des données d’entraînement montre des tendances significatives pour les trois modèles : régression linéaire, Random Forest, et Gradient Boosting. Pour le MAE et le RMSE, les modèles non linéaires (Random Forest et Gradient Boosting) surpassent systématiquement la régression linéaire, particulièrement pour les tailles de données importantes, indiquant leur capacité à capturer des relations complexes dans les données. Le Gradient Boosting, en particulier, atteint les meilleures performances pour les grands ensembles de données, avec des erreurs plus faibles (MAE et RMSE). En revanche, la régression linéaire montre une convergence plus lente, restant inférieure même à partir de petites tailles d’entraînement. En termes de R², les modèles ensemblistes (Random Forest et Gradient Boosting) montrent une amélioration rapide de la qualité d’ajustement dès 100 échantillons, dépassant la régression linéaire, qui reste moins performante pour expliquer la variance. Ainsi, le Gradient Boosting se révèle être le modèle optimal, combinant une faible erreur et une meilleure généralisation, particulièrement pertinent pour des ensembles de données d’entraînement de grande taille.
$VII$. Conclusion Générale¶
Résumé du Projet¶
Le projet visait à développer un modèle performant pour prédire les prix des maisons en fonction de leurs caractéristiques, telles que la superficie, le nombre de chambres, et d’autres commodités. La problématique sous-jacente était de comprendre les facteurs clés influençant le prix des maisons, tout en fournissant un modèle précis et interprétable pour les professionnels de l’immobilier. Pour cela, nous avons appliqué des techniques de data science en utilisant plusieurs modèles parmi lesquels Random Forest et Gradient Boosting, et avons optimisé leurs performances grâce à des approches avancées comme RandomizedSearchCV et GridSearchCV.
Analyse des Modèles Développés¶
Au cours de ce projet, plusieurs modèles, allant des modèles de base à des algorithmes avancés, ont été testés, comparés, et optimisés pour répondre à la problématique. Les résultats obtenus montrent que :
Modèle Basé sur les Moyennes (Baseline) : ce modèle de référence a été construit pour évaluer les performances minimales acceptables. Il a offert un benchmark utile mais des performances très limitées, servant principalement à juger l’efficacité des modèles avancés.
Modèle de Régression linéaire : Ce modèle a permi de poser les bases en évaluant les relations linéaires entre les variables et le prix. Bien qu’il soit simple à interpréter, sa performance était limitée, avec une faible capacité à capturer les relations non linéaires complexes présentes dans les données.
Random Forest : ce modèle ensembliste, bien qu’efficace, a montré une grande capacité à capturer les relations complexes grâce à son architecture basée sur des arbres de décision. Après optimisation (max_depth = 10, n_estimators = 300), il a obtenu des résultats solides mais légèrement surajustés sur les données d’entraînement, indiquant une généralisation perfectible. Ses performances étaient compétitives mais légèrement inférieures à celles du Gradient Boosting pour les données test.
Gradient Boosting : il s'est révélé être le modèle le plus performant pour cette tâche, grâce à sa capacité à généraliser sur des données non vues. Avec des hyperparamètres optimaux tels qu’un taux d’apprentissage de 0.05, une profondeur maximale de 5, et un nombre d’estimations de 200, il a offert un bon équilibre entre précision et généralisation, tout en maîtrisant le surapprentissage. Ce modèle s’est distingué par sa robustesse et son compromis optimal entre performance sur les données d’entraînement et données de test.
Enfin, l’analyse des performances en fonction de la taille des données d’entraînement a mis en évidence l’importance cruciale d’un grand volume de données pour améliorer la précision et la généralisation des modèles, renforçant ainsi l’efficacité globale de la solution proposée.
En conclusion, Gradient Boosting a été retenu comme le meilleur modèle pour cette problématique de prédiction de prix.
Variables Importantes¶
L’analyse des importances des caractéristiques a permis d’identifier les variables ayant le plus grand impact sur la prédiction des prix des maisons :
- Superficie (
area) : Variable la plus influente, représentant environ 40% de l’importance totale. Cela confirme que la superficie est un facteur clé dans l’évaluation des prix immobiliers. - Nombre de salles de bain (
bathrooms) : Contribution significative, représentant environ 27% de l’importance. - Parking (
parking) : Facteur modérément influent, avec une importance de 5%. - Nombre d’étages (
stories) et climatisation (air_conditioning) : Variables complémentaires avec des contributions respectives de 4.5% et 3.7%.
D'autres variables, telles que chauffage à l’eau chaude (hotwaterheating), préférence d’emplacement (prefarea), et chambre d’invité (guestroom), ont également eu une influence moindre mais notable.
En revanche, des caractéristiques comme accès à une route principale (mainroad) et type d’ameublement (furnishing_status) se sont avérées négligeables dans ce contexte.
Perspectives¶
Ce projet a démontré l’efficacité des modèles ensemblistes pour résoudre des problématiques de régression complexes. Les résultats obtenus peuvent être améliorés en :
- Collectant davantage de données pour renforcer la robustesse des modèles.
- Explorant d’autres approches, comme les réseaux neuronaux ou les modèles basés sur des méthodes de boosting avancées comme XGBoost ou LightGBM.
- Intégrant des données supplémentaires sur l’environnement (écoles, commodités, transports publics) pour capturer des facteurs externes influençant les prix.
En conclusion, le modèle Gradient Boosting optimisé avec ses variables importantes constitue une solution fiable et précise pour répondre à la problématique de prédiction des prix des maisons, offrant ainsi un outil précieux pour les acteurs de l’immobilier et les décideurs.
Aucun lien GitHub disponible pour ce projet.