Головна▸Статті▸Рекомендаційні системи

Сесійні рекомендації

Рекомендації на основі анонімних сесій - item-KNN, GRU4Rec, SASRec та виробничі системи сесійних рекомендацій.

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

Сесійні рекомендації працюють з анонімними або короткочасними взаємодіями де немає довгострокової історії користувача. Замість user profiles, вони використовують поточну сесію - послідовність переглядів, кліків або покупок - щоб передбачити наступний item. Це критично для e-commerce, новинних сайтів та стрімінг платформ з анонімними користувачами.

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

Сесійні рекомендації застосовуються де user identity недоступний або непотрібен:

Продукти: Amazon (visitors), news aggregators, Spotify radio.

Відмінності від user-based рекомендацій

Аспект User-based Session-based
Історія Довгострокова Поточна сесія
Identity Потрібна Анонімна
Data User profiles Послідовності items
Cold start User cold start Менш проблематичний
Privacy Потребує tracking Більш приватний

Типові дані

Аспект Типові значення
Session length 2-20 items
Sessions/day 10K до 10M
Catalog size 1K до 10M items
Session duration 5 хв до 1 години
Conversion rate 1-5%

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

  1. Визначте сесію - Cookie, device, time-based cutoff
  2. Зберіть сесійні дані - Перегляди, кліки, покупки в порядку
  3. Фільтруйте короткі сесії - Зазвичай < 2 items
  4. Оберіть модель - Item-KNN, GRU4Rec, SASRec
  5. Тренуйте на історичних сесіях - Передбачення next item
  6. Оптимізуйте retrieval - ANN для швидкого inference
  7. Деплойте real-time - Низька латентність для кожного кліку
  8. A/B тестуйте - CTR, conversion, session length
  9. Моніторте drift - Catalog changes, behavior shifts
  10. Ітеруйте - Додайте features, tune hyperparameters

Спочатку базова лінія: Item-KNN

Найпростіший але ефективний підхід:

import numpy as np
from collections import defaultdict
from scipy.sparse import csr_matrix

class ItemKNN:
    def __init__(self, k=20, similarity='cosine'):
        self.k = k
        self.similarity = similarity

    def fit(self, sessions):
        """Побудова матриці co-occurrence."""
        # Item-to-item co-occurrence
        self.item_counts = defaultdict(int)
        self.cooccurrence = defaultdict(lambda: defaultdict(int))

        for session in sessions:
            for item in session:
                self.item_counts[item] += 1
            for i, item_i in enumerate(session):
                for j, item_j in enumerate(session):
                    if i != j:
                        self.cooccurrence[item_i][item_j] += 1

        # Compute similarities
        self.similarities = {}
        for item_i, neighbors in self.cooccurrence.items():
            sims = []
            for item_j, count in neighbors.items():
                if self.similarity == 'cosine':
                    sim = count / np.sqrt(self.item_counts[item_i] * self.item_counts[item_j])
                sims.append((item_j, sim))
            self.similarities[item_i] = sorted(sims, key=lambda x: -x[1])[:self.k]

    def recommend(self, session, n=10):
        """Рекомендації на основі поточної сесії."""
        scores = defaultdict(float)

        for item in session:
            if item in self.similarities:
                for neighbor, sim in self.similarities[item]:
                    if neighbor not in session:
                        scores[neighbor] += sim

        ranked = sorted(scores.items(), key=lambda x: -x[1])
        return [item for item, _ in ranked[:n]]

Deep Learning підходи

GRU4Rec - RNN для сесій:

import torch
import torch.nn as nn

class GRU4Rec(nn.Module):
    def __init__(self, n_items, embedding_dim=64, hidden_dim=128, n_layers=1):
        super().__init__()
        self.embedding = nn.Embedding(n_items, embedding_dim, padding_idx=0)
        self.gru = nn.GRU(embedding_dim, hidden_dim, n_layers, batch_first=True)
        self.output = nn.Linear(hidden_dim, n_items)

    def forward(self, x, lengths=None):
        # x: (batch, seq_len) - item indices
        embedded = self.embedding(x)  # (batch, seq_len, embed_dim)
        output, hidden = self.gru(embedded)  # (batch, seq_len, hidden_dim)

        # Використовуємо останній hidden state
        logits = self.output(output[:, -1, :])  # (batch, n_items)
        return logits

def train_gru4rec(model, train_loader, epochs=10):
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
    criterion = nn.CrossEntropyLoss()

    for epoch in range(epochs):
        model.train()
        for batch_sessions, batch_targets in train_loader:
            optimizer.zero_grad()
            logits = model(batch_sessions)
            loss = criterion(logits, batch_targets)
            loss.backward()
            optimizer.step()

SASRec - Self-Attention:

class SASRec(nn.Module):
    def __init__(self, n_items, max_len=50, d_model=64, n_heads=2, n_layers=2):
        super().__init__()
        self.item_emb = nn.Embedding(n_items + 1, d_model, padding_idx=0)
        self.pos_emb = nn.Embedding(max_len, d_model)

        encoder_layer = nn.TransformerEncoderLayer(
            d_model=d_model,
            nhead=n_heads,
            dim_feedforward=d_model * 4,
            batch_first=True
        )
        self.transformer = nn.TransformerEncoder(encoder_layer, n_layers)
        self.output = nn.Linear(d_model, n_items + 1)

    def forward(self, x):
        seq_len = x.shape[1]
        positions = torch.arange(seq_len, device=x.device).unsqueeze(0)

        x = self.item_emb(x) + self.pos_emb(positions)

        # Causal mask для auto-regressive
        mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1).bool()
        x = self.transformer(x, mask=mask.to(x.device))

        return self.output(x[:, -1, :])

Метрики оцінки

Метрика Опис Використання
HR@K Hit Rate - чи target в top-K Recall-like
MRR Mean Reciprocal Rank Ranking quality
NDCG@K Normalized DCG Ranking quality
Coverage % catalog recommended Diversity
CTR Click-through rate Online metric
def hit_rate_at_k(recommendations, targets, k=10):
    """Hit Rate @ K."""
    hits = 0
    for recs, target in zip(recommendations, targets):
        if target in recs[:k]:
            hits += 1
    return hits / len(targets)

def mrr(recommendations, targets):
    """Mean Reciprocal Rank."""
    rr_sum = 0
    for recs, target in zip(recommendations, targets):
        try:
            rank = recs.index(target) + 1
            rr_sum += 1 / rank
        except ValueError:
            pass
    return rr_sum / len(targets)

Типові помилки та чек-лист

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

Перед деплоєм: - [ ] Offline метрики валідовані - [ ] Inference latency < 50ms - [ ] A/B test framework готовий - [ ] Fallback до popularity

Після деплою: - [ ] Моніторинг CTR та conversion - [ ] Відстеження session length - [ ] Catalog refresh handling - [ ] Регулярне перенавчання

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

Категорія Інструменти
Бібліотеки RecBole, Surprise, TensorFlow Recommenders
Deep Learning PyTorch, JAX
Serving TensorFlow Serving, Triton
Vector DB Milvus, Pinecone, Faiss

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

Сценарій: Session-based рекомендації для e-commerce.

Підхід: - GRU4Rec з 128-dim embeddings - Items: 50K SKUs - Сесії: 5M/місяць

Результати: - HR@10: 0.35 (vs 0.22 popularity baseline) - +8% conversion rate - Inference: 25ms p99

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

Додаткові матеріали

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

Дослідіть пов'язану модель на практиці: відкрийте Video Recommender — Matrix Factorization Live і змінюйте параметри під час роботи. Усе лишається у вашому браузері.

▶ Відкрити симуляцію Video Recommender — Matrix Factorization Live

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

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