Разработка AI-системы рекомендаций Cross-Sell для продаж

Статические правила не видят скрытых закономерностей в покупках, из-за чего упускаются неочевидные cross-sell-связки. Мы разрабатываем AI-системы рекомендаций на основе машинного обучения, которые находят такие паттерны и персонализируют предложения для каждого клиента. Наша команда реализует проект под ключ — от аудита данных до внедрения и последующей поддержки.
Часто задаваемые вопросы

Реализация AI-системы рекомендаций Cross-Sell для продаж

К нам обратился e-commerce-проект с 50 000 SKU: правило «к принтеру — картридж» давало всего 8% конверсии. Нужна была рекомендательная система на основе машинного обучения, которая находит неочевидные пары и адаптируется под поведение каждого клиента. Мы реализовали гибридную систему для e-commerce и ритейла: ассоциативные правила для базовых связок + градиентный бустинг для персонализации. Результат — рост среднего чека на 18% за первый месяц, что обеспечило значительный прирост выручки. Рассказываю, как это работает.

Проблемы, которые решаем

Статические правила не видят скрытых закономерностей. Например, клиенты, купившие детское автокресло, с вероятностью 22% приобретают органайзер для багажника — хотя товары из разных категорий. ML находит такие паттерны через ассоциативные правила (Lift > 2.0). А персонализация через градиентный бустинг учитывает историю клиента: если пользователь уже брал органайзер, рекомендация смещается на другой товар.

Другая типичная проблема — товары с коротким циклом потребления (расходники, подписки). Здесь требуется предсказание следующей покупки. Модель на основе распределения интервалов выдаёт товары, которые клиент вот-вот закажет снова. Так мы увеличиваем повторные продажи без промоакций.

Третья проблема — холодный старт для новых товаров. Если у товара нет истории покупок, правила Apriori не сработают. Решение — использовать content-based признаки (категория, цена, бренд) и коллаборативную фильтрацию через эмбеддинги. Для этого мы обогащаем модель градиентного бустинга фичами товара.

Как мы строим модель CrossSellRecommender

Market Basket Analysis + персонализация

Гибридная архитектура: сначала Apriori даёт сырые правила, затем градиентный бустинг ранжирует кандидатов под конкретного пользователя. Это сочетание обеспечивает lift конверсии в 3 раза выше, чем rule-based подход.

import pandas as pd
import numpy as np
from mlxtend.frequent_patterns import apriori, association_rules
from mlxtend.preprocessing import TransactionEncoder
from sklearn.ensemble import GradientBoostingClassifier

class CrossSellRecommender:
    def __init__(self, min_support: float = 0.01, min_confidence: float = 0.1):
        self.min_support = min_support
        self.min_confidence = min_confidence
        self.rules = None
        self.cross_sell_map = {}
        self.personalization_model = None

    def fit_association_rules(self, orders_df: pd.DataFrame, order_col: str = "order_id", item_col: str = "item_id"):
        """Поиск ассоциативных правил через Apriori"""
        # Корзины транзакций
        baskets = orders_df.groupby(order_col)[item_col].apply(list).tolist()
        te = TransactionEncoder()
        te_array = te.fit_transform(baskets)
        df_encoded = pd.DataFrame(te_array, columns=te.columns_)

        # Частые наборы
        frequent_itemsets = apriori(
            df_encoded,
            min_support=self.min_support,
            use_colnames=True,
            max_len=3
        )

        # Правила ассоциации
        self.rules = association_rules(
            frequent_itemsets,
            metric="lift",
            min_threshold=1.2
        )
        self.rules = self.rules[self.rules['confidence'] >= self.min_confidence]
        self.rules = self.rules.sort_values('lift', ascending=False)

        # Маппинг: item → список рекомендаций с метриками
        for _, rule in self.rules.iterrows():
            for antecedent in rule['antecedents']:
                if antecedent not in self.cross_sell_map:
                    self.cross_sell_map[antecedent] = []
                for consequent in rule['consequents']:
                    if antecedent != consequent:
                        self.cross_sell_map[antecedent].append({
                            'item_id': consequent,
                            'confidence': rule['confidence'],
                            'lift': rule['lift'],
                            'support': rule['support']
                        })

        # Сортировка по lift
        for item in self.cross_sell_map:
            self.cross_sell_map[item].sort(key=lambda x: x['lift'], reverse=True)

    def recommend_cross_sell(self, cart_items: list[str], user_history: list[str] = None, n: int = 5) -> list[dict]:
        """Cross-sell для текущей корзины"""
        candidates = {}
        for item_id in cart_items:
            related = self.cross_sell_map.get(item_id, [])
            for rec in related:
                rec_id = rec['item_id']
                # Пропускаем уже в корзине или в истории
                if rec_id in cart_items:
                    continue
                if user_history and rec_id in user_history:
                    continue
                if rec_id not in candidates:
                    candidates[rec_id] = {'score': 0, 'triggers': []}
                candidates[rec_id]['score'] += rec['lift']
                candidates[rec_id]['triggers'].append(item_id)

        # Нормализация
        if not candidates:
            return []
        sorted_candidates = sorted(
            [{'item_id': k, **v} for k, v in candidates.items()],
            key=lambda x: x['score'],
            reverse=True
        )
        return sorted_candidates[:n]

    def get_complementary_categories(self, category: str) -> list[str]:
        """Взаимодополняющие категории"""
        category_rules = self.rules[
            self.rules['antecedents'].apply(lambda x: category in str(x))
        ]['consequents'].apply(lambda x: list(x)).explode().value_counts()
        return category_rules.head(5).index.tolist()

Временные паттерны: следующий заказ

class NextPurchasePredictor:
    """Предсказание следующей покупки на основе истории"""

    def predict_next_items(self, user_id: str, purchase_history: list[dict], catalog_features: pd.DataFrame) -> list[tuple]:
        """
        purchase_history: [{item_id, date, quantity, category}]
        Возвращает: [(item_id, probability)]
        """
        if len(purchase_history) < 3:
            return []

        # Паттерн повторных покупок
        item_intervals = {}
        for i in range(1, len(purchase_history)):
            item = purchase_history[i]['item_id']
            prev_same = [h for h in purchase_history[:i] if h['item_id'] == item]
            if prev_same:
                days_between = (
                    pd.to_datetime(purchase_history[i]['date']) - pd.to_datetime(prev_same[-1]['date'])
                ).days
                if item not in item_intervals:
                    item_intervals[item] = []
                item_intervals[item].append(days_between)

        # Предсказание повторных покупок
        predictions = []
        last_purchase_date = pd.to_datetime(purchase_history[-1]['date'])
        today = pd.Timestamp.now()
        days_since_last = (today - last_purchase_date).days

        for item_id, intervals in item_intervals.items():
            avg_interval = np.mean(intervals)
            std_interval = np.std(intervals) if len(intervals) > 1 else avg_interval * 0.3

            # Вероятность через normal distribution
            from scipy.stats import norm
            prob = norm.cdf(days_since_last + 7, avg_interval, std_interval + 1)
            prob -= norm.cdf(days_since_last - 7, avg_interval, std_interval + 1)
            prob = min(max(prob, 0), 1)

            if prob > 0.1:
                predictions.append((item_id, prob))

        return sorted(predictions, key=lambda x: x[1], reverse=True)[:10]
Подробнее о настройке Apriori

Association rules с min_support=0.01, min_confidence=0.1 обычно дают 500-5000 значимых правил на 100K заказов. Lift > 2.0 — сильная ассоциация. Для подбора гиперпараметров используем кросс-валидацию: разбиваем данные по времени (train/test) и выбираем min_support, min_confidence по максимальному lift на валидационной выборке.

Как обучить модель CrossSellRecommender на своих данных

  1. Подготовка данных. Соберите историю заказов с колонками: order_id, item_id, date. Минимальное количество транзакций — 10 000.
  2. Запустите Apriori. Используйте метод fit_association_rules с min_support=0.01 и min_confidence=0.1. Это даст ~1000 правил.
  3. Настройте параметры. Проверьте распределение lift и confidence. Если правил слишком много, увеличьте min_support до 0.02 или min_confidence до 0.2.
  4. Оцените качество. Используйте метрики precision@k и recall@k на отложенной выборке. Cross-sell через правила даёт средний uplift корзины 15-25%. Комбинация с персонализацией (история пользователя) добавляет ещё 5-10% к acceptance rate.

Как измерить качество рекомендаций?

Основные offline-метрики: precision@k, recall@k, lift@k. Мы используем усечённую корзину — скрываем часть товаров из каждой транзакции и проверяем, попадают ли они в рекомендации. Онлайн-метрики — acceptance rate, AOV uplift, доля корзин с cross-sell. На A/B-тесте с 50% аудитории типичный uplift acceptance rate составляет 10-25%.

Почему гибридная модель эффективнее rule-based?

Rule-based (например, "купил А — покажи Б") не масштабируется на тысячи товаров: правила становятся разреженными и не учитывают контекст. ML находит ассоциации с высоким lift даже для редких пар. Кроме того, гибрид персонализирует ранжирование: один и тот же товар ранжируется по-разному для разных пользователей. В наших проектах acceptance rate гибрида в 3 раза выше, чем у правил.

Этапы внедрения

Этап Что делаем Результат Длительность
1. Аудит данных Проверяем полноту и качество: количество SKU, длину истории, частоту заказов Дашборд метрик, список пропусков 1-2 дня
2. Построение пайплайна ETL для сбора данных из CRM, очистка, агрегация корзин Датасет orders_df 3-4 дня
3. Обучение правил Apriori, подбор min_support и min_confidence через кросс-валидацию Файл правил (.pkl) 1-2 дня
4. Персонализация Обучаем градиентный бустинг на истории пользователей Ранжирующая модель 3-5 дней
5. A/B-тест 50% аудитории — старая система, 50% — новая. Считаем конверсию и AOV Отчёт с uplift 7-14 дней
6. Продакшн-деплой Интеграция через REST API, мониторинг latency p99 Документация, дашборд 2-3 дня

Что входит в результат

  • Документация: описание правил, метрик, инструкция для обновления модели.
  • Исходный код: репозиторий с модулями CrossSellRecommender, NextPurchasePredictor, конфиги и тесты.
  • API-эндпоинты: два метода — recommend_by_cart (по текущей корзине) и recommend_by_user (по истории).
  • Обучение команды: показываем, как интерпретировать результаты и дообучать модель.
  • Гарантия: мы сопровождаем A/B-тест до достижения статистически значимого uplift.

Как оценить ROI для вашего бизнеса

Метрика Типичное значение Как рассчитываем
Прирост среднего чека 15-25% (AOV_тест - AOV_контроль) / AOV_контроль * 100%
Acceptance rate 5-12% Клики по рекомендациям / показы
Доля корзин с cross-sell 30-50% Корзины с ≥1 рекомендованным товаром / всего корзин
Время окупаемости 2-4 месяца Стоимость внедрения / ежемесячный прирост выручки

Чтобы оценить потенциал cross-sell для вашего ассортимента, свяжитесь с нами. Мы проведём аудит данных и предоставим прототип за 2 недели с гарантией конфиденциальности. Закажите консультацию — обсудим детали вашего проекта и подготовим индивидуальное предложение.

Направления AI-разработки

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    815
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139