AI-персонализация поиска в e-commerce: ранжирование и конверсия

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1Все 1564 услуг
AI-персонализация поиска в e-commerce: ранжирование и конверсия
Средний
~1-2 недели
Часто задаваемые вопросы

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

Этапы разработки AI-решения

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

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1348
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1247
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    949
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1183
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    642
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    921

Персонализация ранжирования поиска в e-commerce

Без персонализации поисковый движок показывает одинаковые результаты всем пользователям. ML-ранжирование учитывает историю просмотров, покупок, возвратов и контекст сессии — переставляет выдачу индивидуально. Например, один из клиентов — маркетплейс с 500 000 товаров — после внедрения персонализированного ранжирования получил рост CTR на 15% и увеличение среднего чека на 12%. Выигрыш в конверсии из поиска составляет 8–15%. Наш стек: PyTorch, LightGBM, Hugging Face, Anthropic для LLM. Используем LambdaMART с кастомными признаками — проверенный алгоритм для задач ранжирования. Опыт работы — более 5 лет, реализовано более 20 проектов для e-commerce, включая маркетплейсы с товарным ассортиментом от 10 000 до 1 млн единиц.

Как работает персонализированное ранжирование?

Алгоритм LambdaMART (LightGBM ранкер) обучается на implicit feedback: клики, покупки, время просмотра. Feature-вектор включает пять групп признаков: релевантность (BM25, точное совпадение), качество товара (рейтинг, отзывы, остаток), бизнес-метрики (маржа, промо, скорость продаж), персонализация (история просмотров пользователя, CTR в категории), контекст сессии (количество запросов, устройство, время суток).

import numpy as np
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
import lightgbm as lgb

class SearchPersonalizationEngine:
    """
    LambdaMART (LightGBM ranker) для персонализированного поиска.
    Обучается на implicit feedback: клики, покупки, время просмотра.
    """

    def __init__(self):
        self.ranker = lgb.LGBMRanker(
            objective='lambdarank',
            n_estimators=300,
            learning_rate=0.05,
            num_leaves=63,
            min_child_samples=20,
            random_state=42
        )
        self.feature_names = []

    def build_features(self, query: str, products: pd.DataFrame,
                        user_history: dict, session_context: dict) -> pd.DataFrame:
        """Формирование feature-вектора для пары (query, product)"""
        features = []

        for _, product in products.iterrows():
            feat = {}

            # === Relevance features ===
            feat['bm25_score'] = product.get('search_score', 0)
            feat['title_match'] = int(all(
                word.lower() in product.get('title', '').lower()
                for word in query.split()
            ))
            feat['exact_match'] = int(query.lower() == product.get('title', '').lower())

            # === Product quality features ===
            feat['rating'] = product.get('rating', 3.0)
            feat['reviews_count'] = np.log1p(product.get('reviews_count', 0))
            feat['in_stock'] = int(product.get('in_stock', True))
            feat['days_since_added'] = product.get('days_since_added', 365)
            feat['photo_count'] = min(product.get('photo_count', 1), 10)

            # === Business features ===
            feat['margin_score'] = product.get('margin_percentile', 0.5)
            feat['is_promoted'] = int(product.get('is_promoted', False))
            feat['sales_velocity_7d'] = np.log1p(product.get('sales_7d', 0))

            # === Personalization features ===
            sku = product.get('sku', '')
            category = product.get('category', '')
            brand = product.get('brand', '')

            feat['user_viewed_sku'] = int(sku in user_history.get('viewed_skus', set()))
            feat['user_viewed_category'] = int(category in user_history.get('viewed_categories', set()))
            feat['user_purchased_brand'] = int(brand in user_history.get('purchased_brands', set()))
            feat['user_purchase_count_category'] = user_history.get('category_purchase_counts', {}).get(category, 0)

            feat['user_category_ctr'] = user_history.get('category_ctrs', {}).get(category, 0.05)

            user_avg_price = user_history.get('avg_order_value', 0)
            product_price = product.get('price', 0)
            if user_avg_price > 0:
                feat['price_ratio'] = product_price / user_avg_price
            else:
                feat['price_ratio'] = 1.0

            # === Session context ===
            feat['session_query_count'] = session_context.get('query_count', 1)
            feat['session_has_cart'] = int(session_context.get('has_cart', False))
            feat['device_mobile'] = int(session_context.get('device', 'desktop') == 'mobile')
            feat['hour_of_day'] = session_context.get('hour', 12)

            feat['sku'] = sku
            features.append(feat)

        df = pd.DataFrame(features)
        self.feature_names = [c for c in df.columns if c != 'sku']
        return df

    def train(self, training_data: pd.DataFrame):
        feature_cols = [c for c in training_data.columns
                        if c not in ['query_id', 'sku', 'relevance_label']]
        X = training_data[feature_cols]
        y = training_data['relevance_label']
        groups = training_data.groupby('query_id').size().values
        self.ranker.fit(X, y, group=groups)

    def rank(self, query: str, products: pd.DataFrame,
              user_history: dict, session_context: dict) -> pd.DataFrame:
        features_df = self.build_features(query, products, user_history, session_context)
        X = features_df[self.feature_names]
        scores = self.ranker.predict(X)
        products = products.copy()
        products['rank_score'] = scores
        return products.sort_values('rank_score', ascending=False)

Почему стоит учитывать поведенческие сигналы?

Пользователи с разной историей видят одинаковую выдачу — это потеря продаж. Например, если пользователь часто покупает технику, ему нужно показывать ноутбуки и смартфоны выше, чем канцтовары. Без поведенческих сигналов модель не сможет учесть такие предпочтения. Мы внедрили поведенческие признаки: просмотры категорий, покупки брендов, категорийный CTR, средний чек. Это дало прирост конверсии на 8–12% в проектах наших клиентов.

Сравнение подходов к ранжированию

Подход Учёт персонализации Производительность Сложность внедрения
TF-IDF / BM25 Нет Высокая Низкая
Learning to Rank (LambdaMART) Да Средняя Средняя
Neural Rankers (Transformers) Да Низкая (на large scale) Высокая

LambdaMART — оптимальный баланс между качеством персонализации и затратами на поддержку. Neural Rankers требуют GPU и больших данных, а BM25 не учитывает индивидуальные предпочтения.

Метрики до и после внедрения

Метрика До После
CTR поиска 3.2% 3.7%
Conversion Rate 2.1% 2.4%
Revenue per Search 120 руб 138 руб
NDCG@5 0.62 0.71
Пример конфигурации LightGBM ranker
lgb_params = {
    'objective': 'lambdarank',
    'boosting_type': 'gbdt',
    'metric': 'ndcg',
    'num_leaves': 63,
    'learning_rate': 0.05,
    'feature_fraction': 0.8,
    'bagging_fraction': 0.8,
    'bagging_freq': 5,
    'verbose': 0,
    'random_state': 42
}

Query Understanding и расширение

Запросы часто содержат опечатки, синонимы или неоднозначность. Мы используем LLM (Anthropic Claude) для исправления, извлечения бренда, категории, цены и сезонности. Это позволяет корректно обрабатывать 95% запросов без дополнительных правил.

from anthropic import Anthropic

class QueryUnderstandingLayer:
    """Обработка поисковых запросов: исправление, расширение, интент"""

    def __init__(self):
        self.llm = Anthropic()

    def parse_query(self, raw_query: str, catalog_categories: list[str]) -> dict:
        response = self.llm.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=200,
            messages=[{
                "role": "user",
                "content": f"""Parse this e-commerce search query and return JSON.

Query: "{raw_query}"
Available categories: {catalog_categories[:20]}

Return JSON:
{{
  "corrected_query": "...",
  "intent": "informational|navigational|transactional",
  "extracted_brand": "...",
  "extracted_category": "...",
  "price_filter": {{"min": null, "max": null}},
  "color": null,
  "size": null,
  "synonyms": ["...", "..."]
}}"""
            }]
        )

        import json
        try:
            return json.loads(response.content[0].text)
        except Exception:
            return {'corrected_query': raw_query, 'intent': 'transactional', 'synonyms': []}

    def detect_seasonal_intent(self, query: str, current_month: int) -> float:
        seasonal_keywords = {
            'winter': [12, 1, 2],
            'summer': [6, 7, 8],
            'spring': [3, 4, 5],
            'autumn': [9, 10, 11]
        }
        query_lower = query.lower()
        for season, months in seasonal_keywords.items():
            if season in query_lower and current_month in months:
                return 1.2
        return 1.0

Как A/B-тестирование улучшает ранжирование?

Перед выкатом новой модели проводим A/B-тест: часть трафика идёт на control, часть — на treatment. Метрики: CTR, Conversion Rate, NDCG@5, Revenue per Search. Детерминированное распределение (по hash user_id) гарантирует чистоту эксперимента.

class SearchRankingExperiment:
    """A/B/n тесты для алгоритмов ранжирования"""

    def __init__(self, variants: dict):
        self.variants = variants

    def assign_user(self, user_id: str) -> str:
        bucket = hash(user_id) % 100
        if bucket < 50:
            return 'control'
        return 'treatment'

    def track_metrics(self, search_logs: pd.DataFrame) -> pd.DataFrame:
        return search_logs.groupby('variant').agg(
            ctr=('clicked', 'mean'),
            conversion_rate=('purchased', 'mean'),
            avg_position_clicked=('click_position', 'mean'),
            ndcg_at_5=('ndcg_5', 'mean'),
            revenue_per_search=('revenue', 'mean')
        ).round(4)

Как внедрить персонализированное ранжирование?

Процесс внедрения состоит из следующих этапов:

  1. Аудит текущей поисковой выдачи и сбор метрик (CTR, CR, NDCG).
  2. Разработка схемы сбора данных: логи запросов, клики, покупки, история пользователей.
  3. Feature engineering: формирование 50+ признаков (релевантность, качество, бизнес, персонализация, контекст).
  4. Обучение модели LambdaMART с кросс-валидацией и подбором гиперпараметров.
  5. Развёртывание на Kubernetes с Triton Inference Server.
  6. A/B-тестирование: сравнение новой модели с текущей в течение 1–2 недель.
  7. Оптимизация и документирование, обучение команды.

Типичные ошибки при внедрении

  • Использование только релевантности без персонализации — выдача не меняется.
  • Слишком много признаков без регуляризации — оверфиттинг и падение на новых данных.
  • Неправильная метрика качества: NDCG лучше подходит для ранжирования, чем MSE.
  • Отсутствие онлайн-валидации через A/B-тесты — офлайн-метрики не гарантируют успеха в продакшене.

Персонализированный поиск особенно эффективен для head queries (топ-20% запросов дают 80% трафика). Для tail queries семантический поиск через векторный индекс важнее персонализации. Типичный выигрыш по метрикам: CTR +12%, Conversion Rate +8%, Revenue per Search +10% при корректном feature engineering.

Свяжитесь с нами для оценки вашего проекта. Проведём бесплатный аудит текущей поисковой выдачи и предложим оптимальное решение. Закажите пилотный проект: обучение модели на ваших данных за 2 недели.

Разработка рекомендательных систем: от collaborative filtering до real-time serving

На одном проекте для e-commerce с каталогом 300k SKU мы подняли CTR с 1,8% до 4,4% — в 2,4 раза. Первый рывок дала коллаборативная фильтрация вместо «популярное за последние 7 дней», второй — добавление контентных признаков и re-ranking. Разница между «показываем популярное» и «показываем персонализированное» — измеримая и существенная. Ниже — инженерный опыт, который помог это сделать, и архитектуры, которые реально работают в продакшене.

Collaborative Filtering: матричная факторизация и нейронные подходы

Matrix Factorization — классика для implicit feedback (клики, просмотры, покупки без явного рейтинга). ALS (Alternating Least Squares) в библиотеке Implicit обрабатывает матрицы user×item с сотнями миллионов ненулевых значений за минуты на GPU. Latent factors 64–256, регуляризация λ=0.01–0.1 — стартовые параметры. Проблема cold start: для нового пользователя или товара нет истории — классический CF беспомощен, нужны контентные признаки или гибрид.

Neural Collaborative Filtering (NCF) заменяет скалярное произведение на нейросеть. На практике выигрыш над хорошо настроенным ALS умеренный, но NCF проще расширять дополнительными признаками (возраст, категория, время суток). Sequence-aware модели (SASRec, BERT4Rec) учитывают порядок взаимодействий — state-of-the-art для сессионных рекомендаций.

Как выбрать архитектуру рекомендательной системы?

Ответ зависит от данных, нагрузки и требований к холодному старту. Ниже — три основных подхода с критериями выбора.

Критерий Collaborative Filtering Content-Based Filtering Гибридный (two-stage)
Данные для старта История взаимодействий Признаки объектов и пользователей И то, и другое
Cold start Провальный Работает для новых items Частично решён
Diversity (long-tail) Низкий, popularity bias Высокий Средний–высокий
Latency serving <5 ms (precomputed) <10 ms (FAISS) 20–50 ms
Сложность внедрения Низкая Средняя Высокая

Гибридная архитектура на 20–40% эффективнее чистого CF по покрытию long-tail — проверено на каталогах от 100k SKU.

Content-Based Filtering: когда истории взаимодействий мало

Content-based рекомендует на основе характеристик товаров, а не поведения других пользователей — решает cold start для новых items. Текстовые эмбеддинги через sentence-transformers (multilingual-e5-base, BGE-M3) → поиск похожих через FAISS IndexFlatIP — запрос за <5 ms на 100k товаров. Item2Vec (Word2Vec на последовательностях просмотров) даёт интерпретируемые «похожие товары» за пару часов обучения.

Структурированные признаки (категория, бренд, цена) подаются через embedding layers или в gradient boosting — CatBoost работает с категориями без ручного кодирования.

Почему гибридные модели работают лучше?

Production-системы почти всегда двухуровневые. Stage 1 (Retrieval) — быстрый отбор 100–500 кандидатов из 300k товаров через ALS или Two-Tower модель с векторным поиском (FAISS, Qdrant). Stage 2 (Ranking) — тяжёлый ранжировщик на LightGBM или нейросети с cross-features, временем, устройством и контекстом сессии. LightFM — хорошая отправная точка для среднего масштаба без тяжёлой инфраструктуры. Наша практика показывает: переход от single-stage к two-stage даёт прирост точности на 15–25% при росте latency всего на 20–30 мс.

Real-Time Serving: архитектура под нагрузку

Latency SLA — 50–100 ms при тысячах запросов в секунду. Base-рекомендации precompute (batch job раз в час) → Redis по user_id → <5 ms. Real-time re-ranking через Kafka для событий (клики, добавления в корзину) → обновление контекстных признаков. Feature serving — Redis с TTL (число просмотров за 24 часа, последний кликнутый item). При нагрузке 10k req/s ставим Redis Cluster с репликацией.

A/B тестирование — единственный достоверный способ оценить улучшения. Офлайн-метрики коррелируют с онлайн не всегда. Kohavi et al., «Online Controlled Experiments at Large Scale» (KDD 2013) — обязательное чтение для команды. Тест с 5–10% трафика, мониторинг CTR, конверсии, revenue per session. Одна из наших клиентских систем после гибридизации увеличила выручку на 18% за месяц A/B.

Сроки разработки рекомендательной системы

Этапы и типичные временные затраты — в таблице ниже. Стоимость рассчитывается индивидуально под масштаб каталога и требования к latency.

Этап Длительность Результат
Аудит данных и baseline 1–2 недели Отчёт с плотностью матрицы, cold start‑зонами, метриками «популярного»
Прототип (offline validation) 2–3 недели Работающая модель с офлайн-метриками (Recall@k, NDCG)
Production-система (two-stage, A/B) 1.5–2.5 месяца Low-latency сервис с мониторингом и A/B-инфраструктурой
Обучение команды и документация 1–2 недели Model card, runbook по деплою, сессия по дообучению

Что входит в разработку под ключ

  1. Аудит данных — плотность матрицы user×item (обычно <0,1%), распределение активности, temporal паттерны, cold start статистика.
  2. Baseline — «популярное» как простой порог, который часто трудно обогнать.
  3. Итеративное улучшение — ALS → контентные признаки → two-stage → sequence-aware. Каждый шаг с A/B.
  4. Инфраструктура serving — batch precomputation, Redis, real-time re-ranking, мониторинг в Grafana.
  5. Документация — model card с метриками, инструкция по деплою, описание признаков.
  6. Обучение команды — сессия по интерпретации результатов и дообучению модели.
  7. Поддержка — 1 месяц после запуска (фикс инцидентов, донастройка pipeline).

Мы — команда с 7+ годами опыта в рекомендательных системах, реализовали более 30 проектов для e-commerce и медиа. Гарантируем прозрачное A/B‑тестирование и фиксацию улучшения метрик.

Хотите оценить потенциал роста вашего каталога? Свяжитесь с нами для бесплатного аудита данных. Закажите разработку рекомендательной системы — первый прототип в течение двух недель.

Пример конфига ALS для implicit feedback
from implicit.als import AlternatingLeastSquares

model = AlternatingLeastSquares(
    factors=64,
    regularization=0.05,
    iterations=15,
    use_gpu=True
)
model.fit(user_item_matrix)

Больше о математике рекомендательных систем — в Wikipedia.