Учебники

17) Учебник Scikit-Learn

Что такое Scikit-Learn?

Scikit-learn — это библиотека Python с открытым исходным кодом для машинного обучения. Библиотека поддерживает современные алгоритмы, такие как KNN, XGBoost, случайный лес, SVM и другие. Он построен на вершине Numpy. Scikit-learn широко используется в соревнованиях по борьбе, а также в известных технологических компаниях. Scikit-learn помогает в предварительной обработке, уменьшении размерности (выбор параметров), классификации, регрессии, кластеризации и выборе моделей.

Scikit-learn имеет лучшую документацию из всех библиотек с открытым исходным кодом. Он предоставляет вам интерактивную диаграмму по адресу https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html .

Scikit-learn не очень сложен в использовании и дает отличные результаты. Тем не менее, Scikit Learn не поддерживает параллельные вычисления. С ним можно запустить алгоритм глубокого обучения, но это не оптимальное решение, особенно если вы знаете, как использовать TensorFlow.

В этом уроке вы узнаете.

Скачать и установить 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, см. Https://www.guru99.com/download-install-tensorflow.html.

Недавно разработчики Scikit выпустили версию для разработчиков, которая решает общую проблему, с которой сталкивается текущая версия. Мы посчитали более удобным использовать версию разработчика вместо текущей версии.

Если вы установили scikit-learn в среде conda, пожалуйста, следуйте инструкциям по обновлению до версии 0.20.

Шаг 1) Активировать среду tenorflow

source activate hello-tf

Шаг 2) Удалите scikit lean с помощью команды 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 Visual C ++ 14. Вы можете получить его здесь

Машинное обучение с scikit-learn

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

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

В первой части подробно рассказывается, как построить конвейер, создать модель и настроить гиперпараметры, а во второй части представлено современное состояние с точки зрения выбора модели.

Шаг 1) Импортируйте данные

В этом уроке вы будете использовать набор данных для взрослых. Если вы хотите узнать больше об описательной статистике, пожалуйста, используйте инструменты Dive и Overview. Обратитесь к этому руководству, чтобы узнать больше о погружении и обзоре.

Вы импортируете набор данных с помощью Pandas. Обратите внимание, что вам необходимо преобразовать тип непрерывных переменных в формат с плавающей точкой.

Этот набор данных включает в себя восемь категориальных переменных:

Категориальные переменные перечислены в CATE_FEATURES

  • workclass
  • образование
  • супружеский
  • род занятий
  • отношения
  • гонка
  • секс
  • родная страна

кроме того, шесть непрерывных переменных:

Непрерывные переменные перечислены в CONTI_FEATURES

  • возраст
  • fnlwgt
  • education_num
  • прирост капитала
  • capital_loss
  • hours_week

Обратите внимание, что мы заполняем список вручную, чтобы вы лучше понимали, какие столбцы мы используем. Более быстрый способ составить список категориальных или непрерывных списков — использовать:

## 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()
возраст fnlwgt education_num прирост капитала capital_loss hours_week
подсчитывать 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 группу, как показано выше. Например, функции рабочего класса имеют девять групп. Вы можете визуализировать названия групп с помощью следующих кодов

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 + 7 столбцов. Последние семь столбцов являются непрерывными элементами.

Scikit-Learn может позаботиться о преобразовании. Это делается в два этапа:

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

Шаг 2) Создайте поезд / тестовый набор

Теперь, когда набор данных готов, мы можем разделить его на 80/20. 80 процентов для тренировочного набора и 20 процентов для тестового набора.

Вы можете использовать train_test_split. Первый аргумент — это dataframe — это особенности, а второй аргумент — это метка dataframe. Вы можете указать размер набора тестов с помощью 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) Постройте трубопровод

Конвейер облегчает снабжение модели согласованными данными. Идея заключается в том, чтобы поместить необработанные данные в «конвейер» для выполнения операций. Например, в текущем наборе данных вам необходимо стандартизировать непрерывные переменные и преобразовать категориальные данные. Обратите внимание, что вы можете выполнять любые операции внутри конвейера. Например, если у вас есть «NA» в наборе данных, вы можете заменить их средним или медианным. Вы также можете создавать новые переменные.

У вас есть выбор; Жесткий код двух процессов или создать конвейер. Первый выбор может привести к утечке данных и создать несоответствия во времени. Лучшим вариантом является использование конвейера.

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). В текущей версии невозможно выполнить кодировщик меток и один горячий кодировщик в конвейере. Это одна из причин, по которой мы решили использовать версию для разработчиков.

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

Вы можете проверить, работает ли конвейер с помощью fit_transform. Набор данных должен иметь следующую форму: 26048, 107

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

Преобразователь данных готов к использованию. Вы можете создать конвейер с помощью make_pipeline. Как только данные преобразованы, вы можете кормить логистическую регрессию.

model = make_pipeline(
    preprocess,
    LogisticRegression())

Обучение модели с scikit-learn является тривиальным. Вам нужно использовать соответствие объекта, которому предшествует конвейер, то есть модель. Вы можете напечатать точность с помощью объекта Score из библиотеки scikit-learn.

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

Наконец, вы можете предсказать классы с помощьюgnast_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, вы используете:

  • ‘logisticregression__C’: [0.1, 1.0, 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 с параметрами gri и 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)

Чтобы получить доступ к лучшим параметрам, вы используете 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 — это улучшение по сравнению со случайным лесом. Теоретические основы классификатора выходят за рамки этого урока. Имейте в виду, что XGBoost выиграл множество соревнований по борьбе. При среднем размере набора данных он может работать так же хорошо, как алгоритм глубокого обучения или даже лучше.

Классификатор сложно обучать, потому что он имеет большое количество параметров для настройки. Вы можете, конечно, использовать GridSearchCV, чтобы выбрать параметр для вас.

Вместо этого, давайте посмотрим, как использовать лучший способ найти оптимальные параметры. GridSearchCV может быть утомительным и очень долго тренироваться, если вы передадите много значений. Пространство поиска растет вместе с количеством параметров. Предпочтительным решением является использование RandomizedSearchCV. Этот метод состоит из случайного выбора значений каждого гиперпараметра после каждой итерации. Например, если классификатор обучен за 1000 итераций, то оцениваются 1000 комбинаций. Это работает более или менее как. GridSearchCV

Вам нужно импортировать xgboost. Если библиотека не установлена, используйте pip3 install xgboost или

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

В среде Jupyter

Следующий,

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

Следующий шаг включает в себя указание параметров для настройки. Вы можете обратиться к официальной документации, чтобы увидеть все параметры для настройки. Ради учебника вы выбираете только два гиперпараметра с двумя значениями каждый. 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)
)

Вы можете улучшить перекрестную проверку с помощью перекрестного средства проверки Straified K-Folds. Здесь вы строите только три раза, чтобы ускорить вычисления, но при этом снизить качество. Увеличьте это значение до 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
  • Два скрытых слоя с 100 и 50 нейронами соответственно
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

ЛАЙМ: доверяй своей модели

Now that you have a good model, you need a tool to trust it. Machine learning algorithm, especially random forest and neural network, are known to be blax-box algorithm. Say differently, it works but no one knows why.

Three researchers have come up with a great tool to see how the computer makes a prediction. The paper is called Why Should I Trust You?

They developed an algorithm named Local Interpretable Model-Agnostic Explanations (LIME).

Take an example:

sometimes you do not know if you can trust a machine-learning prediction:

A doctor, for example, cannot trust a diagnosis just because a computer said so. You also need to know if you can trust the model before putting it into production.

Imagine we can understand why any classifier is making a prediction even incredibly complicated models such as neural networks, random forests or svms with any kernel

will become more accessible to trust a prediction if we can understand the reasons behind it. From the example with the doctor, if the model told him which symptoms are essential you would trust it, it is also easier to figure out if you should not trust the model.

Lime can tell you what features affect the decisions of the classifier

Data Preparation

They are a couple of things you need to change to run LIME with python. First of all, you need to install lime in the terminal. You can use pip install lime

Lime makes use of LimeTabularExplainer object to approximate the model locally. This object requires:

  • a dataset in numpy format
  • The name of the features: feature_names
  • The name of the classes: class_names
  • The index of the column of the categorical features: categorical_features
  • The name of the group for each categorical features: categorical_names

Create numpy train set

You can copy and convert df_train from pandas to numpy very easily

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

Get the class name The label is accessible with the object unique(). You should see:

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

index of the column of the categorical features

You can use the method you lean before to get the name of the group. You encode the label with LabelEncoder. You repeat the operation on all the categorical features.

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

Теперь, когда набор данных готов, вы можете создать другой набор данных. Вы фактически трансформируете данные вне конвейера, чтобы избежать ошибок с LIME. Обучающий набор в LimeTabularExplainer должен быть пустым массивом без строки. При использовании описанного выше метода у вас уже есть конвертированный тренировочный набор данных.

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, вы можете использовать метод из первой части урока. В противном случае вы можете использовать этот метод, сначала создать набор закодированных данных, настроить получение горячего кодировщика внутри конвейера.

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 в действии, давайте создадим пустой массив с функциями неправильной классификации. Вы можете использовать этот список позже, чтобы понять, что вводит в заблуждение классификатор.

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

Вы конвертируете информационный фрейм панд в массив 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])

Вы можете использовать объяснитель с объяснением объяснения за моделью.

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

Мы видим, что классификатор правильно предсказал домохозяйство. Доход действительно выше 50 тысяч.

Первое, что мы можем сказать, это то, что классификатор не уверен в предсказанных вероятностях. Машина предсказывает, что доход семьи превышает 50 тыс. С вероятностью 64%. Эти 64% состоят из прироста капитала и брачных отношений. Синий цвет отрицательно влияет на положительный класс, а оранжевая линия — положительно.

Классификатор сбит с толку, потому что прирост капитала этого домохозяйства равен нулю, в то время как прирост капитала обычно является хорошим показателем благосостояния. Кроме того, домохозяйство работает менее 40 часов в неделю. Возраст, род занятий и пол положительно влияют на классификатор.

Если бы семейное положение было одиноким, классификатор прогнозировал бы доход ниже 50 тыс. (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)

Классификатор предсказал доход ниже 50 тыс., Хотя это не соответствует действительности. Это домашнее хозяйство кажется странным. У него нет ни прироста капитала, ни потери капитала. Он в разводе и ему 60 лет, и это образованные люди, то есть education_num> 12. Согласно общей схеме, это домашнее хозяйство должно, как объяснено классификатором, получать доход ниже 50 тыс.

Вы пытаетесь поиграть с LIME. Вы заметите грубые ошибки из классификатора.

Вы можете проверить GitHub владельца библиотеки. Они предоставляют дополнительную документацию для классификации изображений и текста.

Резюме

Ниже приведен список некоторых полезных команд с scikit learn version> = 0.20

создать набор данных поезда / теста

ученики разделились

Построить трубопровод

выберите столбец и примените преобразование

makecolumntransformer

тип трансформации

гостирован

StandardScaler

мин Макс

MinMaxScaler

Нормализовать

Normalizer

Вменяйте недостающее значение

Imputer

Преобразовать категоричный

OneHotEncoder

Подгонка и преобразование данных

fit_transform

Сделать трубопровод

make_pipeline

Базовая модель

логистическая регрессия

Логистическая регрессия

XGBoost

XGBClassifier

Нейронная сеть

MLPClassifier

Сетка поиска

GridSearchCV

Рандомизированный поиск

RandomizedSearchCV