Tutoriel Scikit-Learn : Comment installer et exemples Scikit-Learn
Qu'est-ce que Scikit-learn ?
Scikit-apprendre est un open-source Python Bibliothèque pour l'apprentissage automatique. Elle prend en charge les algorithmes de pointe tels que KNN, XGBoost, Random Forest et SVM. Elle est construite sur NumPy. Scikit-learn est largement utilisé dans la compétition Kaggle ainsi que dans les grandes entreprises technologiques. Il aide au prétraitement, à la réduction de la dimensionnalité (sélection des 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.
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 Python Tutoriel Scikit-learn, nous apprendrons 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
NOTE: Windows l'utilisateur devra installer Microsoft Visuel C++ 14. Vous pouvez l'obtenir auprès de ici
Exemple Scikit-Learn avec l'apprentissage automatique
Ce didacticiel Scikit est divisé en deux parties :
- Apprentissage automatique avec scikit-learn
- Comment faire confiance à votre modèle avec LIME
La première partie détaille comment construire un pipeline, créer un modèle et régler les hyperparamètres tandis que la deuxième partie présente 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
- relations
- 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 les codes suivants
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 :
- Standardisez la variable : `StandardScaler()“
- 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 doit avoir la forme suivante : 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 Python Tutoriel 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
La prochaine étape de ce Scikit Python Le didacticiel comprend la spécification des 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 Python Tutoriel 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("Meilleur parameter", random_search.best_params_) print("best logistic regression from grid search: %f" % random_search.best_estimator_.score(X_test, y_test))
Meilleur 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 l'algorithme d'apprentissage profond suivant :
- 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 L'algorithme, en particulier la forêt aléatoire et le réseau neuronal, sont connus pour être des algorithmes de boîte noire. 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. Sinon, 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 pourrez utiliser cette liste plus tard pour avoir une idée de ce qui a 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)
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)
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 |
Le modèle XGBoost, intégrant caractéristiques radiomiques et quatre variables cliniques (site de la tumeur primaire [cavité buccale], TNM, âge et tabagisme), s’est avéré être le meilleur modèle de prédiction pour la progression à ans. Ce dernier a permis d’obtenir une AUC de , une sensibilité de , une spécificité de et une précision de . Nos efforts se concentrent désormais sur l’augmentation de la taille de l’échantillon afin de valider ce modèle et d’évaluer les résultats de la survie sans progression, dans le but de fournir aux cliniciens un score de risque pour une catégorisation plus précise des patients sur la base des TDM de diagnostic. | XGBClassificateur |
Réseau de neurones | Classificateur MLP |
Grille de recherche | GridSearchCV |
Recherche aléatoire | RechercheRandomiséeCV |