探讨AB实验中的分层实验原理与实施方法

675 阅读6分钟

1、背景

AB 实验是 “特性管理” 的主要应用场景之一。随着业务功能、实验数量的增加,特别是对实验流量的管理需求,需要建立统一的组织和规划。在本文中,我们将简要介绍通用实验流量的管理办法,并介绍如何在 FeatureProbe 中实现这种流量管理。

1.1 实验的冲突

大家对实验的冲突和实验的层域规划设计的认识,大多来自于早期 Google 关于 AB 实验的论文《Overlapping Experiment Infrastructure:More, Better, Faster Experimentation》。这里简单回顾一下在什么情况下实验会出现冲突。在同一时间段内,如果只执行一个实验,这意味着同一个用户不会同时参与两个不同的实验,而只会分配到一个实验的A组或B组。然而,通常情况下,即使产品不像 Google 搜索系统那样复杂,也可能需要同时上线多个新功能,这就要求我们同时进行多个 AB 实验。如果我们将所有实验按照时间顺序一个一个进行,验证所有功能所需的时间将会变得非常漫长。

从效率的角度考虑,我们需要能够同时进行多个实验,以节省时间。但从准确性的角度来看,我们不希望同时进行多个实验会导致与一次只进行一个实验时不同的结果。换句话说,我们需要确保实验之间不会互相干扰,使得同时进行多个实验的结果与一次只进行一个实验的结果一致。

2、实验层方案

2.1、原理

实验层的方法通过流量分配来实现多个实验的隔离。简而言之,它将所有用户分成相互隔离的组,然后将每个组的用户引导到不同的实验中(再进一步将组内用户分为实验的A组和B组)。这意味着同一用户只属于一个特定的组,即每个用户只能参与一个实验,从而确保各个实验之间互不影响。  

  • 优点:实验之间用户完全隔离,适用于任何需要绝对隔离的实验场景(即不允许同一个用户同时进入两个实验)。
  • 缺点:流量硬性分割,在总用户量很小的情况下每个实验分到的用户数量可能很小,导致无法获得统计显著所需要的样本量。

2.2、在 FeatureProbe 中配置实验层与实验

FeatureProbe 的功能设计注重通用性和最小化,因此它当前不直接提供实验层的页面配置和管理。不过,我们可以通过使用前置开关(Prerequisite flag)功能来实现实验层的设置与管理。 假设一个场景:我们现在需要一个流量层,其中一部分流量需要被隔离用于两个相互不影响的实验,而剩余的流量需要被保留以备未来可能需要上线的实验。以下是如何进行配置的介绍:

Experiment_layer2.png

首先,我们需要创建一个名为 "Experiment_layer" 的前置开关,以作为实验层的配置。在这里,我们选择使用百分比流量来对用户进行分组,当然也可以选择不同的用户特征来进行分组,比如按城市来分组等。在这个配置中,我们将 10% 的流量分配给实验甲,另外 10% 的流量分配给实验乙,而剩下的 80% 的流量将被预留以供将来使用。我们会使用三个不同的 variation 来代表这三个分组,分别是 "甲"、"乙"和"预留"

Experiment_layer.jpeg

现在我们来配置实验甲,新建一个实验开关,例如取名 “Exp1“。配置前置开关为 "Experiment_layer" 的返回为实验甲,之后将 “Exp1“ 的 2 个 variation 配成 “A组” 和 "B组",将 default 的 variation 设置为“A组”,将 disable 的 variation 设置为“未进入实验”。

Exp1.jpeg

这时,我们可以编写我们的业务代码如下:

FPUser fpUser = new FPUser(userId);
    string var = fpClient.stringValue("exp1", user, "未进入实验");
    if (var == "A") {
        
        // 这个人进入了实验的A组,这里写A组的处理逻辑
        
    }else ifvar == "B"){
        
        // 这个人进入了实验的B组,这里写B组的处理逻辑
        
    }else{
        
        // 这个人没有进入exp1实验,他可能在实验层里别的组,写默认的处理逻辑
    }

2.3、直接使用实验层做实验

以上的场景假设每个实验都有自己的 A组 和 B组(control组 和 treatment组),在更简化的形势下,层内实验可以复用同一个 control组,如下图所示:

Control.png

这种情况下,就可以直接使用 "Experiment_layer" 控制流量,而不用嵌套前置开关。相应的代码示例如下:

 FPUser fpUser = new FPUser(userId);
   
    string var = fpClient.stringValue("experiment_layer", user, "未进入实验");
    if (var == "Control") {
        
    // 这个人进入了Control组,使用之前的处理逻辑
    
    } else if (var == "blue" {
        
    // 这个人进入了blue实验,这里写blue的处理逻辑
    
    } else if (var == "green") {
        
    // 这个人进入了green实验,这里写green的处理逻辑
    
    } else {
        
    // 写错误处理逻辑
    
    }

3、实验正交方案

3.1、原理

除了使用实验层来进行硬隔离流量的方法之外,通常情况下,我们发现大多数实验所要测试的变化点并不是绝对互斥的。这意味着我们可以采用重新随机分配流量的方式,来共享资源,同时又能够将两个不同实验的效果区分开。 

举例来说,一个页面"UI实验"和一个"排序策略实验",我们就可以认为这两个实验是非互斥的,同一个用户可以同时进入这两个实验,只需要将进入两个实验的用户打散,避免所有进入"UI实验A组"的人,进入的"排序策略都是 A 组",而是让进入""UI实验A组"的人,重新打散,均匀进入"排序策略实验"的 A 组和 B 组,这样"UI实验"的效果在"排序策略实验"里就会因为被打散而平衡掉,使得"排序策略实验"的效果只包含排序算法产生的影响而不包含"UI实验"产生的影响。

3.2、在 FeatureProbe 中使用正交实验

FeatureProbe 中所有开关的按流量分割算法参数都是不同的,也就是说所有开关的流量分割分组都是天然互相正交的,无需做特别设置。   例如建立实验 "Exp1" 和 "Exp2",分别都是 50%,50% 返回两个 variation。那么拿同一组用户 id 去请求两个实验结果,进入 "Exp1" 的 A 组用户,一定是打散进入 "Exp2" 的两个 variation 组。

Ex1_layer2.png

4、混合方案

在 Google 的原论文中,还介绍了更复杂的组和实验层,以及正交实验的实验流量规划方法,如下图所示:

image.png

FeatureProbe 中,我们可以通过级联使用前置开关来实现类似的实验层配置,具体示例不一一列举。