Учебное пособие по Scikit-Learn: установка и примеры Scikit-Learn
⚡ Умное резюме
Scikit-learn — это проект с открытым исходным кодом. Python Библиотека, охватывающая предварительную обработку, классификацию, регрессию, кластеризацию и выбор модели, объединена единым согласованным интерфейсом оценщика, что делает весь рабочий процесс машинного обучения коротким, понятным и воспроизводимым — от исходных данных до прогнозов с оценкой.

Что такое Scikit-learn?
Scikit учиться это открытый исходный код Python библиотека для машинного обучения илиОн поддерживает хорошо зарекомендовавшие себя алгоритмы, такие как KNN, градиентный бустинг, случайный лес и SVM, и построен на основе NumPy и SciPy. Scikit-learn широко используется в соревнованиях Kaggle, а также в ведущих технологических компаниях. Он охватывает предварительную обработку данных, снижение размерности, классификацию, регрессию, кластеризацию и выбор модели.
Библиотека 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, обратитесь к следующему разделу. Как скачать и установить TensorFlow.
На момент написания этого руководства разработчики scikit выпустили версию для разработчиков, в которой были исправлены проблемы, присутствовавшие в текущей версии, поэтому в описании ниже используется именно эта сборка для разработчиков. На современной чистой машине текущая стабильная версия уже содержит все используемые здесь преобразователи, и pip install -U scikit-learn достаточно.
Как установить scikit-learn в среде Conda
Если вы установили 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 разделено на две части:
- Машинное обучение с scikit-learn
- Как доверить свою модель с LIME
В первой части подробно описывается, как построить конвейер обработки данных, создать модель и настроить гиперпараметры, а во второй части рассматривается интерпретация модели.
Шаг 1) Импортируйте данные
В этом обучающем руководстве по Scikit-learn вы будете использовать набор данных переписи взрослого населения.
В приведенном ниже коде файл считывается непосредственно из репозитория машинного обучения UCI, поэтому ручная загрузка не требуется. Если вас интересуют описательные статистические данные, стоит обратить внимание на инструменты Dive и Overview. См. этот учебник Чтобы узнать больше о погружениях и обзоре.
Импортируйте набор данных с помощью pandas. Обратите внимание, что необходимо преобразовать непрерывные переменные в формат с плавающей запятой.
Этот набор данных включает восемь категориальных переменных, перечисленных в CATE_FEATURES:
- рабочий класс
- образование
- брачный
- оккупация
- отношения
- раса
- секс
- родная страна
Он также включает шесть непрерывных переменных, перечисленных в CONTI_FEATURES:
- возраст
- фнлвгт
- образование_номер
- прирост капитала
- капитал_убыток
- часы_неделя
Здесь списки заполняются вручную, чтобы у вас было более ясное представление о том, какие столбцы используются. Более быстрый способ составить список категориальных или непрерывных столбцов:
## 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() для фрейма возвращает сводную статистику по шести непрерывным столбцам:
| возраст | фнлвгт | образование_номер | прирост капитала | капитал_убыток | часы_неделя | |
|---|---|---|---|---|---|---|
| считать | 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 столбцов: группы, полученные методом one-hot кодирования, плюс шесть непрерывных признаков.
Библиотека Scikit-learn может выполнить преобразование в два этапа:
- Преобразуйте строку в идентификатор. State-gov становится ID 1, Self-emp-not-inc — ID 2 и так далее. LabelEncoder сделает это за вас.
- Преобразуйте каждый идентификатор в новый столбец. Набор данных содержит 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
Перед передачей данных в логистический классификатор конвейер выполняет две операции:
- Стандартизируйте переменную: StandardScaler()
- Преобразуйте категориальные функции: 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 раз. Если значение перекрестной проверки установлено равным 10, модель обучается и оценивается десять раз. В каждом раунде классификатор обучается на девяти случайно выбранных фолдах, а десятый фолд используется для оценки.
Поиск по сетке
Каждый классификатор имеет гиперпараметры для настройки. Вы можете пробовать значения по одному или задать сетку параметров. В документации scikit-learn перечислены все параметры, которые принимает логистический классификатор. Для ускорения обучения в этом примере настраивается только параметр C, который управляет регуляризацией. Он должен быть положительным, и небольшое значение дает больший вес регуляризатору.
Вы используете объект GridSearchCV, который принимает словарь гиперпараметров для настройки. Перечислите каждый гиперпараметр, а затем значения, которые вы хотите попробовать. Для настройки C вы пишете:
- 'logisticregression__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)
)
Вы можете улучшить перекрестную проверку с помощью стратифицированной K-складчатой перекрестной проверки. Здесь используются только три складки для ускорения вычислений, но с некоторым ухудшением качества; для лучших результатов увеличьте их количество до 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("лучших parameter", random_search.best_params_) print("best logistic regression from grid search: %f" % random_search.best_estimator_.score(X_test, y_test))
лучших 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 формат
- Название функций: Feature_names
- Название классов: class_names
- Индекс столбца категориальных признаков: categorical_features
- Название группы для каждого категориального признака: categorical_names
Создайте набор поездов на основе NumPy.
Вы можете очень легко скопировать и преобразовать df_train из pandas в NumPy.
df_train.head(5)
# Create numpy data
df_lime = df_train
df_lime.head(3)
Получить имя класса
Доступ к метке осуществляется через функцию unique(). Вы должны увидеть:
- '<= 50 КБ'
- '> 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-learn ниже. Преобразование данных происходит вне конвейера, чтобы избежать ошибок с 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]])
Вы преобразуете DataFrame 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 с модулем explain_instance, чтобы изучить логику работы модели. Диаграмма, которую она отображает, показана ниже.
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
Ниже приведён список полезных команд, применимых к scikit-learn версии 0.20 и более поздних.
| Сложность задачи | Функция или класс |
|---|---|
| Создайте обучающий/тестовый набор данных. | train_test_split |
| Построить трубопровод | |
| Выберите столбцы и примените преобразование. | make_column_transformer |
| Тип преобразования | |
| Стандартизируй | StandardScaler |
| Масштабирование по принципу «минимум-максимум» | MinMaxScaler |
| Нормализовать | нормализ |
| Заполнение пропущенных значений | SimpleImputer |
| Преобразование категориального | OneHotEncoder |
| Сопоставьте и преобразуйте данные | fit_transform |
| Сделать трубопровод | make_pipeline |
| Базовая модель | |
| Логистическая регрессия | Логистическая регрессия |
| XGBoost | XGBКлассификатор |
| Нейронная сеть | MLPКлассификатор |
| Поиск по сетке | GridSearchCV |
| Рандомизированный поиск | Рандомизированный поискрезюме |



