A/B测试样本污染怎么排查?分流、曝光与SRM检查清单

作者:456数据 发布:2026-10-08 18:27 浏览量:1 来源:原创

你有没有遇到过这样的实验:前端把登录页的两个按钮文案按一半流量切了出去,认认真真跑了两周,报表里对照组和实验组的转化率却拧成一团——同一批用户今天看到 A、明天看到 B,最后谁也说不清到底是文案起了作用,还是样本早就脏了?我自己刚做实验的第一年,就因为一次"流量对半分"的想当然,把一个本该上线的版本按错误结论毙掉了;事后复盘才发现,问题根本不在文案,而在分流这一步从一开始就没做干净。

不少同行对 A/B 测试的第一印象,就是"把流量分成两半"。可在前端工程师眼里,这句话最多只对了三分之一:分流只是实验的起点,真正决定实验能不能下结论的,是从用户进来到指标上报的整条链路上,有没有人、设备、缓存和并发实验在偷偷"串组"。一旦发生串组,对照组和实验组就不再是两组互斥的人,而是同一批人被重复计数;你在报表上看到的所有差异,都可能只是噪声。

这篇文章想把前端工程师视角下的样本污染讲透:它到底长什么样、怎么在实验设计阶段就拦住它、以及一个企业版实验平台,是靠哪几个模块把分流这件事做扎实的。我不引用任何演示数字,只讲工程实现上可以复用的判断方法。之所以从工程角度切入,是因为样本污染往往不是统计问题,而是代码和配置问题——而代码问题,恰恰是最容易在上线前就排掉的。


分流在报表上是均匀的,组间却不再互斥,差异沦为噪声

一、先回答最关心的问题:流量对半分,为什么还会脏?

对半分只是"表面均匀"

很多人做实验的第一步,是在前端写一个随机数:小于 0.5 走 A,否则走 B。这么写在演示环境里看着没问题,因为每个新会话恰好被切进某一组。但线上是持续的、回访的、多端的:一个用户今天用 Web 打开落在 A,明天用 App 打开又被随机切进 B,后天清了 Cookie 再进来,又换了一组。于是同一个人被同时算进了对照组和实验组,两组样本在统计学上不再独立,任何差异检验的前提都不成立。之所以会这样,是因为"按请求随机"把分流键选错了——它把每一次访问当成了一个独立的人,而不是把一个稳定的人固定在一组里。

样本污染的本质是组间不互斥

A/B 测试成立的前提,是对照组和实验组除了被测试的变量之外,其他条件尽量一致,并且两组人不重叠。样本污染之所以危险,是因为它不是让实验"慢一点出结论",而是直接让结论失效:当同一批人在两组之间来回横跳,两组指标的差异既可能来自文案,也可能来自这批人本身的行为波动,你无法把两者分开。因此,判断一个实验能不能信,第一刀不是看样本量够不够大,而是先问一句:这两组人到底是不是同一批人?

二、哪些情况才算样本污染?——四个判断条件

先看分流是否真的均匀

拿到一份实验报表,第一步不要急着看转化率差异,先做分流体检:对照组和实验组的进组人数比例,在整个实验周期里是否稳定地贴近你设定的切分比例,而不是某几天突然失衡。之所以要先看这个,是因为一旦哈希函数有偏、或者某些端没有正确加载 SDK,就会出现某一类用户被系统性地切进某一组的情况,这种偏倚会直接污染组间对比。均匀不是"总量对半",而是"每一天、每一个维度下都对半"。

再看新老用户和缓存有没有混组

其次要检查的是人群结构和缓存:新用户和老用户对同一个改动的反应本来就不同,如果他们被不成比例地分进两组,差异就不是改动带来的,而是人群结构带来的;此外,浏览器或 App 端的缓存如果把旧版本、旧分组结果下发给了已经换组的用户,就会出现"人在 A 组、看到的却是 B 组页面"的串组。这两类问题单看转化率是看不出来的,必须回到分流日志和缓存策略上核对。

污染类型典型表现工程上的根因识别信号
分流不均两组进组比例偏离设定值,某几天突然失衡哈希函数有偏、部分端未正确加载 SDK逐日进组人数比例不稳定
实验层不互斥同一用户同时命中两个并行实验多个实验共用一个分流桶却未做层隔离一个用户在多个实验里同时有记录
新老用户混入新用户集中在一组、老用户集中在另一组分流未按用户成熟度分层、口径错位两组的新老用户占比明显不一致
缓存串组人在 A 组却渲染了 B 组的页面CDN 或本地缓存下发了旧分组结果分组请求与实际渲染版本对不上

三、一次干净的实验,分析顺序应该怎么走?

第一步,先圈定实验层而不是直接圈人

我自己做实验设计时,第一步永远不是定"切多少流量",而是先定这个实验放在哪一层。之所以先做这一步,是因为同一时间往往有多个实验并行:文案实验和结算流程实验如果挤在同一个分流桶里,一个用户就会被两个实验各自随机一次,互相污染。正确的做法是把实验按改动对象分成不同的层,让同一个用户在同一层里只进一个实验,在不同层之间才允许正交叠加。这样每一层内部是互斥的,层与层之间互不干扰。

第二步,用稳定 ID 做分流哈希,而不是按请求随机

圈好层之后,第二步是把分流键固定下来。前端常用的做法,是拿一个跨会话稳定的用户标识(Web 端可以落到 Cookie,App 端用设备或登录后的用户标识),把它和实验 ID 拼在一起做哈希,再对哈希结果取模落桶。之所以要"用户标识 + 实验 ID"一起哈希,是因为这样能保证同一个用户在同一个实验里永远落同一组,而在不同实验里却能被打散,互不相关。千万不要再用请求级随机当分流键——那是按访问随机,回访一次就换组。

第三步,把指标口径和分流口径对齐

最后一步,是确认你统计指标用的口径,和分流落桶用的口径是同一个用户标识。之所以强调这一点,是因为数据链路里常出现"分流按 Cookie 算、转化按登录账号算"的错位:一个用户可能在未登录时被切进 A 组,登录后又被当成另一个身份记进 B 组的转化,最后两组指标对不上。把分流、曝光、转化三段链路用同一个稳定 ID 串起来,才能保证"被切进哪组"和"被统计了什么行为"说的是同一个人。


按改动对象划分实验层,同层互斥、跨层正交,避免并行实验互相串组

四、能力边界与对比:手工写脚本和实验平台差在哪?

三个工程能力到底管什么

讲完方法,要说清楚边界。样本污染这套工程,拆到产品里其实是三件事:实验管理负责把一个实验从创建、分流到看结果管起来;实验层管理负责维护层与层之间的互斥与正交关系;实验设备管理负责在设备这一层把同一台设备的分流结果固定住,避免跨端、跨版本串组。需要明确的是,A/B 测试在产品里属于智能运营线,面向的是企业版,不是免费版就能开的能力。下面这张表把"自己写脚本分流"和"用一套实验管理平台"在关键工程点上做个对照。

工程维度自己写脚本分流实验管理平台(含实验层管理、实验设备管理)
分流键常误用请求级随机,回访即换组按稳定用户标识哈希落桶,同人同组
并行实验多个实验共用一桶,容易互串提供实验层管理页面,同层互斥与跨层正交的具体配置以接入时为准
设备维度Web 与 App 各切各的,跨端对不齐提供实验设备管理页面,跨端固定分组是否生效需接入时验证
结论可信度污染要事后人肉排查分流与指标同口径,事前就拦住串组

关于这张表要补一句边界:表内只对比工程能力,不涉及任何具体的效果数字。之所以不列数字,是因为不同业务、不同流量下的实验结果差异极大,任何脱离场景的百分比都没有参考意义;像 456数据这类企业版实验平台真正的价值,是把上面这套"层互斥 + 稳定哈希 + 设备固定"的工程默认做进产品,让你不用每次都从零写一遍分流逻辑。

五、上线前的配置动作清单

分流侧要先做的三件事

在实验正式放量之前,我会按顺序过一遍三个分流配置。第一,确认分流键用的是稳定用户标识,而不是会话级或请求级的随机数,Web 端落到 Cookie、App 端落到设备或登录标识;第二,确认这个实验被正确挂进了对应的实验层,和已有实验是互斥还是正交,配得明明白白;第三,确认 Web 端的 JS 埋点脚本、H5 与 App 端 SDK 都正确加载并上报了曝光事件,避免出现"某端根本没进组却被算进了分母"的情况。这三件事之所以要排在最前面,是因为它们一旦配错,后面跑多久都白搭。

数据侧要再做的两件事

分流配好之后,还要在数据侧收尾两步。一是把实验的曝光事件和核心转化事件,用和分流完全一致的用户标识串起来,保证进组和转化说的是同一个人;二是放量初期先开一小部分流量做分流感测,逐日核对两组进组比例、新老用户占比是否对齐,确认没有系统性偏倚之后再逐步放量。之所以强调"先小流量观测",是因为样本污染往往在放量前的小流量阶段就会暴露,越早发现,返工成本越低。


净化分流工程前后,组间互斥、新老分层与缓存串组的对照

六、三个最常见的误区

把"对半分"当成 A/B 测试的全部

第一个误区,就是把"流量对半分"误以为 A/B 测试的全部。对半分只是切分动作,真正难的是让两组人在整个实验周期里保持互斥、保持可比。很多团队写了个 0/1 的随机就以为万事大吉,之所以最后结论不可信,恰恰是漏了层互斥、稳定哈希和缓存这几件更底层的工程。

第二个误区,是以为样本量够大就不怕污染。其实大数定律只会让随机波动变小,并不会修正系统性偏倚——如果新用户被成比例地切进了某一组,样本量翻十倍,这个偏倚只会被放大得更明显,而不是消失。之所以有人会这么想,是把"随机误差"和"系统偏差"搞混了:前者靠样本量压下去,后者必须靠分流设计纠回来。

第三个误区,是把缓存当小事。前端同学常觉得缓存只是性能问题,可一旦 CDN 或本地缓存把旧分组结果下发给了已经换组的用户,就会出现"人在 A 组、看到 B 组页面"的串组;这种污染最隐蔽,因为它不报错,只会让两组的行为指标悄悄错位。

七、常见问题

Q:实验跑了一半才发现污染,还能救吗?

这要分情况看。如果污染发生在分流键层面,比如本该按稳定 ID 哈希却用了请求随机,那已经进组的数据其实已经混了,这时候最稳妥的做法是停止当前实验、修正分流逻辑后重新开一轮,而不是硬着头皮把旧数据跑完。之所以不建议"凑合用",是因为被串组污染的数据没有办法事后干净地拆开——你没法从一份混了的日志里,还原出每个用户本该落在哪一组。

如果污染只是局部的、可定位的,比如某一个端的 SDK 在某几天没正确上报,那可以考虑把这段时间、这个端的数据从分析窗口里剔除,只保留分流干净的时段再下结论。但无论哪种情况,前提都是你得先有分流日志可查——这也是为什么我一直强调,实验平台要把曝光和分流记录留下来,而不是只留最终指标。

Q:App、H5、Web 这些端的分流键为什么不能混用?

因为不同端识别"同一个人"的口径本来就不一样。Web 端通常靠 Cookie 识别访客,App 端靠设备或登录后的用户标识,H5 嵌在 App 里时还要靠 App 把标识传进来。如果一个实验在 Web 端按 Cookie 分、在 App 端按设备分,而不做统一,就会出现同一个人在 Web 落 A、在 App 落 B 的跨端串组。之所以强调这一点,是因为跨端用户越来越多,混用分流键会让看似整齐的两组人,其实在不同端上是错位的。

正确的做法,是在产品内部建立一个跨端统一的稳定用户标识,让分流哈希在所有端都用同一个键;当 H5 从 App 打开时,由 App 把已经确定的标识带进来,H5 直接沿用,而不是重新随机一次。这样才能保证同一个人无论从哪个端进来,都稳定落在同一组。

Q:实验层互斥和正交实验,到底是什么关系?

这两个概念其实是配套的。互斥说的是:在同一个实验层里,一个用户只能进一个实验,不能同时被两个互相关联的实验命中;正交说的是:不同实验层之间,用户的分组是相互独立、互不影响的。之所以要把两者分开设计,是因为你既想让同一个页面上的多个文案实验不互相打架(所以同层互斥),又想让文案实验和结算实验能同时进行、互不干扰(所以跨层正交)。

落到工程上,就是分流哈希时把"实验层"也作为哈希的一部分:同一个用户在层 A 里落的组,和在层 B 里落的组,由不同的哈希输入决定,彼此不相关。这样运营可以并行跑多个实验而不必排队等待,每个实验的结论却依然干净。这正是实验层管理这个模块要解决的核心问题。

上线前检查:实验层互斥只保证不同实验之间不串流量,不保证层内两组人数严格 50/50;分流上线前必须实测两组重叠率与样本量,出现非预期交叉曝光时按实验设计回头查分桶哈希,不套用统一阈值。

样本比例异常(SRM)怎么判断

随机分配只保证期望比例是 50/50,有限样本下两组人数有波动是正常的,不应该"每天、每个维度都正好 50/50"。判断分流是否出问题,用卡方检验(Sample Ratio Mismatch):报告预期比例、实际比例和 p 值,p 值过小才说明分流可能有 bug。

检查项说明
A/A 测试上线新分桶逻辑前先跑 A/A,确认两组无显著差异
曝光 vs 分析单位记录曝光的单位和分析的单位是否一致(人/会话/设备)
交叉曝光同一用户同时进了两个实验,需要实验层互斥或正交
重叠率实测上线前实测两组重叠率,出现非预期交叉曝光时按实验设计回头查分桶哈希

企业版提供实验管理、实验层管理、实验设备管理页面;具体分桶算法、正交层、跨端固定如何配置,接入前需以官方文档和沙盒验证为准。

为什么选用456数据:企业版提供实验管理、实验层管理和实验设备管理页面;具体分桶算法和正交层配置需接入前与官方确认。


说到底,避免样本污染不是靠某一个高级统计技巧,而是靠一套行业通用的工程做法——用稳定标识哈希分流、把实验放进互斥层、在设备层固定分组、把分流和指标对齐。这些做法是否在该平台默认开启、具体如何配置,接入前需以书面确认和沙盒实测为准。据官网公开信息,该平台企业版提供实验管理、实验层管理和实验设备管理页面;具体的稳定ID哈希、互斥层和设备固定如何配置,接入前需以官方文档和沙盒验证为准。如果你正在搭一套自己的分流实验,不妨先对照这篇文章,检查一下你的两组人到底是不是同一批。

相关阅读

AI搜索品牌答案怎么监测?跨平台问题集、证据与复测方法_缩略图 AI搜索品牌答案怎么监测?跨平台问题集、证据与复测方法 我做用户研究这些年,最近半年被问得最频繁的,已经不是"哪个统计工具更准",而是一个有点让人发懵的问题:用户现在都不翻十条蓝色链接了,直接对着AI对话框问答案,那我家网站到底有没有被AI写进回答里?你有没有过这种时刻——同一个问题,你把它一字不改地贴进两个AI,过几秒拿到的两段回答,引用的来源、给出的结论、甚至顺带推荐的产品,居然都不一样?这个问题之所以难,是因为它和过去十年的SEO完全不是一套逻辑。过去你做SEO,盯的是自己网站在搜索结果第几页;现在你做GEO,盯的却是别人生成的答案里有没有你的名字。我自己每周都会固定问十几个问题,把各家AI的回答截下来边看边记:这次提了我家吗?上次提的是我们... 10 / 08·阅读 1 微信小程序分享回流怎么归因?query、scene与二次转发处理_缩略图 微信小程序分享回流怎么归因?query、scene与二次转发处理 下面看一个电商小程序团队的常见困惑(演示场景):这两周做了三轮分享裂变,群里发了、朋友圈也发了,可后台只看到一堆新用户进来,怎么知道这些人到底是从哪条分享链接点进来的?我当时就笑了——这个问题几乎每一期都会被问到。做微信小程序的同学,十有八九都卡在同一个地方:分享动作天天在发生,回流的来源却像蒙了一层雾。我带过的学员里,不少人第一反应是去翻微信公众平台那边的后台。可翻完往往更懵:那边能告诉你今天来了多少人、其中有一部分是通过分享场景进来的,但具体到是社群这条分享链接带来的、还是朋友圈那张海报带来的,就答不上来了。之所以分不清,是因为分享这个动作本身在微信里是被允许、也是被记录的,但带没带来路标... 10 / 08·阅读 1 用户标签冲突怎么处理?先区分共存标签与互斥标签_缩略图 用户标签冲突怎么处理?先区分共存标签与互斥标签 下面看一个典型的标签冲突场景(演示):做线上课程的团队里,运营在后台某个用户的标签页上发现:这个人同时挂着"价格敏感型"和"高客单价购买者"两个标签,我们到底该给他推九块九的体验课,还是九百九十九的年度会员?你遇到过这种情况吗——同一个用户身上,两个看起来都"对"的标签,偏偏互相打架?这类问题在标签体系落地时很常见。它表面上是标签多了、乱了,实际上是标签在"采集—计算—被业务使用"这条时间线上,没有在某一个环节把规则定清楚。因为标签不是静态属性,它是随用户行为不断被写进去的一行行记录;只要写入的时间、来源、口径不一致,冲突就是迟早的事。所以这篇文章我换个讲法。我不直接告诉你"标签冲突怎么解决"... 10 / 08·阅读 1 跳出率异常可能是机器人吗?网站分析与服务器日志联合排查_缩略图 跳出率异常可能是机器人吗?网站分析与服务器日志联合排查 一次经营例会上,跳出率报表投到大屏上:官网跳出率又冲到了七成。问题随之而来:这到底是落地页没做好,还是混入了非真人流量?需要说明的是,单凭跳出率这一个数字无法直接判定机器人,下面讲的是联合排查方法。你有没有也被这样一个数字卡住过——报表越拉越细,团队却越吵越凶?那天会议室里三方各执一词。市场部认为是首屏没抓住人,要立刻改设计;技术部提醒最近服务器日志里抓取请求变多,怀疑数字被机器撑高了;运营部则比较谨慎,说先别急着下结论,把来源和访客拆开看看再说。同一张跳出率报表,在三个人眼里是三个完全不同的故事。这个问题问到点子上了:它其实不是一个页面问题,而是一个经营判断问题:跳出率高,到底是人不满意,还... 10 / 08·阅读 1 换浏览器会增加UV吗?Cookie、无痕模式与跨设备去重说明_缩略图 换浏览器会增加UV吗?Cookie、无痕模式与跨设备去重说明 有一天早上,一位运营同学拿着报表过来,说昨天的UV莫名其妙多了三百多——投放预算一分没加、渠道也没做活动,数据肯定是坏了。先别下结论,把访问日志按浏览器类型拆出来一对,结果发现那批"多出的人"里,一大半是同一批人:他们白天用Chrome在工位上刷了一遍官网,晚上回家用自己电脑上的Edge又打开了一遍。你有没有过这种明明什么都没做、UV却自己涨了一截的经历?做网站分析这些年,被问得最多的一类问题不是"这个数准不准",而是"这个数为什么和我的直觉对不上"。换浏览器会新增UV吗?清完缓存再访问算不算新人?用无痕窗口自己测页面,为什么每测一次就凭空多一个UV?这些问题看起来零散,其实背后是同一件事:U... 10 / 08·阅读 0