Головна▸Статті▸Рекомендації та пошук

Collaborative Filtering

Побудова рекомендаційних систем з collaborative filtering - user-item матриці, матрична факторизація, implicit feedback та рішення cold start.

mysimulator teamОновлено 2026-01≈ 6 хв читання▶ Open the simulation

Collaborative filtering рекомендує елементи на основі того, що сподобалось схожим користувачам. Замість аналізу контенту елементів, він знаходить патерни в поведінці користувачів - "користувачі, які купили X, також купили Y". Ця проста ідея забезпечує рекомендації в Netflix, Spotify, Amazon та більшості сучасних платформ.

Де це використовується

Collaborative filtering - основа персоналізації:

Продукти: Netflix recommendation engine, Spotify Discover Weekly, Amazon recommendations.

Типові дані

Аспект Типові значення
Користувачі 100K-100M
Елементи 10K-10M
Взаємодії 10M-10B
Розрідженість 99%-99.99% sparse
Розмірність embedding 32-256

Типи даних: - Explicit feedback: Рейтинги (1-5 зірок), лайки/дизлайки - Implicit feedback: Перегляди, кліки, покупки, час на сторінці - User features: Демографія, преференції - Item features: Категорії, описи, метадані

Що ви робите на практиці

  1. Визначте взаємодії - Які дії користувача вказують на інтерес?
  2. Побудуйте матрицю взаємодій - User-item sparse матриця
  3. Обробіть implicit feedback - Конвертуйте дії в сигнали
  4. Оберіть алгоритм - Matrix factorization, neural, або гібрид
  5. Натренуйте модель - Оптимізуйте для ранжування або prediction рейтингу
  6. Згенеруйте кандидатів - ANN для елементів
  7. Додайте бізнес-правила - Diversity, freshness, inventory
  8. Оцініть offline - Recall, NDCG на held-out даних
  9. A/B тест online - CTR, engagement, конверсії
  10. Обробіть cold start - Нові користувачі та елементи

Спочатку бейзлайн

Почніть з popularity-based рекомендацій:

# Popularity бейзлайн
def popularity_recommend(n=10):
    """Рекомендувати найпопулярніші елементи."""
    item_popularity = interactions.groupby('item_id').size()
    return item_popularity.nlargest(n).index.tolist()

# User-based co-occurrence
def cooccurrence_recommend(user_id, n=10):
    """Елементи часто куплені з елементами користувача."""
    user_items = set(interactions[interactions.user_id == user_id].item_id)

    # Знайти користувачів зі схожими елементами
    similar_users = interactions[
        interactions.item_id.isin(user_items)
    ].user_id.unique()

    # Їх інші елементи
    candidates = interactions[
        (interactions.user_id.isin(similar_users)) & 
        (~interactions.item_id.isin(user_items))
    ]

    return candidates.item_id.value_counts().head(n).index.tolist()

Чому popularity бейзлайн: - Простий і швидкий - Розумний для нових користувачів - Встановлює мінімальну планку якості - Допомагає виміряти personalization lift

Підходи алгоритмів

Підхід Плюси Мінуси Коли використовувати
Matrix Factorization Швидкий, інтерпретований Обмежена експресивність Більшість випадків, хороша точка старту
Neural CF Захоплює складні патерни Потребує більше даних, повільніший Великий масштаб, складні патерни
Item-based CF Легко пояснити Не персоналізує глибоко Фокус на схожості елементів
Graph-based Захоплює зв'язки Складно масштабувати Соціальні мережі, knowledge graphs

Matrix Factorization з ALS:

from implicit.als import AlternatingLeastSquares
from scipy.sparse import csr_matrix

# Побудувати sparse user-item матрицю
user_item = csr_matrix((
    interactions.rating,
    (interactions.user_idx, interactions.item_idx)
))

# Натренувати ALS модель
model = AlternatingLeastSquares(
    factors=64,
    regularization=0.01,
    iterations=15
)
model.fit(user_item)

# Отримати рекомендації для користувача
user_idx = 0
recommendations = model.recommend(
    user_idx, 
    user_item[user_idx],
    N=10,
    filter_already_liked_items=True
)

Neural Collaborative Filtering:

import torch
import torch.nn as nn

class NCF(nn.Module):
    def __init__(self, n_users, n_items, embedding_dim=64):
        super().__init__()
        self.user_embed = nn.Embedding(n_users, embedding_dim)
        self.item_embed = nn.Embedding(n_items, embedding_dim)

        self.mlp = nn.Sequential(
            nn.Linear(embedding_dim * 2, 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, 1),
            nn.Sigmoid()
        )

    def forward(self, user_ids, item_ids):
        user_emb = self.user_embed(user_ids)
        item_emb = self.item_embed(item_ids)
        concat = torch.cat([user_emb, item_emb], dim=1)
        return self.mlp(concat)

Обробка Implicit Feedback

Більшість продакшн-систем використовують implicit feedback (кліки, перегляди), а не explicit рейтинги:

# Конвертація implicit в training signal
def prepare_implicit_data(interactions):
    """Конвертувати raw interactions в training pairs."""
    # Позитивні семпли: реальні взаємодії
    positives = interactions[['user_id', 'item_id']].copy()
    positives['label'] = 1

    # Негативне семплювання: елементи з якими користувач не взаємодіяв
    all_items = set(interactions.item_id.unique())
    negatives = []

    for user_id in interactions.user_id.unique():
        user_items = set(interactions[
            interactions.user_id == user_id
        ].item_id)

        neg_items = random.sample(
            list(all_items - user_items), 
            k=len(user_items) * 4  # 4:1 негативне ratio
        )

        for item_id in neg_items:
            negatives.append({
                'user_id': user_id,
                'item_id': item_id,
                'label': 0
            })

    return pd.concat([positives, pd.DataFrame(negatives)])

Схеми зважування: - Uniform: Всі взаємодії рівні - Frequency: Вага по кількості взаємодій - Recency: Нещодавні взаємодії важать більше - Confidence: Вага по силі взаємодії

Оцінка та метрики

Метрика Що вимірює Формула
Recall@k Покриття релевантних елементів Релевантні в топ-k / Всього релевантних
Precision@k Точність топ-k Релевантні в топ-k / k
NDCG@k Якість ранжування Normalized discounted cumulative gain
MAP Середня precision Mean precision на кожному релевантному
Hit Rate@k Будь-який релевантний у топ-k Users з hit / Всього users

Налаштування оцінки:

def evaluate_model(model, test_data, k=10):
    """Оцінити рекомендаційну модель."""
    metrics = {'recall': [], 'ndcg': [], 'hit_rate': []}

    for user_id in test_data.user_id.unique():
        # Ground truth: елементи з якими взаємодіяв у тесті
        true_items = set(test_data[
            test_data.user_id == user_id
        ].item_id)

        # Передбачення моделі
        recs = model.recommend(user_id, k)

        # Recall@k
        hits = len(set(recs) & true_items)
        metrics['recall'].append(hits / len(true_items))

        # Hit rate
        metrics['hit_rate'].append(1 if hits > 0 else 0)

        # NDCG@k
        metrics['ndcg'].append(compute_ndcg(recs, true_items, k))

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

Режими відмов та чеклист налагодження

Чеклист для продакшну

Перед деплоєм: - [ ] Offline метрики значно б'ють popularity baseline - [ ] Cold start стратегія визначена (content-based fallback) - [ ] Пайплайн генерації кандидатів побудований (ANN index) - [ ] Бізнес-правила інтегровані (diversity, freshness) - [ ] Latency відповідає SLA (типово < 100ms)

Після деплою: - [ ] Online метрики відстежуються (CTR, engagement, конверсії) - [ ] Розклад оновлення моделі встановлений (daily/weekly) - [ ] A/B тести запущені проти бейзлайну - [ ] Diversity метрики моніторяться - [ ] Cold start продуктивність відстежується окремо

Масштабування: - [ ] Embedding index (Faiss, ScaNN) для швидкого ретривалу - [ ] Incremental training для нових взаємодій - [ ] Feature caching для user/item features - [ ] Batch vs real-time trade-offs

Рішення Cold Start

Проблема Рішення
Новий користувач Популярні елементи, demographic-based, content-based
Новий елемент Content-based similarity, boost в рекомендаціях
Обидва нові Глобально популярні, популярні в категорії

Гібридний підхід:

def recommend(user_id, n=10):
    """Гібридна рекомендація з обробкою cold start."""
    user_interactions = get_user_interactions(user_id)

    if len(user_interactions) == 0:
        # Cold start: popularity + content-based
        return popularity_recommend(n)

    elif len(user_interactions) < 5:
        # Warm start: blend CF з content
        cf_recs = collaborative_recommend(user_id, n)
        content_recs = content_based_recommend(user_id, n)
        return blend(cf_recs, content_recs, alpha=0.5)

    else:
        # Повний CF
        return collaborative_recommend(user_id, n)

Інструменти та стек

Категорія Інструменти
Matrix factorization Implicit, Surprise, LightFM
Deep learning PyTorch, TensorFlow Recommenders
ANN search Faiss, ScaNN, Annoy, Milvus
Feature stores Feast, Tecton
Платформи AWS Personalize, Google Recommendations AI

Приклад міні-кейсу

Сценарій: E-commerce сайт з 2M користувачів, 500K товарів, 50M purchase events потребує персоналізовані рекомендації.

Підхід: 1. Бейзлайн: Популярні елементи - Recall@10: 0.08 2. Підготовка даних: Purchase events з recency weighting 3. Модель: ALS matrix factorization (64 factors) 4. Serving: Faiss index для швидкого candidate retrieval 5. Реранкінг: Додати diversity та freshness правила

Результати: - Recall@10: 0.08 → 0.22 (+175%) - A/B тест CTR: +18% - Дохід на користувача: +12% - Latency: 45ms p99

Ключові уроки: - Recency weighting критичний (нещодавні покупки 3x вага) - Cold start користувачам потрібен category-based fallback - Diversity constraint покращив довготривалий engagement

Пов'язані сторінки

Додаткове читання

Спробуйте наживо

Дослідіть пов'язану модель на практиці: відкрийте Recommendation Systems: Collaborative Filtering, Content-Based & Two-Tower Retrieval і змінюйте параметри під час роботи. Усе лишається у вашому браузері.

▶ Відкрити симуляцію Recommendation Systems: Collaborative Filtering, Content-Based & Two-Tower Retrieval

Що ви знайшли?

Додати кроки відтворення (опційно)