Session-based recommendation generates suggestions from a user's current browsing session, without requiring long-term user history. This is essential for anonymous users, new visitors, and scenarios where real-time context matters more than historical preferences. The challenge: make good recommendations from just a few clicks.
Where It Is Used
Session-based recommendations power real-time personalization:
- E-commerce: Product recommendations during browsing session
- Media: "Up next" video/article recommendations
- Travel: Hotel/flight suggestions based on current search
- Music: Playlist continuation, radio stations
- News: Article recommendations from reading patterns
- Gaming: In-game item recommendations
Use case: User browses 3 products → recommend the 4th without knowing who they are.
How Session-Based Differs from Traditional
| Aspect | Session-Based | User-Based |
|---|---|---|
| User identity | Not required | Required |
| Data needed | Current session clicks | Historical interactions |
| Cold-start users | Handles naturally | Struggles |
| Temporal dynamics | Strong focus | Often ignored |
| Context sensitivity | High | Lower |
| Model complexity | Sequential models | Matrix factorization |
When to use session-based: - High anonymous traffic (>50%) - Short user sessions matter - Real-time context critical - Fast-changing user intent - Privacy-conscious applications
Typical Data You Will See
| Aspect | Typical Values |
|---|---|
| Session length | 2 to 50 interactions |
| Session duration | 1 min to 2 hours |
| Items in catalog | 10K to 10M |
| Sessions per day | 100K to 100M |
| Conversion rate | 1% to 10% |
Session data structure:
session_id | timestamp | item_id | action_type
abc123 | 10:00:01 | item_1 | view
abc123 | 10:00:15 | item_2 | view
abc123 | 10:01:02 | item_3 | add_to_cart
abc123 | 10:01:30 | ? | recommend_here
Key characteristics: - Sequential order matters - Recency is important - Sessions have natural boundaries - Actions have different weights (view < click < cart < purchase)
What You Do in Practice
- Define session boundaries - Time-based, login-based, or hybrid
- Collect session data - Clickstream with timestamps
- Preprocess sequences - Filter short sessions, handle duplicates
- Choose model architecture - RNN, Transformer, or simpler
- Train on historical sessions - Predict next item from prefix
- Handle item embeddings - Pre-trained or learned jointly
- Optimize for latency - Real-time inference required
- Evaluate properly - Hit rate, MRR on held-out sessions
- A/B test online - CTR, conversion, engagement
- Monitor and iterate - Track session patterns, retrain
Baseline First
Start with a simple item-to-item co-occurrence model:
from collections import defaultdict
import numpy as np
class ItemKNN:
def __init__(self, k=20):
self.k = k
self.item_counts = defaultdict(int)
self.co_occurrence = defaultdict(lambda: defaultdict(int))
def fit(self, sessions):
for session in sessions:
for i, item in enumerate(session):
self.item_counts[item] += 1
# Count co-occurrences within session
for j in range(max(0, i-5), i):
other = session[j]
self.co_occurrence[item][other] += 1
self.co_occurrence[other][item] += 1
def recommend(self, session, n=10):
scores = defaultdict(float)
for item in session[-5:]: # Recent items
for other, count in self.co_occurrence[item].items():
if other not in session:
# Normalize by item popularity
scores[other] += count / np.sqrt(self.item_counts[other])
return sorted(scores.keys(), key=lambda x: scores[x], reverse=True)[:n]
Why item-KNN baseline: - Simple, interpretable - Fast training and inference - Works surprisingly well - Good benchmark for complex models
Model Architectures
| Model | Description | Pros | Cons |
|---|---|---|---|
| Item-KNN | Co-occurrence similarity | Simple, fast | No sequence modeling |
| GRU4Rec | GRU on item sequences | Captures sequences | Slower training |
| SASRec | Self-attention on sequences | State-of-the-art | Compute heavy |
| BERT4Rec | Bidirectional transformer | Strong performance | Training complexity |
| SRGNN | Graph neural network | Session as graph | Complex architecture |
| STAMP | Attention + MLP | Balance of complexity | - |
GRU4Rec example:
import torch
import torch.nn as nn
class GRU4Rec(nn.Module):
def __init__(self, n_items, embedding_dim=64, hidden_dim=128):
super().__init__()
self.embedding = nn.Embedding(n_items, embedding_dim)
self.gru = nn.GRU(embedding_dim, hidden_dim, batch_first=True)
self.output = nn.Linear(hidden_dim, n_items)
def forward(self, session_items):
# session_items: [batch, seq_len]
x = self.embedding(session_items) # [batch, seq_len, emb]
_, h = self.gru(x) # h: [1, batch, hidden]
logits = self.output(h.squeeze(0)) # [batch, n_items]
return logits
Training Approach
Next-item prediction:
def create_training_data(sessions):
X, y = [], []
for session in sessions:
for i in range(1, len(session)):
X.append(session[:i]) # Prefix
y.append(session[i]) # Next item
return X, y
# Loss: Cross-entropy over item catalog
loss_fn = nn.CrossEntropyLoss()
for batch_x, batch_y in dataloader:
logits = model(batch_x)
loss = loss_fn(logits, batch_y)
loss.backward()
optimizer.step()
Negative sampling (for large catalogs):
# Sample negatives instead of full softmax
def sampled_softmax_loss(logits, positive_idx, n_negatives=100):
negatives = torch.randint(0, n_items, (n_negatives,))
selected = torch.cat([positive_idx.unsqueeze(0), negatives])
selected_logits = logits[selected]
return F.cross_entropy(selected_logits, torch.zeros(1, dtype=torch.long))
Evaluation and Metrics
| Metric | Description | Use Case |
|---|---|---|
| Hit Rate@K | Next item in top K | Overall accuracy |
| MRR | Mean reciprocal rank | Ranking quality |
| NDCG@K | Normalized DCG | Position-weighted |
| Coverage | Fraction of items recommended | Diversity |
| Popularity bias | Tendency to recommend popular | Fairness |
Evaluation protocol:
def evaluate(model, test_sessions, k=20):
hits, mrrs = [], []
for session in test_sessions:
for i in range(1, len(session)):
prefix = session[:i]
target = session[i]
recommendations = model.recommend(prefix, n=k)
if target in recommendations:
hits.append(1)
rank = recommendations.index(target) + 1
mrrs.append(1.0 / rank)
else:
hits.append(0)
mrrs.append(0)
return np.mean(hits), np.mean(mrrs)
Failure Modes and Debugging Checklist
- [ ] Short sessions: Not enough signal to recommend
- [ ] Popularity bias: Always recommending popular items
- [ ] Position bias: Only using last item, ignoring context
- [ ] Cold items: New items never recommended
- [ ] Latency issues: Model too slow for real-time
- [ ] Session boundary errors: Wrong session segmentation
- [ ] Evaluation leak: Training on test session data
- [ ] Temporal leakage: Future items in training
Production Checklist
Before deployment: - [ ] Model latency < 50ms p99 - [ ] Offline metrics validated - [ ] Session parsing tested - [ ] Fallback for empty sessions - [ ] A/B test designed - [ ] Monitoring in place
After deployment: - [ ] Real-time CTR tracked - [ ] Session length monitored - [ ] Recommendation diversity tracked - [ ] Model freshness maintained - [ ] Cold item handling verified - [ ] Latency percentiles monitored
Infrastructure: - [ ] Model serving optimized - [ ] Session state management - [ ] Item embedding cache - [ ] Feature store integration
Tools and Stack
| Category | Tools |
|---|---|
| Frameworks | PyTorch, TensorFlow, RecBole |
| Session models | GRU4Rec, SASRec implementations |
| Serving | TorchServe, Triton, custom |
| Session tracking | Segment, custom clickstream |
| A/B testing | Optimizely, custom |
| Monitoring | Custom dashboards |
Example Mini-Case
Scenario: E-commerce site with 60% anonymous traffic needs real-time product recommendations.
Data: 10M sessions/month, 500K products, average 8 items/session.
Approach: 1. Baseline: Item-KNN on co-occurrences - HR@20: 0.18 2. Sequential: GRU4Rec with item embeddings - HR@20: 0.25 3. Attention: SASRec with position encoding - HR@20: 0.28 4. Hybrid: + popularity features for cold items - HR@20: 0.30
Architecture:
Clickstream → Session Parser → Feature Store →
Model Inference (< 30ms) → Ranking → Recommendations
Results: - Hit Rate@20: 30% - MRR: 0.12 - Inference latency: 25ms p99 - A/B test: +15% CTR, +8% conversion - Anonymous user engagement: +22%
Key learnings: - Session length strongly predicts recommendation quality - Recent items (last 3) most predictive - Hybrid with popularity crucial for new items - Real-time context beats historical preferences for intent
Related Pages
- Index: ML Field Guide
- Collaborative Filtering
- Content-Based Recommendations
- Semantic Search
Further Reading
Try it live
Everything above runs in your browser — open Video Recommender — Matrix Factorization Live and change the parameters while it is running. Nothing is installed, nothing is uploaded, the whole model lives in one tab.
▶ Open Video Recommender — Matrix Factorization Live simulation