A/B测试设计与实施

专栏:人工智能训练师(三级)备考全攻略
模块:卷三·知识体系 — 第四部分·模型测试与评估
难度:⭐⭐⭐☆☆
考试权重:中高频(选择+简答+计算)


一、什么是A/B测试?

定义:
  将用户随机分为两组,分别使用不同版本(A版本/B版本),
  对比关键指标差异,用统计学方法判断差异是否显著。

核心原则:
  同时段 → 同用户池 → 随机分流 → 对比指标 → 统计判断

类比:
  医学上的"双盲实验"——A组吃新药,B组吃安慰剂,
  看新药是否真的有效。

1.1 A/B测试在AI系统中的应用场景

场景 A组(对照组) B组(实验组) 核心指标
模型升级 旧模型v1.0 新模型v2.0 CTR/转化率/准确率
Prompt优化 原始Prompt 优化后Prompt 回答质量评分
推荐算法 规则推荐 ML推荐 点击率/停留时长
UI改版 旧版界面 新版界面 用户留存/任务完成率
阈值调整 置信度阈值0.9 置信度阈值0.8 精确率/召回率权衡

二、A/B测试完整流程

A/B测试七步法

  ① 提出假设    ② 设计实验    ③ 确定样本量
     "新模型       分流策略       统计功效
      CTR更高"     A/B分流        最少需要多少
                                   用户?
       ↓
  ④ 运行实验    ⑤ 收集数据    ⑥ 统计分析
     随机分流       收集指标       t检验/卡方
     灰度发布       确保完整性     计算p值
       ↓
  ⑦ 决策与上线
     显著 → B全量
     不显著 → 维持A

三、实验设计核心要素

3.1 分流策略

流量分流示意

     全部用户(100%)
          │
    ┌─────┴─────┐
    │  50%       │  50%
    ▼            ▼
 ┌──────┐    ┌──────┐
 │ A组  │    │ B组  │
 │ 对照  │    │ 实验  │
 │ 旧模型│    │ 新模型│
 └──────┘    └──────┘

分流原则:
  ✓ 随机分配(保证两组用户特征一致)
  ✓ 用户级别分流(同一用户始终在同一组)
  ✓ 流量互斥(A/B组不重叠)
  ✗ 时间分流(上午A下午B → 受时间因素干扰)
  ✗ 地区分流(北京A上海B → 受地域因素干扰)

3.2 样本量计算

样本量计算公式(两个比例的比较):

          (Z_α/2 + Z_β)² × [p₁(1-p₁) + p₂(1-p₂)]
  n = ─────────────────────────────────────────────
                       (p₁ - p₂)²

其中:
  n      = 每组所需的样本量
  p₁     = A组(对照组)的当前转化率
  p₂     = B组(实验组)的期望转化率
  Z_α/2  = 显著性水平的Z值(α=0.05时,Z=1.96)
  Z_β    = 统计功效的Z值(1-β=0.80时,Z=0.84)
  p₁-p₂ = 最小可检测效应(MDE)
import math
from scipy import stats

def calculate_sample_size(
    p_control,     # 对照组转化率
    p_expected,    # 实验组期望转化率
    alpha=0.05,    # 显著性水平
    power=0.80     # 统计功效
):
    """
    计算A/B测试所需的最小样本量(每组)
    """
    z_alpha = stats.norm.ppf(1 - alpha / 2)  # 双尾检验
    z_beta = stats.norm.ppf(power)
    
    numerator = (z_alpha + z_beta) ** 2 * (
        p_control * (1 - p_control) + 
        p_expected * (1 - p_expected)
    )
    denominator = (p_expected - p_control) ** 2
    
    n_per_group = math.ceil(numerator / denominator)
    total = n_per_group * 2
    
    print(f"每组样本量: {n_per_group:,}")
    print(f"总样本量:   {total:,}")
    print(f"最小可检测效应(MDE): {p_expected - p_control:.2%}")
    print(f"相对提升: {(p_expected - p_control) / p_control:.1%}")
    
    return n_per_group

# 例:当前CTR=5%,期望提升到5.5%
n = calculate_sample_size(
    p_control=0.05,
    p_expected=0.055,
    alpha=0.05,
    power=0.80
)
# 输出:每组约 27,451,总计约 54,902

3.3 实验运行时长

运行时长 = 总样本量 / 日活用户数 × 安全系数

例:
  总样本量 = 55,000
  日活用户 = 10,000
  安全系数 = 1.5(考虑周末/节假日波动)
  
  运行时长 = 55,000 / 10,000 × 1.5 = 8.25天 → 至少9天

⚠️ 注意事项:
  • 不能随意缩短实验时间
  • 至少包含完整的业务周期(如一周)
  • 避开节假日/促销活动等特殊时期
  • 新用户和老用户可能需要分开分析

四、统计分析方法

4.1 假设检验流程

假设检验五步法:

  Step 1: 设定假设
    H₀(零假设):A组和B组无显著差异(p₁ = p₂)
    H₁(备择假设):B组显著优于A组(p₂ > p₁)

  Step 2: 选择检验方法
    连续型指标(均值)→ t检验
    比例型指标(转化率)→ Z检验 / 卡方检验
    排序/评分 → Mann-Whitney U检验

  Step 3: 计算检验统计量和p值

  Step 4: 判断显著性
    p < 0.05 → 拒绝H₀,差异显著
    p ≥ 0.05 → 不能拒绝H₀,差异不显著

  Step 5: 计算置信区间(效应大小)

4.2 代码实现:完整A/B测试分析

import numpy as np
from scipy import stats
import pandas as pd

# =========================================
# 模拟实验数据
# =========================================
np.random.seed(42)

# A组(对照组):旧模型,CTR 5.0%
n_A = 30000
clicks_A = np.random.binomial(1, 0.050, n_A)
ctr_A = clicks_A.mean()

# B组(实验组):新模型,CTR 5.4%
n_B = 30000
clicks_B = np.random.binomial(1, 0.054, n_B)
ctr_B = clicks_B.mean()

print(f"A组: CTR = {ctr_A:.4f} ({clicks_A.sum()}/{n_A})")
print(f"B组: CTR = {ctr_B:.4f} ({clicks_B.sum()}/{n_B})")
print(f"绝对提升: {(ctr_B - ctr_A):.4f}")
print(f"相对提升: {(ctr_B - ctr_A) / ctr_A:.2%}")

# =========================================
# 方法1:Z检验(比例比较)
# =========================================
def z_test_proportions(success_a, total_a, success_b, total_b):
    """
    两个独立比例的Z检验
    适用于:转化率、点击率等比例型指标
    """
    p_a = success_a / total_a
    p_b = success_b / total_b
    p_pooled = (success_a + success_b) / (total_a + total_b)
    
    se = math.sqrt(p_pooled * (1 - p_pooled) * 
                   (1/total_a + 1/total_b))
    
    z_stat = (p_b - p_a) / se
    p_value = 1 - stats.norm.cdf(z_stat)  # 单尾检验
    
    # 95%置信区间
    ci_lower = (p_b - p_a) - 1.96 * se
    ci_upper = (p_b - p_a) + 1.96 * se
    
    return {
        "z_statistic": z_stat,
        "p_value": p_value,
        "significant": p_value < 0.05,
        "ci_95": (ci_lower, ci_upper),
        "lift": (p_b - p_a) / p_a
    }

import math
result = z_test_proportions(
    clicks_A.sum(), n_A, clicks_B.sum(), n_B
)

print(f"\n===== Z检验结果 =====")
print(f"Z统计量: {result['z_statistic']:.4f}")
print(f"P值: {result['p_value']:.6f}")
print(f"是否显著: {'✅ 是' if result['significant'] else '❌ 否'}")
print(f"95%置信区间: [{result['ci_95'][0]:.4f}, {result['ci_95'][1]:.4f}]")
print(f"相对提升: {result['lift']:.2%}")

# =========================================
# 方法2:卡方检验(分类指标)
# =========================================
def chi_square_test(clicks_a, total_a, clicks_b, total_b):
    """
    卡方检验
    适用于:分类结果的差异检验
    """
    # 构建2×2列联表
    table = np.array([
        [clicks_a, total_a - clicks_a],   # A组:点击/未点击
        [clicks_b, total_b - clicks_b]    # B组:点击/未点击
    ])
    
    chi2, p_value, dof, expected = stats.chi2_contingency(table)
    
    return {
        "chi2_statistic": chi2,
        "p_value": p_value,
        "degrees_of_freedom": dof,
        "significant": p_value < 0.05
    }

chi_result = chi_square_test(clicks_A.sum(), n_A, clicks_B.sum(), n_B)
print(f"\n===== 卡方检验结果 =====")
print(f"卡方值: {chi_result['chi2_statistic']:.4f}")
print(f"P值: {chi_result['p_value']:.6f}")
print(f"是否显著: {'✅ 是' if chi_result['significant'] else '❌ 否'}")

五、A/B测试常见陷阱

5.1 六大经典错误

错误 说明 后果 正确做法
样本量不足 实验运行时间太短 假阳性/假阴性 提前计算最小样本量
多次检验 反复查看结果并停止 显著性膨胀(p-hacking) 固定实验时长,只看最终结果
辛普森悖论 整体B胜但分群A胜 做出错误决策 做子群体分析(Segment Analysis)
新奇效应 用户因"新鲜感"点击 短期数据虚高 延长实验观察稳定性
网络效应 用户间互相影响 组间不独立 按地区/社区分流而非个人
SRM(样本比例失调) 分流比例偏离预期 实验设计有Bug 监控实际分流比

5.2 辛普森悖论示例

整体看:B组转化率更高 ✅

  整体    A组      B组
  ─────────────────────
  转化率  4.5%     5.0%   ← B更高

但按新/老用户分群:

  新用户   A组      B组
  ─────────────────────
  转化率   8.0%    7.0%   ← A更高!
  人数    5,000   15,000

  老用户   A组      B组
  ─────────────────────
  转化率   3.0%    2.5%   ← A更高!
  人数    25,000   15,000

原因:B组中新用户比例更高(新用户天然转化率高),
     拉高了B组的整体转化率,掩盖了B组实际表现更差的事实。

解决:始终做子群体分析!

六、多臂老虎机(Bandit算法)简介

A/B测试 vs 多臂老虎机

  A/B测试(固定分流)           多臂老虎机(动态分配)
  ──────────────────          ──────────────────────
  流量固定50/50分配              流量动态调整
  等实验结束才决策                实时学习最优方案
  浪费流量在差方案上              快速收敛到最优方案

  适合:严谨对比、最终决策        适合:快速探索、实时优化

经典算法:
  ε-Greedy:以ε概率随机探索,1-ε概率选当前最优
  UCB(Upper Confidence Bound):选置信上界最大的臂
  Thompson Sampling:贝叶斯采样,平衡探索与利用

七、考试重点总结

7.1 核心公式与数值

考点 内容
显著性水平α 通常取 0.05
统计功效1-β 通常取 0.80
p值判断 p < 0.05 → 显著
样本量公式 记住各参数含义,不需手算
置信区间 95% CI = 均值 ± 1.96 × 标准误

7.2 高频选择题

Q: A/B测试的零假设(H₀)是什么?
A: A组和B组无显著差异 ✅

Q: p值 = 0.03,应如何决策?
A: 拒绝零假设,认为差异显著(p < 0.05)✅

Q: 以下哪个不是A/B测试的常见错误?
A: 使用随机分流(这是正确做法)✅

Q: 辛普森悖论的原因是?
A: 不同子群体的样本比例不一致 ✅

Q: 多臂老虎机相比A/B测试的优势是?
A: 动态分配流量,减少浪费 ✅

7.3 简答题模板

题目:简述A/B测试的完整流程及注意事项。

答题模板

A/B测试完整流程分为七步:
1. 提出假设:明确要验证的业务假设和核心指标
2. 设计实验:确定分流比例(通常50/50)、分流策略(随机分流)
3. 计算样本量:基于当前指标值、期望提升、显著性水平(α=0.05)
   和统计功效(1-β=0.80)计算最小所需样本量
4. 运行实验:随机分流,确保用户级别一致性
5. 收集数据:确保数据完整性,监控SRM
6. 统计分析:使用Z检验/卡方检验,计算p值和置信区间
7. 决策上线:p<0.05且效果正向则全量,否则维持现状

注意事项:
• 实验时长至少一个完整业务周期
• 避免多次检验(p-hacking)
• 需做子群体分析避免辛普森悖论
• 警惕新奇效应

八、思维导图

A/B测试设计与实施

核心概念

随机分流对比

统计显著性判断

对照组vs实验组

七步流程

提出假设

设计实验

计算样本量

运行实验

收集数据

统计分析

决策上线

统计方法

Z检验比例比较

卡方检验分类

t检验均值比较

p值判断显著性

样本量计算

显著性α=0.05

统计功效1-β=0.80

最小可检测效应MDE

常见陷阱

样本量不足

多次检验p-hacking

辛普森悖论

新奇效应

网络效应

SRM分流失调

进阶方法

多臂老虎机

ε-Greedy

UCB

Thompson Sampling


📌 备考贴士:A/B测试是三级考试的高频考点,重点掌握"七步流程"和"p值判断"两个核心。辛普森悖论几乎每年必考一道选择题,记住"分群后结论反转"的描述即可识别。样本量公式不需手算,但要理解每个参数的含义。简答题背熟"七步流程+四条注意事项"模板。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐