Розробка рекомендаційної системи товарів (Collaborative Filtering)

Проектуємо та впроваджуємо системи штучного інтелекту: від прототипу до production-ready рішення. Наша команда поєднує експертизу в машинному навчанні, дата-інжинірингу та MLOps, щоб AI працював не в лабораторії, а в реальному бізнесі.
Показано 1 з 1Усі 1564 послуг
Розробка рекомендаційної системи товарів (Collaborative Filtering)
Середній
~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 скаржиться: "У нас 200 тисяч товарів, але середній чек не зростає. Каталог гігантський, користувачі губляться". Проблема знайома — без персональних рекомендацій відвідувач іде без покупки, а якщо й купує, то лише один товар. Ми впровадили Collaborative Filtering інтернет-магазину з аудиторією 2 млн користувачів і збільшили cross-sell на 22% за перші два тижні після запуску. Це принесло додаткові 5 млн грн на місяць — конкретна економія для бізнесу.

Collaborative Filtering — найпоширеніший підхід для товарних рекомендацій: "користувачі, схожі на тебе, купували ось це". Не потрібні описи товарів — лише історія взаємодій. Працює для будь-яких доменів, але вимагає достатнього обсягу транзакцій (>50K) і погано справляється з холодним стартом. Докладніше про метод можна прочитати в Collaborative Filtering.

Чому колаборативна фільтрація потребує багато даних? — розробка рекомендаційної системи

Алгоритм будує приховані фактори на основі перетинів користувачів і товарів. При малій кількості транзакцій матриця user-item надто розріджена — фактори не сходяться, рекомендації стають зашумленими. Для стабільного результату потрібно не менше 50 тисяч транзакцій і 5 тисяч унікальних користувачів. Якщо даних менше, використовуємо content-based підхід або гібридні схеми.

Як колаборативна фільтрація вирішує проблему персоналізації?

ALS (Alternating Least Squares) — матрична факторизація, яка розкладає розріджену матрицю user-item на добуток двох щільних матриць прихованих факторів. Масштабується на мільйони рядків: для 1M користувачів × 100K товарів навчання на CPU (8 ядер) — 5-15 хвилин. Ключові гіперпараметри: factors=64-128, iterations=15-30, regularization=0.001-0.1.

Зважування подій: перегляд=1, додавання в кошик=3, покупка=5, повторна покупка=8. Це покращує якість — важливий не лише факт, а й вага дії.

import numpy as np
import scipy.sparse as sp
from implicit import als
import pandas as pd

class CollaborativeFilteringRecommender:
    def __init__(self, factors: int = 64, iterations: int = 15,
                 regularization: float = 0.01):
        self.model = als.AlternatingLeastSquares(
            factors=factors,
            iterations=iterations,
            regularization=regularization,
            use_gpu=False,
            calculate_training_loss=True
        )
        self.user_map = {}
        self.item_map = {}
        self.reverse_item_map = {}

    def fit(self, interactions_df: pd.DataFrame,
            user_col: str = "user_id",
            item_col: str = "item_id",
            weight_col: str = "weight") -> None:
        """
        interactions_df: user_id, item_id, weight (1=перегляд, 2=додавання в кошик, 5=покупка)
        """
        unique_users = interactions_df[user_col].unique()
        unique_items = interactions_df[item_col].unique()

        self.user_map = {u: i for i, u in enumerate(unique_users)}
        self.item_map = {it: i for i, it in enumerate(unique_items)}
        self.reverse_item_map = {i: it for it, i in self.item_map.items()}

        rows = interactions_df[item_col].map(self.item_map)
        cols = interactions_df[user_col].map(self.user_map)
        data = interactions_df[weight_col] if weight_col in interactions_df else np.ones(len(interactions_df))

        self.matrix = sp.csr_matrix(
            (data, (rows, cols)),
            shape=(len(unique_items), len(unique_users))
        )

        self.model.fit(self.matrix)

    def recommend(self, user_id, n: int = 10,
                   exclude_purchased: bool = True) -> list[tuple]:
        """Top-N рекомендацій для користувача"""
        if user_id not in self.user_map:
            return self._popular_items(n)

        user_idx = self.user_map[user_id]
        filter_items = None

        if exclude_purchased:
            user_items = self.matrix.T.getcol(user_idx)
            filter_items = user_items.indices

        item_indices, scores = self.model.recommend(
            userid=user_idx,
            user_items=self.matrix.T[user_idx],
            N=n,
            filter_already_liked_items=exclude_purchased
        )

        return [
            (self.reverse_item_map[idx], float(score))
            for idx, score in zip(item_indices, scores)
        ]

    def similar_items(self, item_id, n: int = 10) -> list[tuple]:
        """Схожі товари (item2item)"""
        if item_id not in self.item_map:
            return []

        item_idx = self.item_map[item_id]
        similar_indices, scores = self.model.similar_items(item_idx, N=n + 1)

        return [
            (self.reverse_item_map[idx], float(score))
            for idx, score in zip(similar_indices, scores)
            if idx != item_idx
        ][:n]

    def _popular_items(self, n: int) -> list[tuple]:
        """Fallback: популярні товари для нових користувачів"""
        item_popularity = np.array(self.matrix.sum(axis=1)).flatten()
        top_indices = np.argsort(item_popularity)[-n:][::-1]
        return [
            (self.reverse_item_map[idx], float(item_popularity[idx]))
            for idx in top_indices
        ]
Конфігурація навчання ALS

Гіперпараметри ALS:

Параметр Рекомендовані значення Вплив
factors 64-256 Якість факторів: більше — точніше, але зростає пам'ять
iterations 15-30 Збіжність: більше 30 рідко покращує
regularization 0.001-0.1 Регуляризація: вище — менше переобучення, але знижується точність
use_gpu True/False Прискорення на GPU: для матриць > 500K×100K

BPR — коли порядок важливіший за точність

BPR (Bayesian Personalized Ranking) оптимізує не рейтинг, а порядок: для кожного користувача модель намагається розташувати куплені товари вище за некуплені. Це ефективніше поєднується з неявними даними — кліками, переглядами. На практиці BPR дає на 10-15% вищий Precision@10 порівняно з ALS на неявних даних.

class BPRRecommender:
    """
    BPR оптимізує порядок, а не точність передбачення рейтингу.
    Краще підходить, коли немає явних оцінок — тільки clicks/views/purchases.
    """

    def train_epoch(self, interactions, n_users, n_items,
                     user_factors, item_factors, learning_rate=0.01, reg=0.01):
        """SGD крок навчання BPR"""
        user_idx = np.random.randint(n_users)
        user_items = interactions[user_idx].indices

        if len(user_items) == 0:
            return 0

        pos_item_idx = np.random.choice(user_items)
        neg_item_idx = np.random.randint(n_items)
        while neg_item_idx in user_items:
            neg_item_idx = np.random.randint(n_items)

        u = user_factors[user_idx]
        i_pos = item_factors[pos_item_idx]
        i_neg = item_factors[neg_item_idx]

        diff = np.dot(u, i_pos - i_neg)
        sigmoid = 1 / (1 + np.exp(-diff))
        loss = -np.log(sigmoid + 1e-10)

        grad = (1 - sigmoid)
        user_factors[user_idx] += learning_rate * (grad * (i_pos - i_neg) - reg * u)
        item_factors[pos_item_idx] += learning_rate * (grad * u - reg * i_pos)
        item_factors[neg_item_idx] += learning_rate * (-grad * u - reg * i_neg)

        return loss

Коли використовувати ALS, а коли BPR?

ALS краще підходить для великих маркетплейсів із мільйонами транзакцій і явними оцінками (рейтинги) або зваженими неявними даними. BPR ефективніший для нішевих магазинів, де важлива персоналізація порядку показу товарів, особливо при неявних сигналах (кліки, перегляди). На практиці ми часто комбінуємо обидва підходи: ALS для базових рекомендацій, BPR для ранжування в топ-N.

Холодний старт: рішення

Холодний старт — головний ворог колаборативної фільтрації. Для нових товарів без історії використовуємо гібридний підхід: комбінуємо ALS з content-based ознаками (категорія, бренд, ціна). Для нових користувачів — fallback на популярні товари (метод _popular_items у коді вище) або сегментні рекомендації на основі демографії. Також застосовуємо item2item рекомендації на основі метаданих.

Як оцінити якість рекомендацій?

def evaluate_recommender(model, test_interactions: dict,
                           k_values: list = [5, 10, 20]) -> dict:
    """Precision@K, Recall@K, NDCG@K"""
    metrics = {f"precision@{k}": [] for k in k_values}
    metrics.update({f"recall@{k}": [] for k in k_values})
    metrics.update({f"ndcg@{k}": [] for k in k_values})

    for user_id, true_items in test_interactions.items():
        if not true_items:
            continue

        recommendations = model.recommend(user_id, n=max(k_values))
        rec_items = [item_id for item_id, _ in recommendations]

        for k in k_values:
            top_k = set(rec_items[:k])
            true_set = set(true_items)

            hits = len(top_k & true_set)
            metrics[f"precision@{k}"].append(hits / k)
            metrics[f"recall@{k}"].append(hits / len(true_set) if true_set else 0)

            dcg = sum(
                1 / np.log2(i + 2)
                for i, item in enumerate(rec_items[:k])
                if item in true_set
            )
            idcg = sum(1 / np.log2(i + 2) for i in range(min(k, len(true_set))))
            metrics[f"ndcg@{k}"].append(dcg / idcg if idcg > 0 else 0)

    return {k: np.mean(v) for k, v in metrics.items()}

Для типового інтернет-магазину ми отримуємо NDCG@10 в діапазоні 0.25-0.45. Це показник того, що перші 10 рекомендацій дійсно релевантні.

Порівняння ALS і BPR

Параметр ALS BPR
Тип даних Рейтинги, неявні (з вагами) Тільки неявні (порядок)
Масштабованість Мільйони рядків, 5-15 хв CPU До 500K рядків, 30-60 хв
Холодний старт Вимагає fallback Краще узагальнення на нових юзерах
Метрика якості NDCG@10 0.25-0.45 Precision@10 трохи вища
Де застосовуємо Великі маркетплейси Нішеві магазини з історією

Процес роботи: від аналітики до деплою

  1. Аналітика: збираємо логи подій, готуємо дані — чистка від ботів, нормалізація ваг, усунення дірявих даних.
  2. Проектування: вибираємо алгоритм (ALS/BPR), визначаємо гіперпараметри, pipeline для інференсу, налаштовуємо кешування.
  3. Реалізація: пишемо код, інтегруємо з вашою базою даних, підключаємо API.
  4. Тест: A/B-тест на 10% трафіку, заміряємо CR, AOV, Engagement. Оцінюємо метрики.
  5. Деплой: розгортаємо модель, підключаємо моніторинг через Prometheus/Grafana, налаштовуємо регулярне перенавчання.

Що входить у роботу (deliverables)

  • Готова модель ALS/BPR з оптимізованими гіперпараметрами.
  • API для real-time рекомендацій (REST/gRPC).
  • Документація з експлуатації та перенавчання.
  • Навчання команди: як інтерпретувати метрики, оновлювати ваги.
  • Підтримка протягом місяця після запуску.

Наш досвід: понад 5 років у ML, 15+ успішних впроваджень в e-commerce, команда сертифікованих інженерів. Гарантуємо якість — результат підтверджений A/B тестами.

Строки орієнтовно

Від 2 до 6 тижнів залежно від обсягу даних і складності інтеграції. Вартість розраховується індивідуально — залежить від необхідної якості, кількості користувачів і SLA. Отримайте консультацію: ми оцінимо ваш проект і запропонуємо рішення під ключ. Замовте розробку рекомендаційної системи та збільште середній чек у вашому магазині.

Розробка рекомендаційних систем: від 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.