Прогнозирование того, что клиенты откажутся от телефонного обслуживания

В этом проекте я пытаюсь предсказать клиентов, которые отказываются от телефонных услуг, чтобы что-то сделать, чтобы удержать этих клиентов.
Набор данных, который я использую для этого, взят из openml.org, и вы можете щелкнуть здесь для прямой ссылки на этот набор данных.

Несбалансированный набор данных

Этот набор данных был несбалансированным, что создавало некоторые проблемы. Сначала давайте посмотрим на дисбаланс:

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

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

Я использовал анализ затрат и выгод для оценки моделей. Расчет для этого был следующим: по информации, которая у меня была, средний ежемесячный счет составлял 60 долларов. Затем я подсчитал, что для продолжительности 24-месячного контракта, получилось 1440 долларов. Затем я выбрал сумму промоакции в размере 500 долларов, просто для целей расчета.
Любые истинно отрицательные или клиенты, которые правильно спрогнозированы как оставшиеся, ничего не добавляют и не вычитают.
Любые ложно отрицательные или клиенты, которые будут отменены, но прогнозируется, что остающиеся убытки составят 1440 долларов каждый.
Любым истинным положительным результатам или покупателям, которые будут отменять и которые прогнозируются как отменяющие, будет предложена специальная акция, поэтому прибыль составит 1440 долларов - 500 долларов = 940 долларов. .
Любые ложные срабатывания или клиенты, которые, по прогнозам, уходят, но не уходят, будут проданы по акции и, следовательно, будут потеряны в размере 500 долларов США.

Соотношение функций

Еще одна вещь, которую я заметил вначале, касалась соотношения функций:

Не большая корреляция с целью, которая была «классовой» особенностью, некоторые - около 0,2, а также минуты и заряд коррелируют почти на 100%. Это имело смысл, поскольку в то время, когда были опубликованы эти данные, протоколы были самой важной частью счета.

Моделирование

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

Экономическая выгода от этого составила 71 320 долларов США, или фактическая прибыль от удержания клиентов, которые должны были бы уйти, составила бы 589 долларов США на каждого клиента, который фактически ушел бы ранее, если бы все 121 клиент, который, по нашим прогнозам, ушел, остались бы.
После пятикратной перекрестной проверки средняя рентабельность для тестового набора составила 51 152 доллара.

Перекрестная проверка

Просто примечание о перекрестной проверке при работе с несбалансированными данными. Поскольку вы должны только обрабатывать данные обучения, а затем прогнозировать на основе реальных данных теста, это ограничивает возможности выполнения перекрестной проверки. Обычные методы перекрестной проверки с этим не сработают.
Чтобы бороться с этим, я использовал стратифицированные K-сгибы, которые берут одинаковое количество из каждой категории, так что вы получаете одинаковые разбиения для каждой складки.
Затем я получил каждое разбиение и применил SMOTE к обучающие данные, обучили модель на этом. Сохраняли оценки для каждой складки, а затем усредняли их.

Вот пример кода:

skf = StratifiedKFold(n_splits=5) # Set up the splits
X_trains2 = {} # Make dictionaries for each train and test split
X_tests2 = {}
y_trains2 = {}
y_tests2 = {}
for i, (train_index, test_index) in enumerate(skf.split(X, y)):
# Get the folds and make the index number the key in each dict
 X_trains2[i] = X.loc[train_index]
 X_tests2[i] = X.loc[test_index]
 y_trains2[i] = y[train_index]
 y_tests2[i] = y[test_index]
scores2 = [] # Make a list to put all scores into 
train = 0 # Setup to save all train scores 
test = 0 # Setup to save all test scores
cb = 0 # Cost-Benefit numbers
for i in range(5):
 smoted_x, smoted_y = sm.fit_sample(X_trains3[i], y_trains3[i])     # SMOTE the training splits
 rf5.fit(smoted_x, smoted_y) # Fit the model
 trainpred = rf5.predict(X_trains3[i]) # Predict train (not Smoted)
 testpred = rf5.predict(X_tests3[i]) # Predict test
 train += recall_score(y_trains3[i], trainpred) # Total all train recall scores for each loop
 test += recall_score(y_tests3[i], testpred) # Total all train recall scores for each loop
 
 cb += cost_benefit(y_tests3[i], testpred) # Total the Cost-benefit scores
 scores2.append((i, recall_score(y_trains3[i], trainpred), recall_score(y_tests3[i], testpred))) # Append a tuple of index, train recall total and test recall total)
 print(f’\n{recall_score(y_tests3[i], testpred)}’)
 cm(y_tests2[i], testpred) # Print a confusion matrix
 print(classification_report(y_tests3[i], testpred))
print(train/5, test/5, cb/5) # Print the total scores / # of Folds

Важность функции

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

Я сделал некоторую разработку функций, чтобы объединить все минуты и все расходы в 2 функции. Поскольку эти две функции сильно коррелированы, я опустил столбец минут. Я также отбросил все столбцы номеров вызовов, а также столбцы state и account_length, поскольку они были близки к нулю по важности функции.
Я также отбросил number_vmail_messages, поскольку это сильно коррелировало с наличием плана голосовой почты.
В итоге я выделил 4 функции, вот они с соответствующей важностью:

Запуск модели только с этими функциями с 5-кратной перекрестной проверкой принес рентабельность до 56 964 долларов.
Это показало мне, что модель стала лучше, комбинируя и отбрасывая функции в зависимости от важности функций.

Анализ функций по важности

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

Цифры по счетам более 72 долларов были следующими:
Ушел: 315
Остался: 247

Учитывая, что в общих данных процент оставшихся составляет 85% против 15%, очень важно, чтобы больше людей уезжали с более высокой скоростью, чем оставались.
Это также соответствовало проверке того, как клиенты с счетами более 72 долларов пересекались с другими функциями.
Если бы мы предложили акцию на 500 долларов каждому со средним счетом более 72 долларов, то за каждого оставшегося клиента мы получили бы 547 долларов, что аналогично тому, что мы получили бы по всему набору данных.
Я бы сделал это. Предлагаем изучить этот вопрос подробнее, чтобы увидеть, как лучше всего обслуживать клиентов с более высокими счетами, чтобы удержать их больше.

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

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

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