В этой статье мы обсудим, как использовать 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')
Прежде чем применять какие-либо методы машинного обучения к любой модели машинного обучения, необходимо выполнить некоторые шаги предварительной обработки, чтобы гарантировать качество данных, которые влияют на прогнозы, сделанные моделями машинного обучения.
Шаги предварительной обработки, которые будут выполнены для этого набора данных, будут следующими:
- Обработка отсутствующих значений / дубликатов
- Обработка выбросов
- Преобразование функций
- Кодирование функций
- Масштабирование функций
- Обработка дисбаланса классов и сокращение функций
Обработка отсутствующих значений / дубликатов
Сначала проверяется, есть ли в наборе данных нулевые значения.
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 для этой статьи можно найти по ссылке ниже