Удержание клиентов является первоочередной задачей для всех компаний, независимо от отрасли или бизнес-модели, будь то большие, средние или малые компании. Конкуренция еще выше в SaaS или других отраслях, основанных на подписке.
Сохранение клиентов становится более важным там, где стоимость привлечения новых клиентов (CAC) высока, например, в сфере технологий, телекоммуникаций и финансов.
Для начала мы можем определить показатель оттока клиентов как процент клиентов, которые отменили свои подписки и решили больше не использовать ваш продукт.
Поскольку мы можем согласиться с тем, насколько важен уровень оттока для всех компаний, его прогнозирование является очень сложной задачей. Особенно, если вы хотите заблаговременно определить клиентов с высоким риском оттока (в этом весь смысл!). Выявление таких рисковых клиентов будет представлять собой дополнительный потенциальный источник дохода для компаний. К счастью, есть много способов выполнить такую задачу с помощью машинного обучения.
Основная цель «модели прогнозирования оттока клиентов» состоит в том, чтобы удержать клиентов с высоким риском оттока путем активного взаимодействия с ними. Здесь я представлю вымышленный бизнес-кейс, в котором компания предложит 10% скидку на стоимость пожизненного контракта тем, кто выявление рисковых клиентов.
Будем считать:
- Стоимость пожизненного контракта = 1000$
- Значение скидки = 100$
- предоставляется клиентам с высоким риском оттока
Основополагающие концепции, с которыми нам необходимо сначала договориться:
- Мы хотим прогнозировать отток клиентов или выявлять рискованных клиентов заранее, т. е. до того, как они уйдут (будь то месяц, квартал, полгода или какое у вас бизнес-кейс). для этого нам нужно быть осторожными, когда дело доходит до даты отсечки. Потому что включение информации после даты отсечения вызовет проблему утечки данных в нашей модели машинного обучения.
- В реальной жизни вам придется немного больше поработать, чтобы создать «столбец состояния оттока» или («целевой» столбец в терминах ML) — обычно это сложная, но очень важная задача.
- Мы обучаем нашу модель/модели на исторических данных. Основываясь на историческом поведении клиента, мы хотим определить схему оттока.
- После того, как модель будет создана и развернута, мы будем периодически вводить в нее данные клиента, чтобы извлечь «вероятности взбалтывания».
- Клиенты с высоким риском оттока будут привлечены и им будет предложена скидка 10%, чтобы зафиксировать их для другого контракта (или, по крайней мере, для продолжения их контракта).
- Клиенты с низкой вероятностью оттока в соответствии с моделью, никаких действий не требуется.
Теперь, когда бизнес-разговор вышел за рамки, давайте запачкаем руки технической реализацией. Пайплайн нашего проекта будет следующим:
- получить данные
- Очистите и подготовьте его
- Разделите наш подход на 4 подхода (из-за несбалансированного целевого класса)
- Без обработки несбалансированного класса данных
- обработка несбалансированного класса данных посредством передискретизации (SMOTE)
- обработка несбалансированного класса данных посредством недостаточной выборки
Используйте библиотеку AutoML, например #PyCaret или #H2O, для построения базовой модели для сравнения.
Разработать 8–9 моделей для каждого подхода
- Настройте эти модели во всех 4 подходах
- оценивать и извлекать информацию из этих моделей
- проверить экономическое обоснование в каждой из моделей во всех 4 подходах
Из-за нехватки времени я расскажу о реализации SMOTE (передискретизация) здесь, чтобы получить представление о реализации всех 4 подходов и получить коды, пожалуйста, не стесняйтесь посетить мой GitHub: «https://github.com/Hussam1/ отток клиентов"
GitHub — Hussam1/customer_churn: Telecom_customer_churn
Мотивация: в рамках моих проектов учебных курсов по науке о данных мы намерены провести «моделирование оттока клиентов для компании…github .com»
Приступаем к кодированию:
Сначала загружаем все необходимые библиотеки:
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns sns.set() from sklearn.model_selection import train_test_split from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer, KNNImputer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.metrics import confusion_matrix, classification_report, f1_score from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier from sklearn.svm import LinearSVC from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier, GradientBoostingClassifier from xgboost import XGBClassifier from imblearn.over_sampling import SMOTENC
примечание: при появлении ошибок, скорее всего, вам нужно загрузить библиотеку в свою среду — попробуйте !pip install [library_name]
Импорт набора данных:
df= pd.read_csv(path_to_your_datasets)
чтобы иметь некоторую информацию о типе и доступности нулевых/пустых строк:
df.info()
После некоторого исследования качество данных относительно хорошее. Требуется некоторая корректировка:
- Измените тип [‘TotalCharges’] на числовой
- переназначить [‘SeniorCitizen’] на «Да» и «Нет» вместо 0 и 1
- Некоторые значения в столбце [‘tenure’] были равны 0, что могло указывать на очень нового клиента или просто на проблему с качеством данных — в любом случае, для простоты мы опустим их здесь. в реальной жизни вам нужно очень хорошо все изучить, прежде чем что-то уронить.
Разработка модели:
как указано, мы будем тестировать несколько моделей:
- Логистическая регрессия
- КНН
- Метод опорных векторов (SVM)
- Древо решений
- RandomForest
- АдаБуст
- Градиент Достойный
- XGBoost
первый вопрос: как корреляция между столбцами/функциями?
plt.figure(figsize=(25, 10)) corr = df.apply(lambda x: pd.factorize(x)[0]).corr() mask = np.triu(np.ones_like(corr, dtype=bool)) ax = sns.heatmap(corr, mask=mask, xticklabels=corr.columns, yticklabels=corr.columns, annot=True, linewidths=.2, cmap='coolwarm', vmin=-1, vmax=1)
Мы видим много функций с сильной корреляцией, хотя и не идеальной, но все же я бы рекомендовал исследовать некоторые из них — все, что выше 0,8, на мой взгляд, стоит проверить.
сначала разделим данные на обучение и тестирование:
X = df.drop(columns=['Churn']) y = df['Churn'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
Отдельные категориальные и числовые признаки:
categorical_feature_idxs = np.where(X_train.dtypes == "object")[0] smtnc = SMOTENC(categorical_features=categorical_feature_idxs) X_train_smote, y_train_smote = smtnc.fit_resample(X_train, y_train) y_train_smote.value_counts()
мы можем видеть, что «SMOTE» помог нам справиться с несбалансированным классом в целевом столбце за счет передискретизации недопредставленного класса. Мы начали:
у нас было 3614 строк со значением класса «НЕТ», т. е. не взбито, и 1308 строк со значением класса «Да». Однако теперь, после использования SMOTE, мы имеем одинаковое представление обоих значений в наших наборах данных:
Создайте конвейер для преобразования:
# Separate categorical from numerical columns categorical_features = X_train.select_dtypes(include=['object']).columns.tolist() numeric_features = X_train.select_dtypes(exclude=['object']).columns.tolist() # Create different transformation for each categorical_transformer = Pipeline(steps=[("onehot", OneHotEncoder(handle_unknown="ignore"))]) numeric_transformer = Pipeline(steps=[("scaler", StandardScaler())]) # Create pre-processer which will be fed into each and every model preprocessor = ColumnTransformer(transformers=[("num", numeric_transformer,numeric_features), ("cat", categorical_transformer,categorical_features)])
Создание моделей, начиная с логистической регрессии
# initiate model and the preprocessing pipeline we created earlier lr_model = LogisticRegression(random_state=42, solver='liblinear') pipeline_lr = Pipeline(steps=[("pre_process", preprocessor), ("model", lr_model)]) # Extract the results pipeline_lr.fit(X_train_smote, y_train_smote) y_pred = pipeline_lr.predict(X_test) class_labels = pipeline_lr.named_steps['model'].classes_ print(classification_report(y_test, y_pred)) cf = confusion_matrix(y_test, y_pred) sns.heatmap(cf, annot=True, fmt='.0f');
Хорошие первые результаты! смогли отозвать 73% ушедших клиентов. т. е. из тех, что в итоге вышли (412 + 149), наша модель предсказала/выделила 412
Давайте продолжим и попробуем: KNN
# as usual, initiate the model and its preprocessing pipeline we created earlier knn = KNeighborsClassifier() pipeline_knn = Pipeline([("pre_process", preprocessor), ("model", knn)]) # Running the model and extracting insights from it pipeline_knn.fit(X_train_smote, y_train_smote) y_pred = pipeline_knn.predict(X_test) class_labels = pipeline_knn.named_steps['model'].classes_ print(classification_report(y_test, y_pred)) cf = confusion_matrix(y_test, y_pred) sns.heatmap(cf, annot=True, fmt='.0f');
Эта модель привела к немного менее точным результатам. Модель KNN напомнила только 67% класса «ДА», а также дала нам более низкий F1-Score 73% (подумайте о F1 Score как о хорошем балансе между показателями точности и отзыва)
Теперь попробуем наш любимый: SVM
# as usual, initiate the model and its preprocessing pipeline we created earlier svc = LinearSVC(random_state=42) pipeline_svc = Pipeline([("pre_process", preprocessor), ("model", svc)]) # Running the model and extracting insights from it pipeline_svc.fit(X_train_smote, y_train_smote) y_pred = pipeline_svc.predict(X_test) class_labels = pipeline_svc.named_steps['model'].classes_ print(classification_report(y_test, y_pred)) cf = confusion_matrix(y_test, y_pred) sns.heatmap(cf, annot=True, fmt='.0f');
Более того, 74% вспоминают о классе «ДА» и 75% F1-Score.
попробуем AdaBoost:
# as usual, initiate the model and its preprocessing pipeline we created earlier ada_boost = AdaBoostClassifier(random_state=42) pipeline_ada_boost = Pipeline([("pre_process", preprocessor_new), ("model", ada_boost)]) # Running the model and extracting insights from it pipeline_ada_boost.fit(X_train_smote, y_train_smote) y_pred = pipeline_ada_boost.predict(X_test) class_labels = pipeline_ada_boost.named_steps['model'].classes_ print(classification_report(y_test, y_pred)) cf = confusion_matrix(y_test, y_pred) sns.heatmap(cf, annot=True, fmt='.0f');
Неплохие результаты, правда! 76% вспоминают о классе «Да» и 76% F1-Score. Последнее, что я буду запускать XGBoost:
# as usual, initiate the model and its preprocessing pipeline we created earlier xgb = XGBClassifier(random_state=42) pipeline_xgb = Pipeline([("pre_process", preprocessor_new), ("model", xgb)]) # Running the model and extracting insights from it pipeline_xgb.fit(X_train_smote, y_train_2) y_pred = pipeline_xgb.predict(X_test) class_labels = pipeline_xgb.named_steps['model'].classes_ print(classification_report(y_test_2, y_pred)) cf = confusion_matrix(y_test_2, y_pred) sns.heatmap(cf, annot=True, fmt='.0f');
С той же концепцией мы можем продолжить работу с остальными моделями. для подробного кода, пожалуйста, посетите мой GitHub и не стесняйтесь использовать все, что хотите.
Помните, мы говорили о проверке результатов с помощью бизнес-кейса? Напоминаем, что наше экономическое обоснование выглядит следующим образом:
- Компания свяжется со всеми, кто определен как «высокий риск» оттока
- Предложите им 10% скидку на стоимость их пожизненного контракта, чтобы обеспечить их обязательство продолжать свой контракт до конца или подпишитесь на новый (предположим, что стоимость контракта составляет 1000 долларов США).
Столбец «Net_business_win» в приведенной ниже таблице представляет расчет для такого случая. Мы видим, что модель AdaBoost принесла самые высокие успехи в бизнесе, а модель XGBoost обеспечила самый высокий балл F1. Здесь важно подчеркнуть, что мы не должны принимать какую-либо метрику за чистую монету и предполагать, что она полезна для нас. нам нужно углубиться в каждую метрику и убедиться, что мы выбираем правильную для нашего случая и ту, которая дает нам наибольшую добавленную стоимость в зависимости от проблемы, которую мы пытаемся решить.
Эти цифры не самые лучшие, мы можем улучшить наши модели, попробовав:
- Исследуйте коррелированные столбцы
- Запустите настройку гиперпараметров, чтобы найти наилучшие параметры для выбранной модели/моделей.
- Сотрудничайте с экспертами в области бизнеса, чтобы попытаться создать больше функций (столбцов), которые могли бы помочь нам определить закономерности оттока клиентов.