Churn + uplift: кому дать скидку (причинный таргетинг)

Кратко

Пользователи уходят, и первая реакция продакта — раздать скидки всем, кто «под угрозой». Это ошибка: часть из них осталась бы и так (билет в никуда), часть уйдёт при любом стимуле, а кого-то скидка даже отпугнёт. Правильный конвейер — два уровня. Сначала диагностика оттока: кто уходит, почему, какие сегменты и leading-индикаторы. Затем причинный таргетинг: у кого из «под угрозой» положительный причинный эффект от скидки — CATE выше нуля. Только этих людей и стоит касаться. Метрика успеха — не churn score, а uplift на рандомизированной A/B-группе.

Кто уходит: типология оттока

Прежде чем строить модель, зафиксируйте определение churn. Без единого правила метрики и выводы плывут. Хард-churn — пользователь не совершил целевого действия за окно T (7, 14, 30 дней). Софт-churn — резкое падение активности относительно базового периода.

Типы оттока разделяются по двум осям. По источнику: добровольный (сам удалил аккаунт, отписался) и принудительный (не продлилась подписка, банк заблокировал карту). По времени: ранний (уход в первые дни/недели, до завершения онбординга) и поздний (перестал заходить спустя 2 года). Отдельно — платный отток среди платящих и сезонное замирание активности (нет входов летом, но возвращается осенью). Сезонность — не churn: нужен отдельный сегмент, иначе метрика искажается.

ТипЧто этоСигнал
ДобровольныйПользователь сам ушёлУдаление аккаунта, отписка
ПринудительныйПродукт исключил пользователяНе продлилась подписка, блок карты
РаннийУход в первые дни/неделиНе завершил онбординг
ПозднийУход после длительного использованияПерестал заходить спустя месяцы
ПлатныйОтток среди платящихОтмена подписки
СезонныйВременное замираниеНет входов летом, возврат осенью

Действие: выберите порог неактивности и зафиксируйте его как baseline. Разные пороги меняют метрику и выводы. Сравнивайте churn rate только внутри однотипных сегментов — платящие уходят по другим причинам, чем бесплатные.

Почему уходят: leading-индикаторы и сегментация

После определения churn — сегментация. Разбейте пользователей по когортам, платформе, тарифу. Сравните поведение churn vs retained: частоту действий, время в продукте, NPS. Цель — найти 3–5 leading-индикаторов, которые появляются заранее, а не в момент ухода.

ИндикаторПочему важенРеакция
Падение частоты входовТеряет привычкуPush / re-engagement
Не завершён онбордингНе получил ценностьПодсказка, помощь на 2-й день
Просмотр «Удалить аккаунт»Явный сигнал намеренияТриггерный чат / оффер
Падение кор-фичиСнижение вовлечённостиПерсонализированная рассылка
Снижение NPSРанний сигнал недовольстваОбратная связь + retention-оффер

Actionable-выводы из типичных паттернов:

  • Ранний отток чаще связан с неудачным онбордингом — улучшайте первую сессию.
  • Платный отток лучше всего предсказывает снижение использования кор-фичи, а не NPS.
  • Возврат после 30+ дней неактивности маловероятен — фокус на первые 7–14 дней.
  • Survivorship bias: анализируете только тех, кто остался — рискуете упустить причины ухода.

Для поиска leading-индикаторов подходят survival analysis (Kaplan–Meier, Cox PH через lifelines) и корреляции. Валидируйте на out-of-time: индикатор, который работает на train, но не держится на отложенной выборке, — не индикатор.

Скоринг оттока: churn-модель

Целевая переменная — бинарная (ушёл / остался). Признаки группируются по четырём блокам.

ГруппаПримеры
ПоведениеЧастота входов, сессии, ключевые события, время с последнего визита
МонетизацияARPU, LTV, сумма последних платежей, время с последней покупки
ПродуктКоличество фич, использованных за период, ошибки, обращения в поддержку
КонтекстКанал привлечения, страна, устройство, возраст аккаунта

Модели: логистическая регрессия как baseline, XGBoost / LightGBM / случайный лес — как рабочие. Метрики — ROC-AUC, precision@k, recall, F1. Класс почти всегда несбалансирован (churn 5–15%), поэтому смотрите на PR-кривую и калибровку, а не только на AUC.

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_recall_curve, average_precision_score
from lightgbm import LGBMClassifier

# features — витрина пользователей за окно активности
# target — churn_label (1 = ушёл в окно T после среза)
X_train, X_test, y_train, y_test = train_test_split(
    features, target, test_size=0.2, stratify=target, random_state=42
)

model = LGBMClassifier(
    n_estimators=400, learning_rate=0.05, max_depth=6,
    class_weight="balanced", random_state=42
)
model.fit(X_train, y_train)

proba = model.predict_proba(X_test)[:, 1]
prec, rec, _ = precision_recall_curve(y_test, proba)
print(f"PR-AUC: {average_precision_score(y_test, proba):.3f}")

# churn_score для всего скоринга
features["churn_score"] = model.predict_proba(features)[:, 1]

Главная ловля признаков — лики из будущего. В фичи не должно попадать ничего, что стало известно после момента принятия решения о воздействии. Если обучили на данных, где «время с последней покупки» измерено на конец окна, а решение принимается в его начале, — модель выучит будущий отток и будет бесполезна.

Uplift-таргетинг: кому дать скидку

Churn-модель говорит, кто уйдёт. Она не говорит, кого скидка удержит. Это две разные задачи. Скидка может быть не нужна лояльным (остались бы и так) и бесполезна для тех, кто уйдёт в любом случае. Нужна оценка причинного эффекта — CATE (Conditional Average Treatment Effect).

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

ГруппаРеакцияЧто делать
Sure thingsОстанутся и без скидкиНе тратить бюджет
PersuadablesУйдут без скидки, останутся со скидкойДать скидку
Lost causesУйдут в любом случаеНе тратить бюджет
Sleeping dogsМогут уйти из-за скидки (раздражает рассылка)Избегать

Цель uplift-модели — найти Persuadables. Churn score тут не работает: высокий churn score говорит о вероятности ухода, но не о чувствительности к воздействию. Lost causes тоже имеют высокий churn score — и им скидка бесполезна.

Подходы к оценке CATE:

ПодходСуть
Two-model (T-learner)Две модели: на treated и control. Разность предсказаний = uplift
S-learnerОдна модель с флагом treatment как признаком. Просто, но недооценивает эффект
X-learnerКорректировка T-learner через взвешивание сегментов
Causal ForestНелинейная оценка CATE через ансамбль деревьев с честным разбиением

Метрики — Qini coefficient и AUUC (Area Under Uplift Curve), это аналоги ROC-AUC для причинного эффекта. Финальная валидация — рандомизированный A/B-тест с оценкой ITT и CATE.

import pandas as pd
from causalml.inference.meta import XLearner
from causalml.inference.tree import CausalRandomForestRegressor
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier

# df — исторический A/B на скидку
# W — флаг treatment (1 = получил скидку, 0 = control)
# Y — бинарный исход (1 = удержался)
# X — матрица признаков (те же, что у churn-модели + контекст)

learner = XLearner(
    outcome_learner=RandomForestClassifier(n_estimators=300, random_state=42),
    effect_learner=RandomForestRegressor(n_estimators=300, random_state=42),
)
learner.fit(X=df[feature_cols], treatment=df["W"], y=df["Y"])

df["cate"] = learner.predict(X=df[feature_cols])

# ранжирование: uplift × churn_score × LTV
df["target_value"] = df["cate"] * df["churn_score"] * df["ltv"]
df = df.sort_values("target_value", ascending=False)

Важно: для uplift-модели нужен исторический рандомизированный эксперимент. Если скидка раздавалась не случайно, а «по бизнес-логике» (тем, кто жаловался), модель выучит смещение отбора, а не эффект воздействия. Без A/B uplift не построить честно.

Кейс: uplift=0.15, реальный lift 3pp

Модель предсказала uplift 0.15 — юзер купит со скидкой на 15 п.п. больше, чем без. Рассылаем 1000 таких пользователей. Реальный lift — 3 п.п. Что не так?

Это значимое отклонение от ожидания модели, и причина — bias two-model подхода. В T-learner дисперсии двух моделей складываются, и на границах распределения получаются завышенные uplift-предсказания — false-high. Модель уверена там, где данных мало.

Диагностика и fix:

  • Валидируйте uplift на честно рандомизированной группе, не на hold-out из обучения. A/A-проверка на random-подвыборке должна давать lift ≈ 0.
  • Переходите с T-learner на X-learner или Causal Forest — они корректируют смещение через взвешивание.
  • Retrain на большем датасете с дополнительным feature engineering для ковариат (канал, тариф, предыдущая активность).
  • Проверьте дисбаланс treatment/control: если treated 5% выборки, variance разносит оценку.

Скидка не «слишком маленькая» — это не причина. И uplift «всегда меньше предсказанного на 70–80%» — это не норма, а симптом сломанной валидации. Правильная модель на правильной рандомизации даёт lift, близкий к предсказанному CATE, в пределах доверительного интервала.

Пайплайн

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

1. Собрать фичи за окно активности (без ликов из будущего)
2. Обучить churn-модель → churn_score
3. Использовать исторический A/B на скидку (или провести новый)
4. Обучить uplift-модель → CATE
5. Ранжировать по uplift × churn_score × LTV
6. Запустить кампанию для Persuadables
7. Измерить результат в новом A/B-тесте

Бизнес-логика скидки — не только CATE. Размер: фиксированный, процент, персонализированный через LTV. Канал: push / email / SMS / in-app — с учётом стоимости и спам-риска. Ограничения: бюджет кампании, частота коммуникаций, срок действия. Этика и cannibalization: скидка не должна системно обесценивать продукт — иначе получите деградацию монетизации у тех, кто платил полную цену.

Подводные камни:

  • Лики из будущего в фичи — самая частая причина «модель работает на train, мертва на проде».
  • Несбалансированность treatment/control по историческим данным.
  • Скидка коррелирует с сезонностью и акциями — нужно контролировать через ковариаты.
  • Эффект может зависеть от канала, а не только от размера скидки. Тестируйте канал отдельно.

Выводы

  • Фиксируйте определение churn и порог неактивности до начала анализа. Разные пороги — разные метрики и разные выводы.
  • Ранний отток — проблема онбординга, поздний — падение кор-фичи. Лечите их разными мерами, не одним «retention-оффером всем».
  • Churn score ≠ uplift. Высокий churn score у Lost causes — скидка им бесполезна. Цель таргетинга — Persuadables, а не «все, кто уходит».
  • Uplift-модель требует рандомизированного A/B. На смещённых исторических данных выучит selection bias, не причинный эффект.
  • Two-model подход раздувает variance и даёт false-high uplift. Валидируйте на честной random-группе, переходите на X-learner / Causal Forest.
  • Ранжируйте по uplift × churn_score × LTV, а не по одному churn score. Билет должен идти туда, где максимальный причинный эффект, а не максимальный риск ухода.

Смотрите также

← К статьям