пятница, 6 января 2023 г.

Машинное обучение с библиотеками PyCaret и TROT : классификация, часть первая

Продолжаем осваивать автоматизированное машинное обучение с с библиотеками PyCaret и TROT, переходим к классификации и начнем с примера из книги "Tolios G. Simplifying Machine Learning with PyCaret: A Low-code Approach for Beginners and Experts"

Классификация является одной из основных задач обучения с учителем, целью которой является предсказание категориальной переменную или метки класса. Эта задача известна как бинарная классификация, когда есть только два класса (0 и 1), или многоклассовая классификация если классов больше. Одной из наиболее широко используемых моделей бинарной классификации является логистическая регрессия. Помимо логистической регрессии, существует множество других доступных моделей классификации, таких как дерево решений, K-ближайших соседей, линейный дискриминантный анализ и XGBoost. В этой статье мы разберем, как библиотека PyCaret может помочь в выборе оптимальной модели классификации. 

Начнем с импорта необходимых библиотек Python.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns
from pycaret.datasets import get_data
from pycaret.classification import *
mpl.rcParams['figure.dpi'] = 300

PyCaret включает множество наборов данных, которые подходят для задач машинного обучения. В этом проекте мы будем использовать Iris, один из самых популярных наборов данных, используемые для обучения и тестирования моделей классификации.
Классы набора данных: Iris Setosa, Iris Versicolor и Iris Virginica, которые являются видами растений рода Iris. Особенности класса включают длину чашелистика, ширину чашелистика, длину лепестка и ширину лепестка каждого растения. 
Мы используем функцию PyCaret get_data() для загрузки набора данных Iris в  DataFrame pandas. Затем выводим первые 5 строк, что эквивалентно выводу методу head Pandas.

data = get_data('iris') 

sepal_lengthsepal_widthpetal_lengthpetal_widthspecies
05.13.51.40.2Iris-setosa
14.93.01.40.2Iris-setosa
24.73.21.30.2Iris-setosa
34.63.11.50.2Iris-setosa
45.03.61.40.2Iris-setosa

Функция info() pandas позволяет нам изучить некоторую базовую информацию о наборе данных. Мы видим, что есть 150 строк и 5 столбцов. Все признаковые переменные  числовые, в то время как целевая переменная является категориальной, как и ожидалось. Типы данных выводятся автоматически, но при необходимости их можно изменить.

data.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 5 columns):
 #   Column        Non-Null Count  Dtype  
---  ------        --------------  -----  
 0   sepal_length  150 non-null    float64
 1   sepal_width   150 non-null    float64
 2   petal_length  150 non-null    float64
 3   petal_width   150 non-null    float64
 4   species       150 non-null    object 
dtypes: float64(4), object(1)
memory usage: 6.0+ KB

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

data['species'].value_counts().plot(kind='pie')
plt.ylabel('')




Круговые диаграммы позволяют легко визуализировать пропорции категориальных переменных. Как мы  видим, классы Iris распределены равномерно, каждый из них составляет 33,3% набора данных.
Это хорошо, так как большинство моделей классификации оптимально работают со сбалансированным классами. Если вы работаете с несбалансированными данными, вы можете обратиться к PyCaret документации, так как она поддерживает различные способы их устранения, такие как SMOTE техника.

Коробчатая диаграмма (boxplot) — это тип графика, который визуализирует распределение числовых переменных, так, чтобы можно было сравнивать категории. Коробчатые диаграммы показывают квартили каждой переменной, включая минимальные, Q1, медианы, Q3 и максимальные значения.  
Используем функцию Seaborn boxplot() для создания коробчатых диаграмм для всех признаков, с отдельным графиком для каждого класса целевой переменной. Очевидно, что каждый класс имеет свое распределение, особенно для лепестковых переменных. Более того, присутствуют некоторые выбросы, визуализированные в виде ромбовидных символов.

fig, axes = plt.subplots(2, 2, figsize = (12, 10))
for ax, col in zip(axes.flatten(), data.columns) :
    sns.boxplot(data = data, x = 'species', y = col, ax = ax)
    ax.set_xlabel('')


Коэффициент корреляции Пирсона — это мера корреляции между двумя числовыми
переменные. Он имеет значение от -1 до 1, где 1 указывает на идеальную линейность.
связь, а -1 указывает на обратную линейную зависимость. Функция data.corr()
возвращает значения корреляции между всеми числовыми переменными фрейма данных. Для большей наглядности используем функцию heatmap Seaborn для значений корреляции в виде матрицы с цветовой кодировкой. Очевидно, некоторые переменные имеют сильную корреляцию, например ширина лепестка и длина лепестка. С другой стороны, ширина чашелистика и длина лепестка имеет обратную линейную зависимость со значением корреляции -0,42.

plt.figure(figsize=(10, 8))
sns.heatmap(data.corr().round(decimals=2), annot=True)



Диаграммы рассеяния визуализируют взаимосвязь между числовыми переменными, помогая понять, связаны ли они. Используем функцию pairplot Seaborn для создания матрицы диаграмм рассеяния для всех пар признаков нашего набора данных с диагональю ядерных оценок плотности (kernel density estimate). Цветом выделяем класс. Очевидно, что некоторые переменные имеют сильную линейную зависимость,
как указывалось ранее коэффициентом корреляции Пирсона. Кроме того, мы можем видеть что Iris setosa четко отделен от двух других классов.


После завершения исследовательского анализа данных инициализируем PyCaret. Это делается с помощью функции setup(). 

Функция setup() инициализирует среду в pycaret и создает конвейер преобразования для подготовки данных для моделирования и развертывания. setup() необходимо вызывать

перед выполнением любой другой функции в pycaret. Он принимает два обязательных

параметра: DataFrame pandas и имя целевого столбца. Все остальные параметры

являются необязательными и используются для настройки конвейера предварительной обработки.


classf = setup(data = data, target = 'species', train_size = 0.8,
normalize = True, session_id = 3934,fold_shuffle=True)

Разберем параметры, заданные в примере :


data - типа DataFrame Pandas


Набор данных с формой (n_samples, n_features), где n_samples — количество выборок, а n_features — количество признаков. Если данные не являются DataFrame pandas, они преобразуются в DataFrame с использованием имен столбцов по умолчанию.


target: int, str или последовательность, по умолчанию = -1


Если int или str, соответственно индекс или имя целевого столбца в данных. Значение

по умолчанию выбирает последний столбец в наборе данных. Если

последовательность, она должна иметь форму (n_samples). Цель может быть либо

бинарной, либо мультиклассовой.


train_size: с плавающей запятой, по умолчанию = 0,7


Доля набора данных, которая будет использоваться для обучения и проверки. Должно

быть между 0,0 и 1,0.


session_id: целое, по умолчанию = None


Предоставляет начальное значение внутреннему генератору случайных чисел,

эквивалентно «random_state» в scikit-learn. Если None, генерируется псевдослучайное

число. Используется для последующей воспроизводимости всего эксперимента.


normalize: bool, по умолчанию = False

Если установлено значение True, он преобразует объекты, масштабируя их до

заданного диапазона. Тип масштабирования определяется параметром

normalize_method.


После запуска функции setup() распечатывается таблица с ее параметрами

и настройками.


Функция compare_models() упрощает процесс выбора наилучшей модели классификации путем обучения всех доступных моделей и печати результатов. Первый столбец — это название модели, а остальные — различные показатели производительности. Модели отсортированы на основе выбранной метрики, в данном случае это точность. Другими словами, точность — это процент правильных прогнозов, которые модель классификации удалось сделать.  По этой метрике
модель линейного дискриминантного анализа показала лучшие результаты с точностью
98,33%.

compare_models(sort = 'Accuracy')

ModelAccuracyAUCRecallPrec.F1KappaMCCTT (Sec)
ldaLinear Discriminant Analysis0.98330.99790.98330.98670.98310.97500.97690.0100
qdaQuadratic Discriminant Analysis0.97501.00000.97500.98220.97380.96250.96680.0110
nbNaive Bayes0.95830.99150.96000.96670.95780.93740.94200.0120
rfRandom Forest Classifier0.95830.99220.96000.96670.95780.93740.94200.1440
etExtra Trees Classifier0.95830.99790.96000.96670.95780.93740.94200.1280
xgboostExtreme Gradient Boosting0.95830.97980.96000.96670.95780.93740.94200.0430
lrLogistic Regression0.95001.00000.95170.96000.94930.92490.93040.7970
knnK Neighbors Classifier0.95000.98730.95170.95670.94950.92450.92820.0140
lightgbmLight Gradient Boosting Machine0.95000.98750.95170.95670.94960.92490.92860.1880
dtDecision Tree Classifier0.94170.95620.94330.95220.94030.91240.91850.0110
gbcGradient Boosting Classifier0.94170.98650.94330.95560.94000.91240.92030.1230
adaAda Boost Classifier0.93330.98820.93500.95240.92870.89990.91130.0530
catboostCatBoost Classifier0.93330.99790.93500.94330.93270.89990.90540.4420
svmSVM - Linear Kernel0.90830.00000.91000.92330.90600.86200.87110.0100
ridgeRidge Classifier0.81670.00000.81110.85900.80120.72280.75250.0120

Теперь мы собираемся использовать функцию create_model() для обучения модели линейного дискриминанта, так как она показала лучшие результаты при сравнении с другими моделями. В ней для оценки точности используется стратифицированная 
k-кратная перекрестная проверка. Набор данных последовательно разделены на k подвыборок, при этом одна подвыборка сохраняется для проверки, а остальные используются для обучения модели. Разница между стратифицированной k-кратной и стандартной k-кратной заключается в том, что подвыборки стратифицируются для сохранения процент каждого класса, в данном случае 33,3% для каждого вида ириса.
После завершения стратифицированной k-кратной перекрестной проверки выводятся результирующие показатели для каждого страта , включая значения среднего и стандартного отклонения.

model = create_model('lda')

AccuracyAUCRecallPrec.F1KappaMCC
01.00001.00001.00001.00001.00001.00001.0000
11.00001.00001.00001.00001.00001.00001.0000
21.00001.00001.00001.00001.00001.00001.0000
31.00001.00001.00001.00001.00001.00001.0000
41.00001.00001.00001.00001.00001.00001.0000
50.91671.00000.91670.93330.91530.87500.8843
60.91670.97920.91670.93330.91530.87500.8843
71.00001.00001.00001.00001.00001.00001.0000
81.00001.00001.00001.00001.00001.00001.0000
91.00001.00001.00001.00001.00001.00001.0000
Mean0.98330.99790.98330.98670.98310.97500.9769
SD0.03330.00630.03330.02670.03390.05000.0463

Модель линейного дискриминантного анализа имеет превосходную производительность с точностью 98,33%. К сожалению, не все модели работали так же хорошо, например CatBoost показала точность 93,33%. Функция tune_model() может улучшить
производительность модели классификации путем нахождения оптимальной комбинации его гиперпараметров, то есть произвести настройку гиперпараметров. По умолчанию, эта функция использует метод рандомизированного поиска библиотеки scikit-learn, но есть различные другие альтернативы, в том числе библиотеки 
scikit-optimize и optuna. Выбираем библиотеку scikit-optimize, которая использует байесовскую оптимизацию, моделируя пространство поиска гиперпараметров, таким образом находя оптимальную комбинацию.
Сначала обучаем модель классификатора CatBoost на наборе данных Iris. После этого
мы передаем модель функции tune_model() вместе со словарем, включающим
гиперпараметры нашего предпочтения. Кроме того, используем NumPy arange()
для установки диапазона значений для каждого гиперпараметра.  После настройки модели, точность увеличилась до 95,83%. Это не большое улучшение, так что
мы могли бы попробовать поэкспериментировать с различными гиперпараметрами или диапазонами значений для каждый из них. Следует также отметить, что настройка гиперпараметров требует значительных вычислительных ресурсов и трудоемкий процесс, так как модель должна быть обучена много раз, чтобы получить оптимальный результат. Если это занимает слишком много времени на компьютере, можно уменьшить количества итераций путем установки меньшего значения параметра n_iter. Наконец, каждая модель классификации имеет свой набор гиперпараметров, которые могут быть настроены, поэтому  придется обратиться к его документации, чтобы выбрать их. 

model_cat = create_model('catboost', verbose = False)

params = {'iterations': np.arange(100, 1000, 100),
'max_depth': np.arange(1, 10),
'learning_rate': np.arange(0.01, 1, 0.01),
'random_strength': np.arange(0.1, 1.0, 0.1),
'l2_leaf_reg': np.arange(1, 100),
'border_count': np.arange(1, 256)}

tuned_model = tune_model(model_cat, optimize = 'Accuracy', fold = 10,
tuner_verbose = False, search_library = 'scikit-optimize',
custom_grid = params, n_iter = 50)

AccuracyAUCRecallPrec.F1KappaMCC
00.91670.97920.91670.93330.91530.87500.8843
11.00001.00001.00001.00001.00001.00001.0000
20.91671.00000.91670.93330.91530.87500.8843
30.91670.98960.91670.93330.91530.87500.8843
41.00001.00001.00001.00001.00001.00001.0000
51.00001.00001.00001.00001.00001.00001.0000
60.91670.97920.91670.93330.91530.87500.8843
70.91671.00000.93330.93330.91670.87370.8830
81.00001.00001.00001.00001.00001.00001.0000
91.00001.00001.00001.00001.00001.00001.0000
Mean0.95830.99480.96000.96670.95780.93740.9420
SD0.04170.00840.04030.03330.04220.06260.0580

Функция predict_model() делает прогнозы на тестовом наборе данных, который был создан во время инициализации среды PyCaret, таким образом
позволяя нам оценить точность модели на данных, которые не использовались для обучения модели. Как мы видим, модель линейного дискриминантного анализа имеет точность 96,67% на тестовом наборе данных, что очень близко к результатам перекрестной проверки. Более того, функция возвращает DataFrame со столбцами набора данных, а также предсказанный класс для каждого экземпляра.

predictions = predict_model(model)
predictions.head(10)

ModelAccuracyAUCRecallPrec.F1KappaMCC
0Linear Discriminant Analysis0.96671.00000.95240.96920.96610.94840.9501

sepal_lengthsepal_widthpetal_lengthpetal_widthspeciesLabelScore
0-0.0856372.256249-1.480436-1.354654Iris-setosaIris-setosa1.000
11.1989250.1149930.8830091.120194Iris-virginicaIris-virginica0.999
2-0.319194-1.3125110.038922-0.182357Iris-versicolorIris-versicolor1.000
30.6150330.3529110.8267371.380705Iris-virginicaIris-virginica1.000
4-0.4359732.732084-1.367891-1.354654Iris-setosaIris-setosa1.000
5-1.136643-0.122924-1.367891-1.354654Iris-setosaIris-setosa1.000
6-0.7863081.066663-1.311618-1.354654Iris-setosaIris-setosa1.000
7-0.9030861.780415-1.255346-1.354654Iris-setosaIris-setosa1.000
8-0.085637-1.0745940.095194-0.052102Iris-versicolorIris-versicolor1.000
9-0.9030861.542498-1.311618-1.094143Iris-setosaIris-setosa1.000


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

plot_model(model, 'confusion_matrix')


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















Комментариев нет:

Отправить комментарий