Data Science en pratique et insertion professionelle

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

Prochain cours le 30 Novembre et le 7 Décembre.

Pour le 30 Novembre, envoyez vos CV par mail.

Cours 3 : Model Evaluation, Selection & Optimization

Objectif du cours:

  • Vu d'ensemble des fonctions d'évaluation
  • Méthode de sélection de modèles
  • Présenter rapidemment les hyperparamètres important de quelques algorithmes connus.
  • Optimisation de paramètres
  • Sauvegarde des modèles
  • TP de mise en pratique

1 - Fonctions d'évaluation

La majorité des fonctions d'évaluation présentées ci-dessous sont disponible dans sklearn ou scipy, néanmoins, il est important de savoir les recoder car ils peut arriver que les résultats ne soient pas ceux attendus.

1.1 - Fonctions d'évaluation pour la régréssion

Il existe plusieurs façons de mesurer les erreurs de prédictions pour un problème de régression. Quelques-une des fonctions d'évaluation pour ce type de problème sont présentées ci-dessous. Cependant, il en existe beaucoup d'autres selon l'interprétabilité que l'on souhaite donner à nos résultats.

Tout dépend du problème à résoudre, et de ce qu'on veut en tirer.

1.1.1 - Mean Error (MAE)

Les fonctions d'erreur moyenne représentent la moyenne des écart entre les prédictions et les observation selon une certaine contrainte. C'est une métrique facilement interprétable. Il en existe plusieurs versions, voici les plus célèbres :

  • Absolute : ${\displaystyle \mathrm {MAE} ={\frac {\sum _{i=1}^{n}\left|y_{i}-\hat{y}_{i}\right|}{n}}}$

  • Square : ${\displaystyle \mathrm {MSE} ={\frac {\sum _{i=1}^{n}\left(y_{i}-\hat{y}_{i}\right)^2}{n}}}$

  • Percentage : ${\displaystyle {\mbox{MAE}}={\frac {100}{n}}\sum _{t=1}^{n}\left|{\frac {y_{i}-\hat{y}_{t}}{y_{i}}}\right|} $

Plus l'erreur est faible, plus le modèle est efficace.

1.1.2 R2

Le ${\displaystyle R^{2}}$ ou coefficient de détermination est une mesure de la qualité de la prédiction d'une régression linéaire.

Il est défini comme le rapport entre la variance expliquée et la variance totale :

$R^2(y, \hat{y}) = 1 - \frac{\sum_{i=0}^{n - 1} (y_i - \hat{y}_i)^2}{\sum_{i=0}^{n - 1} (y_i - \bar{y})^2}$

C'est une mesure d'erreur facilement intérprétable, plus il y a de variables explicative plus le $R^{2}$ augmente. Le meilleur score possible est de 1 et, il peut être négatif si le modèle est vraiment mal choisi ou si il n'y a pas de variables explicatives.

1.2 - Fonctions d'évaluation pour la classification binaire.

Pour évaluer les performances d'un classifieur binaire, il existe quelques fonctions d'évalution importantes. La plus simple est la mesure de précision, c'est à dire la somme des erreurs (1 si je me trompe, 0 sinon), néanmoins, en pratique on préfère utiliser l'erreur AUC.

1.2.2 - ROC-AUC

Pour les erreurs commises par la classification binaire on peut définir les résultats suivant:

Définition (Courbe ROC)*: On suppose que Y est la variable aléatoire des scores des expériences qui ont réussi. X est celle des scores des expériences qui ont échoué. On suppose également que tous les scores sont indépendants. On note $F_X$ et $F_Y$ les fonctions de répartition de ces variables. On définit en fonction d’un seuil $s \in \mathbb{R}$: $$R(s) = 1 - F_Y(s)$$ $$E(s) = 1 - F_X(s)$$

La courbe ROC est le graphe $ \{E(s),R(s)\}$ lorsque $s$ varie dans $\mathbb{R}$.

$VP(s)$ désigne les VP au-dessus du seuil $s$, avec les notations VP, FP, FN, VN, cela revient à :

$\begin{eqnarray*} E(s) &=& 1 - \frac{ VP(s) } { VP(s) + VN(s) } \\ R(s) &=& 1 - \frac{ FN(s) } { FP(s) + FN(s) } \end{eqnarray*}$

En faisant varier $s$ on obtient alors la courbe ROC pour le modèle choisi.

*(voir http://www.xavierdupre.fr, site que je vous conseille par ailleurs.)

L'AUC n'est autre que l'aire sous cette courbe, si elle est égale à 1 le modèle ne se trompe jamais.

1.2.3 - Log Loss

Perte de la régréssion logistique, elle est définit par les probabilités estimées tel qu'avec $ p = \operatorname{Pr}(y = 1)$ on a :

$L_{\log}(y, p) = -\log \operatorname{Pr}(y|p) = -(y \log (p) + (1 - y) \log (1 - p))$

2 - Séléction de modèles

Bien choisir le ou les modèle(s) à utiliser n'est pas une chose aisée. Cependant avec de la pratique on sait rapidemment vers quelle famille de modèles se tourner. Une manière de comparer des modèles est de comparer l'erreur de ceux-ci sur différents sous-ensemble des données. Pour ce faire, il existe plusieurs techniques.

2.1 - Train test split

Comme nous l'avons vu dans les cours précédents, une bonne méthode pour évaluer les performance d'un modèle sur un jeu de données et de séparer le jeu d'apprentissage en deux sous-ensemble, un d'apprentissage et un de validation. La fonction train_test_split de sklearn permet de réaliser cette échantillonnage de manière aléatoire. Un bon ratio est le 70/30.

Cependant, ne peut-on pas aller plus loin ? Ne pourrait-on pas utiliser cette séparation des données pour faire le meilleur modèle possible, on pourrait alors essayer différents choix de paramètres, ou bien regarder la robustesse du modèle? C'est sur cela que repose le principe de la validation croisée.

2.2 - K-fold

La méthode des K-fold permet de divisé l'échantillon K en sous-ensemble de tailles égales, dont l'un est utilisé pour la prévision et les K-1 restants pour l'estimation du modèle. La fonction KFold de sklearn permet de réaliser cette opération.

2.3 - K-folds Cross Validation.

La méthode cross_val_score de skelarn permet d'évaluer les performances d'un modèle selon une fonction d'évaluation et un nombre de folds choisi. Plus, il y aura de sous-ensemble plus le modèle sera robuste et son score sera, selon sa variance, le plus proche de la réalité. On peut alors, pour un découpage fixé, comparer plusieurs modèles grâce à leur performance sur une K-FoldCV. K=5 ou 10, permet d'établir de bons résultats.

Il existe bien d'autre façon de choisir son modèle, nous verrons cela plus en détails plus tard dans l'année.

3 - Hyperparamètres

3.1 Hyperparamètre propre à Sklearn

Dans la majorité des modèles disponibles dans sklearn on trouve les paramètres globaux suivants:

  • n_jobs : nombre de coeurs à utiliser pour effectuer les calculs, dépend de votre cpu.
  • verbose : affichage du déroulement des calculs, 0 = silent.
  • random_state : si la méthode repose sur de l'aléatoire, fixe le générateur pour reproduire les résultats.

3.2 -Arbres CART

Introduit par Breiman en 1984 : https://rafalab.github.io/pages/649/section-11.pdf.

Principe : construire un arbre de décision ou chaque noeud est une question binaire.

Sous sklearn le modèle est dénommé DecisionTreeClassifier & DecisionTreeRegressor, les hyperparamètres à connaitre sont les suivants:

  • criterion : Critère de découpe des noeuds, Gini pour la précision, Entropy pour un gain d'information
  • max_depth : profondeur maximale de l'arbre
  • min_samples_split : Nombre minimal d'observations pour séparer un noeud
  • max_features : Nombre de features à considérer pour la meilleure séparation d'un noeud.

3.3 - Méthodes d'ensemble

Méthodes non paramètrique : Random Forest, GBT, ExtraTrees ...

De manière général, les méthodes d'ensemble sont constituées de la manière suivante:

  • Un espace d'hyphotèse $\mathcal{H}$
  • Des weak learners $h_t$ efficace sur $\mathcal{H}$
  • Les combiner pour obtenir un big learner $H$

On ne s'intéresse ici qu'au weak learners étant des arbres.

3.3.1 Random Forest

Introduit par Breiman en 1999 : https://www.stat.berkeley.edu/~breiman/random-forests.pdf.

Principe : ensemble d'arbres décisionnel où a été introduit de l'aléatoire. Le résultat final est obtenu par vote majoritaire ou moyenne. (Tree bagging + feature sampling)

Sous sklearn le modèle est dénommé RandomForestClassifier & RandomForestRegressor, il y a deux sortes d'hyperparamètres, ceux relatif aux arbres utilisés et ceux de la forêt. Ceux des arbres sont ceux cités plus haut, l'hyperparamètre le plus important pour les random forest est le nombre d'arbre utilisés : n_estimators. La précision a tendance à augmenter quand le nombre d'arbres augmente, néanmoins, le temps de calcul est de plus en plus long.

3.3.2 - Gradient Tree Boosting

Introduit par Friedman en 1999 : https://statweb.stanford.edu/~jhf/ftp/trebst.pdf.

Principe : Boosting + descente de gradient. C'est-à-dire qu'on combine les arbres de façon pondérée en optimisant les poids par descente de gradient.

Sous sklearn le modèle est dénommé GradientTreeClassifier & GradientTreeRegressor. Comme pour les forêts, il y a les paramètres propres aux arbres, et ceux au boosting qui sont les suivants:

  • loss : Fonction de coût pour la GD.
  • learning_rate : pas de la descente de gradient, i.e., vitesse de choix des poids .
  • n_estimators : nombre d'itérations.
  • max_features : profondeur des arbres, cependant, dans le GTB, les arbres sont bien moins profond que pour les RF. On obtient par exemple de bon résultat avec des stump trees (max_depth=2)

3.4 - SVM

Introduit par Vapnik en 1963 : http://web.cs.iastate.edu/~cs573x/vapnik-portraits1963.pdf.

Principe : construire un hyperplan séparateur dans l’espace des observations, c’est-à-dire de séparer linéairement les données des différentes classes. (Et surtout le génial Kernel Trick pour les cas non linéairement séparable)

Sous sklearn le modèle est dénommé SVC & SVR. Les hyperparamètres sont les suivants :

  • kernel : Noyau utilisé, linéaire si cas linéaire etc.
  • c : Pénalité du terme d'erreur.
  • epsilon : taille du tube où la pénalité n'est pas appliquée.
  • gamma : coefficient du noyau.

3.5 - Modèles linéaires

On retrouve les paramètres suivant selon les cas (Ridge, Lasso, ElasticNet ...):

  • lambda : pénalité $L^1$.
  • alpha : pénalité $L^2$.
  • fit_intercept : tenir compte de l'intercept ou non

4 - Optimisation des hyperparamètres aka tuning

Tuning = Optimisation des hyperparamètres, plus ils sont bien choisi plus le modèle est performant.

Les librairies suivantes permettent d'effectuer des recherche d'hyperparamètres optimaux:

  • Sklearn
  • HyperOpt
  • Spearmint
  • H2O

Ici, nous ne verrons que les méthodes liées à sklearn, libre à vous d'aller explorer les autres librairies. Personnellement, j'utilise HyperOpt.

4.1 - Brut Force

Idée naïve de tester pleins de paramètres à la main à tour de rôle. Cependant, les résultats peuvent varier et ne pas être stables.

4.2 - Exhaustive GridSearch

L'idée est d'obtenir les paramètres impliquant les meilleures performances possible du modèle grâce à une validation croisée. La fonction GridSearchCV de sklearn permet d'effectuer cette recherche d'hyperparamètres, elle prend en entrée :

  • L'estimateur
  • Un espace de paramètres, i.e., une liste de valeurs possible pour chaque paramètre
  • Un nombre de folds pour la CV
  • Une fonction d'évaluation
  • Et d'autres paramètres pour le sampling ..

L'algorithme effectue pour chaque n-uplet de paramètres un calcul des performances du modèle sur une K-CV. A la fin, il récupère les paramètres ayant obtenu le meilleur score.

Plus il y a de paramètres et de folds, plus le calcul sera long, par exemple, pour 3 paramètres à 3 choix possibles et 3 folds on doit réaliser 81 fitting...

4.3 - Optimisation aléatoire

La méthode précédente est bien si l'on sait où chercher, ce qui n'est pas toujours le cas. On peut alors utiliser une gridsearch avec un espace de paramètres définit comme les distributions des hyperparamètres et en choisissant un nombre d'itérations de recherche maximal. Par exemple, le nombre d'estimateurs du forêt peut être compris entre 100 et 10000, plutôt que de chercher une valeur exact, on peut tirer des valeurs dans une uniforme discrète entre ces deux valeurs.

RandomizedGridSearch est une implémentation de cet algorithme.

On l'utilise généralement pour trouver un ordre de grandeur des valeurs des paramètres puis on effectue une gridsearch exhaustive.

4.4 - Autres méthodes

Dans la littérature il existe beaucoup d'autres techniques de recherche d'hyperparamètres.

On retrouve notamment la recherche par Optimisation Bayésienne qui consiste à donner un prior de la fonction objectif puis à évaluer les probabilités de la distribution à posteriori grâce à des processus Gaussiens. Et enfin, estimer une fonction objective simple. (voir : https://arxiv.org/pdf/1206.2944.pdf)

Il existe également des manières d'optimiser les hyperparamètres grâce à des descentes de gradient. (Voir les NNet)

5 - Sauvegarde et réutilisation d'un modèle

In [ ]:
import cPickle
from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(X,y)

#sauvegarde
with open('classifieur.object', 'wb') as m:
    cPickle.dump(model, m)    

#ouverture
with open('classifieur.object', 'rb') as m:
    model = cPickle.load(m)
    
#Vous pouvez le réutiliser dans n'importe quel autre code!

TP - Prédire le risque de casse de pipelines - Véolia (Challenge Data)

Problème de classification binaire : prédire si un pipeline à un risque de se casser dans l'année à venir. 1 si il y a casse probable, 0 sinon.

In [38]:
import pandas as pd, numpy as np, matplotlib.pyplot as plt, seaborn as sns
%matplotlib inline
In [41]:
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')

1 - Analyse exploratoire

1.1 - Explorer les données des deux datasets. Variables manquantes, catégorielles ? Que pensez-vous des variables cibles ?

In [52]:
print train.info(), '\n'
print train.describe()
## On remarque qu'il y a des variables objects, donc catégorielles.
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 13016 entries, 0 to 13015
Data columns (total 9 columns):
Id                         13016 non-null int64
Feature1                   13016 non-null object
Feature2                   13016 non-null object
Feature3                   13016 non-null float64
Feature4                   13016 non-null object
Length                     13016 non-null float64
YearConstruction           13016 non-null int64
YearLastFailureObserved    199 non-null float64
target                     13016 non-null int64
dtypes: float64(3), int64(3), object(3)
memory usage: 915.3+ KB
None 

                 Id      Feature3        Length  YearConstruction  \
count  13016.000000  13016.000000  13016.000000      13016.000000   
mean    9697.277428      0.001509     27.437403       1993.146589   
std     5608.059273      1.006591     45.198012         12.972717   
min        1.000000     -0.658872      0.030400       1900.000000   
25%     4816.250000     -0.434356      2.997800       1982.000000   
50%     9753.000000     -0.322098     11.492350       1996.000000   
75%    14536.250000     -0.060164     35.178200       2004.000000   
max    19427.000000      6.675303    778.547000       2013.000000   

       YearLastFailureObserved        target  
count               199.000000  13016.000000  
mean               2007.180905      0.002766  
std                   4.178763      0.052520  
min                1999.000000      0.000000  
25%                2003.500000      0.000000  
50%                2007.000000      0.000000  
75%                2011.000000      0.000000  
max                2013.000000      1.000000  
In [65]:
for i in train.columns:
    if train[i].dtype == 'object':
        print train[i].value_counts(normalize = True),'\n',test[i].value_counts(normalize = True) 

#On constate que l'on a la même répartition des variables catégorielles.
T    0.580286
P    0.419714
Name: Feature1, dtype: float64 
T    0.585868
P    0.414132
Name: Feature1, dtype: float64
U      0.651045
IAB    0.330055
O      0.018900
Name: Feature2, dtype: float64 
U      0.637030
IAB    0.346436
O      0.016534
Name: Feature2, dtype: float64
C     0.493162
D     0.437538
M     0.069069
Dr    0.000230
Name: Feature4, dtype: float64 
C     0.492279
D     0.440025
M     0.067072
Dr    0.000624
Name: Feature4, dtype: float64
In [66]:
print train['target'].value_counts(normalize = True) # 3% de 1, c'est un jeu de données très déséquilibré..
print test['target'].value_counts(normalize = True)
0    0.997234
1    0.002766
Name: target, dtype: float64
0    0.997348
1    0.002652
Name: target, dtype: float64
In [70]:
def Missing_values(data):
    total = data.isnull().sum().sort_values(ascending=False)
    percent = (data.isnull().sum()/data.isnull().count()).sort_values(ascending=False)
    missing_data = pd.concat([total,percent], axis=1, keys=['Total', 'Pourcentage'])
    #Affiche que les variables avec des na
    print missing_data[(percent>0)],'\n' 
    
Missing_values(train)
Missing_values(test) # Beaucoup de NA pour cette variable !
                         Total  Pourcentage
YearLastFailureObserved  12817     0.984711 

                         Total  Pourcentage
YearLastFailureObserved   6320     0.985806 

2.2 - Afficher quelques graphiques apportant de l'information sur ce qui influence les targets.

In [78]:
sns.heatmap(train.corr(),cmap='plasma') #Lenght et feature 3 semblent influer.
Out[78]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fdda3d8e9d0>
In [88]:
fig,axs = plt.subplots(nrows=2,ncols=2,figsize=(15,8))
fig.subplots_adjust(hspace=.3,wspace=.3)
sns.barplot(x='Feature1',y='target',data=train,ax=axs[0][0]);
sns.barplot(x='Feature2',y='target',data=train,ax=axs[0][1]);
sns.barplot(x='Feature4',y='target',data=train,ax=axs[1][0]);

#Certaines catégories ne semblent pas du tout influencer les target de valeur 1..
In [96]:
fig,axs = plt.subplots(nrows=2,ncols=2,figsize=(15,8))
fig.subplots_adjust(hspace=.3,wspace=.3)
sns.boxplot(y='Feature3',x='target',data=train,ax=axs[0][0])
sns.boxplot(y='Length',x='target',data=train,ax=axs[0][1])
sns.boxplot(y='YearConstruction',x='target',data=train,ax=axs[1][0])
sns.boxplot(y='YearLastFailureObserved',x='target',data=train,ax=axs[1][1])

#Les pipelines les plus longs et vieux semblent les plus enclins à se briser.
Out[96]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fdda40b4d10>

2 - Feature engineering

2.1 - Que faire des valeurs manquantes ?

In [100]:
# On pourrait supprimer cette variable disposant de plus de 98% de valeurs manquantes,
# mais ce n'est pas une bonne idée étant donnée l'influence que semble apporter cette variable 
#sur nos risques de casse.
#Remplacons les par une valeur nulle pour former un cluster de variables dont l'information n'est pas communiquée.
#C'est-à-dire que le pipeline ne s'est jamais brisé.

train.fillna(value = 0, inplace = True)
test.fillna(value = 0, inplace = True)
Missing_values(train)
Missing_values(test)
Empty DataFrame
Columns: [Total, Pourcentage]
Index: [] 

Empty DataFrame
Columns: [Total, Pourcentage]
Index: [] 

2.2 - Trouvez la meilleure stratégie permettant de gérer les variables catégorielles.

La meilleure stratégie est ici de binariser les variables étant donné le faible taux d'apparition de certaines catégories et de leur influence sur les données.

In [106]:
train = pd.get_dummies(train,columns = ['Feature1','Feature2','Feature4'])
test = pd.get_dummies(test,columns = ['Feature1','Feature2','Feature4'])

2.3 - Observez si une variable, une catégorie, ou autre, pourrait être supprimée car trop corrélées ou au contraire trop inutile.

In [110]:
sns.heatmap(train.corr(),cmap='plasma') 
#La catégorie M du feature 4 est très corrélée avec le feature length, pour éviter le sur-apprentissage
#c'est raisonnable de supprimer cette variable, qui n'est qu'une catégorie. 
#On pourrait faire de même avec les variables corrélées négativement.
Out[110]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fdda561c610>

2.4 - Supprimer cette variable, la variable Id, et récuperer les variables cibles des deux jeux de données.

In [116]:
Y_train = train['target']
Y_test = test['target']
train.drop(['Id','target','Feature4_M'], axis = 1, inplace = True)
test.drop(['Id','target','Feature4_M'], axis = 1, inplace = True)

3 - Sélection de modèles

Quels sont selon vous les classifieurs qui pourraient être efficace ?

3.1 - Construire une fonction Evaluation qui prend en entrée un dictionnaire de classifieurs, et renvoie la moyenne et l'écart-type de leurs performances sur une cross validation à trois ensemble pour l'erreur auc.

Classifieurs à utiliser : Gradient Tree Boosting, Random Forest, SVM, KNN et un arbre de décision

In [167]:
# Commençons par importer les classifieurs

from sklearn.ensemble import GradientBoostingClassifier,RandomForestClassifier
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import roc_auc_score,roc_curve,auc
from sklearn.model_selection import cross_val_score
In [149]:
def Evaluation(clfs):
    for clf in clfs:
        clfs[clf]['score'] = cross_val_score(clfs[clf]['clf'], train, Y_train, cv=3,scoring='roc_auc')
        print(clfs[clf]['name'] + ": %0.4f (+/- %0.4f)" % (clfs[clf]['score'].mean(), clfs[clf]['score'].std()*2))
In [150]:
clfs = {}
clfs['gbc'] = {'clf': GradientBoostingClassifier(), 'name': 'GradientBoostingClassifier'}
clfs['rf'] = {'clf': RandomForestClassifier( n_jobs=-1), 'name':'RandomForest'}
clfs['tree'] = {'clf': DecisionTreeClassifier(), 'name':'DecisionTreeClassifier'}
clfs['svc'] = {'clf': SVC(), 'name': 'SupportVectorClassifier'}
clfs['knn'] = {'clf': KNeighborsClassifier(), 'name': 'KNeighborsClassifier'}


Evaluation(clfs)
KNeighborsClassifier: 0.5208 (+/- 0.0377)
RandomForest: 0.5592 (+/- 0.1041)
DecisionTreeClassifier: 0.4986 (+/- 0.0010)
GradientBoostingClassifier: 0.8027 (+/- 0.1073)
SupportVectorClassifier: 0.6287 (+/- 0.2461)

3.2 - Quel modèle choisir ? Evaluer ses performances sur le jeu de test, et afficher la courbe ROC.

In [162]:
#Le meilleur modèle est le GradientBoosting.

model = GradientBoostingClassifier().fit(train,Y_train)
pred = model.predict_proba(test)[:,1]
print 'Performance du modèle :', roc_auc_score(Y_test,pred)
Performance du modèle : 0.809214520966
In [181]:
fpr, tpr, _ = roc_curve(Y_test, pred)
plt.plot(fpr,tpr);

4 - Optimisation du modèle

4.1 - Faites varier les principaux paramètres du modèle, et observer les performances.

In [200]:
model = GradientBoostingClassifier(n_estimators=300, learning_rate=0.1,max_depth=3).fit(train,Y_train)
pred = model.predict_proba(test)[:,1]
print 'Performance du modèle :', roc_auc_score(Y_test,pred)
#En faisant à peine bouger les paramètres, on voit une amélioriation des performances.
Performance du modèle : 0.824978380467

4.2 - Effectuer une recherche automatique des paramètres grâce à une des méthodes présentées plus haut, et tester votre modèle.

In [220]:
from sklearn.grid_search import GridSearchCV

#Attention, cela peut être long !
model = GradientBoostingClassifier()

params = {
    'n_estimators' : [100,250,500],
    'max_depth' : [2,3,5],
    'learning_rate':[0.01,0.05,0.1]}

grid = GridSearchCV(model,param_grid=params,cv=3,scoring = 'roc_auc',n_jobs=-1,verbose = 1)
grid.fit(train,Y_train)
Fitting 3 folds for each of 27 candidates, totalling 81 fits
[Parallel(n_jobs=-1)]: Done  42 tasks      | elapsed:   11.5s
[Parallel(n_jobs=-1)]: Done  81 out of  81 | elapsed:   20.0s finished
Out[220]:
GridSearchCV(cv=3, error_score='raise',
       estimator=GradientBoostingClassifier(criterion='friedman_mse', init=None,
              learning_rate=0.1, loss='deviance', max_depth=3,
              max_features=None, max_leaf_nodes=None,
              min_impurity_decrease=0.0, min_impurity_split=None,
              min_samples_leaf=1, min_samples_split=2,
              min_weight_fraction_leaf=0.0, n_estimators=100,
              presort='auto', random_state=None, subsample=1.0, verbose=0,
              warm_start=False),
       fit_params={}, iid=True, n_jobs=-1,
       param_grid={'n_estimators': [100, 250, 500], 'learning_rate': [0.01, 0.05, 0.1], 'max_depth': [2, 3, 5]},
       pre_dispatch='2*n_jobs', refit=True, scoring='roc_auc', verbose=1)
In [222]:
print 'Résultat de la grid search :', grid.best_score_, grid.best_params_

##On peut récupérer le meilleur modèle : 
best = grid.best_estimator_

print 'Performance du modèle optimisé :', roc_auc_score(Y_test,best.predict_proba(test)[:,1])
# 5% de précision en plus !
Résultat de la grid search : 0.872752946523 {'n_estimators': 500, 'learning_rate': 0.01, 'max_depth': 3}
Performance du modèle optimisé : 0.855273326096