
Набор данных об оттоке Orange Telecom, который состоит из очищенных данных об активности клиентов (функций), а также метки оттока, указывающей, отменил ли клиент подписку, будет использоваться для разработки прогнозных моделей.
Постановка задачи. Предскажите поведение, чтобы удержать клиентов. Вы можете анализировать все соответствующие данные о клиентах и разрабатывать целенаправленные программы удержания клиентов.
В этом блоге мы поговорим о модели прогнозирования для определения вероятности оттока клиентов в телекоммуникационном секторе. Датасет можно найти здесь.
Во-первых, мы рассмотрим методы выбора функций и решим, какие функции полезны для обучения нашего набора данных.
Что такое выбор функций и почему это важно?
Выбор функций — это процесс выбора основных функций, которые являются более единообразными, неизбыточными и релевантными для вашей модели машинного обучения. Необходимо использовать выбор функций для вашего проекта машинного обучения, потому что:
- Это помогает уменьшить размер и сложность вашего набора данных, что, в свою очередь, требует меньше времени для обучения вашей модели и требует меньших затрат на вычисления для обучения вашей модели машинного обучения, а также для создания выводов;
- Простые модели машинного обучения с меньшим количеством функций легче понять и объяснить;
- Это позволяет избежать переобучения. Так как с большим количеством признаков ваша модель усложняется и это приносит проклятие размерности (ошибка увеличивается с увеличением количества признаков).
Чтобы подробно изучить выбор признаков, можно обратиться к этой конкретной статье. Я нашел эту статью очень полезной.
#Reading the Telecom client churn dataset
data = pd.read_csv("telecom_churn.csv")
.
.
.
#changing our Main prediction (dependent) variable "Churn" from Bool to Int type for detailed Statistical results
data['Churn'] = data['Churn'].astype('int64')
.
.
.
#Checking the Ranges of the predictor variables and dependent variable
plt.figure(figsize=(40,7))
sns.boxplot(data=data)

Как бороться с колинеарностью?
Есть много способов справиться с этим. Самый простой способ обнаружить сильно коррелированные функции — использовать корреляцию Пирсона и удалить одну из идеально (~ 90%) коррелированных функций. Анализ основных компонентов (АПК) также может использоваться в качестве алгоритма уменьшения размерности. Он обычно используется для уменьшения размерности путем проецирования каждой точки данных только на несколько первых основных компонентов, чтобы получить данные более низкой размерности, сохраняя при этом как можно больше вариаций данных.
Мы применим методы корреляции, чтобы найти корреляцию между переменными в наборе данных.
# Check colinearity among data columns corr = data.corr() plt.figure(figsize=(20,12)) sns.heatmap(corr, annot=True) plt.show()

Этот график тепловой карты ясно показывает, что среди переменных в данном наборе данных существует мультиколлинеарность.
Есть несколько выбросов в нескольких предикторах, таких как количество голосовых сообщений, международный план и план голосовой почты.
Процесс выбора функции
Для этого мы собираемся использовать 3 разных способа поиска функций:
- Корреляция Пирсона
- K Лучшие функции
- Последовательный выбор функций
Корреляция Пирсона
Корреляция Пирсона измеряет силу линейной связи между двумя переменными. Он имеет значение от -1 до 1, где значение -1 означает полную отрицательную линейную корреляцию, 0 означает отсутствие корреляции, а +1 означает полную положительную корреляцию.
K Лучшие функции
Метод SelectKBest выбирает функции в соответствии с наивысшим значением k. Изменив параметр «score_func», мы можем применить метод как к данным классификации, так и к данным регрессии. Выбор лучших функций — важный процесс, когда мы готовим большой набор данных для обучения.
Последовательный выбор функций
Этот последовательный селектор функций добавляет (прямой выбор) или удаляет (обратный выбор) функции для формирования подмножества функций жадным образом. На каждом этапе этот оценщик выбирает лучшую функцию для добавления или удаления на основе оценки перекрестной проверки оценщика.
# Dependency correlation with Churn column
dummy_data.corr()['Churn'].sort_values(ascending=False)
# Output
Churn 1.000000
International plan_Yes 0.268827
Customer service calls 0.217403
Total day charge 0.195553
Total day minutes 0.195552
Voice mail plan_No 0.103167
Total eve minutes 0.089744
Total eve charge 0.089728
Total intl charge 0.074992
Total intl minutes 0.074939
Total night charge 0.039569
Total night minutes 0.039554
Account length 0.023708
Area code 0.012434
Total night calls 0.009808
Total day calls 0.007758
Total eve calls 0.004324
Total intl calls -0.055601
Number vmail messages -0.089303
Voice mail plan_Yes -0.103167
International plan_No -0.268827
Name: Churn, dtype: float64
"""
Now we gonna clean our final training dataset by dropping the columns which are not correlated in any case
or which were overlapping as pointed out in above observation
and get the clean features that will define our Learning Model
"""
columns_to_drop = [
'Account length',
'Area code',
'International plan_No',
'Voice mail plan_Yes',
'Total day charge',
'Total eve charge',
'Total intl minutes',
'Total night minutes',
'Number vmail messages',
'Total night charge',
'Total eve minutes',
'Total intl charge',
'Total day calls',
'Total eve calls',
'Total night calls',
'Total intl calls'
]
.
.
.
.
# Output
Int64Index: 2666 entries, 1382 to 1338
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Total day minutes 2666 non-null float64
1 Customer service calls 2666 non-null int64
2 Churn 2666 non-null int64
3 International plan_Yes 2666 non-null uint8
4 Voice mail plan_No 2666 non-null uint8
dtypes: float64(1), int64(2), uint8(2)
memory usage: 153.1 KB
Теперь находим фичи через KBest
Алгоритм прост: мы просто предоставляем метод расчета важности функции и количества функций, которые мы хотим использовать, обозначаемых как k. Затем алгоритм просто возвращает k лучших признаков.
Основное преимущество этого метода заключается в том, что мы можем выбирать из множества способов вычисления важности признака.
Здесь мы используем критерий хи-квадрат, так как мы работаем с задачей классификации.
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2, f_classif
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import f1_score
from sklearn.model_selection import train_test_split
dt = DecisionTreeClassifier(random_state=42)
# First we make dummy data from original data
dummy_data_v2 = pd.get_dummies(data, columns=['International plan', 'Voice mail plan'])
dummy_data_v2 = dummy_data_v2.drop(['State'], axis=1)
# Make Testing and Training Data
X = dummy_data_v2.drop(['Churn'], axis=1)
y = dummy_data_v2['Churn']
X_train, X_test, y_train, y_test = train_test_split(X,
y,
test_size=0.2,
shuffle=True,
stratify=y,
random_state=42)
# Make copy of this Test Data
X_train_v1, X_test_v1, y_train_v1, y_test_v1 = X_train.copy(), X_test.copy(), y_train.copy(), y_test.copy()
f1_score_list = []
for k in range(1, 20):
selector = SelectKBest(chi2, k=k)
selector.fit(X_train_v1, y_train_v1)
sel_X_train_v1 = selector.transform(X_train_v1)
sel_X_test_v1 = selector.transform(X_test_v1)
dt.fit(sel_X_train_v1, y_train_v1)
kbest_preds = dt.predict(sel_X_test_v1)
f1_score_kbest = round(f1_score(y_test, kbest_preds, average='weighted'), 3)
f1_score_list.append(f1_score_kbest)
print(f1_score_list)

# Create and fit selector selector = SelectKBest(f_classif, k=4) selector.fit(X_new, Y_new) # Output RangeIndex: 3333 entries, 0 to 3332 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Total day minutes 3333 non-null float64 1 Customer service calls 3333 non-null int64 2 International plan_No 3333 non-null uint8 3 International plan_Yes 3333 non-null uint8 dtypes: float64(1), int64(1), uint8(2) memory usage: 58.7 KB None
Идентификация предиктора Значение
#Using OLS for finding the p value to check the significant features import statsmodels.api as sm model = sm.OLS(dummy_data['Churn'], dummy_data[['Total day minutes', 'Customer service calls', 'International plan_No', 'International plan_Yes']]).fit() # Print out the statistics model.summary()
Нарисуем матрицу путаницы
# Now we create confusion matrix from sklearn.metrics import confusion_matrix cf_matrix = confusion_matrix(y_test_v2, s1) print(cf_matrix)

Нормализация набора данных
Нормализация полезна, когда ваши данные имеют переменный масштаб, а используемый вами метод, такой как k-ближайших соседей и искусственные нейронные сети, не делает предположений о распределении ваших данных. Предположение, лежащее в основе стандартизации, заключается в том, что ваши данные следуют распределению Гаусса (гауссова кривая).
""" Normalizing the data in the Total day minutes and Customer service calls column beacuse the value is too high when compared to other independent variable """ from sklearn import preprocessing # Create x to store scaled values as floats x = featured_dataset[['Total day minutes', 'Customer service calls']].values.astype(float) # Preparing for normalizing min_max_scaler = preprocessing.MinMaxScaler() # Transform the data to fit minmax processor x_scaled = min_max_scaler.fit_transform(x) # Run the normalizer on the dataframe featured_dataset[['Total day minutes', 'Customer service calls']] = pd.DataFrame(x_scaled)

Выбор модели с помощью AutoML
Auto ml выбирает лучшую модель для нашего набора данных, оценивая для нас множество моделей.
Здесь ниже мы собираемся использовать AUTOML для прогнозирования пола человека с использованием других переменных, таких как рост, вес, возраст и уровень ожирения и т. д. (т.е. здесь будет выполнено несколько классификаций).
Выполнив AutoML, мы получим переменные имп, а также наиболее эффективную модель для нашего набора данных. Здесь мы будем использовать библиотеку AutoML H2O.
#Importing our dataset with H2O.
df = h2o.import_file('./telecom_churn.csv')
.
.
.
#Using H2O automl
#Setting models thresold to "10", more models we use it takes more time to come up with the best model and hyperparameters.
#To create a simpler model we are excluding "StackedEnsemble" and "DeepLearning", tho they are really good.
aml = H2OAutoML(max_models = 10, seed = 10, exclude_algos = ["StackedEnsemble", "DeepLearning"], verbosity="info", nfolds=0)
#taken from references
#Creates the leader Board for all out models, ranking them by their performance(default: mean_per_class_error )
#We can also observe that as we go below the leaderboard the logloss, rmse and mse are increasing as well.
#Here from the leaderboard we have "GBM_5_AutoML_1_20221107_231030" as the leader(best performing) model .
lb = aml.leaderboard
.
.
.
aml.leader.varimp_plot()
#variance importance plot for the leader model.
#taken from references

out = h2o.get_model([mid for mid in model_ids if "XGBoost" in mid][0]) . . out.convert_H2OXGBoostParams_2_XGBoostParams() . . out.varimp_plot()

Оценка: мы можем оценить нашу модель, используя приведенную выше матрицу путаницы, из нее мы можем сказать, что ошибка и частота ошибок в матрице путаницы значительно низки, что позволяет предположить, что модель работает идеально.
Кроме того, при оценке всех моделей мы видим, что нынешняя модель лидера имеет наименьшее количество mean_per_class_error и потери журнала. Что также говорит о том, что наша модель работает идеально.
Модель также дает наиболее важные переменные, такие как общее количество минут в день и звонки в службу поддержки клиентов, что предполагает зависимость от них оттока клиентов. Кроме того, мы знаем, поскольку модель, в которой участвует GBM, также прекрасно справляется с переоснащением. Вероятно, причина, по которой h2o выбрала его в первую очередь.
Резюме SHAP
Сводная диаграмма SHAP показывает вклад функций для каждого экземпляра (ряд данных). Сумма вкладов функций и члена смещения равна необработанному прогнозу модели, т. е. прогнозу до применения функции обратной связи.

Графики частичной зависимости
График частичной зависимости (PDP) дает графическое изображение предельного влияния переменной на отклик. Влияние переменной измеряется изменением среднего отклика. PDP предполагает независимость между функцией, для которой вычисляется PDP, и остальными.


Сводный график SHAP показывает вклад функций для каждого экземпляра (ряд данных). Сумма вкладов признаков и члена смещения равна необработанному прогнозу модели, т. Е. Прогнозу до применения функции обратной связи.
Заключение
Благодаря всему анализу, который мы провели выше, мы выяснили, что две колонки, в частности, влияют на решения об отношениях клиентов с организацией. Лучшее количество звонков в службу поддержки клиентов и общее количество минут в день улучшают отношения между клиентом и компанией.
Ссылка репо для ознакомления: https://github.com/NikhilOO7/Telecom-churn