Skip to content

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
Online Evaluation: A/B Test, Interleaving, CUPED ve İstatistiksel Güç
🧪 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#

MetricTanımToplama Frekansı
CTR (Click-Through Rate)Clicks / ImpressionsReal-time
Conversion RateConversions / VisitsDaily
Watch Time / Dwell TimeAvg time on contentHourly
Revenue per User (RPU)Total revenue / UsersDaily
Retention (D1, D7, D30)%'si returning after N daysDaily, lagged
Session LengthEvents per sessionPer session

Sample Size Matematiği — "Kaç Kullanıcı Yeter?"#

Anahtar Formül (Two-Sample t-Test için)#

  • 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% relativeDetect 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 calculator
import numpy as np
from 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%'e
n = 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 Interleaving
import random
from 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"
 
 
# Test
list_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))
  • Y_obs:
    deney sırasındaki metric.
  • X_pre:
    deney öncesi covariate (örn. son 14 günkü ortalama CTR).
  • θ:
    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
  1. 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