В этой статье описывается построение 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