Bienvenue dans la deuxième partie sur la prédiction du prix des taxis avec du Machine Learning ! C’est un défi unique, n’est-ce pas ? Nous faisons régulièrement des courses en taxi (parfois même tous les jours !), et pourtant, lorsque nous appuyons sur le bouton “Réserver maintenant”, nous nous fions à des calculs manuels à la volée plutôt qu’à des calculs de type ML. Et c’est ce que j’ai l’intention de démontrer ici : Prix des taxis – Modélisation prédictive.

Dans l’article précédent, nous nous sommes penchés sur le nettoyage des données et leur exploration afin d’identifier les relations entre les variables, et aussi de comprendre les diverses caractéristiques qui auront un impact sur la tarification des taxis.

Dans cet article, nous allons comprendre comment construire des modèles de Machine Learning pour prédire le prix des taxis et examiner l’impact significatif que l’ingénierie des caractéristiques joue dans ce processus. Le code de cet article se trouve ici.

Plan d’attaque : Prix des taxis Modélisation prédictive

Après la phase de nettoyage des données et d’analyse exploratoire, nous sommes enfin arrivés à la phase de construction du modèle. La qualité des résultats à la fin de cette phase dépend de la qualité des données et des caractéristiques utilisées pour la modélisation. Dans cet article, nous allons comprendre en détail les étapes suivantes :

  1. Préparation des données pour la construction du modèle
  2. Création d’une prédiction de référence
  3. Construction de modèles sans ingénierie des caractéristiques
  4. Construction de modèles avec ingénierie des caractéristiques

Préparation des données

Cette étape consiste à nettoyer les données, à supprimer les colonnes indésirables, à convertir les variables catégorielles dans un format compréhensible par la machine et, enfin, à diviser les données d’entrainement en 2 ensembles, l’un d’entrainement et l’autre de validation.

Nous supprimerons tous les tarifs négatifs et les nombres de passagers supérieurs à 7, comme nous l’avons fait dans la première partie.

L’étape suivante de la préparation des données consiste à diviser les données d’entrainement en ensembles de données d’entrainement et de validation. On retrouve cette étape dans presque tous les projets de Machine Learning. Elle constitue l’une des étapes les plus critiques pour évaluer les modèles.

L’ensemble de données de validation nous aide à comprendre comment le modèle, adapté à l’aide des données d’entrainement, fonctionne sur toutes les données inconnues. Cela nous aide à déterminer si le modèle est trop ou pas assez adapté. On parle de sur-ajustement (overfitting) lorsque l’erreur d’entraînement est faible, mais que l’erreur de test est élevée. C’est un problème courant avec les modèles complexes car ils ont tendance à mémoriser les données sous-jacentes et donc à donner de mauvais résultats sur les données inconnues.

Les modèles simples comme la régression linéaire ne mémorisent pas les données sous-jacentes, mais ont des hypothèses simples sur les données. Par conséquent, ces modèles présentent une erreur très élevée (biais élevé), mais une faible variance. L’ensemble des données de validation nous aidera à évaluer les modèles sur la base de la variance et du biais. Dans cette analyse, nous conserverons 25 % de nos données comme données de validation.

from sklearn.model_selection import train_test_split
X=train.drop([fare_amount],axis=1)
y=train[fare_amount]
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.25,random_state=123) # test_size est la proportion de données qui doit être conservée pour validation

Modèle de référence

Un modèle de référence est une solution à un problème sans appliquer de techniques de Machine Learning. Tout modèle que nous construisons doit améliorer cette solution. Certains moyens de construire un modèle de référence consistent à prendre la valeur la plus courante en cas de classification, et à calculer la moyenne dans un problème de régression. Dans cette analyse, puisque nous prédisons le montant du tarif (qui est une variable quantitative), nous allons prédire le montant moyen du tarif. Il en résulte une valeur RMSE de 9,71. Par conséquent, tout modèle que nous construisons devrait avoir une RMSE inférieure à 9,71.

avg_fare=round(np.mean(y_train),2) #11.31
baseline_pred=np.repeat(avg_fare,y_test.shape[0])
baseline_rmse=np.sqrt(mean_squared_error(baseline_pred, y_test))
print("RMSE de référence sur les données de Validation :",baseline_rmse)

Construction du modèle et Évaluation

1. Sans ingénierie des caractéristiques

Dans cette étape, nous utiliserons uniquement les caractéristiques DateTime, sans inclure aucune des fonctions supplémentaires telles que la distance du trajet ou la distance des aéroports ou la distance de prise en charge à partir d’un arrondissement. Pour comprendre et évaluer les modèles, nous allons considérer les algorithmes ML suivants :

  • Régression Linéaire
  • Random Forest
  • Light GBM

À l’exception de la régression linéaire, les autres modèles sont des ensembles d’arbres de décision (Decision Tree), mais ils diffèrent dans la manière dont les arbres de décision sont créés. Avant de poursuivre la discussion, comprenons les définitions de quelques termes clés que nous allons utiliser.

  1. Bagging : Dans cette méthode, plusieurs modèles sont créés et la prédiction de sortie est la moyenne des prédictions des différents modèles. Dans la méthode Bagging, vous prenez des échantillons bootstrap (avec remplacement) de votre ensemble de données et chaque échantillon forme un apprenant faible. Random Forest utilise cette méthode pour les prédictions. Dans le Random Forest, plusieurs arbres de décision sont créés, et le résultat est la prédiction moyenne de chacun de ces arbres. Pour que cette méthode fonctionne, les modèles de référence doivent avoir un biais (taux d’erreur) plus faible.
  2. Boosting : Dans cette méthode, plusieurs apprenants faibles sont réunis pour créer des apprenants forts. La méthode Boosting utilise toutes les données pour former chaque apprenant. Mais les cas qui ont été mal classés par les apprenants précédents se voient accorder plus de poids, de sorte que les apprenants suivants puissent leur accorder plus d’attention pendant l’entrainement. XGBoost et Light GBM sont basés sur cette méthode. Ces deux méthodes sont des variantes des arbres de décision à gradient stimulant (GBDT). Dans les GBDT, les arbres de décision sont formés de manière itérative, c’est-à-dire un arbre à la fois. Les GBM XGBoost et Light utilisent la stratégie de croissance foliaire lors de la croissance de l’arbre décisionnel. Lors de l’entrainement de chaque arbre de décision et de la division des données, XGBoost suit la stratégie par niveau, tandis que Light GBM suit la stratégie par feuille.

Il est enfin temps de construire nos modèles !

1. Régression linéaire

Elle est utilisée pour trouver une relation linéaire entre la cible et un ou plusieurs prédicteur(s). L’idée principale est d’identifier une ligne qui s’adapte le mieux aux données. La ligne la mieux ajustée est celle pour laquelle l’erreur de prédiction est la plus faible. Cet algorithme n’est pas très flexible et présente un biais très élevé. La régression linéaire est également très sensible aux valeurs aberrantes, car elle tente de minimiser la somme des erreurs au carré.

lm = LinearRegression()
lm.fit(X_train,y_train)
y_pred=np.round(lm.predict(X_test),2)
lm_rmse=np.sqrt(mean_squared_error(y_pred, y_test))
lm_train_rmse=np.sqrt(mean_squared_error(lm.predict(X_train), y_train))
lm_variance=abs(lm_train_rmse - lm_rmse)
print("Test RMSE for Linear Regression is ",lm_rmse)
print("Train RMSE for Linear Regression is ",lm_train_rmse)
print("Variance for Linear Regression is ",lm_variance)

Le RMSE de test pour le modèle de régression linéaire était de 8,14, et le RMSE d’entrainement était de 8,20. Ce modèle est une amélioration par rapport à la prédiction de référence. Néanmoins, le taux d’erreur est très élevé dans ce modèle, bien que la variance soit très faible (0,069). Essayons ensuite un modèle plus complexe.

2. Random Forest

Random Forest est bien plus flexible qu’un modèle de régression linéaire. Cela signifie un biais plus faible, et il peut mieux s’adapter aux données. Les modèles complexes peuvent souvent mémoriser les données sous-jacentes et ne permettent donc pas de généraliser correctement. Le réglage des paramètres est utilisé pour éviter ce problème.

rf = RandomForestRegressor(n_estimators = 100, random_state = 883,n_jobs=-1)
rf.fit(X_train,y_train)
rf_pred= rf.predict(X_test)
rf_rmse=np.sqrt(mean_squared_error(rf_pred, y_test))
print("RMSE for Random Forest is ",rf_rmse)

Le modèle Random Forest a donné un RMSE de 3,72 sur les données de validation et un RMSE de 1,41 sur les données d’entrainement. Il y a une énorme variation dans la RMSE d’entrainement et de validation, ce qui indique un overfitting. Pour réduire cet overfitting, nous pouvons ajuster ce modèle.

4. LightGBM

Le LightGBM est un algorithme basé sur un arbre de boosting. La différence entre LightGBM et d’autres algorithmes basés sur les arbres est que LightGBM se développe au niveau des feuilles plutôt qu’au niveau du sol. Cet algorithme choisit le nœud qui entraînera une perte delta maximale pour la division. LightGBM est très rapide, nécessite moins de mémoire vive et se concentre sur la précision du résultat.

train_data=lgb.Dataset(X_train,label=y_train)
param = {'num_leaves':31, 'num_trees':5000,'objective':'regression'}
param['metric'] = 'l2_root'
num_round=5000
cv_results = lgb.cv(param, train_data, num_boost_round=num_round, nfold=10,verbose_eval=20, early_stopping_rounds=20,stratified=False)
lgb_bst=lgb.train(param,train_data,len(cv_results['rmse-mean']))
lgb_pred = lgb_bst.predict(X_test)
lgb_rmse=np.sqrt(mean_squared_error(lgb_pred, y_test))
print("RMSE for Light GBM is ",lgb_rmse)

Ce modèle a donné un RMSE de 3,78 sur les données de validation mais le biais est plus élevé que celui du Random Forest. D’autre part, la variance de ce modèle était de 0,48 contre 2,31 dans notre modèle Random Forest.

Prix des taxis - Modélisation prédictive
Biais et variance pour différents modèles

Étant donné que LightGBM a un taux d’erreur comparable à celui de Random Forest, qu’il présente une variance plus faible et qu’il fonctionne plus vite que ce dernier, nous utiliserons LightGBM comme modèle pour une analyse plus approfondie

2. Ingénierie des caractéristiques et réglage des modèles

L’ingénierie des caractéristiques est le processus de transformation des données brutes en caractéristiques qui sont introduites dans les modèles finaux. L’objectif est d’améliorer la précision des modèles. Avoir de bonnes caractéristiques signifie que nous pouvons utiliser des modèles simples pour produire de meilleurs résultats. Les bonnes caractéristiques décrivent la structure inhérente aux données.

Comme nous l’avons vu dans la première partie, nous utiliserons les caractéristiques identifiées lors de la phase d’Analyse Exploratoire, comme la distance de prise en charge et de dépose à partir des aéroports, la distance de prise en charge et de dépose à partir de chaque arrondissement (si la prise en charge ou la dépose s’est faite à partir de ou vers l’aéroport, et de quel arrondissement la prise en charge ou la dépose s’est faite).

L’application du même modèle LightGBM que celui décrit ci-dessus sur ces données techniques a donné un RMSE de 3,641 (en baisse par rapport à 3,78) et une variance de 0,48.

L’étape suivante consiste à mettre au point ce modèle. Un bon modèle a un faible biais et une faible variance (pour éviter le sur-ajustement). Peu de caractéristiques importantes dans le LightGBM qui peuvent être optimisées pour réduire le sur-ajustement le sont :

  1. max_depth : indique la profondeur maximale de l’arbre. Comme le LightGBM suit la croissance des feuilles s’il n’est pas réglé, la profondeur peut être beaucoup plus élevée que celle des autres algorithmes basés sur les arbres.
  2. subsample : Il indique la fraction des données à utiliser dans chaque itération et il est utilisé pour accélérer l’algorithme et contrôler le sur-ajustement.
  3. colsample_bytree : Il s’agit de la fraction des caractéristiques qui doivent être utilisées dans chaque itération du processus de construction de l’arbre.
  4. min_child_samples : Ceci indique le nombre minimum d’échantillons qui peuvent être présents dans un nœud de feuille. Cela permet de contrôler le sur-ajustement.

J’ai utilisé la bibliothèque hyperopt en Python pour régler le modèle. Hyperopt est un paquet de recherche d’hyperparamètres qui implémente différents algorithmes de recherche pour trouver le meilleur ensemble d’hyperparamètres dans un espace de recherche. Pour utiliser Hyperopt, nous devons spécifier une fonction objective/loss à minimiser, l’espace de recherche et la base de données des essais (en option, MongoTrials peut être utilisé pour paralléliser la recherche). Pour notre problème, l’objectif est de minimiser le RMSE et d’identifier le meilleur ensemble de paramètres. Nous allons régler les paramètres max_depth, subsample, et colsample_bytree en utilisant hyperopt.

La fonction objective à utiliser pour le réglage est de minimiser le RMSE dans un régresseur LightGBM, en fonction d’un ensemble de paramètres.

L’espace de recherche définit l’ensemble des valeurs qu’un paramètre donné peut prendre. Après avoir défini la fonction objectifve et l’espace de recherche, nous effectuons 100 essais et évaluons le résultat des essais sur nos données de validation pour identifier les meilleurs paramètres.

def objective(space):
clf = lgb.LGBMRegressor(
          objective = 'regression',
          n_jobs = -1, # Updated from 'nthread'
          verbose=1,
          boosting_type='gbdt',
        num_leaves=60,
        bagging_freq=20,
       subsample_freq=100,
    max_depth=int(space['max_depth']),
    subsample=space['subsample'],
        n_estimators=5000,
    colsample_bytree=space['colsample'])
          #metric='l2_root')
eval_set=[( X_train, y_train), ( X_test,y_test)]
clf.fit(X_train, np.array(y_train),
            eval_set=eval_set,eval_metric='rmse',
            early_stopping_rounds=20)
pred = clf.predict(X_test)
    rmse = np.sqrt(mean_squared_error(y_test, pred))
    print("SCORE:", rmse)
return{'loss':rmse, 'status': STATUS_OK }
space ={
        'max_depth': hp.quniform("x_max_depth", 5, 30, 3),
       
        'subsample': hp.uniform ('x_subsample', 0.8, 1),
        'colsample':hp.uniform ('x_colsample', 0.3, 1)
    }
trials = Trials()
best = fmin(fn=objective,
            space=space,
            algo=tpe.suggest,
            max_evals=100,
            trials=trials)
print(best)

Les meilleurs paramètres que nous avons obtenus pour le LightGBM avec l’ingénierie des caractéristiques étaient :

{'max_depth': 24.0, 'subsample': 0.9988461076307639, 'colsample_bytree': 0.38429620148564814}

En utilisant ces paramètres, le modèle LightGBM a donné un RMSE de 3,633 et une variance de 0,44. Nous pouvons encore améliorer ce modèle en réglant d’autres paramètres comme num_leaves et en ajoutant des paramètres de régularisation L1 et L2.

Notes de fin

L’ingénierie des caractéristiques a considérablement amélioré la capacité de prédiction de notre modèle de Machine Learning. Une autre façon d’améliorer la précision du modèle est d’augmenter la quantité de données d’apprentissage, et/ou de construire des modèles d’ensemble. Si les données contiennent beaucoup de dimensions (caractéristiques), les techniques de réduction de la dimensionnalité peuvent également contribuer à améliorer la précision du modèle.