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

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

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

В этой статье основное внимание будет уделено практическому примеру того, как вы можете очень дешево начать внедрять модель машинного обучения, чтобы предвидеть и прогнозировать клиентов, которые могут уйти, и тем самым эффективно и результативно использовать свой бюджет на продажи и маркетинг. Читайте мои статьи здесь: Чтобы узнать больше о том, как развивать SaaS-бизнес во время рецессии и Четыре способа сделать ваш SaaS-бизнес прибыльным.

Мусор на входе = мусор на выходе

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

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

  • Определите свои источники данных
  • Определите схему данных и архитектуру данных, которые будут использоваться для сбора и хранения данных.
  • Установить стандарты качества данных и управления данными для обеспечения точности и полноты данных.
  • Разработайте модель данных и словарь данных, определяющий отношения между объектами данных и атрибутами.

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

Итак, как мы прогнозируем отток

Для целей этой статьи мы будем использовать данные о телекоммуникационном бизнесе на Kaggle, поскольку общедоступных наборов данных для бизнеса корпоративного программного обеспечения не так много. Однако эти концепции применимы к набору данных для бизнеса SaaS.

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

1) Импорт необходимых библиотек

#----main libraries----
import numpy as np
import pandas as pd

#----plotting libraries----
import seaborn as sns
import matplotlib.pyplot as plt

#----model and support imports----
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score, cross_val_predict

from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import MinMaxScaler

from sklearn.metrics import confusion_matrix, classification_report, accuracy_score, recall_score, precision_score, f1_score, roc_auc_score, ConfusionMatrixDisplay

from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier

2) Загрузка данных

tel_cus_df = pd.read_csv("/kaggle/input/telco-customer-churn/WA_Fn-UseC_-Telco-Customer-Churn.csv")

3) Изучение данных

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

Следующий код поможет нам визуализировать образец набора данных:

tel_cus_df.head()

Следующий код поможет нам визуализировать тип данных для всех полей в нашем наборе данных:

tel_cus_df.info()

Как видно из Dtype для TotalCharges, это объект. Мы ожидали, что это поле будет числовым. Давайте попробуем преобразовать это поле в числовое значение, а затем проверим наличие нулевых/отсутствующих значений в наборе данных:

tel_cus_df['TotalCharges'] = tel_cus_df['TotalCharges'].replace(' ', np.nan, regex=True)
tel_cus_df['TotalCharges'] = pd.to_numeric(tel_cus_df['TotalCharges'])
tel_cus_df.isnull().sum()

4) Обработка данных

Кажется, что TotalCharges имеет 11 нулевых значений. Нам нужно либо добавить значение в поле, либо удалить строки с нулевыми значениями, чтобы наша модель машинного обучения работала правильно. Мы вернемся к этому позже. Дайте нам знать, проверьте количество уникальных значений в каждом поле/столбце:

columns = tel_cus_df.columns
print("----------- Numeric field ----------\n")
for i in range(len(columns)):
    if tel_cus_df[columns[i]].dtypes!=object:
        print("unique number of {} ---> {}".format(columns[i], len(tel_cus_df[columns[i]].unique())))
        
print("\n---------- Categorical field ----------\n")
for i in range(len(columns)):
    if tel_cus_df[columns[i]].dtypes==object:
        print("unique number of {} ---> {}".format(columns[i], len(tel_cus_df[columns[i]].unique())))

Дайте нам знать, чтобы заполнить недостающие значения для TotalCharges. Мы можем попытаться сделать это, оценив TotalCharges на основе полей Contract и MonthlyCharges. Давайте сначала проверим, какие значения у нас есть в поле Contract:

tel_cus_df['Contract'].unique()

Учитывая, что есть 3 уникальных значения: «Месяц за месяц», «Один год» и «Два года», мы можем использовать их для создания поля ContractMonth для оценки TotalCharges:

tel_cus_df['ContractMonths'] = np.where(tel_cus_df['Contract']=="Month-to-month", 1, 0) + np.where(tel_cus_df['Contract']=="One year", 12, 0) + np.where(tel_cus_df['Contract']=="Two year", 24, 0)
tel_cus_df['ContractMonths'] = pd.to_numeric(tel_cus_df['ContractMonths'])
tel_cus_df.loc[tel_cus_df['TotalCharges'].isnull(), ['Contract','ContractMonths','MonthlyCharges','TotalCharges']]

Теперь мы можем оценить недостающие значения для TotalCharges:

tel_cus_df['cal_TotalCharges'] = tel_cus_df['ContractMonths'] * tel_cus_df['MonthlyCharges']
tel_cus_df['TotalCharges'] = np.where(tel_cus_df['TotalCharges'].isnull(), tel_cus_df['cal_TotalCharges'], tel_cus_df['TotalCharges'])
tel_cus_df.isnull().sum()

Теперь у нас нет нулевых значений.

5) Удаление ненужных полей и визуализация данных

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

tel_cus_df.drop(['customerID','ContractMonths','cal_TotalCharges'], axis=1, inplace=True)
tel_cus_df.columns.values

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

attributes = ['gender', 'SeniorCitizen', 'Partner', 'Dependents',
       'PhoneService', 'MultipleLines', 'InternetService',
       'OnlineSecurity', 'OnlineBackup', 'DeviceProtection',
       'TechSupport', 'StreamingTV', 'StreamingMovies', 'Contract',
       'PaperlessBilling', 'PaymentMethod']
fig, axes = plt.subplots(nrows = 8, ncols = 2, figsize = (22,20))
for i, item in enumerate(attributes):
        if i < 8:
            ax = tel_cus_df[item].value_counts().plot(kind= 'bar', ax=axes[i,0], rot =0, color=['black', 'red', 'green', 'blue', 'cyan'])
        elif i >= 8:
            ax = tel_cus_df[item].value_counts().plot(kind= 'bar', ax=axes[i-8,1], rot =0, color=['black', 'red', 'green', 'blue', 'cyan'])
        ax.set_title(item)
        fig.tight_layout()

Кажется, что во многих полях есть проблемы с качеством данных (например, нет интернет-сервиса, а не просто да или нет). Давайте рассмотрим их подробнее:

tel_cus_df.loc[tel_cus_df['OnlineSecurity'] == "No internet service", :].head()

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

6) Выбор модели

Давайте исследуем поле, которое мы хотим предсказать (в данном случае отток):

sns.countplot(x = "Churn", data = tel_cus_df)
tel_cus_df.loc[:, 'Churn'].value_counts()

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

Мы можем либо использовать подходы для передискретизации класса меньшинства, либо мы можем использовать XGBoost и сбалансировать положительные и отрицательные веса, установив для scale_pos_weight значение sum (отрицательные экземпляры)/sum (положительные экземпляры).

Не волнуйтесь, если вы не понимаете этого сейчас. Все, что мы делаем в XGBoost, — это штрафуем модель за предсказание класса большинства, чтобы сбалансировать вывод. Мы придем к этому позже, как это сделать.

spw = -(-tel_cus_df[tel_cus_df.Churn == "No"].shape[0]// tel_cus_df[tel_cus_df.Churn == "Yes"].shape[0])

print("XGBoost weight balancing ---> neg: {}, pos: {}, scale_pos_weight: {}".format(tel_cus_df[tel_cus_df.Churn == "No"].shape[0], tel_cus_df[tel_cus_df.Churn == "Yes"].shape[0], spw))

Вы увидите следующий вывод: Балансировка веса XGBoost — › neg: 5174, pos: 1869, scale_pos_weight: 3

7) Конфигурация модели

В этом упражнении мы будем использовать три разные модели (i) логистическая регрессия, (ii) случайный лес и (iii) XGBoost. Затем мы также будем использовать TensorFlow, чтобы увидеть, повысим ли мы производительность с помощью нейронной сети. Мы запустим первые две модели (логистическая регрессия и случайный лес) без поправки на дисбаланс данных, и, следовательно, мы должны ожидать, что она будет лучше прогнозировать, когда клиент не уходил, с недостатком отсутствующих клиентов, которые действительно ушли. Мы будем использовать SMOTE (метод передискретизации синтетического меньшинства) для исправления дисбаланса при использовании TensorFlow.

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

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

Давайте сначала закодируем нашу зависимую переменную (т.е. преобразуем ее в 1 и 0):

le = LabelEncoder()
tel_cus_df['Churn'] = le.fit_transform(tel_cus_df['Churn'])

Теперь мы преобразуем наши категориальные зависимые переменные в фиктивные переменные (т. е. создадим отдельный столбец для каждого значения и изменим значения на 1 и 0):

df_dummy = pd.get_dummies(tel_cus_df)
df_dummy.head()

Давайте теперь проверим корреляцию между различными переменными:

matrix = np.triu(np.ones_like(df_dummy.corr()))
fig, ax = plt.subplots(figsize=(25,25),dpi=80, facecolor='w', edgecolor='k')
sns.heatmap(df_dummy.corr(), mask= matrix, cmap="coolwarm", annot = True, fmt = '.1f', center = 0,annot_kws={"fontsize":10})

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

8) Разделение тестовых данных поезда

Давайте теперь разделим данные теста поезда:

X = df_dummy.drop("Churn", axis = 1)
y = df_dummy['Churn']

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 100, test_size = 0.3)
print(y_train.value_counts())

Давайте теперь применим масштабирование к зависимым переменным:

scale = MinMaxScaler()
col = X_train.columns

X_train[col] = scale.fit_transform(X_train[col])
X_test[col] = scale.transform(X_test[col])

9) Обучение и прогнозирование

Как интерпретировать результаты

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

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

Точность: измеряет долю правильных прогнозов, сделанных моделью, из всех сделанных прогнозов. Он рассчитывается как отношение правильных прогнозов к общему количеству сделанных прогнозов.

Точность = (TP + TN) / (TP + TN + FP + FN)

где TP = истинно положительный, TN = истинно отрицательный, FP = ложноположительный и FN = ложноотрицательный.

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

Отзыв = ТП / (ТП + FN)

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

Точность = TP / (TP + FP)

Точность перекрестной проверки: измеряет точность модели применительно к новым неизвестным данным. Обычно он рассчитывается с использованием k-кратной перекрестной проверки, когда данные разбиваются на k подмножеств, а модель обучается и тестируется k раз на разных подмножествах.

Оценка F1: представляет собой гармоническое среднее значение точности и полноты и обеспечивает баланс между двумя показателями. Он рассчитывается как:

Оценка F1 = 2 * (Точность * Отзыв) / (Точность + Отзыв)

Логистическая регрессия

Логистическая регрессия — это контролируемый алгоритм машинного обучения, используемый для задач бинарной классификации. Он моделирует вероятность того, что выходная переменная примет определенное значение с учетом входных характеристик. Он использует сигмовидную функцию для сопоставления вывода со значением вероятности от 0 до 1.

Давайте обучим нашу модель с помощью логистической регрессии и посмотрим на результаты:

accuracy= []
recall =[]
roc_auc= []
precision = []

LR = LogisticRegression()
result_LR = LR.fit(X_train, y_train)
LR_y_pred = LR.predict(X_test)

cross_val_LR = cross_val_score(LR, X_train, y_train, scoring = 'accuracy') 
cross_val_LR_f1 = cross_val_score(LR, X_train, y_train, scoring = 'f1') 

accuracy.append(round(accuracy_score(y_test, LR_y_pred),4))
recall.append(round(recall_score(y_test, LR_y_pred),4))
roc_auc.append(round(roc_auc_score(y_test, LR_y_pred),4))
precision.append(round(precision_score(y_test, LR_y_pred),4))

model_names = ['Logistic Regression']
result_df = pd.DataFrame({'Accuracy':accuracy,'Recall':recall, 'Roc_Auc':roc_auc, 'Precision':precision, 'Cross_val_acc':cross_val_LR.mean(), 'Cross_val_f1':cross_val_LR_f1.mean()}, index=model_names)

result_df

Давайте построим матрицу путаницы для результатов:

cm = confusion_matrix(y_test, LR_y_pred, labels=LR.classes_)
disp = ConfusionMatrixDisplay(confusion_matrix=cm,display_labels=LR.classes_)
disp.plot()
plt.show()

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

Случайный лес

Random Forest — это контролируемый алгоритм машинного обучения, используемый для задач классификации и регрессии. Он создает большое количество деревьев решений, каждое из которых обучается на случайном подмножестве входных признаков и случайном подмножестве обучающих данных. Затем он объединяет прогнозы отдельных деревьев, чтобы сделать окончательный прогноз.

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

В зависимости от того, насколько сложна ваша модель данных, вы можете начать с простой логистической регрессии, прежде чем использовать другие модели.

Давайте теперь реализуем классификатор Random Forest:

accuracy= []
recall =[]
roc_auc= []
precision = []

RF = RandomForestClassifier(criterion='entropy', min_samples_leaf=2,min_samples_split=6, n_estimators=1000, random_state=0)
result_RF = RF.fit(X_train, y_train)
RF_y_pred = RF.predict(X_test)

cross_val_RF = cross_val_score(RF, X_train, y_train, scoring = 'accuracy') 
cross_val_RF_f1 = cross_val_score(RF, X_train, y_train, scoring = 'f1') 

accuracy.append(round(accuracy_score(y_test, RF_y_pred),4))
recall.append(round(recall_score(y_test, RF_y_pred),4))
roc_auc.append(round(roc_auc_score(y_test, RF_y_pred),4))
precision.append(round(precision_score(y_test, RF_y_pred),4))

model_names = ['Random Forest']
result_df_RF = pd.DataFrame({'Accuracy':accuracy,'Recall':recall, 'Roc_Auc':roc_auc, 'Precision':precision, 'Cross_val_acc':cross_val_RF.mean(), 'Cross_val_f1':cross_val_RF_f1.mean()}, index=model_names)

result_df_RF

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

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

XGBoost

XGBoost — это мощный и популярный алгоритм повышения градиента, используемый для задач классификации и регрессии. Он объединяет несколько слабых прогностических моделей для создания более надежной и точной модели. Он известен своей скоростью, масштабируемостью и способностью обрабатывать большие и сложные наборы данных. Он также включает расширенные функции, такие как регуляризация, обрезка деревьев и ранняя остановка для предотвращения переобучения. В нашем примере мы не будем использовать все расширенные функции:

accuracy= []
recall =[]
roc_auc= []
precision = []

XGB = XGBClassifier(random_state=0,scale_pos_weight=spw)
result_XGB = XGB.fit(X_train, y_train)
XGB_y_pred = XGB.predict(X_test)

cross_val_XGB = cross_val_score(XGB, X_train, y_train, scoring = 'accuracy') 
cross_val_RF_XGB = cross_val_score(XGB, X_train, y_train, scoring = 'f1') 

accuracy.append(round(accuracy_score(y_test, XGB_y_pred),4))
recall.append(round(recall_score(y_test, XGB_y_pred),4))
roc_auc.append(round(roc_auc_score(y_test, XGB_y_pred),4))
precision.append(round(precision_score(y_test, XGB_y_pred),4))

model_names = ['XGBoost']
result_df_XGB = pd.DataFrame({'Accuracy':accuracy,'Recall':recall, 'Roc_Auc':roc_auc, 'Precision':precision, 'Cross_val_acc':cross_val_RF.mean(), 'Cross_val_f1':cross_val_RF_f1.mean()}, index=model_names)

result_df_XGB

Матрица путаницы для XGBoost и сравнительные результаты приведены ниже:

Как и ожидалось, XGBoost лучше запоминает три модели, однако общая точность ниже, поскольку мы взвешиваем модель, чтобы наказать отрицательные прогнозы.

Нейронная сеть (Keras на тензорном потоке)

Keras — это API глубокого обучения, работающий поверх TensorFlow, платформы машинного обучения. Он был разработан с упором на обеспечение высокой производительности.

Давайте импортируем необходимые библиотеки:

import tensorflow as tf
from tensorflow import keras
from imblearn.over_sampling import SMOTE

Теперь мы исправим дисбаланс данных с помощью SMOTE:

X_train_sm = X_train.copy()
y_train_sm = y_train.copy()

print("Before OverSampling, counts of label '1': {}".format(sum(y_train == 1)))
print("Before OverSampling, counts of label '0': {} \n".format(sum(y_train == 0)))
  
sm = SMOTE(random_state = 2)
X_train_sm, y_train_sm = sm.fit_resample(X_train_sm, y_train_sm.ravel())
  
print('After OverSampling, the shape of X_train_sm: {}'.format(X_train_sm.shape))
print('After OverSampling, the shape of y_train_sm: {} \n'.format(y_train_sm.shape))
  
print("After OverSampling, counts of label '1': {}".format(sum(y_train_sm == 1)))
print("After OverSampling, counts of label '0': {}".format(sum(y_train_sm == 0)))

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

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

Мы также добавим к нашей модели коэффициент отсева 20%, чтобы избежать переобучения.

model_tf = keras.Sequential([
    keras.layers.Dense(16, input_shape=(X_train_sm.shape[1],), activation='relu'),
    keras.layers.Dense(8, activation='relu'),
    keras.layers.Dropout(0.2),
    keras.layers.Dense(4, activation='relu'),
    keras.layers.Dropout(0.2),
    keras.layers.Dense(1, activation='sigmoid')
])

Теперь, когда мы определили нашу модель, давайте теперь скомпилируем модель и запустим ее. Мы скомпилируем его с оптимизатором adam и скоростью обучения по умолчанию. Для функции потерь мы будем использовать binary_crossentropy и оптимизируем нашу модель для обеспечения точности и полноты.

model_tf.compile(optimizer = 'adam',
                loss = 'binary_crossentropy',
                metrics = ['accuracy', keras.metrics.Recall()])
model_tf.fit(X_train_sm, y_train_sm, epochs=100)

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

model_tf_eval = model_tf.evaluate(X_test, y_test)

Наша модель имеет точность 72,2% и отзыв 75,8%. Теперь мы создадим выходные результаты и матрицу путаницы для нашей модели. Поскольку выходные данные нашей модели возвращают значения, близкие к 0 и близкие к 1, но не точно 0 или 1, мы включим дополнительный шаг для округления значений до 0 или 1.

accuracy= []
recall =[]
roc_auc= []
precision = []

tf_y_pred = model_tf.predict(X_test)

accuracy.append(round(model_tf_eval[1],4))
recall.append(round(model_tf_eval[2],4))
tf_ypred_uns = []
for i in tf_y_pred:
    if i>0.5:
        tf_ypred_uns.append(1)
    else:
        tf_ypred_uns.append(0)
roc_auc.append(round(roc_auc_score(y_test, tf_ypred_uns),4))
precision.append(round(precision_score(y_test, tf_ypred_uns),4))


model_names = ['tf']
result_df_tf = pd.DataFrame({'Accuracy':accuracy,'Recall':recall, 'Roc_Auc':roc_auc, 'Precision':precision, 'Cross_val_acc':"n/a", 'Cross_val_f1':"n/a"}, index=model_names)
result_df_tf

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

cm = tf.math.confusion_matrix(labels=y_test, predictions=tf_ypred_uns)
plt.figure(figsize = (10,10))
sns.heatmap(cm, annot=True, fmt='d', cmap='viridis')
plt.xlabel('Predicted label')
plt.ylabel('True label')

Используя TensorFlow, мы получаем более высокую полноту, но более низкую точность из-за более низкой точности. Отзыв значительно выше, чем у XGBoost. Поскольку мы хотим определить отток всех потенциальных клиентов, мы, вероятно, можем позволить себе немного меньшую точность в пользу более высокого отзыва. Модель можно настраивать дальше, но мы не будем ее настраивать в рамках данной статьи.

Заключение

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

Вы можете получить доступ к полному блокноту (кроме TensorFlow) с кодом здесь или блокноту, включающему TensorFlow, здесь.