Как эффективно обучать и оценивать несколько моделей

Вступление

Вопрос, с которым должны столкнуться все специалисты по обработке данных при работе над любым проектом машинного обучения:

Какая архитектура модели лучше всего подходит для моих данных?

Следует принять во внимание несколько теоретических соображений. Например, если ваши функции демонстрируют сильные линейные отношения с вашей зависимой переменной (целью), то линейная модель, вероятно, будет работать лучше всего. Если отношения нелинейны, то, возможно, лучше всего подойдет SVM или классификатор на основе экземпляров, такой как K-Nearest Neighbours. Если объяснимость имеет решающее значение, лучше всего подойдет древовидная модель. Кроме того, вам следует принять во внимание несколько практических соображений.

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

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

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

Подготовка данных

Прежде чем мы сможем начать какие-либо эксперименты, нам понадобится набор данных. Я предполагаю, что наша проблема - это задача контролируемой двоичной классификации. Для начала загрузим набор данных о раке груди из sklearn.

from sklearn.datasets import load_breast_cancer
X, y = data = load_breast_cancer(return_X_y=True)

Затем нам нужно разделить данные на обучающий и тестовый набор. Я выбрал соотношение 75/25.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=8675309)

Теперь мы готовы провести несколько экспериментов!

Проведите эксперименты

Мы собираемся быстро проверить соответствие 6 различных моделей на этом наборе данных. Я выбрал для тестирования:

  1. Логистическая регрессия: базовый линейный классификатор (от хорошего до исходного)
  2. Случайный лес: классификатор ансамблевого мешка
  3. K-Nearest Neighbours: классификатор на основе экземпляров
  4. Машины опорных векторов: классификатор максимальной маржи
  5. Гауссовский Наивный Байес: вероятностный классификатор
  6. XGBoost: классификатор ансамблевого (экстремального!) Повышения

Для проведения первоначальных экспериментов я использовал параметры по умолчанию для каждой модели. Чтобы получить более точное представление о подгонке для каждой из этих моделей, необходимо изменить параметры по умолчанию; однако для целей этой статьи общая идея становится более ясной, если не настраивать каждую модель.

from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.naive_bayes import GaussianNB
from xgboost import XGBClassifier
from sklearn import model_selection
from sklearn.utils import class_weight
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
import numpy as np
import pandas as pd
def run_exps(X_train: pd.DataFrame , y_train: pd.DataFrame, X_test: pd.DataFrame, y_test: pd.DataFrame) -> pd.DataFrame:
    '''
    Lightweight script to test many models and find winners
:param X_train: training split
    :param y_train: training target vector
    :param X_test: test split
    :param y_test: test target vector
    :return: DataFrame of predictions
    '''
    
    dfs = []
models = [
          ('LogReg', LogisticRegression()), 
          ('RF', RandomForestClassifier()),
          ('KNN', KNeighborsClassifier()),
          ('SVM', SVC()), 
          ('GNB', GaussianNB()),
          ('XGB', XGBClassifier())
        ]
results = []
    names = []
    scoring = ['accuracy', 'precision_weighted', 'recall_weighted', 'f1_weighted', 'roc_auc']
    target_names = ['malignant', 'benign']
for name, model in models:
        kfold = model_selection.KFold(n_splits=5, shuffle=True, random_state=90210)
        cv_results = model_selection.cross_validate(model, X_train, y_train, cv=kfold, scoring=scoring)
        clf = model.fit(X_train, y_train)
        y_pred = clf.predict(X_test)
        print(name)
        print(classification_report(y_test, y_pred, target_names=target_names))
results.append(cv_results)
        names.append(name)
this_df = pd.DataFrame(cv_results)
        this_df['model'] = name
        dfs.append(this_df)
final = pd.concat(dfs, ignore_index=True)
return final

В этом скрипте есть что распаковать. Во-первых, мы создаем переменную dfs для хранения всех наборов данных, которые будут созданы в результате применения 5-кратной перекрестной проверки на обучающем наборе.

Затем models в списке кортежей, содержащих имя и класс для каждого тестируемого классификатора. После этого мы просматриваем этот список и запускаем 5-кратную перекрестную проверку. Результаты каждого запуска записываются в фрейм данных pandas, который мы добавляем в список dfs. Следует отметить, что записанные здесь показатели являются показателями средневзвешенного для обоих классов. Это не сработает для любого несбалансированного набора данных, поскольку производительность в классе большинства будет затмевать класс меньшинства. При желании можно настроить приведенный ниже сценарий, чтобы записывать только показатели для интересующих классов!

Для дальнейшего облегчения оценки на экран выводится отчет о классификации испытательного набора. Наконец, мы объединяем и возвращаем все наши результаты.

Оцените результаты

Чтобы завершить наш анализ, мы собираемся проанализировать данные в final фрейме данных, возвращенном из run_exps() скрипта.

Чтобы получить более точные оценки распределения показателей из каждой модели, я провожу эмпирическую загрузку на 30 выборках. Кроме того, я разделяю данные на два вида: показатели производительности и показатели времени соответствия. Это достигается в следующем блоке кода.

bootstraps = []
for model in list(set(final.model.values)):
    model_df = final.loc[final.model == model]
    bootstrap = model_df.sample(n=30, replace=True)
    bootstraps.append(bootstrap)
        
bootstrap_df = pd.concat(bootstraps, ignore_index=True)
results_long = pd.melt(bootstrap_df,id_vars=['model'],var_name='metrics', value_name='values')
time_metrics = ['fit_time','score_time'] # fit time metrics
## PERFORMANCE METRICS
results_long_nofit = results_long.loc[~results_long['metrics'].isin(time_metrics)] # get df without fit data
results_long_nofit = results_long_nofit.sort_values(by='values')
## TIME METRICS
results_long_fit = results_long.loc[results_long['metrics'].isin(time_metrics)] # df with fit data
results_long_fit = results_long_fit.sort_values(by='values')

Теперь у нас есть надежные данные для построения и анализа. Во-первых, давайте построим наши показатели эффективности на основе пятикратной перекрестной проверки.

import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(20, 12))
sns.set(font_scale=2.5)
g = sns.boxplot(x="model", y="values", hue="metrics", data=results_long_nofit, palette="Set3")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.title('Comparison of Model by Classification Metric')
plt.savefig('./benchmark_models_performance.png',dpi=300)

Сразу становится ясно, что SVM довольно плохо соответствуют нашим данным по всем показателям и что модели дерева решений ансамбля (Random Forest и XGBoost) очень хорошо соответствуют данным.

Как насчет тренировок и подсчета очков?

plt.figure(figsize=(20, 12))
sns.set(font_scale=2.5)
g = sns.boxplot(x="model", y="values", hue="metrics", data=results_long_fit, palette="Set3")
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.title('Comparison of Model by Fit and Score Time')
plt.savefig('./benchmark_models_time.png',dpi=300)

Вау, SVM просто не может передохнуть! Худшая модель и медленно обучается / набирает очки!

Интересно отметить, что XGBoost, несомненно, был самым медленным в обучении; однако это был лучший результат. Обычно мы видим компромисс между производительностью и временем обучения. Рассмотрим нейронные сети, которые требуют большого количества данных и времени на обучение, но которые в целом работают очень хорошо.

RandomForest, будучи относительно медленным по сравнению с KNN, GNB и LogReg, имел вторую лучшую производительность. Если бы я продолжал совершенствовать модели, я бы, вероятно, сосредоточил большую часть своих усилий на RandomForest, потому что он работал почти так же, как XGBoost (их 95% доверительные интервалы могут перекрываться!), но тренироваться почти в 4 раза быстрее!

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

metrics = list(set(results_long_nofit.metrics.values))
bootstrap_df.groupby(['model'])[metrics].agg([np.std, np.mean])

time_metrics = list(set(results_long_fit.metrics.values))
bootstrap_df.groupby(['model'])[time_metrics].agg([np.std, np.mean])

Заключение

Теперь у вас есть инструменты для быстрого тестирования множества различных моделей на ваших данных и определения наиболее подходящей архитектуры.

Я не могу не подчеркнуть, что такие сравнения не являются исчерпывающими!

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

использованная литература

Структуры данных для статистических вычислений в Python, МакКинни, Труды 9-й конференции Python в науке, том 445, 2010 г.

@software{reback2020pandas,
    author       = {The pandas development team},
    title        = {pandas-dev/pandas: Pandas},
    month        = feb,
    year         = 2020,
    publisher    = {Zenodo},
    version      = {latest},
    doi          = {10.5281/zenodo.3509134},
    url          = {https://doi.org/10.5281/zenodo.3509134}
}

Харрис, К.Р., Миллман, К.Дж., ван дер Уолт, С.Дж. и другие. Программирование массивов с помощью NumPy. Nature 585, 357–362 (2020). DOI: 10.1038 / s41586–020–2649–2.

Scikit-learn: машинное обучение в Python, Педрегоса и др., JMLR 12, стр. 2825–2830, 2011.

J. Д. Хантер, «Matplotlib: 2D-графическая среда, Вычисления в науке и технике, т. 9, вып. 3. С. 90–95, 2007 ».

Васком, М. Л., (2021). seaborn: визуализация статистических данных. Журнал открытого программного обеспечения, 6 (60), 3021, https://doi.org/10.21105/joss.03021

Чен, Т., и Гестрин, К. (2016). XGBoost: масштабируемая система повышения качества дерева. В Материалы 22-й Международной конференции ACM SIGKDD по открытию знаний и интеллектуальному анализу данных (стр. 785–794). Нью-Йорк, Нью-Йорк, США: ACM. Https://doi.org/10.1145/2939672.2939785