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.
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:
- Apprendimento automatico con scikit-learn
- 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:
- Standardizza la variabile: `StandardScaler()“
- 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)
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)
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 |