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

Наброски

  1. Обзор набора данных
  2. предварительная обработка набора данных
  3. SMOTE и разделение набора данных
  4. Функциональная инженерия
  5. SVM с ядром
  6. Оценка

1. Обзор набора данных

url- https://archive.ics.uci.edu/ml/datasets/bank+marketing#

Описание атрибутов

1 - возраст (числовой)
2 - работа: тип работы (категориальный: «администратор», «рабочий», «предприниматель», «домработница», «менеджмент», «пенсионер», «само- занятый »,« услуги »,« студент »,« техник »,« безработный »,« неизвестный »)
3 - замужем: семейное положение (категориальное:« разведен »,« женат »,« холост »,« неизвестен » '; примечание:' разведенный 'означает разведенный или овдовевший)
4 - образование (категориальные:' basic.4y ',' basic.6y ',' basic.9y ',' high.school ',' неграмотный ', 'professional.course', 'university.degree', 'unknown')
5 - по умолчанию: есть кредит по умолчанию? (категорично: «нет», «да», «неизвестно»)
6 - жилье: есть ли жилищная ссуда? (категорично: «нет», «да», «неизвестно»)
7 - заем: есть ли личный заем? (категоричный: 'нет', 'да', 'неизвестно')
# связанный с последним контактом текущей кампании:
8 - контакт: тип связи контакта (категориальный: 'сотовый', 'телефон ')
9 - month: месяц последнего контакта в году (категориальные:' jan ',' feb ',' mar ',…,' nov ',' dec ')
10 - day_of_week: последний контакт день недели (категориальный: «пн», «вт», «ср», «чт», «пт»)
11 - duration: длительность последнего контакта в секундах (числовое значение). Важное примечание: этот атрибут сильно влияет на цель вывода (например, если длительность = 0, то y = ’no’). Тем не менее, продолжительность вызова до выполнения вызова неизвестна. Кроме того, после окончания звонка, очевидно, известно y. Таким образом, эти входные данные должны быть включены только для целей сравнения и должны быть отброшены, если намерение состоит в том, чтобы иметь реалистичную модель прогнозирования.
# другие атрибуты:
12 - кампания: количество контактов, выполненных во время этой кампании и для этого клиента (числовой, включая последний контакт)
13 - дней: количество дней, прошедших после того, как с клиентом последний раз связались из предыдущей кампании (числовое значение; 999 означает, что с клиентом ранее не обращались)
14 - предыдущий: количество контактов, выполненных до этой кампании и для этого клиента (числовое значение)
15 - poutcome: результат предыдущей маркетинговой кампании (категорично: «неудача», «несуществующий», «успех»)
# атрибуты социально-экономического контекста
16 - emp.var.rate: уровень изменения занятости - квартальный показатель (числовой)
17 - cons.price.idx: индекс потребительских цен - месячный показатель (числовой)
18 - cons.conf.idx: индекс потребительского доверия - месячный индикатор (числовой)
19 - euribor3m: euribor 3 мес. n-я ставка - дневной показатель (числовой)
20 - кол-во занятых: количество сотрудников - квартальный показатель (числовой)

Выходная переменная (желаемая цель):
21 - y - подписался ли клиент на срочный депозит? (двоичный: «да», «нет»)

на рисунке ниже мы можем увидеть детали каждой функции, такие как количество, тип данных,

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

2. Предварительная обработка

I. Обработка отсутствующих значений

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

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

«Https://towardsdatascience.com/how-to-handle-missing-data- кам8646b18db0d4

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

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

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

Итак, мы рисуем прямоугольные диаграммы непрерывных функций и ясно видим, что выбросы могут быть идентифицированы с помощью функций «кампании» и cons_conf_idx.

На приведенной выше диаграмме мы можем видеть выбросы после 50, поэтому я выбрал данные до 50, чтобы удалить выброс. Эта гистограмма до обработки и после обработки выбросов кампании и использования фрагмента кода показана ниже. «Я отбросил выброс, выбрав только значения, меньшие выброса, используя приведенный ниже код.

import matplotlib.pyplot as plt
import warnings
import seaborn as sns
warnings.filterwarnings("ignore")
fig, axes = plt.subplots(1,2)
plt.tight_layout(0.2)
print("Before Shape:",df_continous.shape)
#select values less than 50
df_temp = df_continous[(df_continous['campaign'] < 50)]
print("After Shape:",df_temp.shape)
sns.boxplot(df['campaign'],orient='v',ax=axes[0])
axes[0].title.set_text("Before")
sns.boxplot(df_temp['campaign'],orient='v',ax=axes[1])
axes[1].title.set_text("After")
plt.show()
df_continous = df_temp

Обработка выбросов в «cons_conf_idx» также использовала тот же метод, и я использую значения до -27, чтобы удалить выбросы и получить гистограмму ниже. Обратите внимание, что если мы выберем -30 здесь, будет пропущено больше данных.

III. Преобразование

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

import scipy.stats as stats
columns = df_continous.columns
for col in columns:
  print(col)
  stats.probplot(df_continous[col], dist="norm", plot=plt)
  plt.show()

Нарисуйте все гистограммы функций

df_continous.hist(layout=(3,4), figsize=(10,10))

Из диаграмм выше мы можем видеть, что возраст, кампания и предыдущий наклонены вправо, а nr_employed - влево.

для возраста, кампании и предыдущего я применил преобразование квадратного корня

from sklearn.preprocessing import FunctionTransformer
sqrt_transformer=FunctionTransformer(np.sqrt,validate=True)
sqrt_cols=['age','campaign','previous']
transformed=sqrt_transformer.transform(df_continous[sqrt_cols])
for index,col in enumerate(sqrt_cols):
    df_continous[col]=transformed[:,index]
    fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(20,5))
    print(col)
    stats.probplot(df_continous[col], dist="norm", plot=axes[0])
    axes[1].hist(df_continous[col])
    plt.show()

после применения преобразования мы можем увидеть график Q-Q и гистограммы преобразованных функций следующим образом:

Для nr_employed я применил экспоненциальное преобразование, чтобы исправить левый перекос

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

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

За одно горячее кодирование

from sklearn.preprocessing import OneHotEncoder
# creating instance of one-hot-encoder
enc = OneHotEncoder(handle_unknown='ignore')
cols = ['job','marital','education','default','housing','loan','month','poutcome']
# We fitting only training data
enc.fit(df_categorical[cols])
colnames = enc.get_feature_names()
df_onehot_encoded = pd.DataFrame(enc.transform(df_categorical[cols]).toarray(),columns = colnames)
df_labeled= pd.concat([df_contact_labled,df_onehot_encoded], axis=1, join="inner")

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

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

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

from sklearn.preprocessing import StandardScaler
df_to_standerlize=df_continous.drop(['age','y'],1)
# Removing Categorical Features before the feature scaling
columns = df_to_standerlize.columns
# Continous col
columns_cont = np.delete(columns,np.s_[9:])
# Categorical col
categorical_data = np.delete(columns,np.s_[0:9])
# Applying Standardization 
# Init StandardScaler
scaler = StandardScaler()
#Transformation of training dataset features
standerlize_except = pd.DataFrame(df_to_standerlize, columns = columns)
scaler.fit(standerlize_except)
df_temp=pd.DataFrame(scaler.transform(standerlize_except), columns = columns)
df_standerlized=pd.concat([df_discrete,df_temp],axis=1,join='inner')
df_standerlized.head(2)

VI. Дискретность

Используя Data Discretization, мы можем преобразовать непрерывные значения в дискретную форму. Этот процесс можно использовать для сокращения большого диапазона набора данных до небольшого диапазона, который может сгладить отношения между наблюдениями.

from sklearn.preprocessing import KBinsDiscretizer
discretization  = pd.DataFrame(df_new, columns=['age'])
# fit the scaler to the  data
discretizer = KBinsDiscretizer(n_bins=8, encode='ordinal', strategy='kmeans') 
discretizer.fit(discretization)
discretized = discretizer.transform(discretization)
df_discretized = pd.DataFrame(discretized,columns=['age'])
df_new['age']=df_discretized['age']

3. SMOTE и разделение набора данных

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

Это отрицательно скажется на производительности модели, поэтому мы должны УДАЛИТЬ обучающий набор при разделении набора данных поезд / тест. Код, который я использовал,

from sklearn.model_selection import train_test_split
from imblearn.over_sampling import SMOTE
os = SMOTE(random_state=0)
df_new=df_new.dropna(axis=0)
df_new=df_new.reset_index(drop=True)
features_df= df_new.drop('y', 1)
target = pd.DataFrame(df_new['y'], columns=["y"])
X_class_train, X_test, y_class_train, y_test = train_test_split(features_df, target, test_size = 0.2, random_state = 0)
data_X, data_y = os.fit_resample(X_class_train, y_class_train)
smoted_X = pd.DataFrame(data=data_X,columns=features_df.columns )
smoted_y= pd.DataFrame(data=data_y,columns=['y'])
#reset index
X_train=smoted_X
y_train = smoted_y
X_train=X_train.reset_index(drop=True)
X_test=X_test.reset_index(drop=True)
y_train=y_train.reset_index(drop=True)
y_test=y_test.reset_index(drop=True)

После SMOTE мы можем устранить дисбаланс классов в обучающем наборе данных.

4. Разработка функций

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

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

здесь мы можем идентифицировать зависимые функции как,

  • emp_var
  • cons_price_idx
  • euribor3m
  • nr_employed

И независимые функции как

  • возраст
  • кампания
  • дней
  • предыдущий
  • cons.conf.idx

Все функции не показывают сильной корреляции, но мы можем идентифицировать зависимые функции, как упомянуто выше, чтобы мы могли оставить одну, имеющую самую высокую корреляцию, с целью из четырех зависимых переменных, а остальные отбросить. Итак, мы можем оставить «nr_employed» и отбросить emp_var_rate, cons_price_idx, euribor3m. Это потому, что nr_employed имеет самую высокую корреляцию с y

падение зависимых функций,

X_train = X_train.drop('emp_var_rate', 1)
X_train = X_train.drop('cons_price_idx', 1)
X_train = X_train.drop('euribor3m', 1)
X_test = X_test.drop('emp_var_rate', 1)
X_test = X_test.drop('cons_price_idx', 1)
X_test = X_test.drop('euribor3m', 1)

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

#test the signifncance of the continuous features
d_data = pd.concat([y_train,X_train[columns_cont]], axis=1, join="inner")
print(d_data.corr())
plt.figure(figsize = (10,10))
sns.heatmap(d_data.corr())

Применить PCA

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

from sklearn.decomposition import PCA
# see explained variance ratios
pca = PCA()
pca.fit(X_train)
pca.explained_variance_ratio_

После подгонки обучающего набора в модель PCA я выбираю значение собственного вектора больше, чем 95%, и количество функций равно 25. Затем я преобразую данные обучения и тестирования в модель PCA, оснащенную 25 компонентами.

pca = PCA(n_components = n_com)
pca.fit(X_train)
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)

5. Поддержка векторных машин (SVM) с ядром

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

Код, который я использовал в своей проблеме:

#Import svm model
from sklearn import svm
svc = svm.SVC(kernel='rbf', C=1000,gamma=0.1).fit(X_train_pca, y_train)

Здесь я использую ядро ​​«rbf», потому что у меня небольшое количество функций, поэтому лучше использовать «линейное» ядро, если набор данных имеет огромное количество функций, таких как более 1000. Другие доступные ядра - «poly», «Sigmoid» и т. Д.

Я изменил значения c и гаммы в разных диапазонах и проверил приведенные ниже оценки из матрицы путаницы, и я пробовал разные комбинации, чтобы получить хотя бы эту точность. Здесь я использовал стоимость (c) как 1 и гамму как 0,1, мы можем настроить модель с помощью изменение параметров c и гаммы. Есть еще параметры, такие как вероятность, сжатие, коэффициент, вес класса и т. Д. Но «c» и «гамма» - это дуэт, который оказывает огромное влияние на модель, поэтому мы использовали только эти два.

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

6. Оценка

Для оценки модели я использую матрицу неточностей.

Код для реализации матрицы путаницы:

from sklearn.metrics import classification_report, confusion_matrix
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize = (8,8))
fig, ax = plt.subplots(1)
ax = sns.heatmap(cm, ax=ax, annot=True) #normalize='all'
plt.title('Confusion matrix')
plt.ylabel('True category')
plt.xlabel('Predicted category')
plt.show()

На горизонтальной оси матрицы неточностей представлены прогнозируемые результаты модели, а на вертикальной оси представлены истинные значения, которые мы используем для тестирования.

  • 9700 правильно предсказаны как «0» (True Negative)
  • 510 предсказываются правильно как «1» (истинно положительный результат)
  • 810 неправильно классифицируются как «0» (ложноотрицательный)
  • 950 неправильно классифицированы как «1» (ложноположительный результат)

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

Отчет о классификации:

Из отчета о классификации мы можем ясно видеть, что точность 0 ниже, чем точность 1. Это связано с тем, что набор данных является несбалансированным. Модель получила точность среднего макроса 0,64 и точность средневзвешенного значения 0,86.

Доступ к полному коду: https://github.com/JayashanT/SVM-model_banking-marketing