Tutoriel Scikit-Learn : Comment installer et exemples Scikit-Learn

Qu'est-ce que Scikit-learn ?

Scikit-apprendre est une bibliothèque Python open source pour l'apprentissage automatique. Il prend en charge des algorithmes de pointe tels que KNN, XGBoost, random forest et SVM. Il est construit sur NumPy. Scikit-learn est largement utilisé dans la compétition Kaggle ainsi que dans les principales entreprises technologiques. Il aide au prétraitement, à la réduction de dimensionnalité (sélection de paramètres), à la classification, à la régression, au clustering et à la sélection de modèles.

Scikit-learn possède la meilleure documentation de toutes les bibliothèques open source. Il vous fournit un graphique interactif à https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html.

Comment fonctionne Scikit Learn
Comment fonctionne Scikit Learn

Scikit-learn n'est pas très difficile à utiliser et donne d'excellents résultats. Cependant, scikit learn ne prend pas en charge les calculs parallèles. Il est possible d'exécuter un algorithme de deep learning avec mais ce n'est pas une solution optimale, surtout si vous savez utiliser TensorFlow.

Comment télécharger et installer Scikit-learn

Maintenant, dans ce didacticiel Python Scikit-learn, nous allons apprendre comment télécharger et installer Scikit-learn :

Option: AWS

scikit-learn peut être utilisé sur AWS. S'il te plaît reportez-vous L'image Docker sur laquelle scikit-learn est préinstallé.

Pour utiliser la version développeur, utilisez la commande dans Jupyter

import sys
!{sys.executable} -m pip install git+git://github.com/scikit-learn/scikit-learn.git

Option: Mac ou Windows utiliser Anaconda

Pour en savoir plus sur l'installation d'Anaconda, reportez-vous https://www.guru99.com/download-install-tensorflow.html

Récemment, les développeurs de scikit ont publié une version de développement qui résout les problèmes courants rencontrés avec la version actuelle. Nous avons trouvé plus pratique d’utiliser la version développeur au lieu de la version actuelle.

Comment installer scikit-learn avec l'environnement Conda

Si vous avez installé scikit-learn avec l'environnement conda, veuillez suivre l'étape pour mettre à jour vers la version 0.20

Étape 1) Activer l'environnement Tensorflow

source activate hello-tf

Étape 2) Supprimez scikit lean à l’aide de la commande conda

conda remove scikit-learn

Étape 3) Installez la version développeur.
Installez la version développeur de scikit learn ainsi que les bibliothèques nécessaires.

conda install -c anaconda git
pip install Cython
pip install h5py
pip install git+git://github.com/scikit-learn/scikit-learn.git

REMARQUE: Windows l'utilisateur devra installer Microsoft Visual C++ 14. Vous pouvez l'obtenir à partir de ici

Exemple Scikit-Learn avec l'apprentissage automatique

Ce didacticiel Scikit est divisé en deux parties :

  1. Apprentissage automatique avec scikit-learn
  2. Comment faire confiance à votre modèle avec LIME

La première partie details comment construire un pipeline, créer un modèle et régler les hyperparamètres tandis que la deuxième partie fournit l'état de l'art en termes de sélection de modèle.

Étape 1) Importez les données

Au cours de ce didacticiel Scikit Learn, vous utiliserez l'ensemble de données pour adultes.

Pour un aperçu de cet ensemble de données, reportez-vous. Si vous souhaitez en savoir plus sur les statistiques descriptives, veuillez utiliser les outils Dive et Overview.

Référer ce tutoriel en savoir plus sur la plongée et la présentation

Vous importez l'ensemble de données avec Pandas. Notez que vous devez convertir le type des variables continues au format float.

Cet ensemble de données comprend huit variables catégorielles :

Les variables catégorielles sont répertoriées dans CATE_FEATURES

  • classe ouvrière
  • l'éducation
  • matrimonial
  • occupation
  • relation amoureuse
  • breed
  • sexe
  • pays d'origine

de plus, six variables continues :

Les variables continues sont listées dans CONTI_FEATURES

  • âge
  • fnlwgt
  • num_education
  • gain_capital
  • perte_de_capital
  • heures_semaine

Notez que nous remplissons la liste à la main afin que vous ayez une meilleure idée des colonnes que nous utilisons. Un moyen plus rapide de construire une liste de catégories ou de continues consiste à utiliser :

## List Categorical
CATE_FEATURES = df_train.iloc[:,:-1].select_dtypes('object').columns
print(CATE_FEATURES)

## List continuous
CONTI_FEATURES =  df_train._get_numeric_data()
print(CONTI_FEATURES)

Voici le code pour importer les données :

# Import dataset
import pandas as pd

## Define path data
COLUMNS = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital',
           'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss',
           'hours_week', 'native_country', 'label']
### Define continuous list
CONTI_FEATURES  = ['age', 'fnlwgt','capital_gain', 'education_num', 'capital_loss', 'hours_week']
### Define categorical list
CATE_FEATURES = ['workclass', 'education', 'marital', 'occupation', 'relationship', 'race', 'sex', 'native_country']

## Prepare the data
features = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital',
           'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss',
           'hours_week', 'native_country']

PATH = "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data"

df_train = pd.read_csv(PATH, skipinitialspace=True, names = COLUMNS, index_col=False)
df_train[CONTI_FEATURES] =df_train[CONTI_FEATURES].astype('float64')
df_train.describe()
âge fnlwgt num_education gain_capital perte_de_capital heures_semaine
compter 32561.000000 3.256100e + 04 32561.000000 32561.000000 32561.000000 32561.000000
signifier 38.581647 1.897784e + 05 10.080679 1077.648844 87.303830 40.437456
std 13.640433 1.055500e + 05 2.572720 7385.292085 402.960219 12.347429
m. 17.000000 1.228500e + 04 1.000000 0.000000 0.000000 1.000000
25 % 28.000000 1.178270e + 05 9.000000 0.000000 0.000000 40.000000
50 % 37.000000 1.783560e + 05 10.000000 0.000000 0.000000 40.000000
75 % 48.000000 2.370510e + 05 12.000000 0.000000 0.000000 45.000000
max 90.000000 1.484705e + 06 16.000000 99999.000000 4356.000000 99.000000

Vous pouvez vérifier le nombre de valeurs uniques des fonctionnalités native_country. Vous pouvez voir qu'un seul ménage vient de Hollande-Pays-Bas. Ce ménage ne nous apportera aucune information, mais le fera par une erreur lors de l'entraînement.

df_train.native_country.value_counts()
United-States                 29170
Mexico                          643
?                               583
Philippines                     198
Germany                         137
Canada                          121
Puerto-Rico                     114
El-Salvador                     106
India                           100
Cuba                             95
England                          90
Jamaica                          81
South                            80
China                            75
Italy                            73
Dominican-Republic               70
Vietnam                          67
Guatemala                        64
Japan                            62
Poland                           60
Columbia                         59
Taiwan                           51
Haiti                            44
Iran                             43
Portugal                         37
Nicaragua                        34
Peru                             31
France                           29
Greece                           29
Ecuador                          28
Ireland                          24
Hong                             20
Cambodia                         19
Trinadad&Tobago                  19
Thailand                         18
Laos                             18
Yugoslavia                       16
Outlying-US(Guam-USVI-etc)       14
Honduras                         13
Hungary                          13
Scotland                         12
Holand-Netherlands                1
Name: native_country, dtype: int64

Vous pouvez exclure cette ligne non informative de l'ensemble de données

## Drop Netherland, because only one row
df_train = df_train[df_train.native_country != "Holand-Netherlands"]

Ensuite, vous stockez la position des entités continues dans une liste. Vous en aurez besoin à l'étape suivante pour construire le pipeline.

Le code ci-dessous parcourra tous les noms de colonnes dans CONTI_FEATURES et obtiendra son emplacement (c'est-à-dire son numéro), puis l'ajoutera à une liste appelée conti_features.

## Get the column index of the categorical features
conti_features = []
for i in CONTI_FEATURES:
    position = df_train.columns.get_loc(i)
    conti_features.append(position)
print(conti_features)  
[0, 2, 10, 4, 11, 12]

Le code ci-dessous fait le même travail que ci-dessus mais pour la variable catégorielle. Le code ci-dessous répète ce que vous avez fait précédemment, sauf avec les fonctionnalités catégorielles.

## Get the column index of the categorical features
categorical_features = []
for i in CATE_FEATURES:
    position = df_train.columns.get_loc(i)
    categorical_features.append(position)
print(categorical_features)  
[1, 3, 5, 6, 7, 8, 9, 13]

Vous pouvez consulter l'ensemble de données. Notez que chaque fonctionnalité catégorielle est une chaîne. Vous ne pouvez pas alimenter un modèle avec une valeur de chaîne. Vous devez transformer l'ensemble de données à l'aide d'une variable factice.

df_train.head(5)

En fait, vous devez créer une colonne pour chaque groupe de la fonctionnalité. Tout d’abord, vous pouvez exécuter le code ci-dessous pour calculer le nombre total de colonnes nécessaires.

print(df_train[CATE_FEATURES].nunique(),
      'There are',sum(df_train[CATE_FEATURES].nunique()), 'groups in the whole dataset')
workclass          9
education         16
marital            7
occupation        15
relationship       6
race               5
sex                2
native_country    41
dtype: int64 There are 101 groups in the whole dataset

L'ensemble de données contient 101 groupes, comme indiqué ci-dessus. Par exemple, les fonctionnalités de workclass comportent neuf groupes. Vous pouvez visualiser le nom des groupes avec la suitewing codes

unique() renvoie les valeurs uniques des caractéristiques catégorielles.

for i in CATE_FEATURES:
    print(df_train[i].unique())
['State-gov' 'Self-emp-not-inc' 'Private' 'Federal-gov' 'Local-gov' '?'
 'Self-emp-inc' 'Without-pay' 'Never-worked']
['Bachelors' 'HS-grad' '11th' 'Masters' '9th' 'Some-college' 'Assoc-acdm'
 'Assoc-voc' '7th-8th' 'Doctorate' 'Prof-school' '5th-6th' '10th'
 '1st-4th' 'Preschool' '12th']
['Never-married' 'Married-civ-spouse' 'Divorced' 'Married-spouse-absent'
 'Separated' 'Married-AF-spouse' 'Widowed']
['Adm-clerical' 'Exec-managerial' 'Handlers-cleaners' 'Prof-specialty'
 'Other-service' 'Sales' 'Craft-repair' 'Transport-moving'
 'Farming-fishing' 'Machine-op-inspct' 'Tech-support' '?'
 'Protective-serv' 'Armed-Forces' 'Priv-house-serv']
['Not-in-family' 'Husband' 'Wife' 'Own-child' 'Unmarried' 'Other-relative']
['White' 'Black' 'Asian-Pac-Islander' 'Amer-Indian-Eskimo' 'Other']
['Male' 'Female']
['United-States' 'Cuba' 'Jamaica' 'India' '?' 'Mexico' 'South'
 'Puerto-Rico' 'Honduras' 'England' 'Canada' 'Germany' 'Iran'
 'Philippines' 'Italy' 'Poland' 'Columbia' 'Cambodia' 'Thailand' 'Ecuador'
 'Laos' 'Taiwan' 'Haiti' 'Portugal' 'Dominican-Republic' 'El-Salvador'
 'France' 'Guatemala' 'China' 'Japan' 'Yugoslavia' 'Peru'
 'Outlying-US(Guam-USVI-etc)' 'Scotland' 'Trinadad&Tobago' 'Greece'
 'Nicaragua' 'Vietnam' 'Hong' 'Ireland' 'Hungary']

Par conséquent, l'ensemble de données de formation contiendra 101 + 7 colonnes. Les sept dernières colonnes sont les fonctionnalités continues.

Scikit-learn peut s'occuper de la conversion. Cela se fait en deux étapes :

  • Tout d’abord, vous devez convertir la chaîne en ID. Par exemple, State-gov aura l'ID 1, Self-emp-not-inc ID 2 et ainsi de suite. La fonction LabelEncoder fait cela pour vous
  • Transposez chaque identifiant dans une nouvelle colonne. Comme mentionné précédemment, l'ensemble de données possède 101 identifiants de groupe. Par conséquent, il y aura 101 colonnes capturant tous les groupes de fonctionnalités catégorielles. Scikit-learn a une fonction appelée OneHotEncoder qui effectue cette opération

Étape 2) Créer l'ensemble d'entraînement/test

Maintenant que l'ensemble de données est prêt, nous pouvons le diviser à 80/20.

80 pour cent pour l’ensemble de formation et 20 pour cent pour l’ensemble de test.

Vous pouvez utiliser train_test_split. Le premier argument est la trame de données contenant les fonctionnalités et le deuxième argument est la trame de données de l'étiquette. Vous pouvez spécifier la taille de l'ensemble de test avec test_size.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(df_train[features],
                                                    df_train.label,
                                                    test_size = 0.2,
                                                    random_state=0)
X_train.head(5)
print(X_train.shape, X_test.shape)
(26048, 14) (6512, 14)

Étape 3) Construire le pipeline

Le pipeline facilite l'alimentation du modèle avec des données cohérentes.

L'idée sous-jacente est de mettre les données brutes dans un « pipeline » pour effectuer des opérations.

Par exemple, avec l'ensemble de données actuel, vous devez standardiser les variables continues et convertir les données catégorielles. Notez que vous pouvez effectuer n’importe quelle opération à l’intérieur du pipeline. Par exemple, si vous avez des « NA » dans l'ensemble de données, vous pouvez les remplacer par la moyenne ou la médiane. Vous pouvez également créer de nouvelles variables.

Vous avez le choix; coder en dur les deux processus ou créer un pipeline. Le premier choix peut entraîner des fuites de données et créer des incohérences au fil du temps. Une meilleure option consiste à utiliser le pipeline.

from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder
from sklearn.compose import ColumnTransformer, make_column_transformer
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression

Le pipeline effectuera deux opérations avant d’alimenter le classificateur logistique :

  1. Standardisez la variable : `StandardScaler()“
  2. Convertissez les fonctionnalités catégorielles : OneHotEncoder(sparse=False)

Vous pouvez effectuer les deux étapes à l'aide de make_column_transformer. Cette fonction n'est pas disponible dans la version actuelle de scikit-learn (0.19). Il n'est pas possible avec la version actuelle d'effectuer l'encodeur d'étiquettes et un encodeur chaud dans le pipeline. C'est l'une des raisons pour lesquelles nous avons décidé d'utiliser la version développeur.

make_column_transformer est facile à utiliser. Vous devez définir quelles colonnes appliquer la transformation et quelle transformation opérer. Par exemple, pour standardiser la fonctionnalité continue, vous pouvez faire :

  • conti_features, StandardScaler() dans make_column_transformer.
    • conti_features : liste avec la variable continue
    • StandardScaler : standardiser la variable

L'objet OneHotEncoder à l'intérieur de make_column_transformer encode automatiquement l'étiquette.

preprocess = make_column_transformer(
    (conti_features, StandardScaler()),
    ### Need to be numeric not string to specify columns name 
    (categorical_features, OneHotEncoder(sparse=False))
)

Vous pouvez tester si le pipeline fonctionne avec fit_transform. L'ensemble de données devrait avoir le résultat suivantwing forme : 26048, 107

preprocess.fit_transform(X_train).shape
(26048, 107)

Le transformateur de données est prêt à l'emploi. Vous pouvez créer le pipeline avec make_pipeline. Une fois les données transformées, vous pouvez alimenter la régression logistique.

model = make_pipeline(
    preprocess,
    LogisticRegression())

Entraîner un modèle avec scikit-learn est trivial. Vous devez utiliser l'ajustement d'objet précédé du pipeline, c'est-à-dire le modèle. Vous pouvez imprimer la précision avec l'objet score de la bibliothèque scikit-learn

model.fit(X_train, y_train)
print("logistic regression score: %f" % model.score(X_test, y_test))
logistic regression score: 0.850891

Enfin, vous pouvez prédire les classes avec prédire_proba. Il renvoie la probabilité pour chaque classe. Notez que cela totalise un.

model.predict_proba(X_test)
array([[0.83576663, 0.16423337],
       [0.94582765, 0.05417235],
       [0.64760587, 0.35239413],
       ...,
       [0.99639252, 0.00360748],
       [0.02072181, 0.97927819],
       [0.56781353, 0.43218647]])

Étape 4) Utilisation de notre pipeline dans une recherche de grille

Ajuster l'hyperparamètre (les variables qui déterminent la structure du réseau comme les unités cachées) peut être fastidieux et épuisant.

Une façon d’évaluer le modèle pourrait être de modifier la taille de l’ensemble d’entraînement et d’évaluer les performances.

Vous pouvez répéter cette méthode dix fois pour voir les mesures de score. Cependant, c'est trop de travail.

Au lieu de cela, scikit-learn fournit une fonction pour effectuer le réglage des paramètres et la validation croisée.

Validation croisée

La validation croisée signifie que pendant la formation, l'ensemble de formation est glissé n nombre de fois en plis, puis évalue le modèle n fois. Par exemple, si cv est défini sur 10, l'ensemble d'entraînement est entraîné et évalué dix fois. À chaque tour, le classificateur choisit au hasard neuf plis pour entraîner le modèle, et le 10e pli est destiné à l'évaluation.

Grille de recherche

Chaque classificateur a des hyperparamètres à régler. Vous pouvez essayer différentes valeurs ou définir une grille de paramètres. Si vous allez sur le site officiel de scikit-learn, vous pouvez voir que le classificateur logistique a différents paramètres à régler. Pour rendre l'entraînement plus rapide, vous choisissez de régler le paramètre C. Il contrôle le paramètre de régularisation. Cela devrait être positif. Une petite valeur donne plus de poids au régularisateur.

Vous pouvez utiliser l'objet GridSearchCV. Vous devez créer un dictionnaire contenant les hyperparamètres à régler.

Vous répertoriez les hyperparamètres suivis des valeurs que vous souhaitez essayer. Par exemple, pour régler le paramètre C, vous utilisez :

  • 'logisticregression__C' : [0.1, 1.0, 1.0] : Le paramètre est précédé du nom, en minuscules, du classificateur et de deux traits de soulignement.

Le modèle essaiera quatre valeurs différentes : 0.001, 0.01, 0.1 et 1.

Vous entraînez le modèle en utilisant 10 plis : cv=10

from sklearn.model_selection import GridSearchCV
# Construct the parameter grid
param_grid = {
    'logisticregression__C': [0.001, 0.01,0.1, 1.0],
    }

Vous pouvez entraîner le modèle à l'aide de GridSearchCV avec les paramètres gri et cv.

# Train the model
grid_clf = GridSearchCV(model,
                        param_grid,
                        cv=10,
                        iid=False)
grid_clf.fit(X_train, y_train)

SORTIE

GridSearchCV(cv=10, error_score='raise-deprecating',
       estimator=Pipeline(memory=None,
     steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None,
         transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,...ty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False))]),
       fit_params=None, iid=False, n_jobs=1,
       param_grid={'logisticregression__C': [0.001, 0.01, 0.1, 1.0]},
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring=None, verbose=0)

Pour accéder aux meilleurs paramètres, vous utilisez best_params_

grid_clf.best_params_

SORTIE

{'logisticregression__C': 1.0}

Après avoir entraîné le modèle avec quatre valeurs de régularisation différentes, le paramètre optimal est

print("best logistic regression from grid search: %f" % grid_clf.best_estimator_.score(X_test, y_test))

meilleure régression logistique à partir de la recherche par grille : 0.850891

Pour accéder aux probabilités prédites :

grid_clf.best_estimator_.predict_proba(X_test)
array([[0.83576677, 0.16423323],
       [0.9458291 , 0.0541709 ],
       [0.64760416, 0.35239584],
       ...,
       [0.99639224, 0.00360776],
       [0.02072033, 0.97927967],
       [0.56782222, 0.43217778]])

Modèle XGBoost avec scikit-learn

Essayons des exemples Scikit-learn pour former l'un des meilleurs classificateurs du marché. XGBoost est une amélioration par rapport à la forêt aléatoire. Le contexte théorique du classificateur sort du cadre de ce didacticiel Python Scikit. Gardez à l’esprit que XGBoost a remporté de nombreux concours Kaggle. Avec une taille moyenne d’ensemble de données, il peut fonctionner aussi bien qu’un algorithme d’apprentissage en profondeur, voire mieux.

Le classificateur est difficile à entraîner car il comporte un grand nombre de paramètres à régler. Vous pouvez bien sûr utiliser GridSearchCV pour choisir le paramètre à votre place.

Voyons plutôt comment utiliser une meilleure méthode pour trouver les paramètres optimaux. GridSearchCV peut être fastidieux et très long à entraîner si vous transmettez de nombreuses valeurs. L'espace de recherche s'agrandit avec le nombre de paramètres. Une solution préférable consiste à utiliser RandomizedSearchCV. Cette méthode consiste à choisir aléatoirement les valeurs de chaque hyperparamètre après chaque itération. Par exemple, si le classificateur est entraîné sur 1000 1000 itérations, alors   combinaisons sont évaluées. Cela fonctionne plus ou moins comme ça. GrilleRechercheCV

Vous devez importer xgboost. Si la bibliothèque n'est pas installée, veuillez utiliser pip3 install xgboost ou

use import sys
!{sys.executable} -m pip install xgboost

In Jupyter sûr, heureux et sain

Ensuite,

import xgboost
from sklearn.model_selection import RandomizedSearchCV
from sklearn.model_selection import StratifiedKFold

L'étape suivante de ce didacticiel Scikit Python consiste à spécifier les paramètres à régler. Vous pouvez vous référer à la documentation officielle pour voir tous les paramètres à régler. Pour le bien du didacticiel Python Sklearn, vous choisissez uniquement deux hyperparamètres avec deux valeurs chacun. XGBoost prend beaucoup de temps à s'entraîner, plus il y a d'hyperparamètres dans la grille, plus vous devez attendre longtemps.

params = {
        'xgbclassifier__gamma': [0.5, 1],
        'xgbclassifier__max_depth': [3, 4]
        }

Vous construisez un nouveau pipeline avec le classificateur XGBoost. Vous choisissez de définir 600 estimateurs. Notez que n_estimators est un paramètre que vous pouvez régler. Une valeur élevée peut conduire à un surapprentissage. Vous pouvez essayer différentes valeurs par vous-même, mais sachez que cela peut prendre des heures. Vous utilisez la valeur par défaut pour les autres paramètres

model_xgb = make_pipeline(
    preprocess,
    xgboost.XGBClassifier(
                          n_estimators=600,
                          objective='binary:logistic',
                          silent=True,
                          nthread=1)
)

Vous pouvez améliorer la validation croisée avec le validateur croisé Stratified K-Folds. Vous ne construisez ici que trois plis pour accélérer le calcul mais réduire la qualité. Augmentez cette valeur à 5 ou 10 chez vous pour améliorer les résultats.

Vous choisissez d’entraîner le modèle sur quatre itérations.

skf = StratifiedKFold(n_splits=3,
                      shuffle = True,
                      random_state = 1001)

random_search = RandomizedSearchCV(model_xgb,
                                   param_distributions=params,
                                   n_iter=4,
                                   scoring='accuracy',
                                   n_jobs=4,
                                   cv=skf.split(X_train, y_train),
                                   verbose=3,
                                   random_state=1001)

La recherche aléatoire est prête à l'emploi, vous pouvez entraîner le modèle

#grid_xgb = GridSearchCV(model_xgb, params, cv=10, iid=False)
random_search.fit(X_train, y_train)
Fitting 3 folds for each of 4 candidates, totalling 12 fits
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8759645283888057, total= 1.0min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8729701715996775, total= 1.0min
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8706519235199263, total= 1.0min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8735460094437406, total= 1.3min
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8722791661868018, total=  57.7s
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8753886905447426, total= 1.0min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8697304768486523, total= 1.3min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8740066797189912, total= 1.4min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8707671043538355, total= 1.0min
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8729701715996775, total= 1.2min
[Parallel(n_jobs=4)]: Done  10 out of  12 | elapsed:  3.6min remaining:   43.5s
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8736611770125533, total= 1.2min
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8692697535130154, total= 1.2min
[Parallel(n_jobs=4)]: Done  12 out of  12 | elapsed:  3.6min finished
/Users/Thomas/anaconda3/envs/hello-tf/lib/python3.6/site-packages/sklearn/model_selection/_search.py:737: DeprecationWarning: The default of the `iid` parameter will change from True to False in version 0.22 and will be removed in 0.24. This will change numeric results when test-set sizes are unequal. DeprecationWarning)
RandomizedSearchCV(cv=<generator object _BaseKFold.split at 0x1101eb830>,
          error_score='raise-deprecating',
          estimator=Pipeline(memory=None,
     steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None,
         transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,...
       reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None,
       silent=True, subsample=1))]),
          fit_params=None, iid='warn', n_iter=4, n_jobs=4,
          param_distributions={'xgbclassifier__gamma': [0.5, 1], 'xgbclassifier__max_depth': [3, 4]},
          pre_dispatch='2*n_jobs', random_state=1001, refit=True,
          return_train_score='warn', scoring='accuracy', verbose=3)

Comme vous pouvez le constater, XGBoost a un meilleur score que la régression logistique précédente.

print("Best parameter", random_search.best_params_)
print("best logistic regression from grid search: %f" % random_search.best_estimator_.score(X_test, y_test))
Best parameter {'xgbclassifier__max_depth': 3, 'xgbclassifier__gamma': 0.5}
best logistic regression from grid search: 0.873157
random_search.best_estimator_.predict(X_test)
array(['<=50K', '<=50K', '<=50K', ..., '<=50K', '>50K', '<=50K'],      dtype=object)

Créer un DNN avec MLPClassifier dans scikit-learn

Enfin, vous pouvez entraîner un algorithme d'apprentissage en profondeur avec scikit-learn. La méthode est la même que pour l’autre classificateur. Le classificateur est disponible sur MLPClassifier.

from sklearn.neural_network import MLPClassifier

Vous définissez le suiviwing algorithme d'apprentissage profond :

  • Solveur d'Adam
  • Fonction d'activation Relu
  • Alpha = 0.0001
  • taille de lot de 150
  • Deux couches cachées avec respectivement 100 et 50 neurones
model_dnn = make_pipeline(
    preprocess,
    MLPClassifier(solver='adam',
                  alpha=0.0001,
                  activation='relu',
                    batch_size=150,
                    hidden_layer_sizes=(200, 100),
                    random_state=1))

Vous pouvez modifier le nombre de couches pour améliorer le modèle

model_dnn.fit(X_train, y_train)
  print("DNN regression score: %f" % model_dnn.score(X_test, y_test))

Score de régression DNN : 0.821253

LIME : faites confiance à votre modèle

Maintenant que vous disposez d’un bon modèle, vous avez besoin d’un outil pour lui faire confiance. Apprentissage automatique Les algorithmes, en particulier les forêts aléatoires et les réseaux neuronaux, sont connus pour être noirs.box algorithme. Dites autrement, ça marche mais personne ne sait pourquoi.

Trois chercheurs ont mis au point un excellent outil pour voir comment l'ordinateur fait une prédiction. Le document s’intitule Pourquoi devrais-je vous faire confiance ?

Ils ont développé un algorithme nommé Explications locales interprétables indépendantes du modèle (LIME).

Prenons un exemple:

parfois, vous ne savez pas si vous pouvez faire confiance à une prédiction du machine learning :

Un médecin, par exemple, ne peut pas faire confiance à un diagnostic simplement parce qu’un ordinateur l’a dit. Vous devez également savoir si vous pouvez faire confiance au modèle avant de le mettre en production.

Imaginez que nous puissions comprendre pourquoi n'importe quel classificateur fait une prédiction, même avec des modèles incroyablement compliqués tels que des réseaux de neurones, des forêts aléatoires ou des svm avec n'importe quel noyau.

il deviendra plus accessible de faire confiance à une prédiction si nous pouvons en comprendre les raisons. À partir de l'exemple avec le médecin, si le modèle lui a dit quels symptômes sont essentiels, vous lui feriez confiance, il est également plus facile de déterminer si vous ne devez pas faire confiance au modèle.

Lime peut vous indiquer quelles fonctionnalités affectent les décisions du classificateur

Préparation des données

Ce sont quelques éléments que vous devez modifier pour exécuter LIME avec python. Tout d'abord, vous devez installer de la chaux dans le terminal. Vous pouvez utiliser pip install lime

Lime utilise l'objet LimeTabularExplainer pour se rapprocher du modèle localement. Cet objet nécessite :

  • un ensemble de données au format numpy
  • Le nom des fonctionnalités : feature_names
  • Le nom des classes : class_names
  • L'index de la colonne des caractéristiques catégorielles : categorical_features
  • Le nom du groupe pour chaque fonctionnalité catégorielle : categorical_names

Créer une rame numpy

Vous pouvez copier et convertir df_train de pandas en numpy très facilement

df_train.head(5)
# Create numpy data
df_lime = df_train
df_lime.head(3)

Obtenez le nom de la classe Le label est accessible avec l'objet unique(). Tu devrais voir:

  • «<= 50 »
  • «> 50 »
# Get the class name
class_names = df_lime.label.unique()
class_names
array(['<=50K', '>50K'], dtype=object)

index de la colonne des caractéristiques catégorielles

Vous pouvez utiliser la méthode que vous avez apprise auparavant pour obtenir le nom du groupe. Vous encodez l'étiquette avec LabelEncoder. Vous répétez l'opération sur toutes les caractéristiques catégorielles.

## 
import sklearn.preprocessing as preprocessing
categorical_names = {}
for feature in CATE_FEATURES:
    le = preprocessing.LabelEncoder()
    le.fit(df_lime[feature])
    df_lime[feature] = le.transform(df_lime[feature])
    categorical_names[feature] = le.classes_
print(categorical_names)    
{'workclass': array(['?', 'Federal-gov', 'Local-gov', 'Never-worked', 'Private',
       'Self-emp-inc', 'Self-emp-not-inc', 'State-gov', 'Without-pay'],
      dtype=object), 'education': array(['10th', '11th', '12th', '1st-4th', '5th-6th', '7th-8th', '9th',
       'Assoc-acdm', 'Assoc-voc', 'Bachelors', 'Doctorate', 'HS-grad',
       'Masters', 'Preschool', 'Prof-school', 'Some-college'],
      dtype=object), 'marital': array(['Divorced', 'Married-AF-spouse', 'Married-civ-spouse',
       'Married-spouse-absent', 'Never-married', 'Separated', 'Widowed'],
      dtype=object), 'occupation': array(['?', 'Adm-clerical', 'Armed-Forces', 'Craft-repair',
       'Exec-managerial', 'Farming-fishing', 'Handlers-cleaners',
       'Machine-op-inspct', 'Other-service', 'Priv-house-serv',
       'Prof-specialty', 'Protective-serv', 'Sales', 'Tech-support',
       'Transport-moving'], dtype=object), 'relationship': array(['Husband', 'Not-in-family', 'Other-relative', 'Own-child',
       'Unmarried', 'Wife'], dtype=object), 'race': array(['Amer-Indian-Eskimo', 'Asian-Pac-Islander', 'Black', 'Other',
       'White'], dtype=object), 'sex': array(['Female', 'Male'], dtype=object), 'native_country': array(['?', 'Cambodia', 'Canada', 'China', 'Columbia', 'Cuba',
       'Dominican-Republic', 'Ecuador', 'El-Salvador', 'England',
       'France', 'Germany', 'Greece', 'Guatemala', 'Haiti', 'Honduras',
       'Hong', 'Hungary', 'India', 'Iran', 'Ireland', 'Italy', 'Jamaica',
       'Japan', 'Laos', 'Mexico', 'Nicaragua',
       'Outlying-US(Guam-USVI-etc)', 'Peru', 'Philippines', 'Poland',
       'Portugal', 'Puerto-Rico', 'Scotland', 'South', 'Taiwan',
       'Thailand', 'Trinadad&Tobago', 'United-States', 'Vietnam',
       'Yugoslavia'], dtype=object)}

df_lime.dtypes
age               float64
workclass           int64
fnlwgt            float64
education           int64
education_num     float64
marital             int64
occupation          int64
relationship        int64
race                int64
sex                 int64
capital_gain      float64
capital_loss      float64
hours_week        float64
native_country      int64
label              object
dtype: object

Maintenant que l'ensemble de données est prêt, vous pouvez construire les différents ensembles de données comme indiqué dans les exemples d'apprentissage Scikit ci-dessous. En fait, vous transformez les données en dehors du pipeline afin d'éviter les erreurs avec LIME. La formation définie dans LimeTabularExplainer doit être un tableau numpy sans chaîne. Avec la méthode ci-dessus, vous disposez d'un ensemble de données d'entraînement déjà converti.

from sklearn.model_selection import train_test_split
X_train_lime, X_test_lime, y_train_lime, y_test_lime = train_test_split(df_lime[features],
                                                    df_lime.label,
                                                    test_size = 0.2,
                                                    random_state=0)
X_train_lime.head(5)

Vous pouvez créer le pipeline avec les paramètres optimaux de XGBoost

model_xgb = make_pipeline(
    preprocess,
    xgboost.XGBClassifier(max_depth = 3,
                          gamma = 0.5,
                          n_estimators=600,
                          objective='binary:logistic',
                          silent=True,
                          nthread=1))

model_xgb.fit(X_train_lime, y_train_lime)
/Users/Thomas/anaconda3/envs/hello-tf/lib/python3.6/site-packages/sklearn/preprocessing/_encoders.py:351: FutureWarning: The handling of integer data will change in version 0.22. Currently, the categories are determined based on the range [0, max(values)], while in the future they will be determined based on the unique values.
If you want the future behavior and silence this warning, you can specify "categories='auto'."In case you used a LabelEncoder before this OneHotEncoder to convert the categories to integers, then you can now use the OneHotEncoder directly.
  warnings.warn(msg, FutureWarning)
Pipeline(memory=None,
     steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None,
         transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,...
       reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None,
       silent=True, subsample=1))])

Vous recevez un avertissement. L'avertissement explique que vous n'avez pas besoin de créer un encodeur d'étiquette avant le pipeline. Si vous ne souhaitez pas utiliser LIME, vous pouvez utiliser la méthode de la première partie du didacticiel Machine Learning avec Scikit-learn. Autrewise, vous pouvez conserver cette méthode, créer d'abord un ensemble de données codé, définir l'encodeur chaud dans le pipeline.

print("best logistic regression from grid search: %f" % model_xgb.score(X_test_lime, y_test_lime))
best logistic regression from grid search: 0.873157
model_xgb.predict_proba(X_test_lime)
array([[7.9646105e-01, 2.0353897e-01],
       [9.5173013e-01, 4.8269872e-02],
       [7.9344827e-01, 2.0655173e-01],
       ...,
       [9.9031430e-01, 9.6856682e-03],
       [6.4581633e-04, 9.9935418e-01],
       [9.7104281e-01, 2.8957171e-02]], dtype=float32)

Avant d'utiliser LIME en action, créons un tableau numpy avec les fonctionnalités d'une mauvaise classification. Vous pouvez utiliser cette liste later pour avoir une idée de ce qui induit le classificateur en erreur.

temp = pd.concat([X_test_lime, y_test_lime], axis= 1)
temp['predicted'] = model_xgb.predict(X_test_lime)
temp['wrong']=  temp['label'] != temp['predicted']
temp = temp.query('wrong==True').drop('wrong', axis=1)
temp= temp.sort_values(by=['label'])
temp.shape

(826, 16)

Vous créez une fonction lambda pour récupérer la prédiction du modèle avec les nouvelles données. Vous en aurez bientôt besoin.

predict_fn = lambda x: model_xgb.predict_proba(x).astype(float)
X_test_lime.dtypes
age               float64
workclass           int64
fnlwgt            float64
education           int64
education_num     float64
marital             int64
occupation          int64
relationship        int64
race                int64
sex                 int64
capital_gain      float64
capital_loss      float64
hours_week        float64
native_country      int64
dtype: object
predict_fn(X_test_lime)
array([[7.96461046e-01, 2.03538969e-01],
       [9.51730132e-01, 4.82698716e-02],
       [7.93448269e-01, 2.06551731e-01],
       ...,
       [9.90314305e-01, 9.68566816e-03],
       [6.45816326e-04, 9.99354184e-01],
       [9.71042812e-01, 2.89571714e-02]])

Vous convertissez la trame de données pandas en tableau numpy

X_train_lime = X_train_lime.values
X_test_lime = X_test_lime.values
X_test_lime
array([[4.00000e+01, 5.00000e+00, 1.93524e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01],
       [2.70000e+01, 4.00000e+00, 2.16481e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01],
       [2.50000e+01, 4.00000e+00, 2.56263e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01],
       ...,
       [2.80000e+01, 6.00000e+00, 2.11032e+05, ..., 0.00000e+00,
        4.00000e+01, 2.50000e+01],
       [4.40000e+01, 4.00000e+00, 1.67005e+05, ..., 0.00000e+00,
        6.00000e+01, 3.80000e+01],
       [5.30000e+01, 4.00000e+00, 2.57940e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01]])
model_xgb.predict_proba(X_test_lime)
array([[7.9646105e-01, 2.0353897e-01],
       [9.5173013e-01, 4.8269872e-02],
       [7.9344827e-01, 2.0655173e-01],
       ...,
       [9.9031430e-01, 9.6856682e-03],
       [6.4581633e-04, 9.9935418e-01],
       [9.7104281e-01, 2.8957171e-02]], dtype=float32)
print(features,
      class_names,
      categorical_features,
      categorical_names)
['age', 'workclass', 'fnlwgt', 'education', 'education_num', 'marital', 'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss', 'hours_week', 'native_country'] ['<=50K' '>50K'] [1, 3, 5, 6, 7, 8, 9, 13] {'workclass': array(['?', 'Federal-gov', 'Local-gov', 'Never-worked', 'Private',
       'Self-emp-inc', 'Self-emp-not-inc', 'State-gov', 'Without-pay'],
      dtype=object), 'education': array(['10th', '11th', '12th', '1st-4th', '5th-6th', '7th-8th', '9th',
       'Assoc-acdm', 'Assoc-voc', 'Bachelors', 'Doctorate', 'HS-grad',
       'Masters', 'Preschool', 'Prof-school', 'Some-college'],
      dtype=object), 'marital': array(['Divorced', 'Married-AF-spouse', 'Married-civ-spouse',
       'Married-spouse-absent', 'Never-married', 'Separated', 'Widowed'],
      dtype=object), 'occupation': array(['?', 'Adm-clerical', 'Armed-Forces', 'Craft-repair',
       'Exec-managerial', 'Farming-fishing', 'Handlers-cleaners',
       'Machine-op-inspct', 'Other-service', 'Priv-house-serv',
       'Prof-specialty', 'Protective-serv', 'Sales', 'Tech-support',
       'Transport-moving'], dtype=object), 'relationship': array(['Husband', 'Not-in-family', 'Other-relative', 'Own-child',
       'Unmarried', 'Wife'], dtype=object), 'race': array(['Amer-Indian-Eskimo', 'Asian-Pac-Islander', 'Black', 'Other',
       'White'], dtype=object), 'sex': array(['Female', 'Male'], dtype=object), 'native_country': array(['?', 'Cambodia', 'Canada', 'China', 'Columbia', 'Cuba',
       'Dominican-Republic', 'Ecuador', 'El-Salvador', 'England',
       'France', 'Germany', 'Greece', 'Guatemala', 'Haiti', 'Honduras',
       'Hong', 'Hungary', 'India', 'Iran', 'Ireland', 'Italy', 'Jamaica',
       'Japan', 'Laos', 'Mexico', 'Nicaragua',
       'Outlying-US(Guam-USVI-etc)', 'Peru', 'Philippines', 'Poland',
       'Portugal', 'Puerto-Rico', 'Scotland', 'South', 'Taiwan',
       'Thailand', 'Trinadad&Tobago', 'United-States', 'Vietnam',
       'Yugoslavia'], dtype=object)}
import lime
import lime.lime_tabular
### Train should be label encoded not one hot encoded
explainer = lime.lime_tabular.LimeTabularExplainer(X_train_lime ,
                                                   feature_names = features,
                                                   class_names=class_names,
                                                   categorical_features=categorical_features, 
                                                   categorical_names=categorical_names,
                                                   kernel_width=3)

Choisissons un foyer aléatoire dans l'ensemble de test et voyons la prédiction du modèle et comment l'ordinateur a fait son choix.

import numpy as np
np.random.seed(1)
i = 100
print(y_test_lime.iloc[i])
>50K
X_test_lime[i]
array([4.20000e+01, 4.00000e+00, 1.76286e+05, 7.00000e+00, 1.20000e+01,
       2.00000e+00, 4.00000e+00, 0.00000e+00, 4.00000e+00, 1.00000e+00,
       0.00000e+00, 0.00000e+00, 4.00000e+01, 3.80000e+01])

Vous pouvez utiliser l'explicateur avec expliquer_instance pour vérifier l'explication derrière le modèle

exp = explainer.explain_instance(X_test_lime[i], predict_fn, num_features=6)
exp.show_in_notebook(show_all=False)

Préparation des données

Nous pouvons voir que le classificateur a prédit correctement le ménage. Le revenu est en effet supérieur à 50 .

La première chose que nous pouvons dire est que le classificateur n’est pas sûr des probabilités prédites. La machine prédit que le ménage a un revenu supérieur à 50 64 avec une probabilité de 64 %. Ces % sont constitués de plus-value et de matrimonial. La couleur bleue contribue négativement à la classe positive et la ligne orange contribue positivement.

Le classificateur est confus car la plus-value de ce ménage est nulle, alors que la plus-value est généralement un bon prédicteur de richesse. De plus, le ménage travaille moins de 40 heures par semaine. L'âge, la profession et le sexe contribuent positivement au classificateur.

Si l'état civil était célibataire, le classificateur aurait prédit un revenu inférieur à 50 0.64 (0.18-0.46 = )

On peut essayer avec un autre ménage mal classé

temp.head(3)
temp.iloc[1,:-2]
age                  58
workclass             4
fnlwgt            68624
education            11
education_num         9
marital               2
occupation            4
relationship          0
race                  4
sex                   1
capital_gain          0
capital_loss          0
hours_week           45
native_country       38
Name: 20931, dtype: object
i = 1
print('This observation is', temp.iloc[i,-2:])
This observation is label        <=50K
predicted     >50K
Name: 20931, dtype: object
exp = explainer.explain_instance(temp.iloc[1,:-2], predict_fn, num_features=6)
exp.show_in_notebook(show_all=False)

Préparation des données

Le classificateur a prédit un revenu inférieur à 50 60 alors que c'est faux. Ce ménage semble étrange. Il ne comporte ni plus-value, ni perte en capital. Il est divorcé et a 12 ans, et c'est une personne instruite, c'est-à-dire education_num > 50. Selon la tendance générale, ce ménage devrait, comme l'explique le classificateur, avoir un revenu inférieur à .

Vous essayez de jouer avec LIME. Vous remarquerez des erreurs grossières de la part du classificateur.

Vous pouvez consulter le GitHub du propriétaire de la bibliothèque. Ils fournissent une documentation supplémentaire pour la classification des images et du texte.

Résumé

Vous trouverez ci-dessous une liste de quelques commandes utiles avec scikit learn version >=0.20

créer un ensemble de données d'entraînement/test les stagiaires sont divisés
Construire un pipeline
sélectionnez la colonne et appliquez la transformation créer un transformateur de colonne
type de transformation
standardiser Échelle standard
min max MinMaxScaler
Normaliser Normalizer
Imputer la valeur manquante Ordinateur
Convertir catégorique OneHotEncoder
Ajuster et transformer les données fit_transformer
Faire le pipeline make_pipeline
Modèle de base
régression logistique Régression logistique
XGBoost XGBClassificateur
Réseau de neurones Classificateur MLP
Grille de recherche GridSearchCV
Recherche aléatoire RechercheRandomiséeCV