Урок за Scikit-Learn: Как да инсталирате & примери за Scikit-Learn

⚡ Умно обобщение

Scikit-learn е платформа с отворен код Python Библиотека, която обхваща предварителна обработка, класификация, регресия, клъстеризация и избор на модел, разположен зад един последователен интерфейс за оценка, който поддържа целия работен процес на машинно обучение кратък, четим и възпроизводим от сурови данни до оценени прогнози.

  • 🔘 Монтаж: Conda и pip работят, а текущата стабилна версия вече съдържа всеки използван тук трансформатор.
  • ☑️ Обработен набор от данни: Файлът за преброяване на възрастните от UCI с 32 561 реда е показан във всеки пример в това ръководство.
  • ✅ тръбопроводи: make_column_transformer мащабира числовите колони, а one-hot кодира категориалните в един обект.
  • 🧪 настройка: GridSearchCV обхваща пълна мрежа с параметри, докато RandomizedSearchCV я семплира и завършва много по-рано.
  • 🛠️ Три модела: Резултатите от логистичната регресия са 0.850891, XGBoost 0.873157 и мрежата MLPClassifier 0.821253.
  • ⚠️ Обяснимост: LIME показва кои характеристики са тласнали дадена прогноза към нейния клас, включително и грешните.

Урок за Scikit-learn със стъпки за инсталиране и реални примери

Какво е Scikit-learn?

Научете се е с отворен код Python библиотека за машинно обучениеТой поддържа утвърдени алгоритми като KNN, градиентно усилване, случайна гора и SVM и е изграден върху... numpy и SciPy. Scikit-learn се използва широко в състезанията на Kaggle, както и във водещи технологични компании. Той обхваща предварителна обработка, намаляване на размерността, класификация, регресия, клъстеризация и избор на модел.

Scikit-learn има една от най-добрите документации сред библиотеките с отворен код. Тя дори предоставя интерактивна диаграма за оценка, Избор на правилния оценител, което ви превежда от размера на вашия набор от данни до кратък списък с алгоритми, които си струва да изпробвате.

Фигурата по-долу илюстрира как работи Scikit-learn.

Как работи Scikit-learn в работен процес на машинно обучение

Scikit-learn не е труден за използване и дава отлични резултати. Той обаче се обучава на процесора: работата се паралелизира между ядра с аргумента n_jobs, а не на графичен процесор. Изпълнението на алгоритъм за дълбоко обучение с него е възможно, но рядко е оптимално, особено ако вече знаете как да го използвате. TensorFlow.

Как да изтеглите и инсталирате Scikit-learn

Сега в това Python Урок за Scikit-learn, ще научите как да изтеглите и инсталирате Scikit-learn:

Вариант 1: AWS

Scikit-learn може да се използва върху AWS. Docker образ с предварително инсталиран scikit-learn спестява изцяло работата по настройката.

За да инсталирате версията за разработчици, изпълнете командата по-долу вътре Jupyter:

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

Вариант 2: Mac или Windows с помощта на Anaconda

За да научите повече за инсталирането на Anaconda, вижте как да изтеглите и инсталирате TensorFlow.

По времето, когато това ръководство беше написано, разработчиците на scikit бяха пуснали версия за разработка, която отстрани проблемите, присъстващи в текущата версия, така че стъпките по-долу използват тази разработческа версия. На нова машина днес текущата стабилна версия вече съдържа всеки трансформатор, използван тук, и pip инсталиране -U scikit-learn е достатъчно.

Как да инсталирате scikit-learn с Conda Environment

Ако сте инсталирали scikit-learn със средата conda, следвайте стъпките по-долу, за да актуализирате до версия 0.20.

Стъпка 1) Активирайте средата tensorflow

source activate hello-tf

Стъпка 2) Премахнете scikit-learn с помощта на командата conda

conda remove scikit-learn

Стъпка 3) Инсталирайте версията за разработчици

Инсталирайте версията за разработчици scikit-learn заедно с необходимите библиотеки.

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

ЗАБЕЛЕЖКА: Windows потребителите се нуждаят Microsoft зрителен C++ 14. Можеш да го получиш тук.

Пример за Scikit-Learn с машинно обучение

Този урок за Scikit е разделен на две части:

  1. Машинно обучение с scikit-learn
  2. Как да се доверите на вашия модел с LIME

Първата част описва подробно как да се изгради конвейер, да се създаде модел и да се настроят хиперпараметрите, докато втората част обхваща интерпретацията на модела.

Стъпка 1) Импортирайте данните

По време на този урок за Scikit learn ще използвате набора от данни за преброяване на възрастни.

Файлът се чете директно от хранилището за машинно обучение на UCI в кода по-долу, така че не е необходимо ръчно изтегляне. Ако се интересувате от описателната статистика, инструментите Dive и Overview си струва да разгледате. Вижте този урок за да научите повече за гмуркането и общия преглед.

Импортирате набора от данни с pandas. Обърнете внимание, че трябва да конвертирате непрекъснатите променливи във формат с плаваща запетая.

Този набор от данни включва осем категорични променливи, изброени в CATE_FEATURES:

  • работен клас
  • образование
  • брачен
  • окупация
  • връзка
  • раса
  • секс
  • родна_страна

Включва и шест непрекъснати променливи, изброени в CONTI_FEATURES:

  • възраст
  • fnlwgt
  • образование_номер
  • капиталова_печалба
  • капиталова_загуба
  • часове_седмица

Списъците се попълват ръчно, за да имате по-ясна представа кои колони са в действие. По-бърз начин за изграждане на списък с категорични или непрекъснати колони е:

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

Ето кода за импортиране на данните:

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

Извикването на describe() върху рамката връща обобщената статистика за шестте непрекъснати колони:

възраст fnlwgt образование_номер капиталова_печалба капиталова_загуба часове_седмица
броя 32561.000000 3.256100e + 04 32561.000000 32561.000000 32561.000000 32561.000000
означава 38.581647 1.897784e + 05 10.080679 1077.648844 87.303830 40.437456
станд 13.640433 1.055500e + 05 2.572720 7385.292085 402.960219 12.347429
мин 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
макс 90.000000 1.484705e + 06 16.000000 99999.000000 4356.000000 99.000000

Можете да проверите броя на уникалните стойности на характеристиката native_country. Само едно домакинство е от Холандия. Това домакинство не носи никаква информация и ще генерира грешка по време на обучението.

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

Можете да изключите този неинформативен ред от набора от данни:

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

След това съхранявате позицията на непрекъснатите характеристики в списък. Ще ви трябва в следващата стъпка за изграждане на тръбопровода.

Кодът по-долу преминава през всички имена на колони в CONTI_FEATURES, чете всяко местоположение (тоест, номера на колоната му) и го добавя към списък, наречен 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]

Следващият блок върши същата работа за категориалните променливи.

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

Сега разгледайте самия набор от данни. Всяка категориална характеристика е низ, а на модел не може да се подаде низова стойност, така че наборът от данни трябва да бъде трансформиран с фиктивни променливи.

df_train.head(5)

Всъщност, ви е необходима по една колона за всяка група във всяка функция. Първо, изпълнете кода по-долу, за да изчислите общия брой необходими колони.

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

Целият набор от данни съдържа 101 групи, както е показано по-горе. Само характеристиката workclass има девет групи. Можете да изброите имената на групите с кода по-долу; unique() връща различните стойности на всяка категорична характеристика.

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

Следователно, наборът от данни за обучение ще съдържа 101 + 6 колони: групите с един активен елемент плюс шестте непрекъснати характеристики.

Scikit-learn може да се погрижи за преобразуването в две стъпки:

  1. Преобразувайте низа в ID. State-gov става ID 1, Self-emp-not-inc става ID 2 и така нататък. LabelEncoder прави това вместо вас.
  2. Транспонирайте всеки идентификатор в нова колона. Наборът от данни има 101 групови идентификатора, така че ще има 101 колони, обхващащи всяка категорична група характеристики. Scikit-learn предоставя OneHotEncoder за тази операция.

Стъпка 2) Създайте комплекта за обучение/тест

След като наборът от данни е готов, разделете го на 80/20: 80 процента за обучителния набор и 20 процента за тестовия набор.

Можете да използвате train_test_split. Първият аргумент е рамката от данни на характеристиките, а вторият е етикетът. Задавате размера на тестовия набор с 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)

Стъпка 3) Изградете тръбопровода

Конвейерът улеснява захранването на модела с последователни данни. Идеята е суровите данни да се прехвърлят през един обект, който изпълнява всяка операция по ред.

С този набор от данни трябва да стандартизирате непрекъснатите променливи и да конвертирате категориалните. Всяка операция може да се извършва в рамките на конвейер: липсващите стойности могат да бъдат заменени със средна стойност или медиана и могат да бъдат създадени нови променливи.

Имате избор: да кодирате твърдо двата процеса или да изградите конвейер. Твърдото кодиране може да доведе до изтичане на тестови данни в съобразените статистики и да създаде несъответствия с течение на времето, така че конвейерът е по-добрият вариант.

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

Тръбопроводът извършва две операции, преди да захранва логистичния класификатор:

  1. Стандартизирайте променливата: StandardScaler()
  2. Преобразувайте категоричните характеристики: OneHotEncoder(sparse=False)

Изпълнявате и двете стъпки с make_column_transformer. Когато това ръководство е написано, функцията не е била в издадената версия на scikit-learn (0.19), поради което е използвана разработческата версия; тя е била включена във всяка стабилна версия от 0.20 насам.

make_column_transformer е лесно: декларирате кои колони да трансформирате и коя трансформация да приложите. За да стандартизирате непрекъснатите функции, които предавате:

  • conti_features, StandardScaler() вътре в make_column_transformer
    • conti_features: списъкът с непрекъснати колони
    • StandardScaler: стандартизира тези колони

Обектът OneHotEncoder вътре в make_column_transformer кодира етикетите автоматично.

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

Бележка към версията: Два аргумента в горния блок са преместени напред. Текущите версии очакват първо трансформатора, а след това колоните, и рядък бе преименуван рядък_изход в scikit-learn 1.2 и премахнато в 1.4, така че по-нов код чете OneHotEncoder(sparse_output=False).

Можете да проверите дали конвейерът работи с fit_transform. Изходът трябва да има формата 26048, 107.

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

Трансформаторът на данни е готов. Създавате конвейера с make_pipeline и след като данните са трансформирани, ги подавате към логистичната регресия.

model = make_pipeline(
    preprocess,
    LogisticRegression())

Обучението на модел със scikit-learn е тривиално: извикайте fit в конвейера. Можете да отпечатате точността с метода score.

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

Накрая можете да предскажете класовете с predict_proba, която връща вероятността за всеки клас. Обърнете внимание, че двете вероятности се сумират до едно.

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

Стъпка 4) Използване на нашия конвейер при търсене в мрежа

Настройването на хиперпараметрите, стойностите, които фиксират структурата на модела, може да бъде досадно и изтощително.

Един от начините за оценка на модела би бил да се промени размерът на обучаващия набор и да се измери производителността, като се повтори упражнението десет пъти, за да се види разпределението на резултата. Това е много ръчна работа.

Вместо това, scikit-learn предоставя функции, които извършват настройка на параметри и кръстосана валидация вместо вас.

Кръстосана проверка

Кръстосаната валидация означава, че по време на обучението обучаващият набор се разделя n пъти на групи и моделът се оценява n пъти. Ако cv е зададено на 10, моделът се обучава и оценява десет пъти. Във всеки кръг класификаторът обучава девет групи, избрани на случаен принцип, а десетата група се запазва за оценка.

Търсене в мрежата

Всеки класификатор има хиперпараметри за настройване. Можете да изпробвате стойности една по една или да зададете мрежа от параметри. Документацията на scikit-learn изброява всички параметри, които логистичният класификатор приема. За да се ускори обучението, този пример настройва само параметъра C, който контролира регуляризацията. Той трябва да е положителен, а малка стойност дава по-голяма тежест на регуларизатора.

Използвате обекта GridSearchCV, който приема речник на хиперпараметрите за настройване. Избройте всеки хиперпараметър, последван от стойностите, които искате да опитате. За да настроите C, пишете:

  • 'logisticeregression__C': [0.001, 0.01, 0.1, 1.0] — името на параметъра е предшествано от името на класификатора с малки букви и две долни черти.

Моделът ще опита четири различни стойности: 0.001, 0.01, 0.1 и 1. Той е обучен с 10 пъти, което е cv=10.

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

Вече можете да обучите модела, използвайки GridSearchCV с параметъра grid и cv.

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

Изход:

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)

Бележка към версията: - идентификатор на друг идентификатор Аргументът , видим в този изход, беше остарял в scikit-learn 0.22 и премахнат в 0.24, така че просто трябва да бъде премахнат от извикването на GridSearchCV в текущите версии.

За достъп до най-добрите параметри използвате best_params_.

grid_clf.best_params_

Изход:

{'logisticregression__C': 1.0}

След обучение на модела с четири различни стойности на регуляризация, оптималният параметър дава:

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

най-добра логистична регресия от търсене в мрежата: 0.850891

За достъп до прогнозираните вероятности:

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

Модел XGBoost с scikit-learn

Сега опитайте един от най-силните класификатори на пазара. XGBoost е подобрение на случайната гора, което увеличава градиента. Теоретичната му основа е извън обхвата на това изследване. Python Урок за Scikit, но имайте предвид, че XGBoost е печелил много състезания на Kaggle. Върху набор от данни със среден размер може да се представи толкова добре, колкото алгоритъм за дълбоко обучение, или дори по-добре.

Класификаторът е труден за обучение, защото предоставя голям брой параметри. Разбира се, можете да използвате GridSearchCV, за да ги изберете вместо вас.

По-добър вариант тук е RandomizedSearchCV. GridSearchCV става бавен, когато мрежата е голяма, защото пространството за търсене нараства с всеки добавен параметър. RandomizedSearchCV вместо това взема проби от стойностите на всеки хиперпараметър на случаен принцип при всяка итерация, така че 1,000 итерации оценяват 1,000 комбинации. Иначе работи подобно на GridSearchCV.

Трябва да импортирате xgboost. Ако библиотеката не е инсталирана, изпълнете pip3 install xgboost или я инсталирайте отвътре Jupyter тефтер с:

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

След това импортирайте класификатора и двата помощника за търсене:

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

Следващата стъпка в този Scikit Python Урокът е да се зададат параметрите за настройване. Официалната документация на XGBoost ги изброява всички. За целите на това Python В урока на Sklearn избирате само два хиперпараметъра с по две стойности всеки, защото XGBoost отнема много време за обучение и всяка допълнителна точка от мрежата добавя към чакането.

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

След това изграждате нов конвейер с класификатора XGBoost и 600 оценки. n_estimators сам по себе си е настройваем и високата стойност може да доведе до пренастройване. Можете да опитате други стойности, но имайте предвид, че това може да отнеме часове. Всеки друг параметър запазва стойността си по подразбиране.

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

Можете да подобрите кръстосаната валидация с помощта на Stratified K-Folds cross-validator. Тук се използват само три сгъвания, за да се ускори изчислението, но с известна загуба на качество; увеличете това до 5 или 10 на вашата машина за по-добри резултати. Моделът се обучава в рамките на четири итерации.

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)

Рандомизираното търсене е готово, така че можете да обучите модела.

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

Както можете да видите, XGBoost се представя по-добре от по-ранната логистична регресия.

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)

Създайте DNN с MLPClassifier в scikit-learn

Накрая, можете да обучите невронна мрежа със самия scikit-learn. Методът е същият като за всеки друг класификатор, а оценката е MLPClassifier.

from sklearn.neural_network import MLPClassifier

Мрежата по-долу е дефинирана с:

  • Решател на Адам
  • Функция за активиране на ReLU
  • Алфа = 0.0001
  • Размер на партидата от 150
  • Два скрити слоя съответно със 200 и 100 неврона
model_dnn = make_pipeline(
    preprocess,
    MLPClassifier(solver='adam',
                  alpha=0.0001,
                  activation='relu',
                    batch_size=150,
                    hidden_layer_sizes=(200, 100),
                    random_state=1))

Можете да промените броя на слоевете, за да подобрите модела.

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

DNN регресионен резултат: 0.821253

LIME: Доверете се на своя модел

След като вече имате добър модел, ви е нужен начин да му се доверите. Алгоритмите за машинно обучение, особено случайните гори и невронните мрежи, са известни като модели на черна кутия: те работят, но никой не може да разбере защо.

Трима изследователи са създали инструмент, който показва как компютърът достига до прогноза. Тяхната статия е... „Защо да ти вярвам?“, а публикуваният от тях алгоритъм се нарича Локално интерпретируеми моделно-агностични обяснения (LIME).

Вземете пример. Понякога не знаете дали може да се вярва на прогноза, получена от машинно обучение. Лекарят не може да приеме диагноза само защото е била поставена от компютър, и е необходимо да знаете дали даден модел е надежден, преди да го пуснете в производство.

Представете си, че можете да разберете защо който и да е класификатор е направил дадено предсказание, дори за модели, толкова сложни като невронни мрежи, случайни гори или SVM с произволно ядро. Става много по-лесно да се доверите на предсказание, когато причините за него са видими, и също толкова по-лесно е да решите кога на даден модел не трябва да се има доверие. LIME ви казва кои характеристики са довели до решението на класификатора.

Подготовка на данните

Има няколко неща, които трябва да промените, за да стартирате LIME с PythonПърво, инсталирайте lime в терминала с командата pip install lime.

Lime използва обект LimeTabularExplainer, за да апроксимира модела локално. Този обект изисква:

  • набор от данни в numpy формат
  • Името на функциите: имена_на_функции
  • Името на класовете: class_names
  • Индексът на колоната на категорийните признаци: категорични_характеристики
  • Името на групата за всяка категорична характеристика: categorical_names

Създайте влаковия комплект NumPy

Можете да копирате и конвертирате df_train от pandas в NumPy много лесно.

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

Вземете името на класа

Етикетът е достъпен чрез unique(). Трябва да видите:

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

Индексиране на колоните с категорични характеристики

Използвайте метода, който научихте по-рано, за да получите името на всяка група. Кодирате етикета с LabelEncoder и повтаряте операцията за всяка категорична характеристика.

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

След като наборът от данни е готов, можете да изградите различните набори от данни, показани в примерите за обучение на Scikit по-долу. Данните се трансформират извън конвейера тук, за да се избегнат грешки с LIME: наборът за обучение, предаден на LimeTabularExplainer, трябва да бъде масив NumPy без низове, а методът по-горе вече е създал такъв.

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)

Можете да направите тръбопровода с оптималните параметри, намерени от 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))])

Получавате предупреждение. То обяснява, че не е необходимо да създавате енкодер за етикети преди конвейера. Ако не използвате LIME, методът от първата част на този урок за машинно обучение със Scikit-learn е подходящ. В противен случай, запазете този подход: първо създайте кодиран набор от данни, след което приложете енкодера one-hot вътре в конвейера.

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)

Преди да пуснете LIME в действие, създайте масив в NumPy, съдържащ характеристиките на неправилно класифицираните редове. Можете да използвате този списък по-късно, за да добиете представа какво е подвело класификатора.

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)

След това създавате ламбда функция, която извлича прогнозата от модела за нови данни. Ще ви е необходима скоро.

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

Конвертирате рамката от данни на pandas в масив 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)

Сега изберете произволно домакинство от тестовия набор и вижте както прогнозата, така и как компютърът е стигнал до нея.

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

Можете да използвате обяснителя с explain_instance, за да проверите обосновката на модела. Диаграмата, която той рендира, е показана по-долу.

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

Обяснителна диаграма на LIME, показваща приноса на характеристиките за правилно прогнозирано домакинство

Класификаторът е предвидил правилно дохода на това домакинство: доходът наистина е над 50 000.

Първото нещо, което трябва да се отбележи, е, че класификаторът не е много сигурен в себе си. Той прогнозира доход над 50 000 с вероятност от 64%, като 64% ​​се определят от капиталова печалба и семейно положение. Синият цвят допринася отрицателно за положителния клас, а оранжевата линия - положително.

Класификаторът е колеблив, защото капиталовата печалба на това домакинство е нула, докато капиталовата печалба обикновено е добър показател за богатство. Домакинствата също така работят по-малко от 40 часа седмично. Възрастта, професията и полът имат положителен принос.

Ако семейното положение беше неженен/неомъжена, класификаторът би предвидил доход под 50 000 (0.64 – 0.18 = 0.46).

Сега опитайте с друго домакинство, което е класифицирано погрешно. Таблицата с обяснения за него следва кода.

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)

Обяснителна таблица на LIME за домакинството, което класификаторът е обозначил неправилно

Класификаторът е предвидил доход под 50 000, което е грешно. Това домакинство е необичайно: то няма нито капиталова печалба, нито капиталова загуба, лицето е разведено, на близо 60 години и образовано, т.е. education_num > 12. Следвайки общата закономерност, класификаторът е поставил домакинството под 50 000.

Поиграйте си с LIME и ще забележите множество груби грешки в класификатора. GitHub хранилището на автора на библиотеката съдържа допълнителна документация за класификация на изображения и текст.

Справочник на командите Scikit-learn

По-долу е даден списък с полезни команди, които се отнасят за scikit-learn версия 0.20 и по-нова.

Task Функция или клас
Създаване на набор от данни за обучение/тест train_test_split
Изградете тръбопровод
Изберете колоните и приложете трансформацията създаване_на_колона_трансформатор
Вид трансформация
Стандартизирайте StandardScaler
Мащабиране от мин. до макс. MinMaxScaler
Нормализиране Нормализатор
Вмъкване на липсващи стойности SimpleImputer
Преобразувайте категорично OneHotEncoder
Нагласете и трансформирайте данните fit_transform
Направи тръбопровода make_pipeline
Основен модел
Логистична регресия Логистична регресия
XGBoost XGB Класификатор
Невронна мрежа MLPClassifier
Търсене в мрежата GridSearchCV
Случайно търсене RandomizedSearchCV

Въпроси и Отговори

Инсталирайте текущата стабилна версия с pip install -U scikit-learn или conda install -c conda-forge scikit-learn. Разработчиковата версия, използвана в горните стъпки, беше необходима само през 2018 г., когато make_column_transformer не беше издаден; сега тя се предлага във всяка стабилна версия.

fit изучава параметри като средна стойност на колоната и стандартно отклонение. transform ги прилага към данните. fit_transform прави и двете с едно извикване и трябва да докосва само обучаващия набор, никога тестовия набор.

Поставянето на скалер или енкодер върху целия набор от данни позволява статистическите данни от тестовия набор да достигнат до модела. Конвейер пренастройва всеки трансформатор във всяка кръстосана валидационна гънка, така че задържаните редове остават невидими до момента, в който бъдат оценени.

Той автоматично допълва познати форми: блокове ColumnTransformer, решетки с параметри и имената с двойно подчертаване, които GridSearchCV очаква. Revпреглеждайте всичко, което е чувствително към версията, защото предложенията често възпроизвеждат по-стари имена на аргументи, като например sparse вместо sparse_output.

Не съвсем. Автоматизираните инструменти за търсене изследват мрежата по-бързо и отстраняват слабите кандидати, но вие все още избирате пространството за търсене, метриката за оценяване и схемата за кръстосана валидация. Тези решения са по-важни от самия алгоритъм за търсене.

Не. Обучението се изпълнява на процесора и се паралелизира между ядра чрез n_jobs. Експериментален слой на Array API позволява на ограничен набор от оценки да приемат GPU масиви, но TensorFlow и подобни рамки остават опцията за GPU.

Да. Извикайте set_output(transform=”pandas”) на трансформатор или цял конвейер и резултатът запазва имената на колоните си, вместо да връща гол резултат. numpy масив, което прави изхода на ColumnTransformer много по-лесен за проверка.

Предайте class_weight=”balanced” на оценките, които го приемат, преобразувайте с придружаваща библиотека като imbalanced-learn и оценявайте с прецизност, извикване или F1 мярка, а не с обикновена точност.

Обобщете тази публикация с: