常见陷阱
问题
A/B 测试中有哪些常踩的坑?如何避免得出错误结论?
答案
陷阱一览
| 陷阱 | 后果 | 频率 |
|---|---|---|
| Peeking | 假阳性膨胀 | ⭐⭐⭐⭐⭐ |
| SRM | 分流偏差 | ⭐⭐⭐⭐ |
| 多重比较 | 假阳性膨胀 | ⭐⭐⭐⭐ |
| 辛普森悖论 | 反向结论 | ⭐⭐⭐ |
| 新奇效应 | 效应虚高 | ⭐⭐⭐⭐ |
| 网络效应 | SUTVA 违反 | ⭐⭐⭐ |
| 幸存者偏差 | 样本偏斜 | ⭐⭐⭐ |
Peeking Problem(提前偷看)
最常见也最致命的陷阱
在实验期间反复查看结果并在"显著时"提前停止,会将实际假阳性率从 5% 膨胀到 26%-50%。
为什么会假阳性膨胀?
天数: 3 5 7 10 14
p值: 0.12 0.03 0.08 0.04 0.06
└──────────────────────┘
如果在第5天看到 p<0.05 就停下,
你做了5次检验,α 实际 ≈ 1-(1-0.05)^5 ≈ 22.6%
解决方案:
| 方案 | 做法 |
|---|---|
| 固定样本量 | 预先确定样本量,到达后才分析 |
| 序贯检验 | 使用 Alpha Spending Function(如 O'Brien-Fleming) |
| 贝叶斯方法 | 不依赖固定假阳性率,可以随时检查 |
SRM(Sample Ratio Mismatch)
分流比例偏离预期设计:
| 预期 | 实际 | 偏差程度 |
|---|---|---|
| 50 : 50 | 50.1 : 49.9 | ✅ 正常波动 |
| 50 : 50 | 48 : 52 | ❌ 可能有问题 |
| 50 : 50 | 45 : 55 | ❌❌ 严重 SRM |
SRM 检测(卡方检验):
from scipy.stats import chisquare
observed = [4800, 5200] # 实际分流
expected = [5000, 5000] # 预期分流
chi2, p_value = chisquare(observed, expected)
print(f"SRM p 值: {p_value:.6f}") # p < 0.05 → 存在 SRM
常见 SRM 原因:
- 分流代码 Bug(某些用户被过滤)
- 机器人/爬虫比例不同
- 实验组触发了重定向,丢失部分用户
- 服务端性能差异导致漏记日志
SRM 一旦检测到,实验结果不可信
无论 p 值多显著,必须先解决 SRM 问题,否则所有结论都可能是错的。
多重比较(Multiple Testing)
| 场景 | 问题 |
|---|---|
| 测了 20 个指标 | 预期 1 个偶然显著 |
| 比较了 3 个实验组 | 3 对比较,α 膨胀 |
| 按 5 个人群拆分看 | 5 次检验 |
校正方法:
| 方法 | 公式 | 特点 |
|---|---|---|
| Bonferroni | α' = α / m | 最简单,最保守 |
| Šidák | 略宽松 | |
| BH(FDR) | 控制 False Discovery Rate | 推荐,适合多指标 |
from statsmodels.stats.multitest import multipletests
p_values = [0.01, 0.03, 0.04, 0.08, 0.15, 0.5]
# Bonferroni 校正
reject_bonf, pvals_bonf, _, _ = multipletests(p_values, method='bonferroni')
# BH (FDR) 校正
reject_bh, pvals_bh, _, _ = multipletests(p_values, method='fdr_bh')
print("Bonferroni 校正后显著:", reject_bonf)
print("BH 校正后显著:", reject_bh)
辛普森悖论
总体结论与子群结论相反:
对照组 实验组
iOS 用户: 10% → 12% ↑ 提升
Android 用户: 15% → 17% ↑ 提升
总体: 13% → 12% ↓ 下降 ← ??
原因:两组中 iOS/Android 用户比例不同(混杂变量)。
解决方案:
- 按关键维度分层分析
- 分层分流(Stratified Randomization)
- 回归调整控制协变量
新奇效应 & 学习效应
| 效应 | 表现 | 时间维度 |
|---|---|---|
| 新奇效应 | 用户因好奇而点击更多 | 效应在前几天偏高 |
| 学习效应 | 用户需要适应新功能 | 效应在前几天偏低 |
检测方法:按天画效应值趋势图,观察是否稳定。
解决方案:
- 至少运行 2 周
- 只分析第 2 周的数据
- 排除实验前 N 天的数据
SUTVA 违反(网络效应)
SUTVA(Stable Unit Treatment Value Assumption):一个用户的实验结果不受其他用户分组影响。
| 违反场景 | 问题 |
|---|---|
| 社交产品 | 实验组用户分享给对照组用户 |
| 双边市场 | 改变骑手端影响乘客端 |
| 库存有限 | 实验组多买 → 对照组少买 |
解决方案:
- 集群随机:按城市/区域分组
- Switchback 实验:按时间交替切换
常见面试问题
Q1: 实验跑了 14 天,每天都看了一次结果,第 8 天显著了但最终不显著,怎么解读?
答案:这是典型的 Peeking 问题。14 次检查将实际 α 从 5% 膨胀到 ~50%。第 8 天的显著大概率是假阳性。应以最终预定样本量/时间的结果为准,或使用序贯检验方法。
Q2: 分层分析时发现不同人群效果不一样,怎么办?
答案:
- 预先注册的分析 vs 事后探索:事后拆分看到的差异可能是偶然的
- 检查交互效应:该差异是否统计显著?
- 多重比较校正:拆 5 个维度就做了 5 次比较
- 决策:如果差异显著且业务合理,考虑针对特定人群上线
Q3: 对照组和实验组的用户流量不是严格 50:50(比如 49.5:50.5),需要担心吗?
答案:
- 小幅偏差是正常随机波动
- 做 SRM 检验:用卡方检验判断是否显著偏离
- 经验阈值:偏差 > 1% 且 p < 0.001 → 需要调查
- SRM 只是质量检查,不代表结果就是错的,但需要排查原因
Q4: 如何避免「p-hacking」?
答案:
- 预注册:实验前明确假设、指标、样本量
- 控制指标数量:主指标 1 个 + 有限的次要指标
- 多重比较校正:所有指标一起做校正
- 固定停止规则:不随意延长或提前停止
- 代码审计:分析代码提前 Review
- 复现:关键结论通过 AA 或 holdback 验证