Samouczek Scikit-Learn: Jak zainstalować i przykłady Scikit-Learn
Czym jest Scikit-learn?
Nauka scikitu jest open-source Python biblioteka do uczenia maszynowego. Obsługuje najnowocześniejsze algorytmy, takie jak KNN, XGBoost, random forest i SVM. Jest zbudowana na bazie NumPy. Scikit-learn jest szeroko stosowany w konkursach Kaggle, a także w wybitnych firmach technologicznych. Pomaga w przetwarzaniu wstępnym, redukcji wymiarowości (wybór parametrów), klasyfikacji, regresji, klastrowaniu i wyborze modelu.
Scikit-learn ma najlepszą dokumentację ze wszystkich bibliotek open source. Zapewnia interaktywny wykres pod adresem https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html.

Scikit-learn nie jest bardzo trudny w użyciu i zapewnia doskonałe wyniki. Jednakże scikit Learn nie obsługuje obliczeń równoległych. Można z nim uruchomić algorytm głębokiego uczenia się, ale nie jest to rozwiązanie optymalne, zwłaszcza jeśli wiesz, jak korzystać z TensorFlow.
Jak pobrać i zainstalować Scikit-learn
Teraz w tym Python Tutorial Scikit-learn, dowiemy się jak pobrać i zainstalować Scikit-learn:
1 opcji: AWS
scikit-learn może być używany w AWS. Proszę odnosić się Obraz okna dokowanego z preinstalowanym modułem scikit-learn.
Aby użyć wersji deweloperskiej, użyj polecenia in Jupyter
import sys !{sys.executable} -m pip install git+git://github.com/scikit-learn/scikit-learn.git
2 opcji: Mac lub Windows za pomocą Anacondy
Aby dowiedzieć się więcej na temat instalacji Anacondy, zobacz https://www.guru99.com/download-install-tensorflow.html
Niedawno twórcy scikit wydali wersję rozwojową, która rozwiązuje typowe problemy napotykane w bieżącej wersji. Uznaliśmy, że wygodniej jest używać wersji dla programistów zamiast wersji bieżącej.
Jak zainstalować scikit-learn w środowisku Conda
Jeśli zainstalowałeś scikit-learn ze środowiskiem conda, wykonaj krok, aby zaktualizować do wersji 0.20
Krok 1) Aktywuj środowisko tensorflow
source activate hello-tf
Krok 2) Usuń scikit Lean za pomocą polecenia conda
conda remove scikit-learn
Krok 3) Zainstaluj wersję deweloperską.
Zainstaluj wersję deweloperską scikit Learn wraz z niezbędnymi bibliotekami.
conda install -c anaconda git pip install Cython pip install h5py pip install git+git://github.com/scikit-learn/scikit-learn.git
UWAGA: Windows użytkownik będzie musiał zainstalować Microsoft Wizualny C++ 14. Możesz to zdobyć tutaj
Przykład Scikit-Learn z uczeniem maszynowym
Ten samouczek Scikit jest podzielony na dwie części:
- Uczenie maszynowe za pomocą scikit-learn
- Jak zaufać swojemu modelowi z LIME
W pierwszej części szczegółowo opisano, jak zbudować potok, stworzyć model i dostroić hiperparametry, natomiast w drugiej części przedstawiono najnowocześniejsze rozwiązania w zakresie wyboru modelu.
Krok 1) Zaimportuj dane
Podczas tego samouczka do nauki Scikit będziesz korzystać ze zbioru danych dla dorosłych.
Aby zapoznać się z tłem tego zbioru danych, zobacz. Jeśli chcesz dowiedzieć się więcej na temat statystyk opisowych, skorzystaj z narzędzi Dive i Przegląd.
Odnosić się ten poradnik dowiedz się więcej o nurkowaniu i przeglądzie
Importujesz zbiór danych za pomocą Pand. Pamiętaj, że musisz przekonwertować typ zmiennych ciągłych na format zmiennoprzecinkowy.
Ten zbiór danych zawiera osiem zmiennych kategorycznych:
Zmienne kategoryczne są wymienione w CATE_FEATURES
- klasa pracy
- Edukacja
- małżeński
- zawód
- związek
- wyścig
- seks
- ojczyźnie
ponadto sześć zmiennych ciągłych:
Zmienne ciągłe są wymienione w CONTI_FEATURES
- wiek
- fnlwgt
- edukacja_num
- zysk kapitałowy
- strata_kapitału
- godziny_tydzień
Pamiętaj, że listę wypełniamy ręcznie, abyś miał lepszy pogląd, jakich kolumn używamy. Szybszym sposobem skonstruowania listy kategorialnej lub ciągłej jest użycie:
## 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)
Oto kod do importowania danych:
# 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()
wiek | fnlwgt | edukacja_num | zysk kapitałowy | strata_kapitału | godziny_tydzień | |
---|---|---|---|---|---|---|
liczyć | 32561.000000 | 3.256100e + 04 | 32561.000000 | 32561.000000 | 32561.000000 | 32561.000000 |
oznaczać | 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 |
min | 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 |
Możesz sprawdzić liczbę unikalnych wartości funkcji native_country. Widać, że tylko jedno gospodarstwo pochodzi z Holandii-Holandii. To gospodarstwo domowe nie przekaże nam żadnych informacji, ale wyświetli błąd podczas szkolenia.
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
Możesz wykluczyć ten pozbawiony informacji wiersz ze zbioru danych
## Drop Netherland, because only one row df_train = df_train[df_train.native_country != "Holand-Netherlands"]
Następnie przechowujesz położenie obiektów ciągłych na liście. Będziesz go potrzebować w następnym kroku do zbudowania rurociągu.
Poniższy kod wykona pętlę po nazwach wszystkich kolumn w CONTI_FEATURES i pobierze jej lokalizację (tj. numer), a następnie dołączy ją do listy o nazwie 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]
Poniższy kod wykonuje to samo zadanie co powyżej, ale dla zmiennej kategorycznej. Poniższy kod powtarza to, co zrobiłeś wcześniej, z wyjątkiem funkcji kategorycznych.
## 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]
Możesz rzucić okiem na zbiór danych. Należy pamiętać, że każda cecha kategoryczna jest ciągiem znaków. Nie można podać modelu wartości ciągu. Musisz przekształcić zbiór danych za pomocą zmiennej fikcyjnej.
df_train.head(5)
W rzeczywistości musisz utworzyć jedną kolumnę dla każdej grupy w obiekcie. Najpierw możesz uruchomić poniższy kod, aby obliczyć całkowitą liczbę potrzebnych kolumn.
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
Cały zestaw danych zawiera 101 grup, jak pokazano powyżej. Na przykład cechy klasy roboczej mają dziewięć grup. Możesz zwizualizować nazwy grup za pomocą następujących kodów
Unique() zwraca unikalne wartości cech kategorycznych.
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']
Dlatego zbiór danych szkoleniowych będzie zawierał 101 + 7 kolumn. Ostatnie siedem kolumn to cechy ciągłe.
Scikit-learn może zająć się konwersją. Odbywa się to w dwóch etapach:
- Najpierw musisz przekonwertować ciąg na identyfikator. Na przykład władza stanowa będzie miała identyfikator 1, Self-emp-not-inc ID 2 i tak dalej. Funkcja LabelEncoder zrobi to za Ciebie
- Transponuj każdy identyfikator do nowej kolumny. Jak wspomniano wcześniej, zbiór danych ma 101 identyfikatorów grup. Dlatego będzie 101 kolumn obejmujących wszystkie grupy cech kategorycznych. Scikit-learn ma funkcję OneHotEncoder, która wykonuje tę operację.
Krok 2) Utwórz zestaw pociągowy/testowy
Teraz, gdy zbiór danych jest gotowy, możemy go podzielić 80/20.
80 procent dla zestawu szkoleniowego i 20 procent dla zestawu testowego.
Możesz użyć train_test_split. Pierwszym argumentem jest ramka danych zawierająca funkcje, a drugim argumentem jest ramka danych etykiety. Możesz określić rozmiar zestawu testowego za pomocą 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)
Krok 3) Zbuduj rurociąg
Potok ułatwia zasilanie modelu spójnymi danymi.
Pomysł polega na umieszczeniu surowych danych w „kanale” w celu wykonania operacji.
Na przykład, w przypadku bieżącego zestawu danych, musisz standaryzować zmienne ciągłe i konwertować dane kategoryczne. Zauważ, że możesz wykonać dowolną operację wewnątrz potoku. Na przykład, jeśli masz „NA” w zestawie danych, możesz je zastąpić średnią lub medianą. Możesz również tworzyć nowe zmienne.
Masz wybór; zakoduj na stałe dwa procesy lub utwórz potok. Pierwszy wybór może z czasem prowadzić do wycieku danych i powstania niespójności. Lepszą opcją jest użycie rurociągu.
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
Przed przekazaniem danych do klasyfikatora logistycznego rurociąg wykona dwie operacje:
- Standaryzuj zmienną: `StandardScaler()“
- Konwertuj cechy kategoryczne: OneHotEncoder(sparse=False)
Możesz wykonać dwa kroki, używając make_column_transformer. Ta funkcja nie jest dostępna w aktualnej wersji scikit-learn (0.19). W obecnej wersji nie jest możliwe wykonanie kodera etykiet i jednego kodera na gorąco w potoku. To jeden z powodów, dla których zdecydowaliśmy się użyć wersji deweloperskiej.
make_column_transformer jest łatwy w użyciu. Musisz zdefiniować, które kolumny zastosować transformację i jaką transformację wykonać. Na przykład, aby ujednolicić funkcję ciągłą, możesz zrobić:
- conti_features, StandardScaler() wewnątrz make_column_transformer.
- conti_features: lista ze zmienną ciągłą
- StandardScaler: standaryzuj zmienną
Obiekt OneHotEncoder wewnątrz make_column_transformer automatycznie koduje etykietę.
preprocess = make_column_transformer( (conti_features, StandardScaler()), ### Need to be numeric not string to specify columns name (categorical_features, OneHotEncoder(sparse=False)) )
Możesz sprawdzić, czy potok działa z fit_transform. Zestaw danych powinien mieć następujący kształt: 26048, 107
preprocess.fit_transform(X_train).shape
(26048, 107)
Transformator danych jest gotowy do użycia. Możesz utworzyć potok za pomocą make_pipeline. Po przekształceniu danych można wprowadzić regresję logistyczną.
model = make_pipeline( preprocess, LogisticRegression())
Trenowanie modelu za pomocą scikit-learn jest trywialne. Należy zastosować dopasowanie obiektu poprzedzone potokiem, czyli model. Możesz wydrukować dokładność za pomocą obiektu score z biblioteki scikit-learn
model.fit(X_train, y_train) print("logistic regression score: %f" % model.score(X_test, y_test))
logistic regression score: 0.850891
Wreszcie możesz przewidzieć klasy za pomocą przewidywania_proba. Zwraca prawdopodobieństwo dla każdej klasy. Zauważ, że sumuje się do jednego.
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]])
Krok 4) Korzystanie z naszego potoku w wyszukiwaniu siatki
Dostrajanie hiperparametrów (zmiennych określających strukturę sieci, takich jak jednostki ukryte) może być żmudne i wyczerpujące.
Jednym ze sposobów oceny modelu może być zmiana rozmiaru zbioru uczącego i ocena jego wydajności.
Możesz powtórzyć tę metodę dziesięć razy, aby zobaczyć metryki wyniku. Jest to jednak za dużo pracy.
Zamiast tego scikit-learn udostępnia funkcję dostrajania parametrów i sprawdzania poprawności krzyżowej.
Walidacja krzyżowa
Walidacja krzyżowa oznacza, że podczas treningu zbiór treningowy jest przesuwany n razy w składach, a następnie oceniany jest model n razy. Na przykład, jeśli cv jest ustawione na 10, zbiór uczący jest szkolony i oceniany dziesięć razy. W każdej rundzie klasyfikator wybiera losowo dziewięć fałd do trenowania modelu, a dziesiąta część jest przeznaczona do oceny.
Wyszukiwanie w siatce
Każdy klasyfikator ma hiperparametry do dostrojenia. Możesz wypróbować różne wartości lub ustawić siatkę parametrów. Jeśli wejdziesz na oficjalną stronę scikit-learn, zobaczysz, że klasyfikator logistyczny ma inne parametry do dostrojenia. Aby przyspieszyć trening, wybierasz dostrojenie parametru C. Kontroluje parametr regularyzacji. Powinno być pozytywne. Mała wartość nadaje większą wagę regulatorowi.
Możesz użyć obiektu GridSearchCV. Aby dostroić, musisz utworzyć słownik zawierający hiperparametry.
Podajesz hiperparametry, a następnie wartości, które chcesz wypróbować. Na przykład, aby dostroić parametr C, użyj:
- 'logisticregression__C': [0.1, 1.0, 1.0]: Parametr jest poprzedzony małą literą nazwy klasyfikatora i dwoma podkreśleniami.
Model wypróbuje cztery różne wartości: 0.001, 0.01, 0.1 i 1.
Trenujesz model za pomocą 10 fałd: cv=10
from sklearn.model_selection import GridSearchCV # Construct the parameter grid param_grid = { 'logisticregression__C': [0.001, 0.01,0.1, 1.0], }
Model można trenować za pomocą GridSearchCV z parametrami gri i cv.
# Train the model grid_clf = GridSearchCV(model, param_grid, cv=10, iid=False) grid_clf.fit(X_train, y_train)
WYDAJNOŚĆ
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)
Aby uzyskać dostęp do najlepszych parametrów, użyj best_params_
grid_clf.best_params_
WYDAJNOŚĆ
{'logisticregression__C': 1.0}
Po przeszkoleniu modelu z czterema różnymi wartościami regularyzacji optymalnym parametrem jest
print("best logistic regression from grid search: %f" % grid_clf.best_estimator_.score(X_test, y_test))
najlepsza regresja logistyczna z wyszukiwania w siatce: 0.850891
Aby uzyskać dostęp do przewidywanych prawdopodobieństw:
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]])
Model XGBoost z nauką scikit
Wypróbujmy przykłady Scikit-learn, aby wytrenować jeden z najlepszych klasyfikatorów na rynku. XGBoost to ulepszenie w stosunku do losowego lasu. Podstawa teoretyczna klasyfikatora poza zakresem tego zagadnienia Python Poradnik Scikita. Pamiętaj, że XGBoost wygrał wiele konkursów kaggle. Przy średnim rozmiarze zbioru danych może działać równie dobrze, jak algorytm głębokiego uczenia się, a nawet lepiej.
Klasyfikator jest trudny do wyszkolenia, ponieważ ma dużą liczbę parametrów do dostrojenia. Możesz oczywiście użyć GridSearchCV, aby wybrać parametr za siebie.
Zamiast tego zobaczmy, jak zastosować lepszy sposób na znalezienie optymalnych parametrów. Uczenie GridSearchCV może być żmudne i bardzo długie, jeśli przekażesz wiele wartości. Przestrzeń poszukiwań rośnie wraz z liczbą parametrów. Preferowanym rozwiązaniem jest użycie RandomizedSearchCV. Metoda ta polega na losowym wyborze wartości każdego hiperparametru po każdej iteracji. Na przykład, jeśli klasyfikator jest szkolony w ciągu 1000 iteracji, ocenianych jest 1000 kombinacji. Działa to mniej więcej tak. GridSearchCV
Musisz zaimportować xgboost. Jeśli biblioteka nie jest zainstalowana, użyj pip3 install xgboost lub
use import sys !{sys.executable} -m pip install xgboost
In Jupyter środowisko
Następnie
import xgboost from sklearn.model_selection import RandomizedSearchCV from sklearn.model_selection import StratifiedKFold
Następny krok w tym Scikicie Python samouczek obejmuje określenie parametrów do dostrojenia. Możesz zapoznać się z oficjalną dokumentacją, aby zobaczyć wszystkie parametry do dostrojenia. Ze względu na Python Sklearn, wybierasz tylko dwa hiperparametry z dwiema wartościami każdy. Uczenie XGBoost zajmuje dużo czasu, im więcej hiperparametrów w siatce, tym dłużej trzeba czekać.
params = { 'xgbclassifier__gamma': [0.5, 1], 'xgbclassifier__max_depth': [3, 4] }
Konstruujesz nowy potok z klasyfikatorem XGBoost. Wybierasz zdefiniowanie 600 estymatorów. Zauważ, że n_estimators to parametr, który możesz dostroić. Wysoka wartość może prowadzić do nadmiernego dopasowania. Możesz samodzielnie wypróbować różne wartości, ale pamiętaj, że może to zająć godziny. Używasz wartości domyślnej dla innych parametrów
model_xgb = make_pipeline( preprocess, xgboost.XGBClassifier( n_estimators=600, objective='binary:logistic', silent=True, nthread=1) )
Możesz ulepszyć weryfikację krzyżową za pomocą walidatora krzyżowego Stratified K-Folds. Konstruujesz tutaj tylko trzy zagięcia, aby przyspieszyć obliczenia, ale obniżyć jakość. Aby poprawić wyniki, w domu zwiększ tę wartość do 5 lub 10.
Decydujesz się na uczenie modelu w czterech iteracjach.
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)
Randomizowane wyszukiwanie jest gotowe do użycia, możesz wytrenować model
#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)
Jak widać, XGBoost ma lepszy wynik niż poprzednia regresja logistyczna.
print("Best parameter", random_search.best_params_) print("best logistic regression from grid search: %f" % random_search.best_estimator_.score(X_test, y_test))
Best parameter {'xgbclassifier__max_depth': 3, 'xgbclassifier__gamma': 0.5} best logistic regression from grid search: 0.873157
random_search.best_estimator_.predict(X_test)
array(['<=50K', '<=50K', '<=50K', ..., '<=50K', '>50K', '<=50K'], dtype=object)
Utwórz DNN za pomocą MLPClassifier w scikit-learn
Wreszcie możesz wytrenować algorytm głębokiego uczenia się za pomocą scikit-learn. Metoda jest taka sama jak w przypadku drugiego klasyfikatora. Klasyfikator jest dostępny na stronie MLPClassifier.
from sklearn.neural_network import MLPClassifier
Definiujesz następujący algorytm głębokiego uczenia:
- Adam Rozwiązujący
- Funkcja aktywacji Relu
- Alfa = 0.0001
- wielkość partii 150 szt
- Dwie ukryte warstwy zawierające odpowiednio 100 i 50 neuronów
model_dnn = make_pipeline( preprocess, MLPClassifier(solver='adam', alpha=0.0001, activation='relu', batch_size=150, hidden_layer_sizes=(200, 100), random_state=1))
Możesz zmienić liczbę warstw, aby ulepszyć model
model_dnn.fit(X_train, y_train) print("DNN regression score: %f" % model_dnn.score(X_test, y_test))
Wynik regresji DNN: 0.821253
LIME: Zaufaj swojemu modelowi
Teraz, gdy masz dobry model, potrzebujesz narzędzia, które mu zaufa. Nauczanie maszynowe Algorytm, zwłaszcza losowy las i sieć neuronowa, są znane jako algorytmy czarnej skrzynki. Inaczej mówiąc, działa, ale nikt nie wie dlaczego.
Trzej badacze wymyślili świetne narzędzie umożliwiające sprawdzenie, w jaki sposób komputer dokonuje przewidywań. Artykuł nosi tytuł Dlaczego powinienem ci ufać?
Opracowali algorytm o nazwie Lokalne interpretowalne objaśnienia niezależne od modelu (LIME).
Brać przykład:
czasami nie wiesz, czy możesz ufać przewidywaniom uczenia maszynowego:
Na przykład lekarz nie może ufać diagnozie tylko dlatego, że tak powiedział komputer. Musisz także wiedzieć, czy możesz zaufać modelowi przed wprowadzeniem go do produkcji.
Wyobraź sobie, że możemy zrozumieć, dlaczego dowolny klasyfikator prognozuje nawet niezwykle skomplikowane modele, takie jak sieci neuronowe, losowe lasy lub svm z dowolnym jądrem
łatwiej będzie zaufać przewidywaniom, jeśli zrozumiemy ich przyczyny. Na przykładzie lekarza, jeśli model powiedziałby mu, jakie objawy są istotne, można mu zaufać, łatwiej jest też zorientować się, czy nie należy ufać modelowi.
Lime może powiedzieć, jakie cechy wpływają na decyzje klasyfikatora
Przygotowywanie danych
Jest to kilka rzeczy, które musisz zmienić, aby uruchomić LIME pyton. Przede wszystkim musisz zainstalować lime w terminalu. Możesz użyć pip install lime
Lime wykorzystuje obiekt LimeTabularExplainer do lokalnego przybliżenia modelu. Ten obiekt wymaga:
- zbiór danych w formacie numpy
- Nazwa funkcji: nazwy_funkcji
- Nazwa klas: nazwy_klas
- Indeks kolumny cech kategorycznych: categorical_features
- Nazwa grupy dla każdej cechy kategorycznej: nazwy_kategorii
Utwórz numpy zestaw pociągów
Możesz skopiować i przekonwertować df_train z pand na tępy bardzo łatwo
df_train.head(5) # Create numpy data df_lime = df_train df_lime.head(3)
Uzyskaj nazwę klasy Etykieta jest dostępna za pomocą obiektu unikatowego(). Powinieneś zobaczyć:
- „<= 50 tys.”
- „> 50 tys.”
# Get the class name class_names = df_lime.label.unique() class_names
array(['<=50K', '>50K'], dtype=object)
indeks kolumny cech kategorycznych
Możesz użyć metody, którą pochyliłeś się wcześniej, aby uzyskać nazwę grupy. Kodujesz etykietę za pomocą LabelEncoder. Powtarzasz operację na wszystkich cechach kategorycznych.
## 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
Teraz, gdy zbiór danych jest gotowy, możesz skonstruować inny zbiór danych, jak pokazano w poniższych przykładach nauki Scikit. W rzeczywistości przekształcasz dane poza potokiem, aby uniknąć błędów w LIME. Zestaw szkoleniowy w LimeTabularExplainer powinien być tablicą numpy bez łańcucha. Dzięki powyższej metodzie masz już przekonwertowany zestaw danych szkoleniowych.
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)
Możesz stworzyć potok o optymalnych parametrach z 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))])
Otrzymujesz ostrzeżenie. Ostrzeżenie wyjaśnia, że nie musisz tworzyć enkodera etykiet przed potokiem. Jeśli nie chcesz używać LIME, możesz użyć metody z pierwszej części samouczka Machine Learning with Scikit-learn. W przeciwnym razie możesz pozostać przy tej metodzie, najpierw utwórz zakodowany zestaw danych, ustaw koder get the hot one w potoku.
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)
Zanim użyjemy LIME w akcji, utwórzmy tablicę numpy z cechami niewłaściwej klasyfikacji. Możesz użyć tej listy później, aby dowiedzieć się, co wprowadziło klasyfikator w błąd.
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)
Tworzysz funkcję lambda, aby pobrać prognozę z modelu przy użyciu nowych danych. Będziesz go wkrótce potrzebować.
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]])
Konwertujesz ramkę danych pand na tablicę 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)
Wybierzmy losowe gospodarstwo domowe ze zbioru testowego i zobaczmy przewidywania modelu oraz sposób, w jaki komputer dokonał swojego wyboru.
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])
Możesz użyć wyjaśniacza z wyjaśnieniem_instancji, aby sprawdzić wyjaśnienie modelu
exp = explainer.explain_instance(X_test_lime[i], predict_fn, num_features=6) exp.show_in_notebook(show_all=False)
Widzimy, że klasyfikator poprawnie przewidział gospodarstwo domowe. Dochody rzeczywiście przekraczają 50 tys.
Pierwszą rzeczą, którą możemy powiedzieć, jest to, że klasyfikator nie jest tak pewny przewidywanych prawdopodobieństw. Maszyna przewiduje, że gospodarstwo domowe ma dochód powyżej 50 tys. z prawdopodobieństwem 64%. Te 64% składa się z zysku kapitałowego i małżeńskiego. Kolor niebieski przyczynia się negatywnie do klasy dodatniej, a linia pomarańczowa, pozytywnie.
Klasyfikator jest mylący, ponieważ zysk kapitałowy tego gospodarstwa domowego jest zerowy, podczas gdy zysk kapitałowy jest zwykle dobrym prognostykiem bogactwa. Poza tym gospodarstwo domowe pracuje mniej niż 40 godzin tygodniowo. Wiek, zawód i płeć mają pozytywny wpływ na klasyfikator.
Gdyby stan cywilny był wolny, klasyfikator przewidziałby dochód poniżej 50 tys. (0.64-0.18 = 0.46)
Możemy spróbować z innym gospodarstwem domowym, które zostało błędnie sklasyfikowane
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)
Klasyfikator przewidział dochód poniżej 50 tys., co nie jest prawdą. To gospodarstwo domowe wydaje się dziwne. Nie ma zysków kapitałowych ani strat kapitałowych. Jest rozwiedziony, ma 60 lat i jest osobą wykształconą, tj. liczba_wykształcenia > 12. Według ogólnego schematu gospodarstwo to powinno, jak wyjaśnia klasyfikator, osiągać dochody poniżej 50 tys.
Próbujesz pobawić się LIME. Zauważysz rażące błędy klasyfikatora.
Możesz sprawdzić GitHub właściciela biblioteki. Zapewniają dodatkową dokumentację do klasyfikacji obrazów i tekstu.
Podsumowanie
Poniżej znajduje się lista przydatnych poleceń w wersji scikit Learn >=0.20
utwórz zestaw danych pociągu/testu | stażyści podzielili się |
Zbuduj rurociąg | |
wybierz kolumnę i zastosuj transformację | makekolumnatransformator |
rodzaj transformacji | |
ujednolicić | Skaler standardowy |
minimum maksimum | MinMax Skaler |
Normalizować | Normalizer |
Przypisz brakującą wartość | przypisać |
Konwertuj kategoryczne | JedenHotEncoder |
Dopasuj i przekształć dane | dopasowanie_transformacji |
Zrób rurociąg | make_pipeline |
Model podstawowy | |
regresja logistyczna | Regresja logistyczna |
XGBoost | Klasyfikator XGB |
Sieć neuronowa | Klasyfikator MLP |
Wyszukiwanie w siatce | GridSearchCV |
Wyszukiwanie losowe | Wyszukiwanie losoweCV |