AB测试中的因果推断:从入门到实战避坑指南

1次阅读
没有评论

共计 1469 个字符,预计需要花费 4 分钟才能阅读完成。

image.webp

背景痛点:为什么传统 AB 测试会翻车?

传统 AB 测试通常直接比较实验组和对照组的均值差异,但这种方法暗藏两个致命漏洞:

AB 测试中的因果推断:从入门到实战避坑指南

  • 用户自选择偏差:比如测试新功能时,活跃用户更可能主动开启实验组,导致两组用户本身存在系统性差异
  • 外部因素干扰:促销活动期间进行测试,节日效应会同时影响实验组和对照组,造成效果误判

去年我们团队就踩过坑——某推荐算法 AB 测试显示点击率提升 12%,但上线后真实收益不足 3%。后来发现是测试期间恰逢春节,实验组中高频用户占比天然更高。

因果推断三剑客:DID/PSM/ 合成控制

方法 适用场景 最小样本量要求 支持并行实验数
双重差分法(DID) 有明确政策冲击时间点 每组≥500 中
倾向得分匹配 非随机分流但特征数据完整 实验组≥200 高
合成控制法 实验组极少(如单个城市) 对照组≥10 地区 低

表:三大方法对比,样本量指每组最低要求

Python 实战:PSM 完整实现

# 安装因果推断工具包
!pip install causalml

from causalml.match import NearestNeighborMatch
from sklearn.linear_model import LogisticRegression
import numpy as np

# 生成模拟数据(实际应替换为业务数据)np.random.seed(42)
X = np.random.normal(size=(1000, 5))  # 5 个特征变量
treatment = (X[:, 0] + X[:, 2] > 0).astype(int)  # 非随机分配

# Step1: 计算倾向得分
ps_model = LogisticRegression()
ps_model.fit(X, treatment)
propensity_scores = ps_model.predict_proba(X)[:, 1]

# Step2: 最近邻匹配(caliper=0.2 限制最大得分差异)matcher = NearestNeighborMatch(
    caliper=0.2,  # WHY: 防止匹配差异过大
    replace=False  # 不放回抽样
)
matched_ids = matcher.match(propensity_scores, treatment)

# Step3: 平衡性检查(标准化差异 <0.1 为佳)from causalml.metrics import smd
for i in range(X.shape[1]):
    print(f"特征 {i} 的 SMD:", 
          smd(X[matched_ids[:, 0], i], 
              X[matched_ids[:, 1], i]))

生产环境避坑指南

  1. 小样本救急方案:
  2. 用 Bootstrap 重复抽样 1000 次,取效果置信区间
  3. 示例:np.quantile([bootstrap_ate() for _ in range(1000)], [0.025, 0.975])

  4. 非随机分流验证:

  5. Wald 检验公式:$\frac{ATE}{\sqrt{Var(治疗组)+Var(对照组)}}$
  6. 当统计量 >1.96 时认为分流有效

  7. 多重检验校正:

  8. Bonferroni 修正:将显著水平 α 除以实验次数
  9. 如同时测 5 个指标,则 p 值需 <0.01 才算显著

血泪教训:三大死亡陷阱

  • 辛普森悖论:某功能整体提升 10%,但分层后发现 iOS 用户下降 5%,Android 用户上升 20%
  • 标准误误用:匹配后的样本是相关的,必须用 bootstrap 或稳健标准误
  • 效果衰减:使用 KM 曲线分析留存率,发现新用户前 3 天效果显著但 7 天后衰减

思考题

当实验组和对照组的特征分布完全重叠时,PSM 是否仍有必要?为什么?

(提示:考虑测量误差和模型误设的影响)

正文完
 0
评论(没有评论)