Scalability Ceilings: Optimization Strategies Above 100M Ratings
MovieLens-1M is too small — in real world you work with 100M+ ratings, 10M+ items. This lesson: offline batch precomputation pattern, LSH (Locality-Sensitive Hashing), MinHash for approximate Jaccard, distributed computation with MapReduce/Spark, Redis-based serving.
Şükrü Yusuf KAYA
22 min read
Advanced📈 Bu dersin amacı
ML-1M'i 30 saniyede eğittik. Amazon-scale (100M user, 10M item) bu kodla yaklaşık 15 yıl sürer. Bu derste: aynı algoritma fikrini büyük ölçeğe taşıma stratejileri. LSH, MinHash, offline batch, Spark — endüstri pattern'leri.
Sorunun Boyutu#
| Dataset | User | Item | Rating | Item-Item Matrix (dense) | Naive runtime |
|---|---|---|---|---|---|
| MovieLens-1M | 6K | 3.7K | 1M | 55 MB | 30 sn |
| MovieLens-25M | 162K | 62K | 25M | 16 GB | 30 dk |
| Amazon Beauty | 800K | 250K | 2M | 250 GB | imkansız (dense) |
| Amazon Books | 8M | 2.3M | 22M | 21 TB | imkansız |
| YouTube ölçeği | 2B | 800M | 100B | imkansız | imkansız |
Mevcut algoritmamız MovieLens-25M'e kadar zorla çalışır. Ötesi için yapısal değişiklik gerek.
Strateji 1: Offline Batch Precomputation#
Fikir#
Item-item similarity matrix statik — kullanıcı her geldiğinde yeniden hesaplamaya gerek yok. Günde 1-2 kez batch olarak hesapla, Redis'e koy.
Cron job (her gece): 1. Tüm rating data'sını oku 2. Item-item similarity matrix hesapla (Spark) 3. Her item için top-100 similar item al 4. Redis'e key-value yaz: item:i:similar → [(j, sim), ...] Online (real-time): 1. User u → Redis'ten history al 2. History'deki her item için Redis'ten similar'lar al 3. Aggregate + filter + top-N 4. 50ms total latency
Niçin Bu Çalışıyor?#
User behavior dataset boyutuyla lineer. Ama item-item similarity matrix subquadratik (top-K filtered). 24 saatte similarity matrix yeniden hesaplanabilir.
Amazon'un 2003 Paper'ının Anahtarı#
Linden et al. 2003 paper'ının ana kontribüsyonu bu fikirdi: "Yapma her online query'de — offline pre-compute, online lookup."
python
# Offline batch script (her gece çalışır)import redis def offline_precompute_similarity(): """Tüm rating data → item-item similarity → Redis.""" # 1. Data load (Spark veya Polars, large data) R = load_full_rating_matrix() # CSR # 2. Similarity compute S = compute_item_item_similarity(R, top_k=100) # 3. Redis write r = redis.Redis(host="redis-prod", port=6379) pipe = r.pipeline() for item_idx in range(S.shape[0]): row = S.getrow(item_idx) neighbors = [(int(row.indices[i]), float(row.data[i])) for i in range(row.nnz)] # Sort by similarity desc neighbors.sort(key=lambda x: -x[1]) # JSON or msgpack serialize pipe.set(f"item_sim:{item_idx}", json.dumps(neighbors), ex=86400 * 2) # 2 day expiry pipe.execute() # Online serving (her API request'te çalışır)def serve_recommendation(user_id: int, k: int = 10) -> list[int]: """Real-time öneri — sadece Redis lookup, 30ms total.""" r = redis.Redis(host="redis-prod", port=6379) # 1. User history (recent N items) history = r.zrange(f"user_hist:{user_id}", 0, 50, desc=True) # son 50 etkileşim # 2. Her item için neighbors al, score aggregate score_dict = defaultdict(float) for item_id in history: neighbors_json = r.get(f"item_sim:{item_id}") if not neighbors_json: continue for neighbor_id, sim in json.loads(neighbors_json)[:50]: score_dict[neighbor_id] += sim # 3. History filter + top-K for h in history: score_dict.pop(h, None) top_k_items = sorted(score_dict.items(), key=lambda x: -x[1])[:k] return [item_id for item_id, _ in top_k_items] Offline batch + online serving pattern.
Strateji 2: LSH (Locality-Sensitive Hashing) — Approximate Similarity#
Sorun#
Item-item similarity matrix'i offline'da hesaplamak hala O(N² × d). 10M item için yıllar sürer.
LSH Fikri#
Yakın item'lar, aynı hash bucket'a düşsün. Sadece aynı bucket içindeki item çiftlerini similarity hesapla.
Geleneksel: O(N²) comparison LSH: O(N · log N) comparison (yaklaşık)
MinHash — Jaccard için LSH#
MinHash, set similarity için (Broder 1997):
- Her set için K random hash function uygula.
- Her hash function'ın minimum değeri signature olur.
- İki set'in signature'larındaki eşleşme oranı ≈ Jaccard similarity.
# Pseudo-code def minhash_signature(item_set, n_hashes=128): return [min(hash_i(x) for x in item_set) for i in range(n_hashes)] # İki set'in tahmini Jaccard: estimated_jaccard = sum(s1 == s2 for s1, s2 in zip(sig_a, sig_b)) / len(sig_a)
LSH Banding#
128-bit signature'ı 32 band'a böl (her band 4 hash). Aynı band'da identical value varsa, "candidate similar pair". Sadece candidate pair'leri tam similarity hesapla.
Pratik#
Spark MLlib ve Datasketch kütüphaneleri kullanılır. 100M item için bile günde 1 kez batch fizibıl.
Strateji 3: Distributed Computation — Spark / Ray / Dask#
Niçin Distributed?#
Tek makinede memory tavan: 1-2 TB RAM. 10M item × 10M item similarity bu sınırı aşar.
Spark Pattern#
from pyspark.sql import SparkSession from pyspark.sql.functions import col, count, sum as _sum spark = SparkSession.builder.appName("ItemItemCF").getOrCreate() # Rating DataFrame ratings = spark.read.parquet("s3://my-bucket/ratings.parquet") # Self-join over users — her user için tüm item çiftleri # Bu kısım Spark'ın güçlü yanı — distributed shuffle pairs = ratings.alias("a").join( ratings.alias("b"), (col("a.user_id") == col("b.user_id")) & (col("a.item_id") < col("b.item_id")) ).select( col("a.item_id").alias("item_i"), col("b.item_id").alias("item_j"), col("a.rating").alias("rating_i"), col("b.rating").alias("rating_j"), ) # Aggregate per pair agg = pairs.groupBy("item_i", "item_j").agg( count("*").alias("n_common"), _sum(col("rating_i") * col("rating_j")).alias("dot_product"), # ... daha fazla aggregate ) # Similarity hesapla (UDF) similarity = agg.withColumn("sim", ...) # Top-K per item windowed = similarity.groupBy("item_i").apply(top_k_udf) windowed.write.parquet("s3://my-bucket/item_similarity.parquet")
Performance Beklentisi#
- 100M rating, 10M item — 50-node Spark cluster — 6-12 saat batch.
- Sonuç parquet'e yazılır, ayrı bir process Redis'e taşır.
Production Mimari — Tüm Parçalar Birlikte#
┌─────────────────────────────────────────────────┐ │ Offline (her gece, 3-6 saat) │ ├─────────────────────────────────────────────────┤ │ │ │ S3 raw data → Spark cluster (50 node) │ │ ↓ │ │ LSH-based item-item similarity batch │ │ ↓ │ │ Top-K per item → Parquet │ │ ↓ │ │ Pusher process → Redis cluster │ │ │ └─────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ Online (real-time, p99 < 50ms) │ ├─────────────────────────────────────────────────┤ │ │ │ User request │ │ ↓ │ │ FastAPI / gRPC service │ │ ↓ │ │ Redis lookup: │ │ - user_hist:{uid} (son 50 etkileşim) │ │ - item_sim:{iid} (her history item için) │ │ ↓ │ │ Aggregate + history filter + business rules │ │ ↓ │ │ Top-N response │ │ │ └─────────────────────────────────────────────────┘
Tipik Latency Budget#
- Network (round-trip): 5ms
- Redis 50 lookup (parallel pipeline): 10ms
- Aggregation in-process: 5ms
- Business rules + diversity: 10ms
- Response serialize + send: 5ms
- Total p99: 35-50ms
🎉 Modül 5 Tamamlandı#
4 ders bitti:
- 5.1 User-user vs item-item felsefesi
- 5.2 4 similarity metric + shrinkage trick
- 5.3 MovieLens-1M üzerinde production-grade item-item k-NN
- 5.4 100M+ scale için LSH, Spark, offline batch
Benchmark tablomuz şimdi 3 satır:
- Popularity: 0.064 NDCG@10
- Content-Based: 0.089
- Item-Item k-NN CF: 0.117
Bir sonraki modülde Modül 6 — Matrix Factorization. Funk SVD'den BiasedMF'e, ALS'ten implicit ALS'e. NDCG@10'umuz 0.13-0.16'ya çıkacak. Recommender literatürünün en güzel bölümü bizi bekliyor. ☕
Frequently Asked Questions
Evet, ama 16-32GB RAM gerek. ML-25M'de 62K item × 62K = ~4B similarity cell. Sparse'da top-K=100 ile 6.2M entry — 1GB. Computation 30-60 dakika. Memory yetersiz ise chunked computation (item'ları 5K'lık batch'lerde işle).
Yorumlar & Soru-Cevap
(0)Yorum yazmak için giriş yap.
Yorumlar yükleniyor...
Related Content
Module 0: Course Framework & Workshop Setup
Why Do Recommender Systems Matter? Birth, Present, and Future of a Discipline
Start LearningModule 0: Course Framework & Workshop Setup
Who Is a Recommender Engineer? Skill Atlas and Junior → Staff Career Map
Start LearningModule 0: Course Framework & Workshop Setup