Прогнозування відтоку (churn prediction) ідентифікує клієнтів, які ймовірно підуть, до того як вони це зроблять. Це один з ML застосувань з найвищим ROI - утримання існуючих клієнтів коштує в 5-25 разів менше ніж залучення нових. Хороші churn моделі дозволяють проактивні retention кампанії, націлені на правильних клієнтів у правильний час.
Де це використовується
Прогнозування відтоку критичне в subscription та service бізнесах:
- Телеком: Скасування мобільних та інтернет послуг
- SaaS: Скасування software підписок
- Стрімінг: Відтік підписок Netflix, Spotify
- Банкінг: Закриття рахунків, скасування кредитних карток
- Геймінг: Прогнозування drop-off гравців
- Рітейл: Прогнозування lapse клієнтів
Компанії: Великі телеком провайдери, всі subscription бізнеси, loyalty програми.
Типові дані
| Аспект | Типові значення |
|---|---|
| Клієнти | 100K-10M |
| Churn rate | 2%-15% щомісяця |
| Кількість фіч | 50-500 |
| Горизонт прогнозу | 30-90 днів |
| Історичні дані | 1-3 роки |
Джерела даних: - Дані використання (логіни, транзакції, engagement) - Історія білінгу та платежів - Взаємодії з підтримкою - Інформація про акаунт та tenure - Marketing touchpoints - Зовнішні дані (демографія, ринок)
Що ви робите на практиці
- Визначте churn - Що саме означає churn для вашого бізнесу?
- Встановіть горизонт прогнозу - На скільки вперед прогнозувати?
- Створіть observation windows - Історичні фічі та періоди міток
- Інженерія фіч - Тренди використання, engagement, поведінкові сигнали
- Обробіть imbalance - Churn типово minority class
- Натренуйте модель - Classification або survival модель
- Калібруйте ймовірності - Точні scores для бізнес-рішень
- Оцініть бізнес-вплив - Збережений дохід, не тільки AUC
- Деплойте retention actions - Тригеріть кампанії на основі ризику
- Виміряйте ефективність інтервенцій - Чи зменшили дії churn?
Спочатку бейзлайн
Почніть з простих правил перед ML:
# Rule-based бейзлайн
def simple_churn_rules(customer):
"""Базові індикатори churn."""
risk_score = 0
# Немає активності за останні 30 днів
if customer.days_since_last_activity > 30:
risk_score += 3
# Використання падає
if customer.usage_trend_30d < -0.3:
risk_score += 2
# Скарги в підтримку
if customer.complaints_last_90d > 2:
risk_score += 2
# Проблеми з платежами
if customer.failed_payments_last_90d > 0:
risk_score += 2
return risk_score >= 4 # Поріг високого ризику
Чому rule бейзлайн: - Доменні експерти знають ключові churn сигнали - Інтерпретований і легко валідувати - Встановлює що ML має побити - Може бути достатнім для малого масштабу
Feature Engineering
Churn фічі типово походять з поведінкових патернів:
| Категорія | Фічі |
|---|---|
| Engagement | Частота логінів, тривалість сесій, використання фіч |
| Тренди | Зміна використання week-over-week, падаючий engagement |
| Tenure | Вік акаунту, тривалість підписки, історія апгрейдів |
| Білінг | Затримки платежів, зміни плану, історія знижок |
| Підтримка | Кількість тікетів, типи скарг, час вирішення |
| Соціальне | Зроблені referrals, соціальні зв'язки, community |
Temporal feature engineering:
def create_churn_features(customer_id, observation_date):
"""Створити фічі для прогнозування churn."""
features = {}
# Activity windows
for window in [7, 14, 30, 60, 90]:
activity = get_activity(customer_id,
end=observation_date,
days=window)
features[f'logins_{window}d'] = activity.login_count
features[f'sessions_{window}d'] = activity.session_count
features[f'duration_{window}d'] = activity.total_duration
# Тренди (порівняння нещодавнього з історичним)
recent = get_activity(customer_id, end=observation_date, days=14)
historical = get_activity(customer_id,
end=observation_date - timedelta(14),
days=14)
if historical.login_count > 0:
features['login_trend'] = (
recent.login_count - historical.login_count
) / historical.login_count
else:
features['login_trend'] = 0
# Recency
last_activity = get_last_activity(customer_id, observation_date)
features['days_since_activity'] = (
observation_date - last_activity
).days
# Білінг
features['payment_failures_90d'] = count_payment_failures(
customer_id, observation_date, days=90
)
return features
Обробка Class Imbalance
Churn типово 2-10% клієнтів - сильний imbalance:
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
from imblearn.pipeline import Pipeline
# Опція 1: SMOTE oversampling
smote = SMOTE(sampling_strategy=0.3)
X_resampled, y_resampled = smote.fit_resample(X_train, y_train)
# Опція 2: Class weights
model = XGBClassifier(
scale_pos_weight=len(y_train[y_train==0]) / len(y_train[y_train==1])
)
# Опція 3: Комбінований підхід
pipeline = Pipeline([
('under', RandomUnderSampler(sampling_strategy=0.5)),
('over', SMOTE(sampling_strategy=0.8))
])
# Опція 4: Cost-sensitive threshold
# Не ресемплювати, налаштувати поріг рішення на основі витрат
Оптимізація порогу:
def optimize_threshold(y_true, y_proba,
cost_fn=1, cost_fp=0.1, cost_tp=-0.5):
"""Знайти поріг що мінімізує бізнес-витрати."""
thresholds = np.arange(0.1, 0.9, 0.01)
costs = []
for thresh in thresholds:
y_pred = (y_proba >= thresh).astype(int)
fn = ((y_true == 1) & (y_pred == 0)).sum() # Пропущені churners
fp = ((y_true == 0) & (y_pred == 1)).sum() # Фальшиві тривоги
tp = ((y_true == 1) & (y_pred == 1)).sum() # Спіймані churners
total_cost = fn * cost_fn + fp * cost_fp + tp * cost_tp
costs.append(total_cost)
best_idx = np.argmin(costs)
return thresholds[best_idx]
Підходи моделей
| Підхід | Плюси | Мінуси | Коли використовувати |
|---|---|---|---|
| Logistic Regression | Інтерпретований, швидкий | Обмежені патерни | Бейзлайн, regulated |
| Gradient Boosting | Найкраща точність | Менш інтерпретований | Більшість випадків |
| Survival Analysis | Обробляє censoring, time-to-event | Складніший | Timing підписки |
| Neural Networks | Sequence патерни | Потребує більше даних | Поведінкові sequences |
Приклад Gradient Boosting:
from lightgbm import LGBMClassifier
model = LGBMClassifier(
n_estimators=200,
learning_rate=0.05,
num_leaves=31,
class_weight='balanced',
random_state=42
)
model.fit(X_train, y_train)
# Probability predictions для risk scoring
churn_proba = model.predict_proba(X_test)[:, 1]
Survival Analysis підхід:
from lifelines import CoxPHFitter
# Підготувати дані з duration та event
df['duration'] = (df['observation_date'] - df['signup_date']).dt.days
df['churned'] = df['churn_date'].notna().astype(int)
# Cox Proportional Hazards
cph = CoxPHFitter()
cph.fit(df[features + ['duration', 'churned']],
duration_col='duration',
event_col='churned')
# Ймовірність виживання на 30 днів
survival_30d = cph.predict_survival_function(
df[features], times=[30]
).T
churn_prob_30d = 1 - survival_30d
Оцінка та метрики
| Метрика | Що вимірює | Типова ціль |
|---|---|---|
| AUC-ROC | Якість ранжування | > 0.75 |
| Precision at K | Якість топ-K ризикованих | > 50% |
| Recall at threshold | Спіймані churners | > 60% |
| Lift at decile 1 | Покращення над random | > 3x |
| Expected value | Вплив на дохід | Позитивний ROI |
Бізнес-орієнтована оцінка:
def calculate_lift_chart(y_true, y_proba, n_bins=10):
"""Розрахувати lift по децилях."""
df = pd.DataFrame({'true': y_true, 'proba': y_proba})
df['decile'] = pd.qcut(df['proba'], n_bins, labels=False)
lift = df.groupby('decile').agg({
'true': ['sum', 'count', 'mean']
})
lift.columns = ['churners', 'total', 'churn_rate']
lift['lift'] = lift['churn_rate'] / y_true.mean()
return lift
# Топ дециль має мати найвищу концентрацію churners
lift_chart = calculate_lift_chart(y_test, churn_proba)
print(f"Lift у топ децилі: {lift_chart.loc[9, 'lift']:.2f}x")
Режими відмов та чеклист налагодження
- [ ] Leakage: Використання churn-related фіч (cancellation calls)
- [ ] Неправильне observation window: Фічі обчислені після prediction point
- [ ] Survivorship bias: Аналіз тільки поточних клієнтів
- [ ] Definition drift: Визначення churn змінюється з часом
- [ ] Intervention leakage: Минулі retention дії у фічах
- [ ] Сезонність: Не враховувати сезонні патерни
- [ ] Segment blindness: Модель працює для average, падає для сегментів
Чеклист для продакшну
Перед деплоєм: - [ ] Визначення churn валідоване з бізнесом - [ ] Feature pipeline gracefully обробляє missing data - [ ] Модель калібрована для accuracy ймовірностей - [ ] Поріг оптимізований для бізнес-витрат - [ ] Intervention стратегія визначена на risk tier
Після деплою: - [ ] Прогнози генеруються за розкладом (daily/weekly) - [ ] Risk scores пушаться в CRM/marketing системи - [ ] Продуктивність моделі моніториться vs actuals - [ ] Ефективність інтервенцій відстежується - [ ] Feature drift моніториться
Інтеграція інтервенцій: - [ ] Risk tiers визначені (high/medium/low) - [ ] Retention actions на tier (знижка, outreach, контент) - [ ] Holdout група для вимірювання впливу інтервенції - [ ] Feedback loop до моделі (що спрацювало?)
Вимірювання впливу інтервенцій
Запуск retention кампаній вимагає вимірювання справжнього впливу:
def measure_intervention_impact(treatment_group, control_group):
"""Порівняти churn rates між групами."""
treatment_churn = treatment_group['churned'].mean()
control_churn = control_group['churned'].mean()
lift = (control_churn - treatment_churn) / control_churn
# Статистична значущість
from scipy.stats import chi2_contingency
contingency = pd.crosstab(
pd.concat([treatment_group, control_group])['group'],
pd.concat([treatment_group, control_group])['churned']
)
chi2, p_value, _, _ = chi2_contingency(contingency)
return {
'treatment_churn': treatment_churn,
'control_churn': control_churn,
'relative_reduction': lift,
'p_value': p_value
}
Інструменти та стек
| Категорія | Інструменти |
|---|---|
| Моделювання | LightGBM, XGBoost, scikit-learn, lifelines |
| Feature engineering | Featuretools, кастомні пайплайни |
| Оркестрація | Airflow, Prefect, dbt |
| Деплоймент | MLflow, кастомні scoring jobs |
| Інтеграція | CRM (Salesforce), marketing automation |
Приклад міні-кейсу
Сценарій: SaaS компанія з 50K клієнтів, 8% місячний churn, $100 середній місячний дохід.
Підхід: 1. Визначення: Немає логіну 30 днів після дати білінгу 2. Фічі: 120 фіч з usage, billing, support 3. Модель: LightGBM з class weights 4. Поріг: Оптимізований для 3:1 FN:FP cost ratio
Результати: - AUC: 0.82 - Lift у топ децилі: 4.2x - Precision у топ 10%: 31% (vs 8% baseline) - Інтервенція: 15% знижка для high-risk клієнтів
Бізнес-вплив: - 200 high-risk клієнтів ідентифіковано щомісяця - 40% acceptance rate знижки - 25% зменшення churn серед intervened - Чистий збережений дохід: $12K/місяць (після витрат на знижку)
Пов'язані сторінки
- Індекс: ML Field Guide
- Бінарна класифікація
- Сегментація клієнтів
- A/B тестування для ML
Додаткове читання
- Fighting Churn with Data
- Survival Analysis for Customer Analytics
- Cost-Sensitive Learning
- Uplift Modeling for Targeting
Спробуйте наживо
Дослідіть пов'язану модель на практиці: відкрийте Customer Churn Predictor — Survival Curve Live і змінюйте параметри під час роботи. Усе лишається у вашому браузері.
▶ Відкрити симуляцію Customer Churn Predictor — Survival Curve Live