В этой статье описывается построение SVM, чтобы предсказать, будет ли клиент вкладывать деньги по срочному плану в банк.
Наброски
- Обзор набора данных
- предварительная обработка набора данных
- SMOTE и разделение набора данных
- Функциональная инженерия
- SVM с ядром
- Оценка
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