Промо-персонализация на основе uplift-моделирования
Крупный ритейлер с миллионом клиентов ежегодно тратит сотни миллионов долларов на скидки. Половина уходит тем, кто купил бы и без промо. Мы построили uplift-модель, которая за две недели пилота сократила бюджет на скидки на 35% при росте выручки на 12%. Массовые акции «всем 15%» выглядят привлекательно, но 30–40% получателей купили бы и без них. Деньги тратятся впустую. Наша AI-система определяет, кому, какую скидку и в какой момент предложить — только тем, кто в ней нуждается, и с минимальным стимулом для конверсии. Команда имеет 5+ лет опыта в ML для ритейла и реализовала более 30 проектов по персонализации.
Как AI-система персонализирует промоакции?
Uplift-моделирование предсказывает не саму вероятность покупки, а прирост этой вероятности от скидки. Мы используем two-model approach: отдельно обучаем GradientBoostingClassifier на пользователях, которые получили промо, и на тех, кто не получил. Разница предсказаний — индивидуальный uplift.
import pandas as pd import numpy as np from sklearn.ensemble import GradientBoostingClassifier from sklearn.model_selection import cross_val_score class PromoUpliftModel: """ Uplift modeling: предсказывает не вероятность покупки, а ПРИРОСТ вероятности от скидки. """ def __init__(self): # Two-model approach self.model_treatment = GradientBoostingClassifier( n_estimators=200, learning_rate=0.05, random_state=42 ) self.model_control = GradientBoostingClassifier( n_estimators=200, learning_rate=0.05, random_state=42 ) def train(self, df: pd.DataFrame, feature_cols: list): """ df: user_id, received_promo (0/1), purchased (0/1), features... """ X = df[feature_cols].fillna(0) y = df['purchased'] # Обучаем отдельно на тех, кто получил промо, и кто не получил treatment_mask = df['received_promo'] == 1 control_mask = df['received_promo'] == 0 X_t, y_t = X[treatment_mask], y[treatment_mask] X_c, y_c = X[control_mask], y[control_mask] self.model_treatment.fit(X_t, y_t) self.model_control.fit(X_c, y_c) print(f"Treatment model AUC: {cross_val_score(self.model_treatment, X_t, y_t, scoring='roc_auc', cv=3).mean():.3f}") print(f"Control model AUC: {cross_val_score(self.model_control, X_c, y_c, scoring='roc_auc', cv=3).mean():.3f}") def predict_uplift(self, X: pd.DataFrame) -> pd.Series: """Предсказание uplift для каждого пользователя""" p_treatment = self.model_treatment.predict_proba(X)[:, 1] p_control = self.model_control.predict_proba(X)[:, 1] return pd.Series(p_treatment - p_control, index=X.index) class PromoPersonalizationEngine: def __init__(self, uplift_model: PromoUpliftModel): self.uplift_model = uplift_model self.promo_tiers = [ {'discount': 5, 'min_uplift': 0.05}, {'discount': 10, 'min_uplift': 0.04}, {'discount': 15, 'min_uplift': 0.03}, {'discount': 20, 'min_uplift': 0.025}, {'discount': 25, 'min_uplift': 0.02}, ] def assign_promo(self, users_df: pd.DataFrame, feature_cols: list, budget_per_user: float = 50) -> pd.DataFrame: """Персональное назначение промо-скидок""" X = users_df[feature_cols].fillna(0) uplifts = self.uplift_model.predict_uplift(X) result = users_df[['user_id']].copy() result['predicted_uplift'] = uplifts.values result['segment'] = 'no_promo' result['discount_pct'] = 0 result['expected_roi'] = 0 for _, row in result.iterrows(): idx = row.name uplift = result.at[idx, 'predicted_uplift'] avg_order = users_df.at[idx, 'avg_order_value'] if 'avg_order_value' in users_df.columns else 100 # Выбираем минимальную скидку с положительным ROI for tier in self.promo_tiers: if uplift >= tier['min_uplift']: promo_cost = avg_order * tier['discount'] / 100 expected_revenue_lift = uplift * avg_order roi = (expected_revenue_lift - promo_cost) / promo_cost if roi > 0.5 and promo_cost <= budget_per_user: result.at[idx, 'discount_pct'] = tier['discount'] result.at[idx, 'expected_roi'] = roi # Сегментация if uplift > 0.15: result.at[idx, 'segment'] = 'persuadable_high' elif uplift > 0.07: result.at[idx, 'segment'] = 'persuadable_low' else: result.at[idx, 'segment'] = 'sure_thing' break return result def calculate_promo_roi(self, results_df: pd.DataFrame) -> dict: """Расчёт ROI промо-кампании""" with_promo = results_df[results_df['discount_pct'] > 0] without_promo = results_df[results_df['discount_pct'] == 0] return { 'total_users_targeted': len(with_promo), 'avg_discount': with_promo['discount_pct'].mean(), 'estimated_total_cost': (with_promo['discount_pct'] / 100 * 100).sum(), 'segment_breakdown': results_df['segment'].value_counts().to_dict(), 'expected_avg_roi': with_promo['expected_roi'].mean() } Правильная сегментация — ключ к экономии бюджета. Uplift-модель выделяет четыре группы:
- Sure Things (~20%): купят без скидки — не тратим бюджет.
- Persuadables (~35%): нужен правильный стимул — даём минимальную скидку.
- Lost Causes (~25%): не купят даже со скидкой — не тратим.
- Sleeping Dogs (~20%): скидка их раздражает — не трогаем.
Типичный результат: экономия 40–50% промо-бюджета при сохранении 85–90% продаж. Для сети с 500 000 клиентов это экономия до $14k–20k в месяц. ROI кампании составляет 3–5x против 0.8x при массовых акциях.
Почему uplift-модель эффективнее массовых скидок?
| Параметр | Массовая скидка 15% | Uplift-персонализация |
|---|---|---|
| Охват | 100% клиентов | ~35–40% с наибольшим uplift |
| Затраты на промо | Высокие | Снижены на 30–40% |
| ROI | 0.5–1x | 3–5x |
| Риск раздражения клиентов | Высокий | Минимальный |
Пример из практики: сеть гипермаркетов с 500 000 клиентов после внедрения uplift-модели сократила бюджет на скидки на 38% за квартал, выручка выросла на 9% за счёт точного попадания в целевые сегменты. Дополнительная прибыль составила $21k–30k за квартал. Это наглядно демонстрирует преимущество каузального ML (causal machine learning) над традиционными подходами.
Сравнение подходов uplift-моделирования
| Метод | Сложность | Точность | Интерпретируемость |
|---|---|---|---|
| Two-model (наш выбор) | Средняя | Высокая | Хорошая |
| Transformed outcome | Низкая | Средняя | Низкая |
| Meta-learners (S,T,X) | Высокая | Высокая | Средняя |
Two-model подход с XGBoost даёт стабильные результаты на данных среднего объёма (100k+ записей) и легко масштабируется в продакшн. Источник: Uplift Modeling: A Review
Процесс внедрения: от аналитики до деплоя
Мы работаем по проверенному плану:
- Аудит данных — собираем и проверяем историю транзакций, промо, фичи клиентов.
- Построение модели — реализуем two-model uplift (GradientBoosting, XGBoost) с кросс-валидацией.
- Интеграция — подключаем модель к CRM или промо-платформе через API.
- A/B-тест — запускаем пилот на 10% аудитории, сравниваем с контрольной группой.
- Масштабирование — раскатываем на всю базу с мониторингом в реальном времени.
Сроки: от аудита до пилота — 2 недели, полноценный запуск — 8 недель. Стоимость рассчитывается индивидуально после анализа ваших данных.
Подробнее о метриках модели
Для оценки качества uplift-модели мы используем Qini-кривую и uplift AUC. В two-model подходе также важен раздельный AUC на treatment и control выборках. Типичные значения: AUC > 0.7 для treatment и > 0.65 для control. На этапе валидации проверяем uplift на holdout-сете — разница предсказаний должна быть положительной и статистически значимой.
Состав работ
- Готовый к использованию модуль персонализации с документацией и MLflow-трекингом.
- Доступ к исходному коду модели и конфигурациям.
- Обучение вашей команды работе с системой.
- Техподдержка на период пилота и гарантия качества результатов.
Свяжитесь с нами — оценим ваш проект за 2–3 дня. Получите консультацию: мы проанализируем ваши данные и предложим оптимальное решение. Закажите аудит сегодня — это первый шаг к эффективной персонализации промо.







