p 值 0.03,这个功能就该上线吗
两组完全相同的页面做 A/B 实验,按常规的 0.05 判断,每 20 次里有 1 次会得出「有显著差异」。如果实验期间每天打开看板看一眼、p 值一低于 0.05 就宣布结果,这个比例是每 5 次里 1 次。真实存在 1 个百分点提升的功能,每组 2000 人的实验 83% 的时候会告诉你「不显著」。
A/B 实验的统计结论,回答的问题比看上去窄得多。p 值是「假设两组没有差别,观察到这么大(或更大)差值的概率」;它不是「这个功能有效的概率」,也不是「结论出错的概率」。在这个定义之上,实验怎么做、数据怎么看、看几次,都会改变结论的可靠程度,而看板上那个数字不会告诉你这些。
本文用模拟来说明。每个场景把「做一次实验」重复几千次,数其中有多少次得出显著的结论。指标是转化率,检验是两比例 z 检验(双侧,显著性水平 0.05),随机种子固定,结果可以逐字复现。实验见文末。
一、5% 的含义:没有差别时,也有 5% 显著
先做 A/A:两组的真实转化率都是 10%,每组 10000 人,重复 4000 次。203 次得到 p < 0.05,占 5.1%。
这就是显著性水平的定义:两组没有差别时,仍然有 5% 的实验会被判为显著。它是事先接受的误报率。由此可以推出几件事:
- 一次显著的结果,可能就是那 5%。团队一年做 100 个没有效果的改动,会有 5 个左右显示显著;
- p = 0.03 不表示「有 97% 的把握功能有效」。功能有效的可能性还取决于这类改动通常有多大比例真的有效,p 值里没有这个信息;
- p = 0.03 和 p = 0.06 之间没有本质的界线,0.05 只是约定。
二、中途偷看:误报率从 5% 到 20%
同样的 A/A,但不等样本攒够,每进来一批用户就算一次 p 值,第一次低于 0.05 就停下来宣布结果:
| 看的次数 | 被判为显著的实验 |
|---|---|
| 只在结束时看 1 次 | 5.1% |
| 5 次 | 14.5% |
| 10 次 | 20.2% |
| 20 次 | 24.3% |
p 值会随着数据进来上下波动。只承诺在一个固定的时刻看,它低于 0.05 的概率是 5%;允许在很多个时刻里挑一个最低的,概率就大得多。「显著了就停、不显著就再跑几天」是同一件事的另一种说法。
做法:
- 实验开始前定好样本量(或运行时长),到了再做判断;
- 中途可以看数据是否异常(分流比例、报错、核心指标暴跌),但不据此宣布「胜出」;
- 确实需要提前停止的,用为此设计的方法(序贯检验),它们在每次查看时使用更严格的门槛,把总的误报率控制在 5%。实验平台如果提供「随时可看」的结论,要确认它用的是这类方法。
三、样本量:实验之前就能算
真实存在提升时,实验能检出它的概率叫功效(power)。它由三个量决定:基线转化率、想检出的最小提升、样本量。基线 10%、想检出提升到 11%、功效 80% 时:
每组样本量 ≈ (1.96 + 0.84)² × [p1(1-p1) + p2(1-p2)] / (p2 - p1)²
= 7.85 × (0.09 + 0.0979) / 0.0001
≈ 147491.96 对应显著性水平 0.05(双侧),0.84 对应功效 80%。差值在分母上是平方:想检出的提升减半,样本量变成 4 倍。
| 每组样本量 | 检出提升的实验 | 显著结果里观察到的平均提升(真实值 1.00 个百分点) |
|---|---|---|
| 2000 | 17.4% | 2.44 |
| 10000 | 64.1% | 1.26 |
| 14749 | 80.6% | 1.12 |
样本不够有两个后果。第一个是漏报:每组 2000 人时,真实有效的功能 5 次里有 4 次被判为「不显著」。第二个更隐蔽:显著的那些结果,效果被夸大了。样本小时误差大,只有碰巧抽到偏大差值的实验才能跨过显著性门槛,所以被筛选出来的都是高估的。按 2.44 个百分点去做收益预估,上线后看到的是 1 个百分点。
流量不够检出想要的提升时,可选的做法是延长实验、放大实验的改动幅度,或者接受这个实验只能发现较大的效果。跑一个功效 17% 的实验再解读结果,无论显著与否都得不到可靠的结论。
四、「不显著」不等于「没有差别」
接着上面每组 2000 人的场景:82.8% 的实验不显著。这些实验的 95% 置信区间平均宽 3.8 个百分点,也就是典型的结果类似「提升 1.0,区间 -0.9 到 +2.9」。这个区间同时包含「没有效果」和「提升接近 3 个百分点」,实验没有能力区分它们。
把这种结果汇报成「实验证明没有差别」是错的,准确的说法是「没有检出差别,区间是多少」。区间的含义也可以用模拟验证:4000 次实验里,95% 置信区间包含真实差值的占 95.0%。
汇报实验结果时给出差值和置信区间,而不只是「显著 / 不显著」。区间窄且不含 0,结论可靠;区间很宽,说明样本不够,结论是「不知道」。
反过来,样本量极大时,很小的差别也会显著。统计显著只说明「差值不太可能是 0」,值不值得上线要看差值本身有多大、成本多高。事先定一个「小于它就不值得做」的最小提升,比事后争论更有用。
五、分析单位要和分流单位一致
按用户分流,每个用户可能访问多次。把每次访问当作一个独立样本来检验,样本量看起来大了好几倍。模拟一个 A/A:每组 2000 个用户,每人访问次数不等,每个用户有自己的转化倾向。
| 检验方式 | 被判为显著的实验 |
|---|---|
| 把每次访问当作独立样本 | 17.9% |
| 按用户(是否转化过) | 4.6% |
同一个用户的多次访问不是独立的:爱买的用户每次都更可能买。按访问计算时,检验以为自己有几万个独立样本,把误差估小了,于是把用户之间的自然差异当成了组间差异。
分流按什么单位,分析就按什么单位。按用户分流,就先把数据聚合到每个用户一行,再比较两组。这和 SQL 里「一行代表什么」是同一个问题,见 JOIN 之后,合计从 480 变成了 880。具体数值取决于用户行为的分布,这里的 17.9% 只说明方向。
六、指标多了,总有一个显著
一次实验通常会看很多指标。A/A 实验同时看 20 个互不相关的指标:
| 做法 | 至少一个指标显著的实验 |
|---|---|
| 每个指标都按 0.05 判断 | 63.9%(理论值 1 - 0.95²⁰ = 64.2%) |
| 显著性水平除以指标数(0.0025) | 4.8% |
没有任何效果的改动,三分之二的时候能找到一个「显著变好」的指标。按人群、渠道、设备拆开看也是一样的效果,每多一个切片就多一次机会。
实验开始前指定一个主要指标,用它做决策。其余指标用来理解发生了什么、检查有没有副作用;要用它们下结论,就做校正(除以指标数是最简单的一种,偏保守),或者把它当作下一个实验的假设。
七、不要先按天汇总再检验
一种常见的做法是把每组每天的转化率算出来,得到两列各 10 个数,再做 t 检验。模拟真实提升 1 个百分点、每组每天 1000 人、跑 10 天,每天的基础转化率不同(周中周末有 ±3 个百分点的波动,两组一起波动):
| 检验方式 | 检出提升的实验 |
|---|---|
| 按用户做两比例检验(每组 10000 人) | 63.5% |
| 每组压成 10 个日转化率再做 t 检验 | 0.1% |
压成日转化率之后,每组只剩 10 个数,日与日之间的波动(±3 个百分点)远大于要检出的差别(1 个百分点),提升被淹没了。同一天里两组的波动是同步的,这个信息在分别求两列的方差时被丢掉了。
用原始粒度(分流单位)的数据做检验。按天看趋势图没有问题,但不要用它做显著性判断。
八、检查清单
实验开始前:
- 写下假设、一个主要指标、想检出的最小提升。
- 算出样本量和需要运行的时长;流量不够就调整预期。
- 确定分流单位,分析时用同一个单位。
- 至少跑满一个完整的周期(通常是一周),覆盖周中和周末。
实验运行中:
- 检查分流比例是否与设定一致。50:50 的实验实际分成 52:48,说明分流或埋点有问题,结果不能用。
- 看异常,不看胜负。
实验结束后:
- 报告差值和置信区间。
- 「不显著」时看区间宽度,区分「没有效果」和「样本不够」。
- 次要指标和分人群的结果,当作线索而不是结论。
- 小样本上得到的大提升,按「很可能被夸大」来预估收益。
模拟的是理想条件:用户相互独立、转化率稳定。真实实验还有新奇效应、用户之间相互影响、分流故障,这些问题统计检验本身发现不了。
配套实验
- codesphere-labs/engineering/ab-test-decisions:Python 3.14.8 标准库、固定随机种子的七组模拟:A/A 假阳性、中途偷看、样本量与功效、显著结果的夸大、置信区间、分析单位、多指标、按天汇总(验证记录)
参考资料
- The ASA Statement on p-Values: Context, Process, and Purpose(美国统计协会关于 p 值含义与误用的声明,2016)
- Ron Kohavi、Diane Tang、Ya Xu,《Trustworthy Online Controlled Experiments》,Cambridge University Press,2020(样本量、分流比例检查、偷看与多重比较)
- Python 3.14 文档:statistics.NormalDist
- Python 3.14 文档:random.binomialvariate