Промо-персонализация на основе 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 клиентов это экономия до 1.5 млн рублей в месяц. 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% за счёт точного попадания в целевые сегменты. Дополнительная прибыль составила 2.3 млн рублей за квартал. Это наглядно демонстрирует преимущество каузального 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 дня. Получите консультацию: мы проанализируем ваши данные и предложим оптимальное решение. Закажите аудит сегодня — это первый шаг к эффективной персонализации промо.







