Хорошая бизнес-стратегия для роста доходов — удержание клиентов. Одно исследование ранее показало, что 80% будущей прибыли бизнеса будет приходиться на лишь 20% существующих клиентов компании (1 ). Также по сравнению с привлечением новых клиентов удержание клиентов оценивается в 5–25 раз дешевле (2). Здесь вступают в действие показатели оттока и оттока.

Что такое отток?

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

Коэффициент оттока = (Cb-Ce)/Cb × 100, где Cb — количество клиентов на начало периода, а Ce — количество клиентов на конец периода.

Для телекоммуникационных компаний уровень оттока оценивается в 1,9–2,1% в месяц и 10–67% в год (3). Эти высокие показатели оттока показывают, почему компаниям важно выявлять потенциальных отточников, чтобы попытаться предотвратить потерю дохода. Здесь мы проанализируем набор данных об оттоке из такой компании и применим стратегии машинного обучения для предотвращения потенциальных оттоков.

Получение данных

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

Исследовательский анализ

Как упоминалось ранее, набор данных содержит несколько столбцов с информацией о клиентах:

  • customerID = Уникальный идентификатор для каждого клиента.
  • gender = Клиенты указали пол.
  • SeniorCitizen = Является ли клиент пожилым гражданином (1) или нет (0).
  • Partner = Есть ли у клиента партнер (Да) или нет (Нет).
  • Dependents = Есть ли у клиента иждивенцы (Да) или нет (Нет).
  • tenure = Количество месяцев, в течение которых клиент оставался в компании.
  • PhoneService = Есть ли у клиента телефонная связь (Да) или нет (Нет).
  • MultipleLines = Есть ли у клиента несколько линий (Да) или нет (Нет или Нет телефонной связи).
  • InternetService = интернет-провайдер клиента (DSL, оптоволокно, нет).
  • OnlineSecurity = есть ли у клиента онлайн-безопасность (да) или нет (нет или нет интернет-сервиса).
  • OnlineBackup = Есть ли у клиента онлайн-резервное копирование (Да) или нет (Нет или Нет интернет-сервиса).
  • DeviceProtection = есть ли у клиента защита устройства (да) или нет (нет или нет интернет-сервиса).
  • TechSupport = Есть ли у клиента техническая поддержка (Да) или нет (Нет или Нет интернет-сервиса).
  • StreamingTV = Есть ли у клиента потоковое телевидение (Да) или нет (Нет или Нет интернет-сервиса).
  • StreamingMovies = Есть ли у клиента потоковое воспроизведение фильмов (Да) или нет (Нет или Нет интернет-сервиса).
  • Contract = Срок контракта клиента (Месяц за месяц, Один год, Два года).
  • PaperlessBilling = Есть ли у клиента безбумажный биллинг (Да) или нет (Нет).
  • PaymentMethod = способ оплаты клиента (электронный чек, чек по почте, банковский перевод (автоматически), кредитная карта (автоматически)).
  • MonthlyCharges = Сумма, взимаемая с клиента ежемесячно.
  • TotalCharges = Общая сумма, выставленная клиенту.
  • Churn = Ушел ли клиент (Да) или нет (Нет).

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

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

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

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

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

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

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

На первый взгляд может показаться, что переменные, содержащие No, Yes и No internet service, представляют собой перекрытие между No и No internet service. Это неправда. Наличие одной из этих записей как No означает, что у клиента может быть интернет-услуга, но нет конкретных дополнительных услуг, упомянутых в столбце. Мы можем проверить этот факт.

Доказав это, мы оставляем эти переменные нетронутыми. Хотя это представляет собой избыточную информацию, изменение этих переменных на No дало бы неверную информацию для нашей модели.

Еще один интересный момент — классовый дисбаланс. В нашем наборе данных классы, которые могут оказывать влияние на наш набор данных, — это переменные gender (которые могут влиять на некоторые наборы данных, в зависимости от контекста, но я не думаю, что это относится к нашему текущему анализу) и наша целевая переменная Churn. Давайте проверим, как они распределяются.

На этих графиках видно, что gender не представляет собой проблему дисбаланса, а Churn — да. Еще один аспект нашего набора данных, который необходимо рассмотреть, — это то, как кодируются переменные. Поскольку категориальные переменные являются строками, мы перекодируем их в следующем сеансе.

Кодирование категориальных переменных

Теперь перекодируем наши переменные. Числовые переменные останутся прежними, в то время как двоичные переменные будут закодированы с использованием подхода LabelEncoder (включая нашу целевую переменную), а категориальные переменные с более чем двумя элементами будут преобразованы в фиктивные переменные.

Модель машинного обучения

Предварительный анализ

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

Данные будут разделены, поэтому тестовые данные будут изолированы от данных обучения/проверки, в которых мы будем строить и тестировать наши модели. Поскольку мы используем несбалансированный набор данных, мы используем подход стратифицированной выборки при разделении набора данных, чтобы сохранить относительные частоты классов. Для балансировки набора данных мы будем использовать подход Random Under Sampling (RUS) из библиотеки imbalanced-learn для случайного выбора образцов из класса по умолчанию. Подробнее о RUS можно узнать здесь. Это улучшит наши показатели отзыва, которые будут предпочтительным показателем оценки из-за дисбаланса. Перед этим мы также стандартизируем данные. Я создал простенькую функцию для предварительного анализа классификационных моделей (подробности в блокноте), результаты были такие:

Основываясь на этих предварительных результатах, мы видим, что LGBMClassifier превосходит другие методы, когда мы рассматриваем recall и его стандартное отклонение, а также время, необходимое для выполнения вычислений. Наш лучший recall был найден в модели LogisticRegression, но она предлагает мало параметров для настройки модели по сравнению с моделью повышения градиента LightGBM. Поскольку говорят, что LightGBM плохо работает с наборами данных ниже 10 000 наблюдений, мы будем использовать модель, которая была так же эффективна в этих результатах, а именно XGBClassifier или XGBoost.

Настройка модели XGBoost

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

  • Изменение параметров сложности = max_depth, min_child_weight и gamma.
  • Добавление случайности (устойчивости к шуму) = subsample, colsample_bytree и eta (уменьшение требует увеличения num_round).

Хорошей отправной точкой в ​​моделях повышения градиента является настройка скорости обучения (learning_rate) и количества оценок (n_estimators) в модели. После настройки для них мы настроим параметры сложности. Мы не будем добавлять случайность в модель, так как нам еще предстоит увидеть, как наша модель будет работать на тестовых данных. Эти шаги будут выполнены с помощью подхода перекрестной проверки поиска в сетке.

Сначала мы начали нашу модель с learning_rate0,1 и настроили на n_estimators:

Best score was: 0.78 for {'n_estimators': 50}

Видя, что 50 дает лучший результат, мы повторно инициализируем нашу модель, используя эти параметры, и ищем другие. Теперь попробуем оптимизировать для max_depth, min_child_weight и gamma.

Best score was: 0.82 for {'gamma': 0.0, 'max_depth': 1, 'min_child_weight': 1}

Теперь мы оцениваем лучшее значение для learning_rate:

Best score was: 0.89 for {'learning_rate': 0.01}

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

Тестируем нашу модель

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

              precision    recall  f1-score   support

           0       0.93      0.57      0.71      1294
           1       0.43      0.89      0.58       467

    accuracy                           0.66      1761
   macro avg       0.68      0.73      0.64      1761
weighted avg       0.80      0.66      0.67      1761

     ROC AUC       0.73

Мы видим, что на этапе тестирования мы получили результаты, аналогичные результатам обучающей модели. Отзыв или чувствительность — это способность нашей модели правильно идентифицировать все положительные случаи. Хотя, безусловно, есть возможности для улучшения (с помощью выбора функций/разработки или дальнейшей настройки гиперпараметров), мы добились хороших показателей отзыва как на этапах обучения, так и на этапах тестирования. Но что здесь означает показатель отзыва 89%? Как это связано с оттоком и коэффициентом оттока?

Коэффициент оттока

Как упоминалось в начале, Churn Rate представляет собой процент клиентов, отказавшихся от услуг компании за анализируемый период времени. Для телекоммуникационных компаний уровень оттока оценивается в 1,9–2,1% в месяц и 10–67% в год (3). Еще одним полезным KPI для оценки оттока является валовой коэффициент оттока MRR. Суммарный ежемесячный повторяющийся доход, в свою очередь, представляет собой процент дохода, потерянного компанией из-за оттока клиентов (4).

Эти KPI могут быть рассчитаны следующим образом:

Коэффициент оттока = (Cb-Ce)/Cb × 100, где Cb — количество клиентов на начало периода, а Ce — количество клиентов на конец периода.

Общий коэффициент оттока MRR = MRRc/MRRt × 100, где MRRc — это MRR, потерянный из-за оттока в этом месяце, а MRRt — это общий MRR, который был бы без потери клиентов за этот период.

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

Из этих результатов видно, что мы смогли идентифицировать большинство оттока в этом наборе данных. Среди ушедших в наборе данных мы определили 23% из 26% ушедших (отличие от реального числа ушедших на 3%), что составляет 29 704,05 доллара валового MRR.

Заключение

В целом, не прилагая особых усилий для настройки гиперпараметров, мы смогли получить уровень отзыва 89% как в обучающих, так и в тестовых данных. Благодаря этому мы смогли определить 23% из 26% оттока в наборе данных. Стратегии компании, направленные на удержание этих клиентов, могут предотвратить потерю 29 704,05 долларов в месяц. Это открывает большие перспективы для компаний, которые имеют дело с высокой текучестью кадров.

На странице набора данных на Kaggle указано, что данные об оттоке представляют оттоки, которые ушли в течение последнего месяца анализируемого промежутка времени. Это означает, что в наших данных показатель оттока за месяц намного выше (26,5%), чем в среднем (1,9–2,1%).

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

Рекомендации

1: https://smallbiztrends.com/2016/10/customer-retention-statistics.html

2: https://hbr.org/2014/10/the-value-of-keeping-the-right-customers

3: https://www.dbmarketing.com/telecom/churnreduction.html

4: https://www.geckoboard.com/best-practice/kpi-examples/gross-mrr-churn-rate/

Спасибо за чтение :)

Подробный разбор: Jupyter Notebook
Где меня найти: GitHub | ЛинкедИн