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

Набор данных можно скачать по следующей ссылке.

Https://archive.ics.uci.edu/ml/datasets/bank+marketing#

Этот набор данных относится к кампании прямого маркетинга Банковского института в Португалии. Источник данных представляет следующую информацию об атрибутах набора данных.

Attribute Information:
Input variables:
1 - age (numeric)
2 - job : type of job (categorical: 'admin.','blue-collar','entrepreneur','housemaid','management','retired','self-employed','services','student','technician','unemployed','unknown')
3 - marital : marital status (categorical: 'divorced','married','single','unknown'; note: 'divorced' means divorced or widowed)
4 - education (categorical: 'basic.4y','basic.6y','basic.9y','high.school','illiterate','professional.course','university.degree','unknown')
5 - default: has credit in default? (categorical: 'no','yes','unknown')
6 - housing: has housing loan? (categorical: 'no','yes','unknown')
7 - loan: has personal loan? (categorical: 'no','yes','unknown')
8 - contact: contact communication type (categorical: 'cellular','telephone')
9 - month: last contact month of year (categorical: 'jan', 'feb', 'mar', ..., 'nov', 'dec')
10 - day_of_week: last contact day of the week (categorical: 'mon','tue','wed','thu','fri')
11 - duration: last contact duration, in seconds (numeric). Important note: this attribute highly affects the output target (e.g., if duration=0 then y='no'). Yet, the duration is not known before a call is performed. Also, after the end of the call y is obviously known. Thus, this input should only be included for benchmark purposes and should be discarded if the intention is to have a realistic predictive model.
12 - campaign: number of contacts performed during this campaign and for this client (numeric, includes last contact)
13 - pdays: number of days that passed by after the client was last contacted from a previous campaign (numeric; 999 means client was not previously contacted)
14 - previous: number of contacts performed before this campaign and for this client (numeric)
15 - poutcome: outcome of the previous marketing campaign (categorical: 'failure','nonexistent','success')
16 - emp.var.rate: employment variation rate - quarterly indicator (numeric)
17 - cons.price.idx: consumer price index - monthly indicator (numeric)
18 - cons.conf.idx: consumer confidence index - monthly indicator (numeric)
19 - euribor3m: euribor 3 month rate - daily indicator (numeric)
20 - nr.employed: number of employees - quarterly indicator (numeric)

Output variable (desired target):
21 - y - has the client subscribed a term deposit? (binary: '1','0')

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

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

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import scipy.stats as stats
from sklearn.preprocessing import FunctionTransformer
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import SMOTE
from sklearn import svm, datasets
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
#mount google drive
from google.colab import drive
drive.mount("/content/gdrive")
#read dataset to a dataframe
df = pd.read_csv('/content/gdrive/My Drive/data/banking.csv')

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

Шаги предварительной обработки, которые будут выполнены для этого набора данных, будут следующими:

  1. Обработка отсутствующих значений / дубликатов
  2. Обработка выбросов
  3. Преобразование функций
  4. Кодирование функций
  5. Масштабирование функций
  6. Обработка дисбаланса классов и сокращение функций

Обработка отсутствующих значений / дубликатов

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

df.isnull().any()

Нет нулевых значений. А затем сбросьте дубликаты, если они есть.

df=df.drop_duplicates()

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

df=df.drop(['duration'],axis=1)

Обработка выбросов

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

df.boxplot(column=['campaign'])

В этом атрибуте есть один выброс. Его можно удалить из набора данных.

df=df.drop(df[df['campaign'] >50].index)
df.boxplot(column=['campaign'])

df.boxplot(column=['cons_conf_idx'])

Имеется несколько выбросов, которые можно заменить максимальным значением.

Q1 = df["cons_conf_idx"].quantile(0.25)
Q3 = df["cons_conf_idx"].quantile(0.75)
IQR = Q3-Q1
lower_limit = Q1 - 1.5*IQR
upper_limit = Q3 + 1.5*IQR
df['cons_conf_idx'] = np.where(df['cons_conf_idx']>upper_limit,upper_limit,df['cons_conf_idx'])
df.boxplot(column=['cons_conf_idx'])

На всех диаграммах других атрибутов не было никаких выбросов.

Преобразование функций

Распределение точек данных каждого непрерывного атрибута должно быть нормализовано, если оно искажено. Это поспособствует хорошему обучению модели. Распределение данных непрерывных атрибутов можно просмотреть с помощью гистограмм и графиков Q-Q.

#plot the histgrams
df.hist(figsize=(30,20))

#plot Q-Q plots for continuous valued attributes
stats.probplot(df["age"], dist="norm", plot=plt)
plt.title("age")
plt.show()
stats.probplot(df["campaign"], dist="norm", plot=plt)
plt.title("campaign")
plt.show()
stats.probplot(df["pdays"], dist="norm", plot=plt)
plt.title("pdays")
plt.show()
stats.probplot(df["previous"], dist="norm", plot=plt)
plt.title("previous")
plt.show()
stats.probplot(df["emp_var_rate"], dist="norm", plot=plt)
plt.title("emp_var_rate")
plt.show()
stats.probplot(df["cons_price_idx"], dist="norm", plot=plt)
plt.title("cons_price_idx")
plt.show()
stats.probplot(df["cons_conf_idx"], dist="norm", plot=plt)
plt.title("cons_conf_idx")
plt.show()
stats.probplot(df["euribor3m"], dist="norm", plot=plt)
plt.title("euribor3m")
plt.show()
stats.probplot(df["nr_employed"], dist="norm", plot=plt)
plt.title("nr_employed")
plt.show()

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

Для преобразований использовался FunctionTransformer в библиотеке sklearn.preprocessing.

#create a copy of the dataset
df_new=df.copy()
#apply squareroot transformation
transformer = FunctionTransformer(np.sqrt)
new_age= sqrt_transformer.transform(df_new['age'])
df_new['age']=new_age
df_new['age'].hist()

new_previous= sqrt_transformer.transform(df_new['previous'])
df_new['previous']=new_previous
df_new['previous'].hist()

new_campaign= sqrt_transformer.transform(df_new['campaign'])
df_new['campaign']=new_campaign
df_new['campaign'].hist()

Атрибуты nr_employed и emp_var_rate можно рассматривать как смещенные влево. Поэтому для нормализации распределения было применено преобразование в квадрат. Вместо этого можно также использовать экспоненциальное преобразование.

#applying squared transformation
squared_transformer = FunctionTransformer(lambda x:x**2)
new_nr_employed = squared_transformer.transform(df_new['nr_employed'])
df_new['nr_employed']= new_nr_employed

new_emp_var_rate = squared_transformer.transform(df_new['emp_var_rate'])
df_new['emp_var_rate']= new_emp_var_rate

Кодировка функций

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

df_new['job']=df_new['job'].astype('category').cat.codes
df_new['marital']=df_new['marital'].astype('category').cat.codes
df_new['education']=df_new['education'].astype('category').cat.codes
df_new['default']=df_new['default'].astype('category').cat.codes
df_new['housing']=df_new['housing'].astype('category').cat.codes
df_new['loan']=df_new['loan'].astype('category').cat.codes
df_new['contact']=df_new['contact'].astype('category').cat.codes
df_new['month']=df_new['month'].astype('category').cat.codes
df_new['day_of_week']=df_new['day_of_week'].astype('category').cat.codes
df_new['poutcome']=df_new['poutcome'].astype('category').cat.codes

Масштабирование функций

Масштабирование используется для изменения масштаба диапазона атрибутов с непрерывным значением до стандартного нормального распределения. Здесь среднее значение распределения становится равным 0, а стандартное отклонение становится равным 1. Наиболее подходящим методом масштабирования является стандартная стандартизация. Здесь использовался StandardScaler из библиотеки sklearn.preprocessing.

columnfeatures=['age','campaign','pdays','previous','emp_var_rate','cons_price_idx','cons_conf_idx','euribor3m','nr_employed']
scaler = StandardScaler()
scaler.fit(df_new[columnfeatures])
df_new[columnfeatures]=scaler.transform(df_new[columnfeatures])

Устранение дисбаланса классов и сокращение числа функций

Чтобы определить значимость и корреляцию атрибутов, можно составить матрицу корреляции. Если значения атрибутов, за исключением переменной класса (y), ближе к 1 0r -1, то говорят, что эти атрибуты сильно коррелированы, и из двух атрибутов, относящихся к этому значению, следует отбросить один. Если значения столбца y с другими атрибутами очень низкие (близкие к нулю), эти атрибуты не имеют значения при прогнозировании y. Следовательно, эти атрибуты также можно отбросить.

plt.figure(figsize = (20,20))
sns.heatmap(df_new.corr(),annot = True);
#save the matrix in the files of Google Colab
plt.savefig('svm_conf.png', dpi=400)

Согласно приведенной выше матрице, в наборе данных нет сильно коррелированных атрибутов. Но он показывает, что атрибуты «месяц», «возраст», «ссуда», «день_недели» и «жилье» не имеют значения. Таким образом, эти атрибуты можно отбросить.

df_new = df_new.drop(['month','loan','age','day_of_week','housing'], axis = 1)

Переменная класса, которая является прогнозируемым атрибутом, не имеет сбалансированного количества значений для 0 и 1.

df_new['y'].value_counts()
sns.countplot(x='y', data=df_new)
plt.show()

Поскольку количество записей, имеющих 0 (отрицательные), больше, чем количество записей, имеющих 1 (положительные), процент истинных положительных результатов (правильное предсказание действительно положительных записей как положительных) станет очень низким. Чтобы решить эту проблему, к набору данных, который будет использоваться для обучения, может быть применен метод синтетической передискретизации меньшинства (SMOTE).

Поэтому сначала необходимо разделить набор данных, который используется для обучения. Здесь набор данных был разделен на обучение и тестирование с соотношением 0,8: 0,2.

#seperating the y variable from the dataset
y=df_new['y']
x=df_new.drop(['y'],axis=1)
#splitting the dataset for training and testing 
X_class_train, X_test, y_class_train, y_test = train_test_split(x, y, test_size=0.2, random_state=0)

SMOTE, доступный в библиотеке imblearn.over_sampling, можно использовать для применения SMOTE к набору обучающих данных.

os = SMOTE(random_state=0)
data_X, data_y = os.fit_sample(X_class_train, y_class_train)
#plotting results
columns = X_class_train.columnssmoted_X = pd.DataFrame(data=data_X,columns=columns )
smoted_y= pd.DataFrame(data=data_y,columns=['y'])
sns.countplot(x='y', data=smoted_y)
plt.show()

После применения SMOTE анализ главных компонентов (PCA) также можно использовать для большего сокращения функций как в обучающих, так и в тестовых наборах данных. Переменная класса (y) не должна применяться к PCA. Для этого используется PCA, доступный в библиотеке sklearn.decomposition. Там можно указать количество функций, необходимых для прогнозов, путем сравнения с результатами прогнозов. В противном случае вы можете проверить сумму отношений дисперсии PCA. Например, если оно больше 95%, то будет сохранено более 95% информации в наборе данных.

train_X=smoted_X
y_train=smoted_y
pca = PCA(n_components=7)
pca.fit(train_X)
X_train= pca.transform(train_X)
test_X = pca.transform(X_test)
pca.explained_variance_ratio_
pca.explained_variance_ratio_[:7].sum()

Применение SVM с ядрами

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

SVM с ядрами используется для повышения эффективности вычисления опорных векторов, которые поддерживают модель, чтобы найти границу решения для набора данных. Он используется, когда набор данных нельзя разделить линейно. Это означает, что не существует линейной линии или линейной гиперплоскости, которые могли бы разделить данные на два класса без какой-либо неправильной классификации. Можно использовать линейное ядро ​​и проверить точность, чтобы определить, является ли набор данных линейно разделимым.

В этой статье svm, доступный в библиотеке sklearn, используется для обучения и прогнозирования, а также используется в качестве ядра rbf.

#training
trainedsvm = svm.SVC(kernel='rbf', C=50, gamma=0.05).fit(X_train,y_train)
#predicting
predictionsvm = trainedsvm.predict(test_X)

Здесь ядро ​​может быть rbf, poly или sigmoid. c - гиперметр в SVM для контроля ошибок. Когда c низкий, ошибка низкая. Но низкая ошибка не означает, что она дает лучшую границу принятия решения. гамма - это коэффициент ядра, который определяет, какую кривизну мы хотим получить на границе решения. Когда значение гаммы увеличивается, оно будет пытаться точно соответствовать набору данных обучения. Наилучшие значения для c и gamma можно определить, анализируя полученные результаты и изменяя значения c и gamma. Значение c и гамма будет зависеть от набора данных.

Оценка

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

#classification_report
print(classification_report(y_test,predictionsvm))
#confusion_matrix
confusion_matrix = confusion_matrix(y_test,predictionsvm)
fig, ax = plt.subplots(1)
ax = sns.heatmap(confusion_matrix, ax=ax, cmap=plt.cm.Greens, annot=True)
plt.ylabel('True value')
plt.xlabel('Predicted value')
plt.show()
print("Precision: {}".format(precision_score(y_test,predictionsvm)))
print("Recall: {}".format(recall_score(y_test,predictionsvm)))
print("F1-Score: {}".format(f1_score(y_test,predictionsvm)))

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

Точность указывает из всех прогнозируемых положительных значений (истинные положительные и ложные положительные результаты), сколько на самом деле попадает в класс положительных результатов (истинные положительные результаты). Напомним, из всех значений в положительном классе (Истинно-положительные и Ложно-отрицательные), сколько правильно предсказано как положительных (Истинно-положительных). F1-оценка составляет 2 * ((точность * отзыв) / (точность + отзыв)).

Если точность, отзывчивость и F1-оценка высоки, можно сделать вывод, что модель хорошо сделала прогнозы.

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

Хотя точность довольно высока, отзывчивость и оценка f1 значительно ниже.

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

Согласно приведенным выше результатам, это показывает, что отзывчивость и оценка F1 улучшились.

Заключение

SVM - это подход к обучению с учителем, в котором переменная класса является категориальной. SVM с ядрами используется, когда набор данных не является линейно разделимым. Шаги предварительной обработки в огромной степени способствуют повышению качества набора данных, что помогает в обучении хорошей модели машинного обучения. Если переменная класса имеет дисбаланс классов, ее необходимо удалить, чтобы получить более точные прогнозы.

Надеюсь, что у вас есть кое-что относительно шагов, которые необходимо выполнить при выполнении классификации с использованием SVM с ядрами.

Спасибо за чтение!!!

Блокнот Colab для этой статьи можно найти по ссылке ниже