☰
推荐系统AB测试实战:用假设检验把点击率提升30%
2026/10/3 10:45:45 网站建设 项目流程

做推荐系统的人,几乎都经历过这种尴尬:离线指标涨了一大片,AUC、GAUC全线飘红,模型工程师信心满满,结果灰度一上,线上点击率纹丝不动,甚至偶尔掉头向下。反过来,还有一种情况更气人——模型网络结构一点没动,只是改了召回策略或者展示规则,点击率却实实在在涨了十几个点。这两种体验之间的差距,就是有没有一套可靠AB测试流程的差距。今天想聊的,就是推荐系统里如何用AB测试把点击率真正做上去,而不是把报表做得好看。这个话题适合正在做推荐、搜索、广告排序的算法同学,也适合刚搭起实验平台但还没踩过坑的团队,产品经理和数据分析师也可以看,因为指标口径和分流逻辑,最终影响的是整个团队对“什么有效、什么无效”的判断。

1. 先说结论:推荐系统的点击率,是被一个假设检验流程托起来的

1.1 离线评测为什么解决不了线上问题

很多团队喜欢把离线指标当成算法强弱的唯一标准,AUC涨0.01能高兴一整天。问题是推荐系统是个闭环:模型改版会改变展示内容,展示内容会改变用户行为,用户行为回流成训练数据后又影响下一轮模型。在这种闭环里,离线评测天然是有偏的。历史数据里只包含旧策略产生的行为,新策略会让用户看到不同的内容,产生在历史上根本不存在的交互,离线数据再大也模拟不出这条反馈路径。

更麻烦的是,离线评测很难处理用户构成的漂移。今天的新用户明天变成老用户,老用户会疲劳,行为随时间衰减,这些都不是静态离线数据能刻画的。我见过太多项目,离线AUC涨得漂亮,上了线以后点击率原地踏步,最后排查发现是模型学会了记住训练集里高频出现但已经过时的热门物品,离线指标好看,线上并没有增量。

AB测试要解决的,恰好就是离线评测回答不了的问题:一个策略改动,放在真实用户、真实流量、真实反馈闭环里,到底能不能带来可测量的行为变化。它不替代算法研发,而是给算法研发装上一个可靠的测量仪表,让每一次改动都能在真实世界里得到验证。

1.2 AB测试的本质是一场随机对照实验

把AB测试说得再玄,本质上就是随机对照实验。把实验单元(通常是用户)随机分到对照组和实验组,对照组走旧策略,实验组走新策略,因为随机分组,两组用户构成在统计意义上是可比的,最后观测到的行为差异,就可以归因于策略差异,而不是用户差异、时间差异或者其他干扰因素。

这个归因逻辑听着简单,落地时会有大量细节问题。随机化怎么做才均匀?实验单元选用户还是曝光?要不要分层?样本量够不够?统计显著怎么判断?哪个指标做主指标?这些都是实验设计阶段就必须想清楚的事。设计做错了,后面的数据再漂亮也可能得出错误结论。AB测试不是简单地跑个A组B组,它是一整套假设检验流程:先提出原假设(新策略没效果)和备择假设(新策略有效果),再定显著性水平和统计功效,最后用数据决定是否拒绝原假设。

换句话说,AB测试真正托起来的不是某个具体算法,而是整个推荐系统迭代的“可信度”。没有这套流程,团队就只能凭感觉做决策,谁的口才好谁说了算,改版变成赌运气。

1.3 30%这个数字到底怎么来的

标题里写的“点击率提升30%”,看起来夸张,但在我做过和见过的项目里,30%并不是靠一次实验拿到的,而是靠多点叠加、多轮累积出来的。举一个典型的组合:召回从两路扩展到四路,增加向量召回和语义召回,聚类点击率涨12%;排序模型加入实时点击序列特征和位置偏置校正,点击率再涨8%;重排侧加了多样性和冷启动探索机制,头部流量的点击率再涨6%。这三部分相乘,算下来是(1+12%)×(1+8%)×(1+6%)减1,大概27%,再算上展示位上的一些细节优化,冲到30%是很正常的事。

关键是,这个过程中每一步都必须有AB测试背书,不然你根本不知道哪个改动真正贡献了提升。有时候你以为涨的是召回,实际上涨的是排序;有时候模型改了没效果,重排换个打散策略反而有效。只有把每一步都用实验量化清楚,30%才不是一个拍脑袋的数字,而是一串可追溯的实验记录。

2. 实验设计:样本量、分流与分层,做错一个就白干

2.1 样本量计算:一个可以直接套用的公式

样本量是AB测试的地基。样本太少,真实效果检测不出来;样本太多,浪费流量,实验周期拉长到不可接受。多数CTR类指标是二项分布,可以用比例检验的样本量公式估算。

假设当前基线点击率p1=3%,你想检测出10%的相对提升,也就是p2=3.3%,显著性水平取α=0.05,统计功效取1-β=0.8。双侧检验下z值分别取1.96和0.84,公式是:

n = (z_{1-α/2} + z_{1-β})² × [p1×(1-p1) + p2×(1-p2)] / (p2-p1)²

代入数字,p1×(1-p1)=0.03×0.97=0.0291,p2×(1-p2)=0.033×0.967=0.0319,两者相加0.0610,(1.96+0.84)²=7.84,剂量差0.003²=0.000009。算下来n约等于5.3万,也就是实验组约5.3万人,对照组约5.3万人,总共需要约10.6万用户。

这个数字意味着什么?如果产品每天日活10万,切一半流量进实验,一天就能达到样本量,但我会建议至少跑满7天。原因有两个:一是单日用户行为波动大,节假日、晚间高峰都会影响点击率;二是推荐策略存在新奇效应,用户对新策略刚开始会好奇,点击率虚高,跑几天才会回落到真实水平。另外,如果指标不是点击率而是人均点击次数这种连续变量,公式要换成基于均值方差的版本:n = (z1+z2)² × 2σ² / δ²,其中σ是历史数据的标准差,δ是最小可检测提升。

一个很多人会犯的错,是把曝光当作独立样本去套公式。同一个用户贡献了几十次曝光,这些曝光高度相关,不是独立样本,直接按曝光数算样本量会把方差严重低估,p值也就不可信。所以样本量计算和后续显著性分析都要锚定在实验单元上,通常是用户。

2.2 分流策略:用户级还是请求级

分流是AB测试最容易埋雷的环节。最简单的做法是拿用户ID取模,比如user_id % 100 < 50进实验组,但自增ID的低位往往有规律,取模会造成分桶不均匀,而且不同实验如果共用同一个分桶,效果会互相污染。更稳妥的做法是用哈希函数加salt,保证每个实验拥有独立的随机序列,并且同一实验里同一个用户永远落在同一组。

一个可以直接用的分流代码逻辑:

import hashlib def bucket(user_id, salt, total=1000): key = f"{user_id}_{salt}" digest = hashlib.md5(key.encode()).hexdigest() return int(digest[:8], 16) % total

把用户的唯一标识和实验salt拼起来做MD5,取前8位十六进制转成整数再取模,落在0到999之间。用MD5不是为了安全,只是为了得到一个分布均匀的伪随机数。salt要每个实验独立,避免用户在不同实验里拿到完全相同的分桶结果。total建议用1000或10000,方便按千分之一或万分之一的粒度切流量。

这里有个经验:排序实验老老实实按用户级分流。如果按请求级分流,同一个用户刷一次刷新看到旧策略,下一次刷新看到新策略,用户感受到的体验是混乱的,而且两次点击行为会互相影响,实验数据被污染。用户级分流保证一个用户在一整场实验里只看到一个策略版本,归因才干净。只有纯展示层实验,比如按钮颜色、文案样式,对长期用户行为没有跨次影响,才可以考虑请求级分流。

2.3 分层实验与互斥域

做过推荐系统的人都知道,一个推荐链路里同时会有好几个团队在改东西:召回组在换向量模型,排序组在改特征,重排组在调多样性,如果所有实验都切同一批流量,几天就撞车了。解决方法是流量分层。

把100%流量切成多个互不干扰的层,召回实验跑在召回层,排序实验跑在排序层,重排实验跑在重排层。同一层内的实验互斥,不同层之间的实验可以交叉运行。层与层之间靠不同的salt来做正交化,保证用户落在召回层的分组和落在排序层的分组是独立的,这样具体某一个用户可能同时参与召回实验和排序实验,但两组实验各自的统计推断不会互相干扰。

这套思路在业界已经验证过很多年,核心价值是让多个实验并行不悖。实操中要注意,同一层内的实验切分要保证流量池互斥,两个实验都期望覆盖100%流量时,就要分成两个层而不是挤在同一层。还要小心同一组件被两个实验同时修改,比如排序实验改了排序模型,另一个实验也在改同样的排序模型,那这两场实验的结果都不能信。

3. 指标体系与统计判断:别被p值骗了

3.1 主指标、护栏指标、代理指标怎么搭

点击率是推荐系统AB测试里最常用的主指标,但单独盯CTR有个大坑:新策略可能压缩曝光量,只把高点击率的内容展示出来,CTR在涨,用户真实体验却在变差。所以实验必须同时设置护栏指标,比如人均点击次数、人均曝光次数、人均停留时长、次日留存,如果CTR涨了但人均点击跌了,这种实验不该上线。

更精细一点,可以把指标拆成漏斗来看:曝光到点击的CTR是转化效率,人均点击总数是整体粘性,点击后进入详情页的到达率是内容匹配度,次日留存是长期价值。一次实验上线前就把这些指标定义清楚,谁当主指标、谁当护栏指标、哪个反向指标的阈值是多少,写进实验登记表。等实验跑完再挑指标看结果,大概率会挑出对自己有利的那一个,这是数据决策里最隐蔽的作弊。

商业场景还要加商业指标,比如下单率、客单价、GMV。我见过不止一次这种局面:CTR显著提升,下单率显著下降,说明新策略把用户注意力吸引到了“好看但不动手买”的内容上。算法团队和商业化团队对这种实验的看法会完全相反,所以实验开始前最好就是先达成共识:目标到底是点击率,还是点击后的商业转化。

3.2 指标稀释与辛普森悖论为什么危险

指标稀释是新手团队最常犯的统计口径错误。某次实验只切了10%流量,但数据分析师拿全量大盘的CTR来对比,实验效应被稀释到原来的十分之一,真实涨10%在大盘里只表现出1%的波动,噪声一冲就看不出来了。正确做法是严格限定统计范围:只统计实验桶内、策略实际生效的曝光和点击记录,不要扯进无关流量。

辛普森悖论是另一个经典陷阱。可能整体上看实验组CTR等于对照组,但拆开新老用户再看,实验组的新用户CTR高于对照组,老用户CTR也高于对照组,整体却出现相反结果。原因通常是实验改变了新老用户占比:新策略对老用户留存更好,实验组的老用户比例变高了,而老用户本身的点击率就低于新用户,结构变化把真实的提升掩盖了。

处理办法是分层分析,实验前先按新老用户、终端类型、访问源等维度把用户分层,计算各层内部的实验效应,再用加权方法或者回归模型做个综合估计。不要一上来就看总平均,先回答“每个层级内部是否一致”,再回答“整体结论是什么”。

3.3 置信区间、p值和多重比较的正确姿势

p值小于0.05,意思是在“新策略没有效果”的原假设前提下,看到当前数据这么极端结果的概率小于5%。这个定义很容易被误解,但更要命的是多重比较问题。如果你同时看了10个指标,每个指标都按p<0.05来判断,在没有真实效果的情况下,至少有一个指标假阳性的概率约为1-0.95^10,接近40%。所以你看到的那几个显著指标,可能纯粹是噪声。

两个实用建议。第一,主指标要预先注册,实验开始前定好一个主指标作为决策依据,其他指标属于探索性分析,参考但不能单独当上线依据。第二,如果确实想看多个指标,做多重比较校正,简单一点用Bonferroni,把显著性水平除以指标数,比如看5个指标就要求p<0.01;更精细一点用BH FDR校正,控制错误发现率。

还要提醒一点,实验进行中不要每天都去盯p值,看到某天p<0.05就急着下线切全量。中间反复查看会膨胀假阳性率,正确姿势是把观察次数和决策规则提前定好,要么固定跑多少天看结果,要么用序贯检验框架,把alpha分配到多次观察中。我自己的习惯是实验跑满7天以后才开始认真看置信区间,看效应量下界是否超过业务上认可的最低值,而不是只看p值。

4. 算法上怎么做:召回、排序、重排的点击率提升路径

4.1 召回侧:多路召回和向量召回怎么贡献点击率

很多推荐系统早期只有两路召回:协同过滤加热门兜底,导致推荐池很窄,大量长尾内容永远没有曝光机会。排序模型再强,也排不出召回里不存在的内容。召回侧改动对点击率的影响经常是被低估的。

实践中提升最明显的是两件事。第一,召回路数扩展,从两路扩到四路甚至六路,加入向量召回、语义召回、同城或同标签召回,让更多的候选物料进入排序池。第二,对冷启动用户和低活用户,不要一股脑推热门,而是用热门兜底加探索通道结合,给新内容一定的展示机会。之前有个项目,在新用户流量上加了探索通道后,新用户人均点击次数涨了6%左右,新用户次日留存也有小几个点的提升。

向量召回通常用双塔模型,用户侧塔和物品侧塔分别输出embedding,线上用ANN近邻检索快速取回相似物品。这类改动的AB验证,不建议只看整体点击率,还要看召回覆盖率、长尾曝光占比、以及不同活跃度用户的分层效果。因为整体点击率可能被头部内容掩盖,真实收益在长尾和冷启动用户身上。

4.2 排序侧:特征、目标函数与多目标平衡

排序模型是推荐系统里迭代最频繁的部分。点击率提升也常常出在排序侧,但要注意排序侧并不是换个模型结构就一定涨。我见过只有增加有效特征才涨的,也见过网络结构不变把损失函数换成带位置偏置校正版本后明显变好的。

位置偏置是个典型的坑:线上展示时,排在第二位的物品点击率高,很多时候不是因为它更相关,而是因为它位置靠前。模型如果直接学曝光日志里的点击标签,会把位置因素当内容相关学进去,导致重排位置变动后预估不准。解决思路是位置偏置校正,比如训练时把物品位置特征显式建模、用期望曝光分数加权的无偏学习方法,或者常见的shrinkage方法。这类改动离线表现往往温和,但上线后真实的排序能力提升会反映在CTR上。

多目标排序也很关键。单纯优化点击率,模型容易学会“标题党”内容。用MMoE或者PLE这类多专家网络,把点击、点赞、转发、停留时长一起建模,通过门控机制在不同任务间共享信息,同时观察主指标CTR和护栏指标时长、互动率,最后按业务权重折算成综合收益。AB测试在这种场景里尤其重要,因为多目标模型的“总收益”不是单一指标能定义的,必须靠实验同时验证多个指标的走向。

4.3 重排侧:多样性和探索如何避免点击率虚高

重排策略经常被忽视,但它对点击率的影响可以直接到展示层。信息流里最常见的操作是打散,用MMR或者DPP算法,让同一类目、同一作者的物品不要连续出现。表面上看打散可能损失局部相关性,但用户沉浸感和滚动深度上来了,总点击次数反而增加。

这里有个反直觉的经验:在某些场景下,越相关越好,打散过度反而降低点击率。所以打散强度不是越大越好,AB测试里可以设两档、三档强度梯度,看不同档位下CTR和人均点击的权衡曲线。我做过一个实验,打散强度从0调到中等时人均点击涨了4%,继续调高强度后人均点击又跌回去了,曲线的拐点非常清晰,这种信息只有AB测试能给。

冷启动探索也能带动点击率。新用户没有任何行为历史,推荐系统只能猜,如果只按热门推,新用户容易觉得无聊。用Bandit算法或简单的探索机制,给新内容分配一定曝光,虽然前期单次点击率可能略低,但用户发现感兴趣内容的概率变大,7天累计点击和留存数据反而更好。这种收益必须用长周期实验才能体现,只看第一天CTR很容易误判。

5. 完整跑一场AB测试:从数据埋点到放量决策

5.1 实验准备阶段:把坑提前踩完

一场规范AB测试,准备工作最少占一半时间。首先把实验假设写清楚:原假设是新策略相对旧策略没有点击率提升,备择假设是提升超过某个最小值,主指标、护栏指标、反向指标全都登记到文档里。

然后是埋点检查。推荐系统的曝光日志、点击日志、到达日志必须能按用户ID和时间关联。我每次实验上线前都会跑一遍数据质量检查:抽一小批曝光日志,确认点击日志里每一条点击都能找到对应曝光上下文,确认实验桶和对照桶的用户量比例符合预期,确认没有大面积的日志丢失。这一步偷懒,后面分析全是空中楼阁。

再然后是配置管理和白名单测试。实验配置要支持按用户ID白名单走实验策略,内部人员先真机体验一遍,看看推荐结果有没有明显异常,再开放给真实流量。灰度放量建议按1%、5%、10%、50%逐级往上加,每级观察一段时间,不用一次切满。放量过程本身就是一个小型实验,能看到不同流量规模下系统稳定性和用户反馈的变化。

5.2 实验进行中:监控什么才不会被数据骗

实验上线后的每一天,我都会看一份标准监控报告,包括实验组和对照组的曝光次数、点击次数、CTR、人均点击、停留时长,以及用户数是否平稳。核心原则是:先看数据质量,再看显著性。如果用户数不对、埋点有缺失,p值再小都别信。

一个很重要的检测叫SRM,样本量比例失配。比如设计上实验组应该占50%,但是统计曝光日志发现实验组只占48%,这个差距超过随机波动范围,说明分流链路可能被缓存、客户端逻辑或者日志过滤影响了。有一个非常典型的案例,某个分桶的用户被客户端缓存拦住了,导致实验组样本量少了5%,分析结果p值差点穿过0.05,最后通过SRM检测发现分流比例失配,才避免了一次错误上线。SRM可以用简单的卡方检验检测,应该作为每个实验监控看板的标配。

显著性方面,每天计算主指标的p值和置信区间是对的,但决策要看趋势而不是某一天。我习惯把连续7天每天的数据都拿出来看:如果前3天显著、后4天不显著,说明可能是新奇效应减退;如果前3天不显著、后4天连续显著,说明策略需要一段时间才能体现效果。只看最后一天的p值会丢掉时间维度的信息。

5.3 实验决策:上线、下线还是继续观察

实验跑完后按预设规则决策。主指标显著提升,护栏指标没变差,反向指标在容忍范围内,就继续放量。主指标显著变差,立刻下线,不需要犹豫。最麻烦的是主指标没有显著变化,但不是一点没涨。

这种时候看两个东西:一是置信区间的宽度,如果置信区间很宽,说明样本量还不够,最小可检测效应没有达到,应该延长实验时间或者加大流量;如果置信区间已经很窄,效应量本身就小于业务认可最低值,可以判断为“没有值得上线的提升”。很多团队在这里犯拖延症,跑了两周还在继续跑,其实统计功效早就够了,再跑也只是浪费时间。

全量以后还要做一件事:长期监控。新策略上线一周、一月的指标变化,和实验期间是否一致。如果实验期间CTR涨了8%,全量后两周掉回原值,需要排查是不是外部环境变化,还是策略的真实价值被高估。必要时可以做反转实验,比如短期把一部分流量恢复到旧策略观察对比,用来确认之前的提升确实来自新策略。

6. 我踩过的坑,以及最后一点个人建议

第一个坑:用户级分流做了,但客户端缓存没做实验标识隔离,导致同一个用户在一个请求看到对照组内容,下一个请求看到实验组内容。用户感受到策略跳变,整体行为都变了,实验没法看。后来是把实验标识和分桶结果一起进缓存key,才解决。

第二个坑:只看全量大盘CTR。有一段时间我复盘某个召回策略,内部评审时测试组拿全量大盘的CTR说没效果,后来把统计口径严格限定在实验桶曝光日志里再算,发现其实涨了4%,但p=0.09,距离显著就差一点。这个问题不是策略无效,而是分析口径错误,差点把一个真实收益砍掉。

第三个坑:新奇效应让我高兴了一周,然后被打脸。一个排序模型刚上线时CTR涨了10%,所有人都觉得成了,结果一周后收益归零。后来养成了习惯,所有实验至少跑7天,并且看第1天、第3天、第7天的指标曲线,再做决策。新策略上线初期用户有新鲜感,点击意愿会虚高,这个效应必须用时间来消化。

第四个坑:多重指标同时看的假阳性。有一次同时看8个指标,其中2个显著,差点因为其中一个指标的上线理由。后来做了多重比较校正,做完以后那两个指标里有一个不显著了,才知道是噪声。从那以后,每个实验都在开始前指定一个主指标、几个护栏指标,而不是到时候翻报表挑好看的。

如果让我给刚搭实验平台的团队几个建议,我会说三句话。第一,先做A/A测试,用同一策略的两组跑一遍,确认分流没有系统性偏差,再谈A/B。第二,实验假设、指标定义、最小可检测效应,实验开始前就全部写清楚,不要边跑边看数据边调整规则。第三,30%这类数字,不是靠一次实验跑出来的,而是靠多轮小步验证叠出来的。我过去最骄傲的不是某次实验CTR涨了30%,而是一个季度里几乎没有出现过“假阳性误导上线”的事故。把AB测试从一种统计工具变成团队的工作习惯,推荐系统的每一次改动,才算真正有据可依。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询