Tutorial Scikit-Learn: come installare ed esempi di Scikit-Learn

Che cos'è Scikit-learn?

Scikit-learn è un open source Python libreria per l'apprendimento automatico. Supporta algoritmi all'avanguardia come KNN, XGBoost, random forest e SVM. È basato su NumPy. Scikit-learn è ampiamente utilizzato nella competizione Kaggle e in importanti aziende tecnologiche. Aiuta nella pre-elaborazione, riduzione della dimensionalità (selezione dei parametri), classificazione, regressione, clustering e selezione del modello.

Scikit-learn ha la migliore documentazione di tutte le librerie open source. Ti fornisce un grafico interattivo su https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html.

Come funziona Scikit Learn
Come funziona Scikit Learn

Scikit-learn non è molto difficile da usare e fornisce ottimi risultati. Tuttavia, scikit learn non supporta i calcoli paralleli. È possibile eseguire un algoritmo di deep learning con esso, ma non è una soluzione ottimale, soprattutto se sai come utilizzare TensorFlow.

Come scaricare e installare Scikit-learn

Ora in questo Python Tutorial Scikit-learn, impareremo come scaricare e installare Scikit-learn:

Opzione 1: AWS

scikit-learn può essere utilizzato su AWS. Per favore riferimento L'immagine docker con scikit-learn preinstallato.

Per utilizzare la versione per sviluppatori utilizzare il comando in Jupyter

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

Opzione 2: Mac o Windows utilizzando Anaconda

Per informazioni sull'installazione di Anaconda fare riferimento https://www.guru99.com/download-install-tensorflow.html

Recentemente, gli sviluppatori di scikit hanno rilasciato una versione di sviluppo che affronta i problemi comuni riscontrati nella versione attuale. Abbiamo trovato più conveniente utilizzare la versione per sviluppatori anziché la versione corrente.

Come installare scikit-learn con Conda Environment

Se hai installato scikit-learn con l'ambiente conda, segui il passaggio per aggiornare alla versione 0.20

Passo 1) Attiva l'ambiente tensorflow

source activate hello-tf

Passo 2) Rimuovi scikit lean usando il comando conda

conda remove scikit-learn

Passo 3) Installa la versione per sviluppatori.
Installa la versione per sviluppatori di Scikit Learn insieme alle librerie necessarie.

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

NOTA: Windows l'utente dovrà installare Microsoft Visivo C++ 14. Puoi ottenerlo da qui

Esempio di Scikit-Learn con l'apprendimento automatico

Questo tutorial di Scikit è diviso in due parti:

  1. Apprendimento automatico con scikit-learn
  2. Come fidarti del tuo modello con LIME

La prima parte spiega nel dettaglio come costruire una pipeline, creare un modello e regolare gli iperparametri, mentre la seconda parte fornisce le nozioni più avanzate in termini di selezione del modello.

Passaggio 1) Importa i dati

Durante questo tutorial di apprendimento di Scikit, utilizzerai il set di dati per adulti.

Per informazioni generali su questo set di dati fare riferimento. Se sei interessato a saperne di più sulle statistiche descrittive, utilizza gli strumenti Dive e Panoramica.

Fare riferimento questo tutorial scopri di più su Immersione e Panoramica

Importi il ​​set di dati con Pandas. Tieni presente che devi convertire il tipo delle variabili continue in formato float.

Questo set di dati include otto variabili categoriali:

Le variabili categoriali sono elencate in CATE_FEATURES

  • classe operaia
  • continua
  • coniugale
  • occupazione
  • rapporto
  • gara
  • sesso
  • Paese d'origine

inoltre, sei variabili continue:

Le variabili continue sono elencate in CONTI_FEATURES

  • fnlwgt
  • numero_istruzione
  • plusvalenza
  • perdita_capitale
  • ore_settimana

Tieni presente che compiliamo l'elenco a mano in modo che tu abbia un'idea migliore di quali colonne stiamo utilizzando. Un modo più rapido per costruire un elenco di categoriali o continui è utilizzare:

## 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)

Ecco il codice per importare i dati:

# 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()
fnlwgt numero_istruzione plusvalenza perdita_capitale ore_settimana
contare 32561.000000 3.256100e + 04 32561.000000 32561.000000 32561.000000 32561.000000
significare 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
verbale 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

Puoi controllare il conteggio dei valori univoci delle funzionalità native_country. Puoi vedere che solo una famiglia proviene dall'Olanda-Paesi Bassi. Questa famiglia non ci fornirà alcuna informazione, ma lo farà a causa di un errore durante l'addestramento.

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

Puoi escludere questa riga non informativa dal set di dati

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

Successivamente, memorizzerai la posizione delle caratteristiche continue in un elenco. Ne avrai bisogno nel passaggio successivo per costruire la pipeline.

Il codice seguente eseguirà il loop su tutti i nomi delle colonne in CONTI_FEATURES e ne otterrà la posizione (ovvero il suo numero) e quindi lo aggiungerà a un elenco chiamato 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]

Il codice seguente fa lo stesso lavoro di sopra ma per la variabile categoriale. Il codice seguente ripete ciò che hai fatto in precedenza, tranne che per le funzionalità categoriche.

## 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]

Puoi dare un'occhiata al set di dati. Tieni presente che ogni caratteristica categoriale è una stringa. Non è possibile alimentare un modello con un valore stringa. È necessario trasformare il set di dati utilizzando una variabile fittizia.

df_train.head(5)

Infatti, devi creare una colonna per ciascun gruppo nella funzione. Innanzitutto, puoi eseguire il codice seguente per calcolare la quantità totale di colonne necessarie.

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'intero set di dati contiene 101 gruppi come mostrato sopra. Ad esempio, le caratteristiche di workclass hanno nove gruppi. Puoi visualizzare il nome dei gruppi con i seguenti codici

unique() restituisce i valori univoci delle caratteristiche categoriali.

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']

Pertanto, il set di dati di addestramento conterrà 101 + 7 colonne. Le ultime sette colonne sono le caratteristiche continue.

Scikit-learn può occuparsi della conversione. Si effettua in due passaggi:

  • Innanzitutto, devi convertire la stringa in ID. Ad esempio, il governo statale avrà l'ID 1, l'ID Self-emp-not-inc 2 e così via. La funzione LabelEncoder lo fa per te
  • Trasponi ciascun ID in una nuova colonna. Come accennato in precedenza, il set di dati ha l'ID del gruppo 101. Pertanto ci saranno 101 colonne che cattureranno tutti i gruppi di caratteristiche categoriali. Scikit-learn ha una funzione chiamata OneHotEncoder che esegue questa operazione

Passaggio 2) Creare il set di treno/test

Ora che il set di dati è pronto, possiamo dividerlo 80/20.

80% per il set di training e 20% per il set di test.

Puoi usare train_test_split. Il primo argomento è il dataframe e le funzionalità e il secondo argomento è l'etichetta dataframe. È possibile specificare la dimensione del set di test con 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)

Passaggio 3) Costruisci la pipeline

La pipeline semplifica l'alimentazione del modello con dati coerenti.

L'idea alla base è quella di inserire i dati grezzi in una "pipeline" per eseguire operazioni.

Ad esempio, con il set di dati attuale, è necessario standardizzare le variabili continue e convertire i dati categorici. Tieni presente che puoi eseguire qualsiasi operazione all'interno della pipeline. Ad esempio, se nel set di dati sono presenti "NA", è possibile sostituirli con la media o la mediana. Puoi anche creare nuove variabili.

Hai la scelta; codificare i due processi o creare una pipeline. La prima scelta può portare alla fuga di dati e creare incoerenze nel tempo. Un'opzione migliore è utilizzare la 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

La pipeline eseguirà due operazioni prima di alimentare il classificatore logistico:

  1. Standardizza la variabile: `StandardScaler()“
  2. Converti le caratteristiche categoriche: OneHotEncoder(sparse=False)

È possibile eseguire i due passaggi utilizzando make_column_transformer. Questa funzione non è disponibile nella versione attuale di scikit-learn (0.19). Non è possibile con la versione attuale eseguire il codificatore di etichette e un codificatore a caldo in pipeline. È uno dei motivi per cui abbiamo deciso di utilizzare la versione per sviluppatori.

make_column_transformer è facile da usare. È necessario definire a quali colonne applicare la trasformazione e quale trasformazione operare. Ad esempio, per standardizzare la funzionalità continua, puoi fare:

  • conti_features, StandardScaler() all'interno di make_column_transformer.
    • conti_features: elenco con la variabile continua
    • StandardScaler: standardizza la variabile

L'oggetto OneHotEncoder all'interno di make_column_transformer codifica automaticamente l'etichetta.

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

Puoi testare se la pipeline funziona con fit_transform. Il set di dati dovrebbe avere la seguente forma: 26048, 107

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

Il trasformatore dati è pronto per l'uso. Puoi creare la pipeline con make_pipeline. Una volta trasformati i dati, è possibile alimentare la regressione logistica.

model = make_pipeline(
    preprocess,
    LogisticRegression())

Addestrare un modello con scikit-learn è banale. È necessario utilizzare l'adattamento dell'oggetto preceduto dalla pipeline, ovvero model. Puoi stampare la precisione con l'oggetto punteggio dalla libreria scikit-learn

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

Infine, puoi prevedere le classi con predit_proba. Restituisce la probabilità per ciascuna classe. Nota che la somma è uno.

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]])

Passaggio 4) Utilizzo della nostra pipeline in una ricerca nella griglia

Mettere a punto gli iperparametri (variabili che determinano la struttura della rete come le unità nascoste) può essere noioso ed estenuante.

Un modo per valutare il modello potrebbe essere quello di modificare la dimensione del training set e valutare le prestazioni.

Puoi ripetere questo metodo dieci volte per vedere le metriche del punteggio. Tuttavia, è troppo lavoro.

Invece, scikit-learn fornisce una funzione per eseguire l'ottimizzazione dei parametri e la convalida incrociata.

Convalida incrociata

Convalida incrociata significa che durante l'addestramento, il set di addestramento viene inserito n numero di volte in pieghe e quindi valuta il modello n tempo. Ad esempio, se cv è impostato su 10, il training set viene addestrato e valutato dieci volte. Ad ogni round, il classificatore sceglie casualmente nove pieghe per addestrare il modello e la decima piega è destinata alla valutazione.

Ricerca a griglia

Ogni classificatore dispone di iperparametri da ottimizzare. Puoi provare valori diversi oppure impostare una griglia di parametri. Se vai al sito ufficiale di scikit-learn, puoi vedere che il classificatore logistico ha diversi parametri da ottimizzare. Per rendere l'allenamento più veloce, scegli di ottimizzare il parametro C. Controlla il parametro di regolarizzazione. Dovrebbe essere positivo. Un valore piccolo dà più peso al regolarizzatore.

È possibile utilizzare l'oggetto GridSearchCV. È necessario creare un dizionario contenente gli iperparametri da ottimizzare.

Elenca gli iperparametri seguiti dai valori che vuoi provare. Ad esempio, per ottimizzare il parametro C, si utilizza:

  • 'logisticregression__C': [0.1, 1.0, 1.0]: Il parametro è preceduto dal nome, in minuscolo, del classificatore e da due trattini bassi.

Il modello proverà quattro valori diversi: 0.001, 0.01, 0.1 e 1.

Alleni il modello utilizzando 10 pieghe: cv=10

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

Puoi addestrare il modello utilizzando GridSearchCV con il parametro gri e cv.

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

USCITA

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)

Per accedere ai parametri migliori, usi best_params_

grid_clf.best_params_

USCITA

{'logisticregression__C': 1.0}

Dopo aver addestrato il modello con quattro diversi valori di regolarizzazione, il parametro ottimale è

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

migliore regressione logistica dalla ricerca sulla griglia: 0.850891

Per accedere alle probabilità previste:

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]])

Modello XGBoost con scikit-learn

Proviamo gli esempi di Scikit-learn per addestrare uno dei migliori classificatori sul mercato. XGBoost è un miglioramento rispetto alla foresta casuale. Il background teorico del classificatore esula dall'ambito di questo Python Tutorial su SciKit. Tieni presente che XGBoost ha vinto molte competizioni di kaggle. Con una dimensione media del set di dati, può funzionare altrettanto bene di un algoritmo di deep learning o anche meglio.

Il classificatore è difficile da addestrare perché ha un numero elevato di parametri da ottimizzare. Ovviamente puoi utilizzare GridSearchCV per scegliere il parametro per te.

Vediamo invece come utilizzare un modo migliore per trovare i parametri ottimali. GridSearchCV può essere noioso e molto lungo da addestrare se si passano molti valori. Lo spazio di ricerca cresce insieme al numero di parametri. Una soluzione preferibile è utilizzare RandomizedSearchCV. Questo metodo consiste nello scegliere in modo casuale i valori di ciascun iperparametro dopo ogni iterazione. Ad esempio, se il classificatore viene addestrato su 1000 iterazioni, vengono valutate 1000 combinazioni. Funziona più o meno così. GridSearchCV

Devi importare xgboost. Se la libreria non è installata, utilizzare pip3 install xgboost o

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

In Jupyter Industria XNUMX

Il prossimo,

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

Il prossimo passo in questo Scikit Python il tutorial include la specifica dei parametri da ottimizzare. Puoi fare riferimento alla documentazione ufficiale per vedere tutti i parametri da ottimizzare. Per il bene di Python Tutorial Sklearn, scegli solo due iperparametri con due valori ciascuno. XGBoost richiede molto tempo per l'addestramento, maggiore è il numero di iperparametri nella griglia, maggiore è il tempo di attesa.

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

Costruisci una nuova pipeline con il classificatore XGBoost. Si sceglie di definire 600 stimatori. Tieni presente che n_estimators è un parametro che puoi ottimizzare. Un valore elevato può portare a un overfitting. Puoi provare da solo valori diversi, ma tieni presente che possono volerci ore. Si utilizza il valore predefinito per gli altri parametri

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

Puoi migliorare la convalida incrociata con il validatore incrociato Stratified K-Folds. Costruisci solo tre pieghe qui per velocizzare il calcolo ma abbassando la qualità. Aumenta questo valore a 5 o 10 a casa per migliorare i risultati.

Scegli di addestrare il modello in quattro iterazioni.

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 ricerca randomizzata è pronta per l'uso, puoi addestrare il modello

#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)

Come puoi vedere, XGBoost ha un punteggio migliore rispetto alla precedente regressione logistica.

print("migliori parameter", random_search.best_params_)
print("best logistic regression from grid search: %f" % random_search.best_estimator_.score(X_test, y_test))
migliori 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)

Crea DNN con MLPClassifier in scikit-learn

Infine, puoi addestrare un algoritmo di deep learning con scikit-learn. Il metodo è lo stesso dell'altro classificatore. Il classificatore è disponibile su MLPClassifier.

from sklearn.neural_network import MLPClassifier

Si definisce il seguente algoritmo di apprendimento profondo:

  • Adam risolutore
  • Funzione di attivazione Relu
  • Alfa = 0.0001
  • dimensione del lotto di 150
  • Due strati nascosti rispettivamente con 100 e 50 neuroni
model_dnn = make_pipeline(
    preprocess,
    MLPClassifier(solver='adam',
                  alpha=0.0001,
                  activation='relu',
                    batch_size=150,
                    hidden_layer_sizes=(200, 100),
                    random_state=1))

È possibile modificare il numero di livelli per migliorare il modello

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

Punteggio di regressione DNN: 0.821253

LIME: fidati del tuo modello

Ora che hai un buon modello, hai bisogno di uno strumento per fidarti di esso. apprendimento automatico algoritmo, in particolare random forest e neural network, sono noti per essere algoritmi black-box. In altre parole, funzionano ma nessuno sa perché.

Tre ricercatori hanno ideato un ottimo strumento per vedere come il computer fa una previsione. Il documento si intitola Perché dovrei fidarmi di te?

Hanno sviluppato un algoritmo denominato Spiegazioni indipendenti dal modello interpretabile locale (LIME).

Facciamo un esempio:

a volte non sai se puoi fidarti di una previsione di apprendimento automatico:

Un medico, ad esempio, non può fidarsi di una diagnosi solo perché lo ha detto un computer. Devi anche sapere se puoi fidarti del modello prima di metterlo in produzione.

Immagina di poter capire perché qualsiasi classificatore sta facendo una previsione anche di modelli incredibilmente complicati come reti neurali, foreste casuali o svm con qualsiasi kernel

Diventerà più facile fidarsi di una previsione se riusciremo a comprenderne le ragioni. Dall'esempio con il medico, se il modello gli dicesse quali sintomi sono essenziali ti fideresti, è anche più facile capire se non dovresti fidarti del modello.

Lime può dirti quali caratteristiche influenzano le decisioni del classificatore

Preparazione dei dati

Ci sono un paio di cose che devi cambiare per eseguire LIME python. Prima di tutto, devi installare lime nel terminale. Puoi usare pip install lime

Lime utilizza l'oggetto LimeTabularExplainer per approssimare il modello localmente. Questo oggetto richiede:

  • un set di dati in formato numpy
  • Il nome delle funzionalità: feature_names
  • Il nome delle classi: class_names
  • L'indice della colonna delle caratteristiche categoriche: categorical_features
  • Il nome del gruppo per ciascuna funzionalità categoriale: categorical_names

Crea un set di treni intorpiditi

Puoi copiare e convertire df_train da panda a numpy molto facilmente

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

Ottieni il nome della classe L'etichetta è accessibile con l'oggetto unique(). Tu dovresti vedere:

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

indice della colonna delle caratteristiche categoriali

Puoi utilizzare il metodo che hai utilizzato prima per ottenere il nome del gruppo. Codifichi l'etichetta con LabelEncoder. Si ripete l'operazione su tutte le caratteristiche categoriali.

## 
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

Ora che il set di dati è pronto, puoi costruire i diversi set di dati come mostrato negli esempi di apprendimento di Scikit di seguito. In realtà trasformi i dati all'esterno della pipeline per evitare errori con LIME. Il set di training in LimeTabularExplainer dovrebbe essere un array Numpy senza stringa. Con il metodo sopra, hai un set di dati di addestramento già convertito.

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)

Puoi creare la pipeline con i parametri ottimali di 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))])

Ricevi un avviso. L'avviso spiega che non devi creare un codificatore di etichette prima della pipeline. Se non vuoi usare LIME, puoi usare il metodo della prima parte del tutorial Machine Learning con Scikit-learn. Altrimenti, puoi continuare con questo metodo, prima crea un set di dati codificato, imposta get the hot one encoder all'interno della 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)

Prima di usare LIME in azione, creiamo un array numpy con le caratteristiche della classificazione sbagliata. Puoi usare quell'elenco più tardi per farti un'idea di cosa ha tratto in inganno il classificatore.

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)

Crei una funzione lambda per recuperare la previsione dal modello con i nuovi dati. Ne avrai bisogno presto.

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]])

Converti il ​​dataframe panda in un array 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)

Scegliamo una famiglia a caso dal set di test e vediamo la previsione del modello e come il computer ha fatto la sua scelta.

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])

Puoi utilizzare l'explainer con aware_instance per verificare la spiegazione dietro il modello

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

Preparazione dei dati

Possiamo vedere che il classificatore ha previsto correttamente la famiglia. Il reddito è, infatti, superiore a 50k.

La prima cosa che possiamo dire è che il classificatore non è così sicuro delle probabilità previste. La macchina prevede che la famiglia abbia un reddito superiore a 50 con una probabilità del 64%. Questo 64% è composto da plusvalenza e coniugale. Il colore blu contribuisce negativamente alla classe positiva e la linea arancione positivamente.

Il classificatore è confuso perché la plusvalenza di questa famiglia è nulla, mentre la plusvalenza è solitamente un buon indicatore della ricchezza. Inoltre, la famiglia lavora meno di 40 ore settimanali. Età, occupazione e sesso contribuiscono positivamente al classificatore.

Se lo stato civile fosse single, il classificatore avrebbe previsto un reddito inferiore a 50k (0.64-0.18 = 0.46)

Possiamo provare con un'altra famiglia che è stata classificata erroneamente

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)

Preparazione dei dati

Il classificatore prevedeva un reddito inferiore a 50 mentre non è vero. Questa famiglia sembra strana. Non presenta plusvalenza, né minusvalenza. È divorziato e ha 60 anni, ed è un popolo istruito, cioè education_num > 12. Secondo il modello generale, questa famiglia dovrebbe, come spiegato dal classificatore, ottenere un reddito inferiore a 50k.

Prova a giocare con LIME. Noterai errori grossolani dal classificatore.

Puoi controllare il GitHub del proprietario della libreria. Forniscono documentazione aggiuntiva per la classificazione di immagini e testo.

Sommario

Di seguito è riportato un elenco di alcuni comandi utili con la versione di scikit learn >=0.20

creare un set di dati di training/test i tirocinanti si dividono
Costruisci una pipeline
seleziona la colonna e applica la trasformazione makecolumntransformer
tipo di trasformazione
standardizzare StandardScaler
minimo Massimo MinMaxScaler
Normalizzare Normalizer
Assegnare il valore mancante imputare
Converti categoriale OneHotEncoder
Adattare e trasformare i dati fit_transform
Realizza la conduttura make_pipeline
Modello base
regressione logistica Regressione logistica
XGBoost Classificatore XGB
Rete neurale Classificatore MLP
Ricerca a griglia GrigliaRicercaCV
Ricerca randomizzata CV randomizzato di ricerca