Data Science en pratique et insertion professionelle

Arthur Llau, data scientist chez Safety Line : arthur.llau@safety-line.fr

Prochain cours les mercredi 7 et 21 Février, ainsi que le 7 et le 21 Mars. Salle habituelle.

Cours à venir :

  • Cours 6: Algorithmes et techniques avancés
  • Cours 7: Méthode d'amélioration des prédictions (Agrégation, Stacking, Super Learner)
  • Cours 8: Introduction au NLP/Text Engineering
  • Cours 9: Introduction à la Classification d'images
  • Cours 10: Challenge par équipe

Cours 6 : Algorithmes et techniques avancés

Objectif du cours:

  • Présentation de quelques algorithmes et implémentations avancés.
  • Présenter rapidemment l'importance des features
  • TP de mise en pratique (as usual)

1 - Algorithmes et implémentations avancés

1.1 - Extremely Randomized Trees

Original paper (P. Gueurts et al., 2005) : https://link.springer.com/article/10.1007/s10994-006-6226-1

Très similaire à l'algorithme des forêts aléatoire, cette algorithme possède toutefois deux différences majeurs:

  • L'algorithme des ETR n'utilise pas de procédure de bagging pour construire l'ensemble d'entrainement de chaque arbre. Les arbres apprennent tous sur le jeu de données tout entier.
  • Là où dans l'algorithme des RF la séparation de chaque noeud est choisie de manière heuristique : on prend le meilleur noeud possible dans un ensemble aléatoire de variables. L'algorithme des ETR choisit de manière aléatoire la séparation.

Les hyperparamètres des ETR sont similaires, aux différences algorithmique près, à ceux des RF. On trouve une implémentation de cet algorithme dans Sklearn sous le nom de ExtraTrees[Reg/Clf].

1.2 Boosting & XtremBoosting

Les trois algorithmes décrits ci-dessous sont des méthodes de gradient boosting. LightGBM et Xgboost sont des implémentations dites d'extrême boosting, c'est-à-dire qu'elles sont construites de manière à utiliser du mieu possible les ressources computationnelles. Néanmoins, quelques points diffères dans l'algorithme lui même.

Il est important de noté que ces méthodes sont implémentées de manière propre à chacune. Mais elles possèdent également un wrapper scikit-learn.

Un autre atout majeur de ces implémentations est l'utilisation de jeux de validation pour obtenir le nombre d'itérations optimal. À chaque itération on regarde si les performances sur le jeu de validation sont améliorés, sinon on arrête. On prend alors la dernière itération la plus performante. Évidemment, il est possible de choisir un nombre d'itération pour lequel il n'y a d'améliorations. (voir Tp)

1.2.1 XGBoost

Original paper (T.Chen, 2016) : https://arxiv.org/abs/1603.02754

Il existe plusieurs diffèrences notable avec l'implémentation du GBT classique :

  • Cette implémentation gère les valeurs manquantes
  • Le modèle est régularisé,i.e. on contrôle mieux l'overfitting, ce qui explique ces bonnes performances
  • On trouve des paramètres de pénalisation $L^1$ et $L^2$, qui n'existe pas dans la version originale
  • D'autres methodes que les arbres CART peuvent être utilisés: des régréssions linéaire (Linear Boosting), ou des arbres DART (Dropouts Additive Regression Trees, http://proceedings.mlr.press/v38/korlakaivinayak15.pdf)
  • On peut résoudre la grande majorité des problématiques industrielles avec cette implémentation, de la régréssion au ranking, en passant par de la classification multi-classes.
  • Très customisable

On retrouve les même paramètres que pour l'implémentation du GBT dans sklearn. Attention, le nom n'est pas forcément le même, je vous invite à lire la documentation. Cependant, il en existe quelques autres qui sont importants:

  • booster : type de méthode à booster
  • objective : l'objectif du modèle (régéssion, etc.)
  • tree_method : méthodes de construction des arbres (exact, approx, histogramme)
  • eval_metric : choix de la métrique d'évaluation

Pour utiliser la version sklearn : "from xgboost import XGBRegressor" (ou XGBClassifier).

/!\ Tous les paramètres ne sont pas disponibles dans le wrapper sklearn.

In [1]:
#Sans wrapper cela ressemble à cela.
import xgboost as xgb
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

train = load_boston()['data']
target = load_boston()['target']
x_train,x_test,y_train,y_test = train_test_split(train,target)

#Definition des objets d'apprentissage et test
dtrain = xgb.DMatrix(x_train,y_train)
dtest = xgb.DMatrix(x_test,y_test)

#Apprentissage
param = {'boost':'linear',
         'learnin_rate':0.1,
         'max_depth': 5, 
         'objective': 'reg:linear',
          'eval_metric':'rmse'}
num_round = 100
bst = xgb.train(param, dtrain, num_round)


#Prediction
preds = bst.predict(dtest)
print 'Xgboost scoring : {}'.format(mean_squared_error(y_test,preds))
Xgboost scoring : 10.9779122244

1.2.2 Lightgbm

Original paper (Microsoft Research, 2017) : https://papers.nips.cc/paper/6907-lightgbm-a-highly-efficient-gradient-boosting-decision-tree

Introduit par Microsoft Research cet algorithme est très performant, mais difficile à calibrer. Des différences majeures avec les anciennes implémentations sont également présente:

  • Construction des arbres verticale et non horizontale, i.e, l'algorithme choisi la feuille avec la meilleure loss pour grandir.
  • Très efficace et rapide sur les données sparse et, les gros volumes de données.
  • Comme pour xgboost, d'autres booster sont disponible comme les random forest.
  • Il consomme très peu de ressources mémoire.
  • Résolution de n'importe quel type de problématique.
  • Très customisable

On retrouve les même paramètres que pour l'implémentation du GBT dans sklearn. Attention, le nom n'est pas forcément le même, je vous invite à lire la doc. Cependant, il en existe quelques autres qui sont importants - les noms varient par rapport à xgb:

  • boosting_type : type de méthode à booster
  • task : l'objectif du modèle (régéssion, etc.)
  • num_leaves : contrôle la complexité du modèle, en relation avec le GBT num_leaves = $2^{\text{max_depth}}$
  • device : CPU/GPU
  • metric : choix de la métrique d'évaluation

Pour utiliser la version sklearn : "from lightgbm import LGBMRegressor" (ou LGBMClassifier).

/!\ Tous les paramètres ne sont pas disponibles dans le wrapper sklearn.

In [2]:
import lightgbm as lgb

lgb_train = lgb.Dataset(x_train, y_train)
lgb_eval = lgb.Dataset(x_test, y_test, reference=lgb_train)

params = {
   
    'boosting_type': 'rf',
    'objective': 'regression',
    'metric': {'l2', 'rmse'},
    'num_leaves': 31,
    'learning_rate': 0.05,
    'feature_fraction': 0.9,
    'bagging_fraction': 0.8,
    'bagging_freq': 5,
    'verbose_eval':5
}

print('Start training...')
gbm = lgb.train(params,
                lgb_train,
                num_boost_round=50,
                valid_sets=lgb_eval,
                early_stopping_rounds=5) # Arret si 5 iterations sans gain de performance


print('Start predicting...')
preds = gbm.predict(x_test, num_iteration=gbm.best_iteration)
print('LGBM scoring :', mean_squared_error(y_test, preds))
Start training...
[1]	valid_0's rmse: 6.4842	valid_0's l2: 42.0449
Training until validation scores don't improve for 5 rounds.
[2]	valid_0's rmse: 6.4842	valid_0's l2: 42.0449
[3]	valid_0's rmse: 6.45817	valid_0's l2: 41.7079
[4]	valid_0's rmse: 6.46152	valid_0's l2: 41.7512
[5]	valid_0's rmse: 6.54774	valid_0's l2: 42.8729
[6]	valid_0's rmse: 6.516	valid_0's l2: 42.4583
[7]	valid_0's rmse: 6.49382	valid_0's l2: 42.1697
[8]	valid_0's rmse: 6.24507	valid_0's l2: 39.001
[9]	valid_0's rmse: 6.24156	valid_0's l2: 38.9571
[10]	valid_0's rmse: 6.25525	valid_0's l2: 39.1282
[11]	valid_0's rmse: 6.2966	valid_0's l2: 39.6472
[12]	valid_0's rmse: 6.33072	valid_0's l2: 40.078
[13]	valid_0's rmse: 6.22891	valid_0's l2: 38.7993
[14]	valid_0's rmse: 6.15115	valid_0's l2: 37.8366
[15]	valid_0's rmse: 6.09114	valid_0's l2: 37.102
[16]	valid_0's rmse: 6.17311	valid_0's l2: 38.1073
[17]	valid_0's rmse: 6.10094	valid_0's l2: 37.2215
[18]	valid_0's rmse: 6.10263	valid_0's l2: 37.2421
[19]	valid_0's rmse: 6.10793	valid_0's l2: 37.3068
[20]	valid_0's rmse: 6.03273	valid_0's l2: 36.3939
[21]	valid_0's rmse: 5.98	valid_0's l2: 35.7604
[22]	valid_0's rmse: 5.93069	valid_0's l2: 35.1731
[23]	valid_0's rmse: 5.9716	valid_0's l2: 35.6601
[24]	valid_0's rmse: 5.93222	valid_0's l2: 35.1912
[25]	valid_0's rmse: 5.89791	valid_0's l2: 34.7853
[26]	valid_0's rmse: 5.86187	valid_0's l2: 34.3615
[27]	valid_0's rmse: 5.84091	valid_0's l2: 34.1162
[28]	valid_0's rmse: 5.82116	valid_0's l2: 33.8858
[29]	valid_0's rmse: 5.80514	valid_0's l2: 33.6996
[30]	valid_0's rmse: 5.78587	valid_0's l2: 33.4762
[31]	valid_0's rmse: 5.77161	valid_0's l2: 33.3114
[32]	valid_0's rmse: 5.75622	valid_0's l2: 33.1341
[33]	valid_0's rmse: 5.74516	valid_0's l2: 33.0069
[34]	valid_0's rmse: 5.73333	valid_0's l2: 32.8711
[35]	valid_0's rmse: 5.72642	valid_0's l2: 32.7918
[36]	valid_0's rmse: 5.71813	valid_0's l2: 32.697
[37]	valid_0's rmse: 5.70963	valid_0's l2: 32.5999
[38]	valid_0's rmse: 5.73011	valid_0's l2: 32.8341
[39]	valid_0's rmse: 5.72223	valid_0's l2: 32.7439
[40]	valid_0's rmse: 5.74146	valid_0's l2: 32.9644
[41]	valid_0's rmse: 5.71764	valid_0's l2: 32.6914
[42]	valid_0's rmse: 5.69546	valid_0's l2: 32.4383
[43]	valid_0's rmse: 5.67574	valid_0's l2: 32.214
[44]	valid_0's rmse: 5.66334	valid_0's l2: 32.0734
[45]	valid_0's rmse: 5.6452	valid_0's l2: 31.8683
[46]	valid_0's rmse: 5.64804	valid_0's l2: 31.9003
[47]	valid_0's rmse: 5.65154	valid_0's l2: 31.9399
[48]	valid_0's rmse: 5.66964	valid_0's l2: 32.1448
[49]	valid_0's rmse: 5.65941	valid_0's l2: 32.0289
[50]	valid_0's rmse: 5.66476	valid_0's l2: 32.0896
Early stopping, best iteration is:
[45]	valid_0's rmse: 5.6452	valid_0's l2: 31.8683
Start predicting...
('LGBM scoring :', 31.868316460801914)

1.2.3 CatBoost

Original paper (Yandex, 2017) : https://arxiv.org/abs/1706.09516

Dernier né de la firme russe Yandex, CatBoost est une implémentation similaire à celle de xgboost et de lightgbm mais qui a la particularité de tenir compte des variables catégorielles pour l'apprentissage. L'algorithme va construire à partir des variables catégorielles diverses statistiques* et, tenir compte de celles-ci pour l'apprentissage. On trouve quelque différences notables avec les deux autres algorithmes présentés ci-dessous:

  • Très performant mais très lent.
  • Pas d'autre booster que les arbres de disponibles
  • Deux taches : régréssion ou classification
  • Plusieurs paramètres pour l'apprentissage des variables catégorielles.
  • Paramètres de détéction de l'overfitting
  • Il y a une interface graphique super sexy...

*Voir https://tech.yandex.com/catboost/doc/dg/concepts/algorithm-main-stages_cat-to-numberic-docpage/

Quelques paramètres importants :

  • iterations : nombre d'arbres
  • eval_metric : la métrique d'évaluation
  • ctr : paramètre de transformation des variables catégorielles
  • fold_permutation_block_size : nombre de permutations des catégorielles

Pour utiliser la version sklearn : "from catboost import CatBoostRegressor" (ou CatBoostClassifier). Et pour utiliser l'apprentissage sur les variables catégorielles, ajouter dans fit cat_features = [index des features catégorielles].

/!\ Tous les paramètres ne sont pas disponibles dans le wrapper sklearn.

In [3]:
import catboost as cat
train_pool = cat.Pool(x_train,y_train,cat_features = [3,8])
test_pool = cat.Pool(x_test,cat_features = [3,8])

param = {'logging_level':'Silent'}
model = cat.CatBoost(param)
model.fit(train_pool) 

preds = model.predict(test_pool)
print('Cat scoring :', mean_squared_error(y_test, preds))
('Cat scoring :', 12.603604896736309)
In [4]:
w = cat.CatboostIpythonWidget('')
w.update_widget()
Widget Javascript not detected.  It may not be installed or enabled properly.

Notez que les trois implémentations ci-dessus sont également portable sur GPU.

Chaque algorithme aura des performances différentes selon le jeu de données et le problème à traiter, à vous de choisir (et de tester) le meilleur modèle.

1.3 - Regularized Greedy Forest

Original paper (R. Johnson et al., 2014) : https://arxiv.org/pdf/1109.0887.pdf

Popularisé il y a peu, les RGF sont une sorte de mélange entre des forêts aléatoire et du boosting. Une RGF est un boosting de forêts d'arbres décisionnels construit de manière détérministe, et non aléatoirement.

C'est un algorithme puissant mais complexe, je vous invite si cela vous intéresse à lire le papier original.

La liste des paramètres est disponible ici : https://github.com/fukatani/rgf_python

Deux modèles sont implémentés : RGFRegressor et RGFClassifier du package rgf_python.

1.4 Autres algorithmes plus ou moins célèbres

Vous pouvez également vous penchez sur les algorithmes suivant :

Et le non supervisé ?

Il existe également d'autres algorithmes avancés pour la réduction de dimension et le clustering :

Pour découvrir de nouveaux algorithmes je vous invite à consulter les divers forums de machine learning indiqués au premier cours, ainsi que les papiers des conférences NIPS, ICML & MLConf...

2 - Feature importance & Feature Selection

2.1 - Feature importance

On parle de feature importance presque uniquement dans le cas des algorithmes de machine learning basés sur les arbres CART. L'importance d'une variable, n'est autre que le poids - selon une mesure donnée - qu'apporte cette variable dans l'apprentissage. Pour les arbres CART on retrouve la définition suivante :

Les arbres aléatoires sont une règle de décision interprétable pour la prédiction des classes.En effet, les variables de découpe sont choisies selon leur capacité à classifier les données. De ce fait, une variable permettant de découper les premiers nœuds a un pouvoir discriminant plus important qu’une variable apparaissant dans les dernières découpes (ou n’apparaissant pas dans l’arbre). B.Gregorutti- https://tel.archives-ouvertes.fr/tel-01146830/document

Dans les méthodes d'ensemble, l'importance des features est calculée généralement de la manière suivante: On compte le nombre de fois où la variable est séléctionnée pour séparer un noeud, pondérée par l'amélioration du modèle à chaque séparation. Puis on moyenne les résultats.

Pour résumer : variable très utilisée qui sépare bien les données = variable importante.

2.2 - Feature Selection

Il est parfois nécéssaire d'effectuer une séléction des variables utilisées dans le modèle pour plusieurs raisons:

  • Trop de corrélations entre certaines variables
  • Trop de variables ce qui entraine un temps de calcul excessif
  • Expertise métier
  • Problème de dimensions
  • Variable à la distribution étrange
  • Et enfin, importance faible

Il existe alors plusieurs technique pour séléctionner les variables, autre que les critères d'Akkaike etc.

Voici la liste de ceux que j'utilise en pratique:

  • VarianceThreshold : On supprime les features avec une variance dépassant un certain seuil.
  • SelectKBest : On ne sélectionne que les $k$ features les plus important
  • RFECV : On supprime récursivement des features et on regarde les performance du modèle

Tous ces modèles de séléction sont disponible dans sklearn.

TP de mise en pratique

Les données proviennent du Kaggle suivant : https://www.kaggle.com/c/house-prices-advanced-regression-techniques

La variable à prédire est la "SalePrice".

In [5]:
import pandas as pd, numpy as np, matplotlib.pyplot as plt, seaborn as sns,time

from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import LabelEncoder

from xgboost import XGBRegressor
from lightgbm import LGBMRegressor
from catboost import CatBoostRegressor, Pool
from rgf.sklearn import *

1 Importer les données.

In [6]:
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
train.head() #Tout est bon
Out[6]:
Id MSSubClass MSZoning LotFrontage LotArea Street Alley LotShape LandContour Utilities ... PoolArea PoolQC Fence MiscFeature MiscVal MoSold YrSold SaleType SaleCondition SalePrice
0 831 20 RL 80.0 11900 Pave NaN IR1 Lvl AllPub ... 0 NaN NaN NaN 0 6 2008 WD Normal 166000
1 1012 90 RL 75.0 9825 Pave NaN Reg Lvl AllPub ... 0 NaN NaN NaN 0 5 2010 WD Normal 100000
2 1165 80 RL NaN 16157 Pave NaN IR1 Lvl AllPub ... 0 NaN NaN NaN 0 6 2007 WD Normal 194000
3 1247 60 FV 65.0 8125 Pave NaN Reg Lvl AllPub ... 0 NaN NaN NaN 0 3 2006 New Partial 186500
4 487 20 RL 79.0 10289 Pave NaN Reg Lvl AllPub ... 0 NaN NaN NaN 0 6 2007 WD Normal 156000

5 rows × 81 columns

2 Faites une rapide étude statistique et quelques graphiques sur l'importance des features par rapport à la variable cible. Les valeurs manquantes ?

In [7]:
train.describe()
Out[7]:
Id MSSubClass LotFrontage LotArea OverallQual OverallCond YearBuilt YearRemodAdd MasVnrArea BsmtFinSF1 ... WoodDeckSF OpenPorchSF EnclosedPorch 3SsnPorch ScreenPorch PoolArea MiscVal MoSold YrSold SalePrice
count 1022.000000 1022.000000 837.000000 1022.000000 1022.000000 1022.000000 1022.000000 1022.000000 1017.000000 1022.000000 ... 1022.000000 1022.000000 1022.000000 1022.000000 1022.000000 1022.000000 1022.000000 1022.000000 1022.000000 1022.000000
mean 730.187867 56.409002 70.425329 10565.642857 6.147750 5.583170 1971.843444 1985.197652 102.954769 445.538160 ... 94.549902 46.992172 22.358121 3.405088 16.104697 2.235812 51.559687 6.218200 2007.849315 183237.352250
std 418.632789 41.195064 25.416685 9805.194187 1.410292 1.114604 30.029091 20.494190 182.495831 470.025894 ... 123.202122 66.679595 62.730733 30.150200 57.155644 36.229005 582.624705 2.707833 1.315468 82327.088548
min 1.000000 20.000000 21.000000 1300.000000 1.000000 1.000000 1872.000000 1950.000000 0.000000 0.000000 ... 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000 2006.000000 34900.000000
25% 368.250000 20.000000 60.000000 7633.000000 5.000000 5.000000 1954.000000 1968.000000 0.000000 0.000000 ... 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 4.000000 2007.000000 131500.000000
50% 734.500000 50.000000 70.000000 9580.000000 6.000000 5.000000 1973.500000 1994.000000 0.000000 374.500000 ... 0.000000 26.500000 0.000000 0.000000 0.000000 0.000000 0.000000 6.000000 2008.000000 164700.000000
75% 1092.500000 70.000000 80.000000 11700.000000 7.000000 6.000000 2001.000000 2004.000000 165.000000 707.500000 ... 168.000000 68.000000 0.000000 0.000000 0.000000 0.000000 0.000000 8.000000 2009.000000 215000.000000
max 1459.000000 190.000000 313.000000 215245.000000 10.000000 9.000000 2010.000000 2010.000000 1600.000000 5644.000000 ... 857.000000 547.000000 552.000000 508.000000 480.000000 738.000000 15500.000000 12.000000 2010.000000 755000.000000

8 rows × 38 columns

In [8]:
sns.heatmap(train.corr())
plt.show()