层和参数
层是一个逻辑结构,表示您将针对其进行实验的用户群。通常,每个层对应于您系统中的一个实际模块。例如,您可能为网页的用户界面建立一个层,并为支持您的页面的后端服务器建立另一个层。
在每个模块中,许多功能可能需要实验。考虑您登录页面上的一个按钮:您可能希望对三个参数进行实验 - 按钮的大小、文本颜色和背景颜色。需要注意的是,同一模块内的参数通常是相互依赖的。例如,蓝色文本颜色可能与蓝色背景颜色不兼容。
这种相互依赖性为参数与层之间的关系提供了另一个视角。我们可以将所有参数划分为 N 个子集,每个子集对应一个层。来自不同子集的参数彼此完全独立,而来自同一子集的参数通常是相关的。因此,它们应该属于同一层。
在一个层内并行运行实验
每个层允许为每个实验分配互斥的流量,从而实现多个实验的同时执行。假设已创建一个名为 layer_on_landing_page
的层,其中包含三个参数:button_size
、button_text_color
和 button_background_color
。这些参数的当前值分别为 12
、white
和 blue
,我们可以将其设置为默认值。目标是确定优化按钮的 CTR(点击率)的组合。为实现这一目标,同时进行三个实验。第一个实验测试按钮大小,第二个测试文本和背景颜色的组合,第三个测试所有三个参数的组合。每个实验使用总流量的 10%,在层内留下 70% 的未分配流量。
experiment_for_button_size
experiment_for_button_color
experiment_for_button_combination
对于 experiment_for_button_size
,没有为参数 button_text_color
和 button_background_color
设置显式值。在运行时,它们将默认为层的默认值,稍后将进行解释。
利用参数检索实验分配
使用层参数运行实验可以显著提高工程效率和迭代速度。这在部署新代码可能需要几天或几周的时间时尤为关键,例如移动应用程序需要进行构建发布周期的情况。如果不利用参数进行实验,您的代码可能如下所示:
buttonSize := DEFAULT_SIZE
buttonTextColor := DEFAULT_TEXT_COLOR
buttonBackgroundColor := DEFAULT_BACKGROUND_COLOR
experiment, err := abcUserContext.GetExperiment(context.TODO(), projectID, "layer_on_landing_page")
if experiment.ExperimentName == "experiment_for_button_size" && experiment.GroupName == "Control" {
buttonSize = 12
} else if experiment.ExperimentName == "experiment_for_button_size" && experiment.GroupName == "Treatment_A" {
buttonSize = 16
} else if experiment.ExperimentName == "experiment_for_button_color" && experiment.GroupName == "Control" {
buttonTextColor = "white"
buttonBackgroundColor = "blue"
} else if experiment.ExperimentName == "experiment_for_button_color" && experiment.GroupName == "Treatment_A" {
buttonTextColor = "yellow"
buttonBackgroundColor = "green"
} else if experiment.ExperimentName == "experiment_for_button_combination" && experiment.GroupName == "Control" {
buttonSize = 12
buttonTextColor = "white"
buttonBackgroundColor = "blue"
} else if experiment.ExperimentName == "experiment_for_button_combination" && experiment.GroupName == "Treatment_A" {
buttonSize = 16
buttonTextColor = "yellow"
buttonBackgroundColor = "green"
}
这段代码冗长、容易出错,并且每次进行新实验时都需要更新和部署新代码。然而,通过利用层键和强类型 API 来检索实验分配,过程变得更加简单:
experiment, err := abcUserContext.GetExperiment(context.TODO(), projectID, "layer_on_landing_page")
buttonSize := experiment.GetInt64WithDefault("button_size", DEFAULT_SIZE)
buttonTextColor := experiment.GetStringWithDefault("button_text_color", DEFAULT_TEXT_COLOR)
buttonBackgroundColor := experiment.GetStringWithDefault("button_background_color", DEFAULT_BACKGROUND_COLOR)
现在的代码更简洁、更清晰。添加新实验只需通过 UI 在同一层 layer_on_landing_page
下创建新实验并配置相应参数。这允许在不修改代码的情况下执行新实验。如果参数没有在实验下明确设置,API 将默认为层的默认值。API 默认值(如 DEFAULT_TEXT_COLOR
)是在参数被遗漏添加到层时的后备,其优先级低于层的默认值。当用户未分配到任何实验,如 layer_on_landing_page
下剩余的 70% 流量时,API 将返回三个参数的层默认值。
基于参数的实验启动
在进行了几天的实验后,假设我们观察到 experiment_for_button_size
的处理组的点击率明显高于对照组。这表明对于参数 button_size
,16
的值优于 12
。如果我们希望为剩余用户启动这个获胜值,我们只需执行两个步骤:
归档实验
experiment_for_button_size
,因为我们已经得出结论。这使得层内80%
的流量未分配。在层页面上,将参数
button_size
的默认值从12
修改为16
。这个修改将影响80%
未分配流量的button_size
参数值,以及experiment_for_button_color
下的流量,因为experiment_for_button_color
没有为button_size
明确设置值。对于experiment_for_button_combination
下的流量,button_size
的值应继续遵循该实验内的明确设置(优先级高于层默认值),因为我们不希望在进行实验时修改用户行为。
下图说明了完整的流程: