Розробка AI-системи для персоналізації промоакцій
Промо-персоналізація на основі 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 клієнтів це значна економія промо-бюджету щомісяця. 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% за рахунок точного попадання в цільові сегменти. Це наочно демонструє перевагу каузального 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 дні. Отримайте консультацію: ми проаналізуємо ваші дані та запропонуємо оптимальне рішення. Замовте аудит сьогодні — це перший крок до ефективної персоналізації промо.







