Анализируемый датасет содержит размеченные сведения о том, купил ли товар определенный пользователь или нет. О пользователе известны такие данные как: возраст, пол и ориентировочная зарплата. О покупках данные представлены в виде: 1 - покупка совершена, 0 - нет. В данной статье попробуем обучить модель, прогнозирующую вероятность соверешения покупки, если нам известны данные о пользователе. Будут применены методы: метод ближайшего соседа (KNN, K nearest neighbor), метод опорных векторов (SVM, support vector machines) и ядра SVM (Kernal SVM) с подбором оптимальных гиперпараметров для модели.
1. Импортирование нужных библиотек и загрузка данных из датасета
4. Кодирование категориальных данных
5. Разделение датасета на тренировочный и тестовый блоки
6. Нормализация данных (масштабирование признаков (фич))
7. Метод K-ближайших соседей (K Nearest Neighbor(KNN)
# библиотека линейной алгебры
import numpy as np
# библиотека обработки данных
import pandas as pd
# библиотеки для визуализации
import seaborn as sns
import matplotlib.pyplot as plt
# библиотека обработки фич
from sklearn.preprocessing import StandardScaler
# библиотека для разделения данных на тренировочные и тестовые
from sklearn.model_selection import train_test_split
# библиотеки для классификации в моделях
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
# библиотека настройки гиперпараметров
from sklearn.model_selection import GridSearchCV
# библиотеки оценивания\метрики
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report,recall_score,precision_score
# библиотека для "раскраски" таблиц.
import jinja2
# если модуль не установлен, то выполнить команду ниже
# !pip install Jinja2
Загрузка данных из csv-файла.
df = pd.read_csv('data.csv')
# Смотрим размер таблицы.
print("Количество строк = ", df.shape[0], " \nКоличество столбцов = " , df.shape[1])
Количество строк = 400 Количество столбцов = 5
# Смотрим список столбцов.
df.columns
Index(['ID', 'Пол', 'Возраст', 'Зарплата', 'Покупка'], dtype='object')
# Смотрим информацию о столбцах и выводим несколько случайных строк с данными.
print(df.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 400 entries, 0 to 399 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Возраст 400 non-null int8 1 Зарплата 400 non-null int32 2 Покупка 400 non-null int8 3 Пол_м 400 non-null uint8 dtypes: int32(1), int8(2), uint8(1) memory usage: 2.9 KB None
Видим, что числовые столбцы определились неоптимальным типом (int64), поэтому при больших данных можно применить функцию оптимизации из этой статьи.
# Выводим несколько случайных строк с данными.
# Цвета "расскраски" см. тут https://matplotlib.org/stable/tutorials/colors/colormaps.html
# "расскраска" по столбцам
df.sample(10).style.background_gradient(axis=0, cmap='Spectral')
Возраст | Зарплата | Покупка | Пол_м | |
---|---|---|---|---|
171 | 41 | 60000 | 0 | 0 |
42 | 22 | 27000 | 0 | 0 |
107 | 45 | 45000 | 1 | 0 |
62 | 45 | 32000 | 1 | 1 |
47 | 33 | 28000 | 0 | 0 |
362 | 36 | 125000 | 1 | 1 |
377 | 32 | 135000 | 1 | 0 |
9 | 22 | 18000 | 0 | 1 |
76 | 20 | 36000 | 0 | 0 |
13 | 19 | 19000 | 0 | 1 |
Все поля числовые, кроме пола (имеем одну категорию Пол).
# анализируем основные показатели по числовым столбцам
# "расскраска" по строкам
df.describe().T.style.background_gradient(axis=1, cmap='Spectral')
count | mean | std | min | 25% | 50% | 75% | max | |
---|---|---|---|---|---|---|---|---|
ID | 400.000000 | 5058.500000 | 115.614301 | 4859.000000 | 4958.750000 | 5058.500000 | 5158.250000 | 5258.000000 |
Возраст | 400.000000 | 37.655000 | 10.482877 | 18.000000 | 29.750000 | 37.000000 | 46.000000 | 60.000000 |
Зарплата | 400.000000 | 69742.500000 | 34096.960282 | 15000.000000 | 43000.000000 | 70000.000000 | 88000.000000 | 150000.000000 |
Покупка | 400.000000 | 0.357500 | 0.479864 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 |
# проверяем на наличие дубликатов.
df.duplicated().sum()
0
# Анализ на пропущенные значения
df.isnull().sum()
ID 0 Пол 0 Возраст 0 Зарплата 0 Покупка 0 dtype: int64
В данном датасете нет пропущенных значений.
# Тепловая карта (heatmap)
plt.figure(figsize=(10, 5))
mask = np.triu(np.ones_like(df.corr(), dtype=bool))
sns.heatmap(df.corr(), mask = mask, annot=True, cmap='Spectral');
correlation = pd.DataFrame(df.corr().Покупка)
correlation
Покупка | |
---|---|
ID | 0.614944 |
Возраст | 0.622454 |
Зарплата | 0.362083 |
Покупка | 1.000000 |
Колонка с идентификатором пользователя уникальная для всех, поэтому не может участвовать в прогнозе и может быть удалена.
df.drop('ID', axis = 1, inplace=True)
mask = np.triu(np.ones_like(df.corr(), dtype=bool))
sns.heatmap(df.corr(), mask = mask, annot=True, cmap='Spectral');
# Смотрим количество покупок и "не покупок"
df['Покупка'].value_counts()
0 257 1 143 Name: Покупка, dtype: int64
# в виде гистограммы
plt.figure(figsize=(10, 3))
sns.countplot(x = "Покупка", data=df, palette="Spectral")
plt.show()
Визуализация и анализ цели в зависимости от пола
plt.figure(figsize=(10, 3))
sns.catplot(x='Покупка', col='Пол', kind='count', data=df,palette="Spectral");
plt.show()
<Figure size 720x216 with 0 Axes>
pd.crosstab(df.Пол, df.Покупка, normalize = "index" ).style.background_gradient(cmap='Spectral')
Покупка | 0 | 1 |
---|---|---|
Пол | ||
ж | 0.622549 | 0.377451 |
м | 0.663265 | 0.336735 |
На данном примере видно соотношение женщин и мужчик совершивших покупки (целевой критерий).
Распределение и визуализация значений возраста и ориентировочной зарплаты
# анализ распределения возраста
sns.histplot(df['Возраст'], kde=True, color='green', bins=30)
<AxesSubplot:xlabel='Возраст', ylabel='Count'>
# анализ распределения зарплаты
sns.histplot(df['Зарплата'], kde=True, color='blue', bins=30)
<AxesSubplot:xlabel='Зарплата', ylabel='Count'>
Смещение в сторону возрастной группы 35-40 лет, а по заработной плате - от 70 до 90 тысяч
Парный график возраста и зарплаты в соотнесении с целевым критерием (покупкой)
sns.pairplot(df , hue = 'Покупка', palette='Spectral', corner=True )
<seaborn.axisgrid.PairGrid at 0x146bce1a100>
Диаграмма разброса возраста и заработной платы по отношению к цели
sns.scatterplot(x = 'Возраст', y = 'Зарплата', data = df, hue = 'Покупка', palette ='Spectral')
<AxesSubplot:xlabel='Возраст', ylabel='Зарплата'>
Из парного графика и диаграммы разброса видно, что если масштабирование не выполняется, зарплата будет преобладать над возрастом, что приведет к смещению модели. Масштабирование фич будет выполнено после разделения модели на тренировочный и тестовый наборы.
Колонка Пол содержит категориальные значения, поэтому ее необходимо закодировать в числовые значения до построения модели.
При кодировании категориальных данных для каждого различного значения будет создана фиктивная числовая переменная для указанных колонок. Для этого применяем функцию pandas.get_dummies(), которая возвращает преобразованные данные, а также удаляет исходные (в зависимости от значения параметра drop_first).
df = pd.get_dummies(df, columns = ['Пол'], drop_first=True)
df.head()
Возраст | Зарплата | Покупка | Пол_м | |
---|---|---|---|---|
0 | 26 | 15000 | 0 | 1 |
1 | 26 | 15000 | 0 | 0 |
2 | 30 | 15000 | 0 | 1 |
3 | 31 | 15000 | 0 | 0 |
4 | 21 | 16000 | 0 | 0 |
Сохраняем отдельно целевые значения и отдельные данные без целевых значений.
X = df.drop(['Покупка'], axis = 1)
y = df['Покупка']
# разделение данных
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, random_state = 0)
X_train.shape
(300, 3)
Масштабирование признаков (фич) - это приведение всех значений всех признаков к "схожему масштабу". Это предотвращает преобладание одного признака над другим. В данном примере без масштабирования зарплата преобладает над возрастом.
Эта процедура делается после разделения датасета на тренировочный и тестовый наборы и применяется к тренировочному набору.
Для масштабирования признаков используем метод fit_transform() для тренировочных данных и метод transform() для тестовых.
fit_transform() : - применяется только к тренировочному набору данных. Он содержит два метода: fit() и transform(). Fit() произведет необходимые математические вычисления и получит средние значения и стандартные отклонение на базе входных данных. Это делается на тренировочных данных для того, чтобы модель не знала значения из тестового набора, т.к. предполагается, что тестовые данные - это как бы реальные данные для проверки работы модели. Метод transform() применит вычисленные значения к набору данных или преобразует данные в соответствии с моделью.
transform() :- Метод transform() применяется к тестовому набору данных, и тестовые данные трансформируются или масштабируются при помощи параметров модели, обученной на применении соответствующего метода на тренировочном датасете.
cols = X_train.columns
cols
Index(['Возраст', 'Зарплата', 'Пол_м'], dtype='object')
# Выполнение стандартизации при помощи sklearn.preprocessing.StandardScaler
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
X_train
array([[ 0.18361861, 0.2184173 , 1.05483461], [ 0.7564323 , -1.14483856, -0.94801592], ... [ 0.85190124, -0.64102661, -0.94801592], [-0.48466403, -1.26338255, -0.94801592], [ 1.71112178, -0.31503065, 1.05483461]])
X_test
array([[ 0.27908756, -0.55211862, 1.05483461], [-0.77107087, 0.54441326, 1.05483461], [ 2.09299757, 1.1074972 , -0.94801592], .... [ 0.85190124, 2.17439309, -0.94801592], [-0.86653982, 0.27768929, 1.05483461], [-0.19825718, 1.64094515, -0.94801592]])
# Создание датасетов на базе полученных данных.
X_train = pd.DataFrame(X_train,columns=cols)
X_test = pd.DataFrame(X_test,columns=cols)
# результат нормализации даных.
X_train
Возраст | Зарплата | Пол_м | |
---|---|---|---|
0 | 0.183619 | 0.218417 | 1.054835 |
1 | 0.756432 | -1.144839 | -0.948016 |
2 | -1.248416 | 0.574049 | 1.054835 |
3 | -0.962009 | -0.344667 | 1.054835 |
4 | 2.092998 | 0.366597 | 1.054835 |
... | ... | ... | ... |
295 | 0.947370 | 0.751865 | -0.948016 |
296 | 1.042839 | -0.166851 | 1.054835 |
297 | 0.851901 | -0.641027 | -0.948016 |
298 | -0.484664 | -1.263383 | -0.948016 |
299 | 1.711122 | -0.315031 | 1.054835 |
300 rows × 3 columns
X_test
Возраст | Зарплата | Пол_м | |
---|---|---|---|
0 | 0.279088 | -0.552119 | 1.054835 |
1 | -0.771071 | 0.544413 | 1.054835 |
2 | 2.092998 | 1.107497 | -0.948016 |
3 | -1.630291 | -0.077943 | -0.948016 |
4 | -0.293726 | 0.188781 | -0.948016 |
... | ... | ... | ... |
95 | -0.102788 | -0.463211 | 1.054835 |
96 | -0.102788 | -0.522483 | 1.054835 |
97 | 0.851901 | 2.174393 | -0.948016 |
98 | -0.866540 | 0.277689 | 1.054835 |
99 | -0.198257 | 1.640945 | -0.948016 |
100 rows × 3 columns
# просмотр распределения возраста на базе нормализованных данных
sns.histplot(X_train['Возраст'], kde=True, color='green', bins=30)
<AxesSubplot:xlabel='Возраст', ylabel='Count'>
# просмотр распределения зарплаты на базе нормализованных данных
sns.histplot(X_train['Зарплата'], kde=True, color='blue', bins=30)
<AxesSubplot:xlabel='Зарплата', ylabel='Count'>
K Nearest Neighbour (KNN) - это алгоритм "обучения с учителем" использующихся в осноном для зада классификации или регрессии. Он наиболее эффективен для зада классификации, в которых алгоритм определяет к какому классификационному признаку отнести исходную точку путем определения K-ближайщих точек. KNN классифицирует точку данных, базируясь на известной классификации других точек (на базе тренировочных данных).
KNN простой алгоритм, работающий на базе пространственной близости/расстояния и не нуждается в построении модели, поскольку он настраивает несколько параметров и делает предположения. KNN универсален, но становится медленнее по мере увеличения признаков (фич).
# Создание классификатора
classifierKNN = KNeighborsClassifier(n_neighbors = 5, metric = 'minkowski', p = 2)
# Обучение классификатора на тренировочных данных
classifierKNN.fit(X_train.values, y_train.values)
KNeighborsClassifier()
Метрика minkowski здесь означает MinkowskiDistance, которая рассчитывается по формуле как сумма ( | x - y | ^ p) ^( 1/p )
# Применение классификации к тестовым данным.
y_pred = classifierKNN.predict(X_test.values)
# Результат предсказания покупок на тестовых данных.
y_pred
array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1], dtype=int8)
y_test.values
array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1], dtype=int8)
# Оценка результатов KNN классификации
# Смотрим матрицу ошибок.
cm1 = confusion_matrix(y_test,y_pred)
print(cm1)
[[63 5] [ 2 30]]
# Она же, но графически.
sns.heatmap(cm1, annot=True, fmt='d', cmap='Spectral')
plt.show()
Из 100 значений предсказаний, только 7 ошибочных.
Отчет о классификации
print(classification_report(y_test, y_pred))
precision recall f1-score support 0 0.97 0.93 0.95 68 1 0.86 0.94 0.90 32 accuracy 0.93 100 macro avg 0.91 0.93 0.92 100 weighted avg 0.93 0.93 0.93 100
Общая точность (accuracy) составляет 93%, а точность (precision), полнота (recall) и F1-мера для предсказания "не покупок" выше 94%, а для проноза покупок близка к 90%.
# значение ошибки.
np.mean(y_pred != y_test)
0.07
В методе KNN выбор значения k очень важен. При маленьком значении k "шум" будет оказывать сильное воздействие на итоговый результат. Большое значение делает его затратным с точки зрения вычислений и противоречит основной идее KNN, что близкие точки могут иметь схожие плотности или классы. Значение по умолчанию для количества k-соседей - 5.
Далее в цикле пробуем применить значения k от 1 до 40 с записью значения ошибки в массив, в дальнейшем с построением графика по списку ошибок для определения оптимального значения k.
error_rate = []
for i in range(1,40):
# создание KNN классификатора с заданным значением k
knn = KNeighborsClassifier(n_neighbors = i)
# обучение его на тренировочных данных
knn.fit(X_train.values, y_train.values)
# прогноз покупок на тестовых данных
pred_i = knn.predict(X_test.values)
# сохранение значения ошибки
error_rate.append(np.mean(pred_i != y_test))
# графическое представление процента ошибок
plt.figure(figsize=(10,6))
plt.plot(range(1,40), error_rate, color='red', linestyle='dashed', marker='o', markerfacecolor='yellow', markersize=6)
plt.title('Процент ошибок в зависимости от значения K')
plt.xlabel('K')
plt.ylabel('Процент ошибок')
Text(0, 0.5, 'Процент ошибок')
Из графика видно, допустимый уровень ошибок при K от 5 до 18, а при значении K выше 35 уровень ошибок максимальный.
# анализ результатов при значении K = 10
knn10 = KNeighborsClassifier(n_neighbors = 10)
knn10.fit(X_train.values,y_train.values)
pred10 = knn10.predict(X_test.values)
print('При K=10')
print(confusion_matrix(y_test, pred10))
sns.heatmap(confusion_matrix(y_test, pred10), annot=True, fmt='d', cmap='Spectral')
print(classification_report(y_test, pred10))
print('\n')
При K=10 [[65 3] [ 3 29]] precision recall f1-score support 0 0.96 0.96 0.96 68 1 0.91 0.91 0.91 32 accuracy 0.94 100 macro avg 0.93 0.93 0.93 100 weighted avg 0.94 0.94 0.94 100
K = 5 и K = 10 имеют почти одинаковую точность, полноту и F1-меру.
knn35 = KNeighborsClassifier(n_neighbors = 35)
knn35.fit(X_train.values, y_train.values)
pred35 = knn35.predict(X_test.values)
print('При K=35')
print(confusion_matrix(y_test, pred35))
sns.heatmap(confusion_matrix(y_test, pred35), annot=True, fmt='d', cmap='Spectral')
print(classification_report(y_test, pred35))
print('\n')
При K=35 [[65 3] [10 22]] precision recall f1-score support 0 0.87 0.96 0.91 68 1 0.88 0.69 0.77 32 accuracy 0.87 100 macro avg 0.87 0.82 0.84 100 weighted avg 0.87 0.87 0.87 100
Метод опорных векторов (SVM, Support vector machines) использует гиперплоскость, чтобы классифицировать данные по 2 классам.
Метод опорных векторов - это метод "обучения с учителем", использующий классификацию, регрессию и определение выбросов (outlier). Главное преимущество SVM заключается в том, что он эффективен в многомерном пространстве, даже когда количество выборок меньше количества измерений. Но в случаях, когда измерения превышают количество образцов, есть вероятность переобучения.
Основная цель алгоритма SVM - создать оптимальную линию или границу решения, представляющую собой гиперплоскость, которая может разделить n-мерное пространство на классы, чтобы в будущем можно было поместить новую точку данных в правильную категорию.
В отличие от других классификаторов, которые обращают внимание на все точки, этот метод сосредотачиваются только на точках, которые труднее всего классифицировать.
Гиперплоскость - это подпространство, размерность которого на единицу меньше, чем размер его окружающего пространства или пространства, окружающего объект. Если пространство
- трехмерно, то его гиперплоскости являются двумерными,
- двумерно, его гиперплоскости являются одномерными линиями,
- одномерное, его гиперплоскость представляет собой единственную точку.
SVM использует набор математических функций, известных как ядро, для создания границы оптимального решения, принимая данные в качестве входных. Наиболее предпочтительный вид функции ядра - это RBF. Потому что он локализован и имеет конечный отклик по всей оси абсцисс.
Если данные слишком похожи или их сложно разделить по какой-либо причине, или из-за того, что они нелинейны, тогда SVM с ядром может добавить еще одно измерение с «гиперплоскостью», которая может разделить точки данных.
Значениями ядра могут быть:
- linear
- poly
- rbf (по умолчанию)
- sigmoid
- precomputed
SVM с линейным ядром
Это ядро лучше всего подходит для задач классификации, когда данные разделены линейно, как задача классификации текста.
# обучение модели
classifierLin = SVC(kernel = 'linear')
classifierLin.fit(X_train, y_train)
print(classifierLin.gamma)
print(classifierLin.C)
scale 1.0
# проверка на тестовых данных
y_pred_svc = classifierLin.predict(X_test)
y_pred_svc
array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1], dtype=int8)
# анализ результатов
# матрица ошибок
cm2 = confusion_matrix(y_test, y_pred_svc)
print(cm2)
[[66 2] [10 22]]
sns.heatmap(cm2, annot=True, fmt='d', cmap='Spectral')
plt.show()
11 неверных прогнозов из 100.
print(classification_report(y_test, y_pred_svc))
precision recall f1-score support 0 0.87 0.97 0.92 68 1 0.92 0.69 0.79 32 accuracy 0.88 100 macro avg 0.89 0.83 0.85 100 weighted avg 0.88 0.88 0.87 100
Точность 88%, но полнота для совершенных покупок всего 69%.
SVM с ядром RBF (Radial Basis Function)
# обучение модели
classifierrbf = SVC(kernel = 'rbf')
classifierrbf.fit(X_train, y_train)
print(classifierLin.gamma)
print(classifierLin.C)
scale 1.0
# проверка на тестовых данных
y_pred_rbf = classifierrbf.predict(X_test)
y_pred_rbf
array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1], dtype=int8)
# анализ результатов
# матрица ошибок
cm3 = confusion_matrix(y_test, y_pred_rbf)
print(cm3)
[[64 4] [ 3 29]]
7 неверных прогнозов из 100.
print(classification_report(y_test,y_pred_rbf))
precision recall f1-score support 0 0.96 0.94 0.95 68 1 0.88 0.91 0.89 32 accuracy 0.93 100 macro avg 0.92 0.92 0.92 100 weighted avg 0.93 0.93 0.93 100
Общая точность (accuracy) составляет 93%, а точность (precision), полнота (recall) и F1-мера для предсказания "не покупок" выше 94%, а для проноза покупок близка к 90%.
Оптимизация гиперпараметров - это метод выбора набора оптимальных гиперпараметров для алгоритма обучения. Модель машинного обучения может иметь разные ограничения, веса или скорости обучения для обобщения различных шаблонов данных. Эти меры называются гиперпараметрами и должны быть настроены так, чтобы модель могла оптимально решить проблему машинного обучения. Основное различие между параметрами и гиперпараметрами заключается в том, что параметры изучаются автоматически, а значения гиперпараметров устанавливаются вручную.
Результатом оптимизации гиперпараметров является единый набор хорошо работающих гиперпараметров, которые можно настроить для модели для достижения наилучших результатов. Для получения оптимальных гиперпараметров существуют различные подходы к поиску, такие как: поиск по сетке (Grid Search), случайный поиск (Random Search), байесовская оптимизация (Bayesian optimization), оптимизация на основе градиента (Gradient-based optimization), оптимизация на основе эволюции (Evolutionary optimization), на основе популяции (Population-based). Рассмотрим поиск по сетке.
Поиск по сетке - это своего рода процесс настройки, который выбирает лучшие параметры для алгоритма для оптимизации его производительности, но его выполнения требует довольно много времени. Параметры оценивания, используемого для применения этих методов, оптимизируются с помощью поиска по сетке с перекрестной проверкой по сетке параметров. Функция GridSearchCV реализует методы «соответствия» и «оценки».
При обучении SVM необходимо учитывать два параметра:
-
C
-
gamma
Используя поиск по сетке, можно найти наилучшее оптимальное значение для C и гаммы, а также лучшее ядро для SVM.
Параметр C
Реализация SVC основана на libsvm (библиотека для машин опорных векторов). C - параметр регуляризации в SVC и его значение с плавающей запятой по умолчанию 1. Сила регуляризации обратно пропорциональна C. Она должна быть строго положительной.
C определяет компромисс между увеличением размера поля и обеспечением того, чтобы точки обучения лежали на правильной стороне поля. Если значение C мало, это создаст разделяющую гиперплоскость с большим запасом, что может неправильно классифицировать больше точек и приведет к большему количеству ошибок обучения модели. Если же значение C очень велико, оптимизатор выберет разделяющую гиперплоскость с меньшим запасом, которая будет правильно классифицировать тренировочные точки, но может привести к переобучению.
Выбор значения C имеет решающее значение для SVC и может быть выполнен путем настройки гиперпараметров.
Параметр gamma
Параметры гаммы можно рассматривать как инверсию радиуса влияния выборок, выбранных моделью в качестве опорных векторов, и это коэффициент ядра для 'rbf', 'poly' и 'sigmoid'. Значение гаммы по умолчанию - 'scale'. и его значение равно 1 / (n_features * X.var()).
Когда гамма очень мала, модель слишком ограничена и не может уловить сложность или «форму» данных. Но более высокое значение гаммы точно соответствует точкам обучающих данных, но приведет к переобучению.
# поиск опимальных гиперпараметров
g = GridSearchCV(estimator=SVC(),
param_grid={'C': [1, 10], 'gamma': [1,0.1,0.01,0.001,0.0001,1.1,1.2], 'kernel': ('linear', 'rbf')})
g.fit(X_train,y_train)
GridSearchCV(estimator=SVC(), param_grid={'C': [1, 10], 'gamma': [1, 0.1, 0.01, 0.001, 0.0001, 1.1, 1.2], 'kernel': ('linear', 'rbf')})
Поиск по сетке дает комбинацию значений, которая имеет наивысшую точность среди всех значений.
g.best_params_
{'C': 1, 'gamma': 1, 'kernel': 'rbf'}
Лучшие параметры для метода SVM с ядром rbf определились как gamma = 1 и C = 1.
g.best_estimator_
SVC(C=1, gamma=1)
# анализ на тестовых данных по полученным параметрам.
grid_pred = g.predict(X_test)
# оценка результатов
print(confusion_matrix(y_test, grid_pred))
[[64 4] [ 2 30]]
sns.heatmap(confusion_matrix(y_test,grid_pred), annot=True, fmt='d', cmap='Spectral')
plt.show()
6 неверных предсказаний из 100.
print(classification_report(y_test, grid_pred))
precision recall f1-score support 0 0.97 0.94 0.96 68 1 0.88 0.94 0.91 32 accuracy 0.94 100 macro avg 0.93 0.94 0.93 100 weighted avg 0.94 0.94 0.94 100
Общая точность (accuracy) составляет 94%, а точность (precision), полнота (recall) и F1-мера для предсказания "не покупок" выше 94%, а для проноза покупок близка к 92%.