Collaborative filtering рекомендує елементи на основі того, що сподобалось схожим користувачам. Замість аналізу контенту елементів, він знаходить патерни в поведінці користувачів - "користувачі, які купили X, також купили Y". Ця проста ідея забезпечує рекомендації в Netflix, Spotify, Amazon та більшості сучасних платформ.
Де це використовується
Collaborative filtering - основа персоналізації:
- Стрімінг: Рекомендації Netflix, Spotify, YouTube
- E-commerce: Amazon "customers also bought", рекомендації товарів
- Соціальні: Facebook пропозиції друзів, Twitter "who to follow"
- Контент: Рекомендації новин, пропозиції блогів
- Робота: LinkedIn рекомендації вакансій, матчинг кандидатів
- Реклама: Персоналізований ad targeting
Продукти: 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: Категорії, описи, метадані
Що ви робите на практиці
- Визначте взаємодії - Які дії користувача вказують на інтерес?
- Побудуйте матрицю взаємодій - User-item sparse матриця
- Обробіть implicit feedback - Конвертуйте дії в сигнали
- Оберіть алгоритм - Matrix factorization, neural, або гібрид
- Натренуйте модель - Оптимізуйте для ранжування або prediction рейтингу
- Згенеруйте кандидатів - ANN для елементів
- Додайте бізнес-правила - Diversity, freshness, inventory
- Оцініть offline - Recall, NDCG на held-out даних
- A/B тест online - CTR, engagement, конверсії
- Обробіть 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()}
Режими відмов та чеклист налагодження
- [ ] Cold start: Немає рекомендацій для нових користувачів/елементів
- [ ] Popularity bias: Завжди рекомендує популярні елементи
- [ ] Filter bubbles: Рекомендації занадто однорідні
- [ ] Sparsity: Недостатньо взаємодій для значущих патернів
- [ ] Застарілі рекомендації: Не оновлюються з новою поведінкою
- [ ] Position bias: Тренування на biased feedback
- [ ] Leakage: Використання майбутніх даних у тренуванні
Чеклист для продакшну
Перед деплоєм: - [ ] 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
Пов'язані сторінки
- Індекс: ML Field Guide
- Learning to Rank
- Системи семантичного пошуку
- Feature Stores
Додаткове читання
- Matrix Factorization for Recommender Systems
- Implicit Feedback Collaborative Filtering
- Neural Collaborative Filtering
- Two-Tower Models for Recommendations
Спробуйте наживо
Дослідіть пов'язану модель на практиці: відкрийте Recommendation Systems: Collaborative Filtering, Content-Based & Two-Tower Retrieval і змінюйте параметри під час роботи. Усе лишається у вашому браузері.
▶ Відкрити симуляцію Recommendation Systems: Collaborative Filtering, Content-Based & Two-Tower Retrieval