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







