Online Evaluation: A/B Test, Interleaving, CUPED and Statistical Power
You raised offline NDCG +2% — verify with A/B test before production deploy that user behavior really changes. A/B test sample size math, interleaving (10x more efficient), CUPED variance reduction, and switchback testing.
Şükrü Yusuf KAYA
26 min read
Advanced🧪 Bu dersin amacı
Offline metric'lerin iyileşmesi yeter değil. Production'da gerçek kullanıcıların davranışını değiştirmen lazım. Bu derste: A/B test'in matematiği, neden çoğu A/B test'in sample size yetersiz ve sonuç güvensiz, interleaving ile aynı sonucu 10x az trafik ile alma, ve CUPED ile varyansı azaltma.
A/B Test 101#
Temel Yapı#
Tüm kullanıcılar ↓ Random assignment (50/50) ↓ ┌────────────────┬────────────────┐ │ Control (A) │ Treatment (B) │ │ Eski model │ Yeni model │ └────────────────┴────────────────┘ ↓ ↓ Metric topla Metric topla ↓ ↓ İstatistiksel test ↓ Conclude: B, A'dan iyi mi?
Yaygın Metrikler#
| Metric | Tanım | Toplama Frekansı |
|---|---|---|
| CTR (Click-Through Rate) | Clicks / Impressions | Real-time |
| Conversion Rate | Conversions / Visits | Daily |
| Watch Time / Dwell Time | Avg time on content | Hourly |
| Revenue per User (RPU) | Total revenue / Users | Daily |
| Retention (D1, D7, D30) | %'si returning after N days | Daily, lagged |
| Session Length | Events per session | Per session |
- n: Her grup için gereken örneklem sayısı.
- α: Type I error (false positive) — genelde 0.05.
- β: Type II error (false negative) — genelde 0.20.
- z_{α/2}: İki yönlü test için α/2 quantile (0.05 için 1.96).
- z_β: Statistical power için (0.80 için 0.84).
- Δ: Beklenen etki büyüklüğü.
- σ: Metric'in standart sapması.
Çıkarılan Pratik Tablo#
| Metric | σ (tipik) | Detect 1% relative | Detect 5% relative |
|---|---|---|---|
| CTR (mean 5%) | 0.22 | ~6.8M user/grup | ~272K user/grup |
| Revenue per user ($10) | $15 | ~16M user/grup | ~640K user/grup |
| D7 retention (40%) | 0.49 | ~3.8M user/grup | ~152K user/grup |
Gözlem: %1 mutlak iyileşme için milyonlarca user gerek. Çoğu startup bu örneklem hacmine sahip değil — yani A/B test'leri istatistiksel olarak güvensiz.
Pratik Tavsiye#
Eğer küçük şirketteysen, ya daha büyük effect beklerken yap (5-10% iyileşme), ya da interleaving kullan.
python
# evaluation/ab_test.py — Sample size calculatorimport numpy as npfrom scipy import stats def required_sample_size( baseline_mean: float, baseline_std: float, min_detectable_effect: float, # absolute or relative? relative: bool = True, alpha: float = 0.05, power: float = 0.80, two_sided: bool = True,) -> int: """ Her grup için gereken sample size'ı hesaplar. Returns: int (sample size per group) """ z_alpha = stats.norm.ppf(1 - alpha / (2 if two_sided else 1)) z_beta = stats.norm.ppf(power) if relative: delta = baseline_mean * min_detectable_effect else: delta = min_detectable_effect n = 2 * ((z_alpha + z_beta) ** 2) * (baseline_std ** 2) / (delta ** 2) return int(np.ceil(n)) # Pratik örnek: CTR 5%'ten 5.05%'en = required_sample_size( baseline_mean=0.05, baseline_std=np.sqrt(0.05 * 0.95), # Bernoulli std min_detectable_effect=0.01, # 1% relative iyileşme)print(f"CTR test için her grup: {n:,} user")# CTR test için her grup: 6,232,448 user — 12M total, bayağı çok # Yüksek effect ile:n2 = required_sample_size( baseline_mean=0.05, baseline_std=np.sqrt(0.05 * 0.95), min_detectable_effect=0.10, # 10% relative iyileşme)print(f"CTR (10% effect) test için her grup: {n2:,} user")# CTR (10% effect) için her grup: 62,325 user — toplam 125K, gerçekçi A/B test sample size calculator — pratik formül.
Interleaving — 10x Daha Verimli A/B Test#
Sorun#
A/B test'te user'ları bölersin — yarısı A, yarısı B. Bu yüzden her grup yarım örneklem büyüklüğünde. Plus, user'lar arası varyans büyük (bazı user'lar günde 100 click, bazıları 1).
Interleaving'in Fikri#
Aynı user'a hem A hem B'nin önerilerini karıştırılmış liste olarak göster. Her user'da hem A hem B'yi ölçebilirsin.
Eski yöntem (A/B): User 1: sadece A önerileri gördü User 2: sadece B önerileri gördü Sonuç: between-user varyans çok büyük Interleaving: User 1: A ve B'nin önerileri karışık liste içinde gördü User 2: A ve B'nin önerileri karışık liste içinde gördü Sonuç: each user same baseline → between-user varyans elenir
Team Draft Interleaving (Joachims, Microsoft 2003)#
List A: [a1, a2, a3, a4, a5] List B: [b1, b2, b3, b4, b5] Coin flip: A first 1. A picks a1 2. B picks b1 3. A picks a2 4. B picks b2 ... interleave Result: [a1, b1, a2, b2, a3, b3, a4, b4, a5, b5] User clicks: [a1, b3] Wins: A team (a1) +1, B team (b3) +1 Equal! Eğer user clicks [a1, a2, a3]: A +3, B 0 → A wins
Niçin 10x Daha Az Trafik?#
Within-user varyans, between-user'dan çok küçük. Aynı user için aynı baseline → daha tight estimate.
Microsoft'un 2018 paper'ı (Schuth et al.): interleaving A/B'den 10-100x sample efficient. Practical truth: 100K user yerine 10K user yetiyor.
python
# evaluation/interleaving.py — Team Draft Interleavingimport randomfrom typing import Literal def team_draft_interleave( list_a: list[int], list_b: list[int], seed: int = None,) -> tuple[list[int], list[Literal["A", "B"]]]: """ Team Draft interleaving — Joachims 2003. Returns: interleaved_list: final ranking shown to user ownership: each position'ın hangi takıma ait olduğunu söyler """ if seed is not None: random.seed(seed) interleaved = [] ownership = [] used = set() a_idx, b_idx = 0, 0 # Coin flip — who picks first a_first = random.random() < 0.5 while a_idx < len(list_a) and b_idx < len(list_b): # A's turn if a_first: # A picks while a_idx < len(list_a) and list_a[a_idx] in used: a_idx += 1 if a_idx < len(list_a): interleaved.append(list_a[a_idx]) ownership.append("A") used.add(list_a[a_idx]) a_idx += 1 # B picks while b_idx < len(list_b) and list_b[b_idx] in used: b_idx += 1 if b_idx < len(list_b): interleaved.append(list_b[b_idx]) ownership.append("B") used.add(list_b[b_idx]) b_idx += 1 else: # B picks first while b_idx < len(list_b) and list_b[b_idx] in used: b_idx += 1 if b_idx < len(list_b): interleaved.append(list_b[b_idx]) ownership.append("B") used.add(list_b[b_idx]) b_idx += 1 while a_idx < len(list_a) and list_a[a_idx] in used: a_idx += 1 if a_idx < len(list_a): interleaved.append(list_a[a_idx]) ownership.append("A") used.add(list_a[a_idx]) a_idx += 1 return interleaved, ownership def score_interleaving( clicks: list[int], ownership_map: dict[int, Literal["A", "B"]],) -> Literal["A", "B", "tie"]: """Click'leri team A ve B arasında say.""" a_score = sum(1 for c in clicks if ownership_map.get(c) == "A") b_score = sum(1 for c in clicks if ownership_map.get(c) == "B") if a_score > b_score: return "A" elif b_score > a_score: return "B" return "tie" # Testlist_a = [101, 102, 103, 104, 105]list_b = [201, 102, 202, 203, 105] # 102 ve 105 ortak interleaved, ownership = team_draft_interleave(list_a, list_b, seed=42)print(f"Interleaved: {interleaved}")print(f"Ownership: {ownership}") ownership_map = dict(zip(interleaved, ownership))clicks = [interleaved[0], interleaved[2]] # user 1. ve 3. sıraya tıkladıprint(f"Winner: {score_interleaving(clicks, ownership_map)}") Team Draft Interleaving — Joachims 2003 implementasyonu.
CUPED — Controlled-Experiment Using Pre-Existing Data#
Sorun#
A/B test'in varyansını azaltma — daha az user ile aynı confidence.
Fikir (Microsoft 2013, Deng et al.)#
User'ın deney öncesi davranışından tahmin edilebilen kısmı çıkar. Geri kalan deney etkisi.
Y_post = Y_obs - θ · (X_pre - mean(X_pre))
- deney sırasındaki metric.
Y_obs: - deney öncesi covariate (örn. son 14 günkü ortalama CTR).
X_pre: - regresyon coefficient (X_pre → Y_obs).
θ:
Varyans Azalmasının Matematiği#
Eğer X ve Y arası korelasyon ρ ise:
Var(Y_CUPED) = Var(Y_obs) · (1 - ρ²)
ρ=0.7 ise → varyans %51 azalır → sample size %51 azalır → süre / cost %51 azalır.
Pratik#
import numpy as np from sklearn.linear_model import LinearRegression def apply_cuped( metric_obs: np.ndarray, # deney sırasındaki metric covariate_pre: np.ndarray, # deney öncesi covariate ) -> np.ndarray: """CUPED-adjusted metric.""" mean_pre = covariate_pre.mean() centered_pre = covariate_pre - mean_pre # Regression: metric_obs ~ centered_pre reg = LinearRegression() reg.fit(centered_pre.reshape(-1, 1), metric_obs) theta = reg.coef_[0] metric_adjusted = metric_obs - theta * centered_pre return metric_adjusted, theta
Pratik Kazanım#
Microsoft (2013 paper'ı): Bing search arama testlerinde CUPED ile %30-50 daha az sample ile aynı confidence. Hala A/B test'lerin altın standart varyans azaltma tekniği.
Switchback Testing — Marketplace'lerin Hayat Kurtarıcısı#
Sorun#
Uber, DoorDash, Airbnb gibi marketplace'lerde A/B test problematik. Çünkü:
- Driver/host arzı paylaşılan kaynak.
- A grubu çok driver kullanırsa, B grubu kıt arzla karşılaşır.
- Bu spillover effect, A/B'yi geçersiz kılar.
Çözüm#
Zamanı bölmek, kullanıcıyı değil. Örneğin: 10 dakikalık periyotlar — her periyot tüm marketplace'e A veya B uygula.
Saat 00:00-00:10: tüm pazara A Saat 00:10-00:20: tüm pazara B Saat 00:20-00:30: tüm pazara A ...
İstatistik#
Time-blocks arasında autocorrelation olduğu için klasik t-test geçerli değil. Cluster-randomized analysis veya time-series modeling gerek.
Recommender'da Switchback?#
Genelde gerekmez — kullanıcılar paylaşılan kaynak değil. Ama marketplace recommender (örn. Airbnb ev sıralaması) bu yöntemi kullanır.
🚨 A/B test'te 4 büyük hata
- P-hacking — test sırasında metric'lere peek atma, anlamlı görünce durdurma. 'Sequential testing' yapacaksan açıkça planla. 2) Multiple testing — 20 metric kontrol ediyorsan, p<0.05'te 1 tanesi tesadüf çıkacak. Bonferroni veya FDR düzeltmesi yap. 3) Novelty effect — yeni özellik 1. hafta yüksek metrics — 2-3 hafta bekle. 4) Network effect — A grubu user'ı B grubuna referans yolluyor → contamination. Cluster randomization (örn. country-level) ile çöz.
Sıradakİ Ders#
Bir sonraki derste (3.5 — modülün son dersi) — offline-online correlation problemi. Dacrema krizi ve neden çoğu paper'ın sonuçları reproduce edilemiyor. 'Doğru' offline metric'i seçmenin sanatı. Bu ders senin recommender literatürü okurken bullshit detector'ünü açacak.
Frequently Asked Questions
İki ana sebep: (1) Weekly seasonality — hafta içi/sonu farkları, en az 1 hafta. (2) Novelty effect — yeni özellik 1. hafta ilgi çekici, 2. hafta normalize olur. Pratik kural: minimum 2 hafta. Eğer büyük effect arıyorsan veya marketplace context'i ise 4 hafta. Sample size yeterince doluyorsa daha uzun süre tutmak gerekmez.
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