上周和一个做增长的朋友吃饭,他给我看了一份挺典型的复盘:改版页面跑了 14 天,转化率从 4.8% 涨到 5.1%,p 值 0.03,团队欢天喜地全量上线。三个月后大盘转化率没动,甚至还掉了 0.1 个百分点。他问我,明明当时是显著的,为什么上线之后什么都没发生。
这个问题我大概被问过几十次。答案不复杂——他们做的那份 A/B test,从头到尾其实只做对了一半:把流量分成了两组,却没有建立起一套能让结论站得住脚的推断逻辑。这篇东西我打算把 A/B test 背后的原理从头到尾捋一遍,不写平台操作手册,只讲这台机器是怎么转的:随机化为什么能换来因果结论、p 值究竟在回答什么问题、样本量公式里的每一个符号对应现实中的什么、以及哪些环节会让一个"显著"的结果悄悄变成噪声。适合已经会用工具但说不清原理的产品、运营、数据分析同学,也适合刚开始接手实验平台的后端工程师。
1. 从一次"翻车"的改版说起:A/B test 到底在比较什么
1.1 没有随机化,你比的是人群不是策略
很多人对 A/B test 的理解停在"分两组、看数据、选好的"这一层。但如果只做到这一步,你得到的其实是一份观察性研究,不是实验。区别在哪?举个特别土的例子:假设你想验证"喝咖啡能不能提高工作效率",你找了一群自愿喝咖啡的人,发现他们效率确实更高。这个结论成立吗?不一定。因为愿意喝咖啡的人可能本来就更年轻、更抗压、更愿意加班——你比较的不是咖啡的效果,而是"喝咖啡的人"和"不喝咖啡的人"这两拨人的差异。
线上实验同理。如果改版页面的用户是通过"用户自己选择进入新页面"这种方式分出来的,那你测出来的差异里,混着用户主动性的差异、渠道来源的差异、活跃度的差异。这些混杂变量(confounder)就像一个永远关不掉的背景噪音,让你无法把效果归因到策略本身。
随机化解决的正是这个问题。当分流完全由系统在用户访问的那一刻决定,并且和用户的一切属性无关时,两组在实验开始前的期望值是相同的:同样的性别比例、同样的地域分布、同样的历史消费金额、同样的设备型号,甚至连"今天心情好不好"这种你根本采集不到的变量,期望上也是平衡的。这时候两组之间唯一的系统性差异,就只剩下你施加的那个策略。
这里有个很关键的细节容易被忽略:随机化的单位要和你的分析单位一致。分流按用户分,分析就要按用户聚合;分流按会话分,分析就得按会话来算。我见过一个团队分流按 device_id 做,分析时却按 session 去重,结果同一个用户在不同会话里可能进了不同组,两组之间的污染让效应量被稀释了一大截,本来能测出来的提升变成了不显著。
1.2 潜在结果框架:因果推断的最小内核
要真正理解 A/B test,绕不开一个叫"潜在结果"(potential outcomes)的框架。它看起来很抽象,但一旦想通,后面所有统计概念都会变得特别好理解。
对每一个用户,我们想象存在两个平行世界的结果:如果他看到旧版本,转化率是 Y(0);如果他看到新版本,转化率是 Y(1)。我们真正想知道的,是这两者的差 Y(1) − Y(0),也就是个体处理效应。而 A/B test 之所以需要那么多流量,根本原因就在于:一个人不可能同时出现在两个世界里,我们永远只能观测到其中一个值,另一个永远缺失——这就是统计学里说的"因果推断的根本问题"。
既然个体效应测不到,我们退而求其次,去测平均处理效应(ATE):E[Y(1)] − E[Y(0)]。随机化之后,实验组的平均结果是对 E[Y(1)] 的无偏估计,对照组的平均结果是对 E[Y(0)] 的无偏估计,两者相减,就得到 ATE 的无偏估计。
理解了这一层,你就能明白为什么"两组基线必须可比"是所有实验设计的第一原则,也能明白为什么那些事后补救的办法(比如拿历史数据做对照组、用倾向性得分匹配)永远只能算近似——它们试图在随机化缺席的情况下人工造出可比性,但总有一些看不见的变量匹配不到。
2. 假设检验这台机器是怎么转起来的
2.1 零假设与两类错误:把"证据不足"和"证明无效"分开
A/B test 的结论输出本质上是一次假设检验。我们先把立场摆成最保守的样子:零假设 H0 是"新版本和旧版本没有差异",备择假设 H1 是"有差异"。然后我们看手里的数据,在这两个假设下分别有多大概率出现——如果数据在 H0 下出现的概率低到某个阈值以下,我们就说"不太可能是巧合,拒绝 H0"。
拒绝 H0 的时候可能犯错,这是第一类错误(假阳性):策略其实没用,我们却说有用,然后全量上线,白白浪费改动成本,甚至伤害用户体验。不拒绝 H0 的时候也可能犯错,这是第二类错误(假阴性):策略其实有用,我们却没测出来,把一个好的改动扔进了垃圾堆。
这两类错误的代价完全不对称。第一类错误的代价是把坏东西推给全量用户,第二类错误的代价是错过一个机会——但机会可以重来,糟糕的改动上线之后想撤回就麻烦了。所以业界默认把第一类错误率 α 卡在 0.05(也即 95% 置信水平),而把第二类错误率 β 放到 0.2(也即 80% 统计功效)。这个不对称的选择不是数学推导出来的,是业务代价权衡出来的。
注意:α 和 β 是你在实验开始之前就要定下来的,不能在看到结果之后再调。看到结果之后说"这次我们放宽到 0.1 吧",本质上就是在给自己发一张作弊许可证。
2.2 p 值不是"策略有效的概率"
这是我在面试和评审会上见过最多的误解。p 值的定义是:在零假设成立的前提下,观测到当前结果或更极端结果的概率。注意它的条件——它假设 H0 为真,然后算数据的概率。它完全没有回答"策略有效"这件事的概率有多大。
用更直白的话说:p = 0.03 的意思不是"这个策略有 97% 的概率有效",而是"如果新版本其实毫无作用,那么纯靠随机波动,也有 3% 的概率出现至少这么大的差异"。这是一个关于数据的陈述,不是一个关于假设的陈述。
还有一个更隐蔽的坑:p 值显著不等于效应量大。当样本量足够大时,哪怕真实差异只有 0.01 个百分点,也能算出极小的 p 值。反过来,样本量小的时候,一个 20% 的真实提升也可能给出 p = 0.4。所以看结果永远要两件事一起看:p 值回答"是不是真的有效果",效应量回答"效果值不值得做"。我在评审时最常说的一句话就是:先看置信区间,再看 p 值。
2.3 置信区间:比 p 值信息量更大的一种读法
置信区间给的是效应量的一个区间估计。95% 置信区间的含义是:如果把这个实验重复无数次,每次算一个区间,那么大约 95% 的区间会包含真实的效应值。注意,不是"真实效应有 95% 的概率落在这个区间里"——真实效应是个固定值,它要么在里面要么在外面,没有概率可言。这个区别有点学究气,但它会影响你对结论的解读方式。
实操上,置信区间至少告诉你三件事:效应量的方向(区间是否跨过 0)、效应量的量级(区间的中心在哪)、估计的精度(区间有多宽)。一个很典型的场景是:新版本提升 0.8%,95% CI 是 [0.1%, 1.5%]。这个结果说明策略大概率是正向的,但上限和下限差了 15 倍,说明精度还不够,这时候贸然全量上线是有风险的,更稳妥的做法是继续累积样本或者做一个更长期的观察。
另外一个经验:如果你的置信区间下限刚好压在 0 附近,等同于 p 值刚好压在 0.05 附近,这种"临界显著"的结论千万别当成坚实的证据。它可能就是一次运气,重复实验很容易翻盘。
3. 样本量这道算术题,十个项目九个算错
3.1 从 MDE 反推:先想清楚你要抓住多大的提升
样本量计算的第一步不是套公式,而是回答一个业务问题:这个改动至少要带来多大的提升,我才愿意为它投入资源?这个值叫最小可检测效应(MDE,Minimum Detectable Effect)。
MDE 定得越大,需要的样本量越小,实验越快出结果,但小提升你就抓不到;MDE 定得越小,实验越灵敏,但需要的样本量和时间会指数级上升。这里有个反直觉的地方:样本量和 MDE 的平方成反比。MDE 缩小一半,样本量要涨 4 倍。所以如果你想把能检测的最小提升从 1% 缩到 0.5%,流量的压力是四倍,不是两倍。
怎么定 MDE 才算合理?我一般用三个参考:一是这个指标的日常波动幅度,MDE 至少要比这大,否则实验永远做不出结论;二是历史实验的典型效应量,看看同类改动一般能带来多少提升;三是这次改动的投入产出比,如果一个小改动只值 0.1 个百分点的提升,那就别做实验了,直接上线看大盘反而更经济。
3.2 把公式拆成可以手算的四个因子
两比例检验(转化率类指标最常用)的每组样本量公式是这样的:
n = (z_{1-α/2} + z_{1-β})² × [p1(1-p1) + p2(1-p2)] / (p1 - p2)²我们把它拆成四个因子来看:
| 因子 | 含义 | 典型取值 | 对样本量的影响 |
|---|---|---|---|
| z_{1-α/2} | 显著性水平对应的分位数 | α=0.05 → 1.96 | 降低 α 需要更多样本 |
| z_{1-β} | 统计功效对应的分位数 | β=0.2 → 0.84 | 提高功效需要更多样本 |
| p1(1-p1)+p2(1-p2) | 指标的方差项 | 随基线转化率变化 | 转化率越接近 50%,方差越大 |
| (p1-p2)² | 效应量的平方 | 由 MDE 决定 | 效应越小,需求越大,且是平方关系 |
第一、第二个因子合起来,(1.96 + 0.84)² = 7.84,这个常数在 α=0.05、功效 80% 的条件下是固定的。你可以记成"7.84 法则",后面手算会快很多。
第三个因子有个容易被忽视的性质:当基线转化率接近 50% 时,方差项最大,需要的样本量最大;当转化率接近 0% 或 100% 时,方差项很小,样本量需求下降。所以同样是提升 5% 的相对幅度,从 50% 提升到 52.5% 需要的样本量,比从 2% 提升到 2.1% 要大得多——因为后者是绝对提升 0.1 个百分点,前者是 2.5 个百分点,绝对值差了 25 倍。
3.3 一个完整的手算案例与实验时长
假设你在优化一个注册页,基线注册转化率 5%,你希望抓住相对 10% 的提升,也就是从 5% 提到 5.5%,绝对提升 0.5 个百分点。α = 0.05,功效 80%。
代入公式:
p1 = 0.05, p2 = 0.055 方差项 = 0.05 × 0.95 + 0.055 × 0.945 = 0.0475 + 0.051975 = 0.099475 效应项 = (0.005)² = 0.000025 n = 7.84 × 0.099475 / 0.000025 ≈ 31,195每组大约需要 3.12 万人,两组加起来 6.24 万人。接下来算时长:如果你的页面每天有 1 万 UV,分流后每组 5000 人,那么 31195 / 5000 ≈ 6.24 天。这时候千万别直接取 7 天完事,要往后凑到整周——一周七天里,工作日和周末的用户行为差异往往比你想的大。取 14 天更稳,因为可以顺带覆盖两个完整周期,同时也能观察效应随时间的变化趋势。
还有个隐藏的坑:样本量和时长的换算里,必须考虑"实验曝光率"。分流进了实验组,不等于用户真的看到了改动。如果一个页面的首屏触发率只有 60%,那实际有效样本只有名义样本的 60%,你的实验时长要按 1/0.6 放大。我见过太多实验"跑了很久还是没结论",最后发现是曝光埋点埋错了位置,一半用户压根没进分析口径。
4. 让结论失效的几类隐性 bug
4.1 SRM:分流比例不对,后面全白算
SRM(Sample Ratio Mismatch)是实验平台里最应该被自动化监控的一项指标。它的意思是:实际分流比例和设计比例出现了统计上显著的偏离。你设计的是 50/50,跑完发现有 50.8% 在实验组、49.2% 在对照组,这个偏离如果超出了随机波动的范围,就说明分流链路出了问题。
为什么 SRM 这么要命?因为它意味着分流的随机性被破坏了。可能的原因五花八门:实验组页面加载变慢导致一部分用户没等到曝光就走了、某个渠道的流量在分流前就被过滤掉了、埋点上报失败在某一组更严重、甚至不同组的缓存策略不一致。不管是哪种,一旦随机性被破坏,两组就不再可比,你后面所有的统计推断都建立在一个错误的前提上。
检测方法很简单,用卡方检验:
χ² = Σ (观测值 - 期望值)² / 期望值两组各 5 万流量的实验,期望 25000/25000,观测到 25400/24600,代入得到 χ² = 160000/25000 + 160000/25000 = 12.8,自由度 1,对应 p 值约 0.00035,严重超出 0.001 的告警阈值。
提示:行业里常用的 SRM 告警阈值是 p < 0.001 而不是 0.05。原因是 SRM 检查是每次实验都要跑的例行检查,属于多重比较场景,用 0.05 会产生大量误报,把真正的问题淹没在噪音里。
4.2 多重比较与 p-hacking:看得越多,越容易"发现"效果
这是新手最常踩的坑,也是最难自查的坑。假设你做一个实验,同时监控 20 个指标,每个指标在 α = 0.05 下独立检验。那么"至少有一个指标显著"的概率是 1 − 0.95²⁰ ≈ 64%。也就是说,即使你的改动完全没有任何作用,你也有六成以上的概率找到一个"显著"的指标。
这就是多重比较问题。每多看一个维度、多切一刀人群,你就多买了一张中奖概率 5% 的彩票。切分维度是 p-hacking 的重灾区:整体不显著,那就按性别切、按地域切、按新老用户切,切到某个子群体显著为止,然后拿这个结论去汇报。这个操作在方法上有个名字,叫"子组分析",如果它不是实验前预先注册好的,那它的结论可信度非常低。
正确的做法有几种。最保守的是 Bonferroni 校正:把 α 除以检验次数,做 20 个检验就把阈值降到 0.0025。这个办法过于保守,容易把真实效应也过滤掉。更常用的是 Benjamini-Hochberg 方法,控制错误发现率(FDR)而不是整体错误率,允许一定的假阳性比例,在指标数量多的时候更平衡。
不管用哪种方法,有一条原则是铁律:事前注册分析计划。在实验开始之前,把主指标、次指标、护栏指标、要切分的维度全部写清楚,跑完之后严格按计划分析。任何计划外的探索性分析,都要明确标注为"探索性",并且用一个独立的实验去验证它。
4.3 辛普森悖论、新奇效应与幸存者偏差
辛普森悖论可能是最反直觉的一个坑。举个具体的例子:假设实验组在移动端的转化率是 6%,对照组是 5%;实验组在 PC 端的转化率是 3%,对照组是 2.5%。两个端上实验组都赢,但整体一算,实验组的整体转化率反而比对照组低。原因很简单:移动端整体转化率高,但实验组的移动端流量占比只有 30%,而对照组的移动端占比有 70%,加权之后整体就被拉低了。
这个悖论在实验里的实际表现就是:分流虽然随机,但"用户在实验期间使用什么设备"这件事可能在两组间不自平衡,尤其是实验周期短、样本量小的时候。所以在看整体结论之前,先检查关键分层维度上的流量分布是否平衡,是一个非常值得养成的习惯。
新奇效应是另一个慢性毒药。用户看到一个全新的界面,出于好奇会多点几下,前几天的数据特别好看,之后逐渐回落到正常水平。反过来还有"首因效应",老用户对新界面不习惯,短期内数据下滑,适应期过后才回升。这两种效应都会让短周期实验的结论失真。
对抗它的办法有两个:一是看趋势不看均值,把实验期间按天拆分,画出两组的日度指标曲线,看差异是不是随时间收敛;二是拉长观察窗口,对涉及到用户习惯改变的改动,实验周期至少要覆盖两周以上,理想情况下还要做一个"上线后回看"的对比,看全量后的效果是否和实验期一致。
幸存者偏差则常见于需要用户多次回访的场景。比如一个改动可能让一部分用户第一次访问就流失了,但留下来的用户活跃度更高,导致后期的指标看起来变好了。这种时候必须把指标定义成"每个进入实验的用户"的口径,而不是"每个完成某项行为的用户"的口径,否则你就是在幸存者里挑数据。
5. 把方差压下去:CUPED、分层和序贯检验
5.1 CUPED 的直觉:用实验前的自己当自己的对照组
前面算样本量的时候,那个方差项 p(1-p) 是整条公式里最让人不甘心的地方。它代表的是用户之间的天然差异——有的人天生爱下单,有的人天生不下单,这些差异和你的改版毫无关系,却贡献了大部分的统计噪音。
CUPED(Controlled-experiment Using Pre-Experiment Data)的思路很朴素:既然每个人在实验前都有一段历史行为数据,那我能不能用这段历史数据来解释掉一部分实验期指标的波动?如果能,剩下的波动就更小,样本量需求就更低。
具体做法是构造一个协变量 X(通常是实验前同一指标的取值,比如实验前 14 天的日均活跃天数或历史下单金额),然后调整后的指标是:
Y_cuped = Y - θ × (X - E[X]) θ = Cov(Y, X) / Var(X)E[X] 是 X 在整个实验人群里的均值。这个调整的巧妙之处在于:θ 和 E[X] 都是基于全量数据估计的,对实验组和对照组施加的是同一个偏移量,所以调整之后两组之间的期望差异不变,但方差被压低了。方差降低的比例正好等于 ρ²,也就是 Y 和 X 的相关系数的平方。
如果历史行为和实验期指标的相关系数是 0.5,那么方差降低 25%,等价于样本量需求降低 25%,或者实验时长缩短四分之一。相关系数到 0.7,方差降低 49%,接近砍半。这个收益在流量紧张的业务里是实打实的救命稻草。
选协变量有几个讲究:第一,必须是实验前的数据,任何实验期间的数据都不能用,否则就是引入了策略本身的效应;第二,必须和实验期指标有稳定的相关性,相关性太差的话 θ 会接近 0,白白增加复杂度;第三,不能对策略本身敏感,比如你的策略可能影响用户的回访频率,那就别用回访频率当协变量。
5.2 序贯检验与"偷看"问题
几乎所有做实验的人都会犯同一个错误:每天看一次数据。"今天涨了,再等等看""今天跌了,是不是该停了"。这个行为有一个专业名字,叫 peeking,它会让实际的假阳性率大幅膨胀。
原因在于,p 值是随着样本累积而随机游走的。实验跑到第 3 天 p 值可能是 0.3,第 5 天变成 0.04,第 7 天又回到 0.15。如果你只在 p < 0.05 的那一刻决定停止实验,那你实际上是在做无数次检验并只保留了成功的那一次——这就是前面说的多重比较,只不过检验的次数变成了天数。有研究做过模拟,持续 peeking 的情况下,名义 α = 0.05 的实验实际假阳性率能膨胀到 0.2 以上。
解决思路有两条。一条是固定地平线:实验开始前定好结束时间,中途绝对不看结果,到点一次性分析。简单粗暴但有效,适合大部分中小规模的实验。
另一条是序贯检验。它的做法是根据已经累积的样本比例,动态调整显著性阈值。经典的实现有几种边界设计:Pocock 边界在各阶段用同一个较宽的阈值,适合希望尽早停止的场景;O'Brien-Fleming 边界早期极严格、后期逐渐放松,适合绝大多数实验,因为它在早期几乎不给你停止的理由,从而保护了整体的 α。
最近几年还流行起来一种贝叶斯视角的替代方案,它直接计算"新版本比旧版本更好的概率",让业务方更容易理解。但要注意,贝叶斯方法的结论依赖于先验分布的选择,先验取得越保守,越难得出显著结论。如果团队里没人能说清楚先验是怎么来的,那这个方法反而容易造成误读。
5.3 分层、配对与 CUPED 的边界条件
CUPED 之外,还有两个常用技术。分层随机化是把用户按某个关键属性(比如历史活跃度分桶、地域、新老用户)先分成若干层,然后在每一层内部独立随机分组。这样做的结果是关键属性在两组间几乎完全平衡,方差也随之降低。它的实现成本比 CUPED 低,不需要历史数据的存储和计算,适合任何有明确分层维度的场景。
配对设计是把相似的用户两两配对,然后在每一对里随机指定一个进实验组、一个进对照组。理论上能最大程度消除个体差异,但实现复杂,而且要求你有足够可靠的相似度度量,实际业务里用得不多。
最后说说 CUPED 的边界。它有一个前提经常被忽视:实验分流的随机化单位必须和分析单位一致,并且协变量需要在分流之前就已经确定。如果一个用户可能在实验期间被重新分流,那他的历史数据对应的协变量就不能用了。另外,CUPED 对极端值的处理要特别小心,如果历史消费金额的分布有严重的重尾(比如电商场景里 1% 的用户贡献 40% 的 GMV),θ 会被少数极端值主导,调整后的结果反而可能不稳。这种情况下我一般会先对协变量做截尾或取对数,再算 θ。
6. 从指标到决策:实验设计与工程实现里的硬骨头
6.1 指标口径:主指标、护栏指标与触发时机
一个完整的实验至少要有三类指标。主指标是你想改进的那一个,通常只有一个,最多两三个,多了就变成多重比较灾难。次指标用来解释主指标为什么变化,帮助你判断这个变化是好是坏,比如主指标是下单转化率,次指标可以看加购率、页面停留时长、支付成功率。护栏指标是那些你绝对不能让它变差的指标,比如崩溃率、页面加载时间、退款率、投诉量。
护栏指标的作用不是告诉你策略有效,而是告诉你策略有没有副作用。我见过一个很典型的案例:某个弹窗优化让点击率提升了 30%,看着很好,但护栏指标里的"次日留存"掉了 2 个百分点——因为弹窗太打扰,用户关掉 App 就不想再打开了。如果只看主指标,这个改动会被当成大成功上线,实际上是长期伤害。
另一个容易被忽略的是触发时机。用户在什么时刻被算作"进入了实验",直接影响样本量和指标口径。常见的有三种口径:进入页面就算、看到改动区域就算(曝光触发)、完成某个动作才算(转化触发)。前者的样本量最大但噪音也最大,因为很多人其实没看到改动;后者的样本最干净但会引入选择偏差——因为"是否完成动作"本身可能被改动影响。行业里比较通用的折中是曝光触发,用它来定义分析人群,同时把只看过一半页面的用户排除掉。
6.2 分流实现:哈希、层与域
分流的技术实现说起来简单,做起来细节很多。最基础的做法是哈希:
def get_group(user_id, salt, buckets=10000): key = f"{user_id}:{salt}" h = int(hashlib.md5(key.encode()).hexdigest()[:8], 16) return h % buckets用 user_id 加上实验专属的 salt 做哈希,能保证同一个用户在同一实验里每次都进同一组,同时不同实验之间的分组相互独立(因为 salt 不同)。取模的桶数用 10000 而不是 100,是为了支持精细的流量切分,比如只放 5% 流量做小流量实验。
再往上走就是分层与域的概念。如果每个实验都独立切流量,那么同时跑 10 个实验,每个用 10% 流量,用户要分到 10 次,很快就会出现"实验流量不够"和"用户被太多实验覆盖"的问题。解决办法是把流量分成多个相互正交的层,每个层内部再切分给不同的实验。同一层内的实验互斥(一个用户只能进一个),不同层之间正交(一个用户可以同时进多个不同层的实验)。这样做的好处是流量利用率大幅提升,同时只要不同层之间的实验互不干扰,正交性就能保证结果不被污染。
判断两个实验能不能放在不同层,关键看它们是否作用于同一批用户、是否会影响同一批指标。如果两个实验都改了首页按钮,即使放在正交层里,也会互相干扰,必须放进同一个互斥域。
6.3 网络效应与不可拆分的场景
前面所有讨论都基于一个隐含假设:一个用户的结果不受其他用户分组的影响,这叫 SUTVA(个体处理稳定性假设)。这个假设在很多场景下是成立的——一个人看到新版注册页,不会影响另一个人的注册行为。
但在社交、双边市场、共享资源类场景里,这个假设会直接崩掉。举个例子:如果实验组用户因为新功能更活跃了,会去给对照组用户发消息、点赞、互动,那么对照组其实也间接接受了处理。这时候两组之间的效应差异会被稀释,你测出来的就是"直接效应 + 部分间接效应",量级上偏低。
处理这种网络效应有几种思路。集群随机化是把相互有交互的用户打包成一个集群(比如一个公司、一个班级、一个小区),随机化在集群层面进行,集群内部的交互不影响组间可比性。代价是集群内用户的相关性很高,有效样本量远小于名义样本量,需要额外的方差校正。
切换实验适用于流动性强的市场场景,比如骑手派单、司机接单。做法是在时间维度上交替开关策略,比如上午用新算法、下午用旧算法,或者按小时、按天轮换。这种设计的前提是时间维度的可比性,需要确保不同时段之间的用户群体没有系统性差异。
还有一种更轻量的做法是基于地理位置的随机化。同城用户之间的互动概率远低于同小区,所以按城市切分往往能在控制干扰和控制样本量之间取得不错的平衡。
7. 我自己踩过的几个坑
说完原理,最后聊几个我实际踩过、并且觉得值得反复提醒的坑。
第一个是指标突变。有一次实验跑到第 6 天,主指标突然跳了 15%,团队差点当场宣布成功。后来查出来是那天有一个大促活动,整体流量结构变了,两组受到的冲击不一样。所以实验期间如果有任何运营动作、版本发布、外部事件,一定要记录下来,最好在平台上打上标记,事后分析时能把这段数据单独处理。
第二个是小流量实验的错觉。5% 流量跑三天,样本量根本不够,但 p 值有时候就是会低于 0.05,这时候千万别兴奋。我现在的做法是,对流量不足的实验直接不输出显著性判定,只显示"样本不足,结论不可靠",从工具层面堵住这个口子。
第三个是上线后不验证。实验结论和全量上线的效果不一致,通常有三种原因:实验人群和全量人群不一致(比如实验只在上海做了,全量是全国)、实验环境和生产环境的技术差异(比如实验期的服务容量更充裕)、以及前面说的新奇效应。养成"上线后回看一周大盘"的习惯,能帮你越来越准地判断哪些实验结论可以信任。
第四个是把 A/B test 当成唯一工具。不是所有问题都适合做实验。品牌类的改动、长周期的战略决策、样本量永远达不到的场景,硬做实验只会得到一堆无法解读的结果。有些时候,用户访谈、定性研究、甚至小规模的灰度观察,比一个统计上不显著的实验结论更有信息量。工具是拿来解决问题的,不是拿来表演科学性的。
最后一个想法:A/B test 的价值不在于给你一个"显著的"结论,而在于它强迫你把决策的依据从直觉变成可验证的假设。哪怕一次实验的结论是"没测出差异",只要它帮你排除了一个方向,这次实验就是有价值的——前提是你在开始之前,真的想清楚了要测什么、样本够不够、以及什么样的结果会让你改变决策。