Tubi 实验之道

在 Tubi,从功能、基础架构变更,到 ML 模型,每个团队都非常依赖实验来帮助制定决策。在过去 3 年中,Tubi 的实验速度增长了 18 倍。在上个季度,有高达三分之一的 ML 实验显示出对公司 KPI 的正向影响。可以毫不夸张地说,Tubi 的实验文化对公司过去 3 年的成功起到了重要作用。

Tubi 的实验体系并不是一蹴而就的。经过多个团队的共同努力,我们才建立了用于配置、实施和分析实验的良好系统。在这篇文章中,我们将介绍为支持实验和决策而建立的三个组件:

  • 实验引擎 - Popper;

  • 降低实验门槛以普惠全公司的 UI;

  • 自动分析和实验 QA 方法。

最后,我们将分享一下在整个过程中学到的一些重要的经验教训。

Popper 实验引擎 

Tubi 实验平台的一个关键组件是实验引擎,这是一个用 Scala 编写的基于 Akka[1] 框架的后端服务。该实验引擎已经是第二次迭代版本,设计时考虑到了我们从基于 Facebook 的 PlanOut[2] 库的第一个版本中获得的经验教训。鉴于我们希望培养一种更科学的实验方法,我们以 Karl Popper 的名字为新服务命名。

In so far as a scientific statement speaks about reality, it must be falsifiable

Karl Popper

核心概念

顶层概念是一个命名空间(Namespace),代表一组相互排斥的实验。这给我们带来了灵活性,例如,ML 团队和移动工程团队不需要协调实验计划,而是自动地帮助每个团队内的并行实验保持互不影响。

每个命名空间都能够(通过一致的哈希)将所有的实验目标(如设备、用户、IP等)分配到一个可配置数量的分段(Segment)中,每个分段与目标的持久哈希值相关联。每个分段被分配给最多一个实验,然后实验进一步将分段分配给一个特定的分组(Treatment Group,通常包含实验组和对照组)。实验可以包含多个阶段(Phase),这些阶段保留不同数量的分段(例如,阶段 1:第 1 天分配 1%,阶段 2:此后 2 天分配 10%,阶段 3:实验剩余时间分配 100%)。最后,可以通过条件(例如,"只有新用户")进一步细化分段,以便更有效地使用命名空间。

Tubi 实验之道_第1张图片

实验运行的核心概念

内置的重现性

实验和命名空间以 JSON 配置的形式存储在 git 仓库中。在每个命名空间中,Popper 使用一个只追加序列(append-only sequence)来存储对实验的任何 CRUD 操作。

持久化序列允许我们在服务重启时始终将用户散列到相同的实验/组中,而不需要在数据库中存储段分配。这意味着任何命名空间的状态和历史都完全由其序列决定。

序列和持久化哈希也让我们大大简化了服务架构。在分段逻辑方面,不需要节点间的协调。部署新的实验也是低风险的(*重点来了* 我们还没有出现过失败的部署)。

Tubi 实验之道_第2张图片

使配置和更改易于跟踪,非常有助于重现性

让实验人人可用

不是每个人都是实验专家,也不应该非要成为实验专家才能成功运行实验。我们越是能抽离呆板的细节,比如 JSON 配置模式和启动/停止实验所需的协调步骤,用户就越容易进行实验。

实验的开始和结束日期是在配置中指定的,这让我们可以将部署新配置和启动实验分开。得益于此,我们可以自动部署最新的配置更新(一旦有代码被合并到主分支就会触发自动更新),而不必总是将其与实验开始时间对齐。

虽然核心分割逻辑是无状态的,但 Popper 的确利用数据库来存储特定实验和分组中包含开发和测试设备的覆盖。这使得它更容易帮助 QA 测试实验和重现错误。

将这一切联系在一起的最后一块是使用 React 构建的 UI,它能够帮助用户完成创建实验配置的整个工作流程,而无需处理 JSON 模式的细节。它还提供了一个有过滤条件的日历视图,以查看指定时间段内正在运行的实验。最后,它还是记录是否决定发布某一实验的中枢。

Tubi 实验之道_第3张图片

Popper UI 帮助我们图形化展示和创建实验
 

决策支持 

度量

实验客户端将从 Popper 实验引擎中获取配置,以决定采取哪个代码分支。在代码中进行实验时,会向后台发送一个曝光事件(Exposure Event),表明某个用户或设备受到了这个特定命名空间、实验和分组的影响。这个数据处理是通过 Spark Streaming(运行在Databricks上)和Akka Streams 服务组成的管线来完成的,这些服务都是用 Scala 编写的。

一旦我们能够确定什么用户在什么时候使用了什么功能,我们就可以将其与用户参与度数据结合起来,计算出我们所需的指标,以了解正在测试的变化的效果,并帮助我们做出是否发布实验的决定。

分析报告

通过将曝光数据和用户与应用的互动数据放在一起,我们能够根据群体差异计算出观看时间、转化率、留存率等各种指标。然后,这些指标会按照观看平台或内容类型(例如电影与电视剧)等重要维度进行细分。每个指标的显著性是用 CUPED[3] 计算出来的,当我们试图决定整体实验是否具有统计学意义时,我们会使用 Benjamini-Hochberg[4] 修正来降低错误发现率。

为了实现决策的大众化,所有的指标都会自动计算,并通过 BI 仪表盘向整个公司展示。一小部分最接近公司层面 KPI 的指标子集被封为“北极星指标”。北极星指标作为一个整体来决定整个实验的意义。我们还设置了一个“不伤害”标准,如果一个实验伤害了任何北极星指标,在未经更全面细致审查过程之前,就不能发布。

Tubi 实验之道_第4张图片

使实验结果易于访问和理解

实验健康检查

尽管 Tubi 只雇用“完美的工程师”,但我们也曾罕见地犯过一两个错误。由于实验平台是整个公司依赖做出重要决策的工具,因此对这个平台的信任是至关重要的。维护这种信任的一个关键组成部分是创建一个验证系统,它可以使有问题的实验浮出水面,这样我们就不会根据错误的信息做出决定。

失败模式五花八门,我们在这里简单总结三类最常见的问题:

  • 不均匀的分组:我们永远无法保证完全均匀的分组,但如果每组用户数之间的差异很大(通过标准差空间衡量),那么这可能说明 Popper 实验引擎本身存在问题,或者是只影响到一组的实验 bug;

  • 相互影响:绝大多数时候,比如说 Android 团队和 ML 团队之间的实验不会相互干扰。但是,在极少数情况下,有可能一个实验中的变种触发了另一个实验中的某些条件,导致效果出现偏差;

  • 有偏见的曝光:在少数情况下,我们遇到了实验客户端代码中的 bug,不会导致不均匀的分组,但对一个组的影响比其他组更大。这些往往会夸大组间的差异,并可能导致在其它方面看起来都很正常的假阳性。为了抓住这一类问题,我们添加了一个健康检查,在实验开始前比较组间的同一指标。如果该差异在统计学上是显著的,那么这就是一个危险信号,表明实验可能无效。

Tubi 实验之道_第5张图片

实验前的高 t-stat 度量差异是实验有效性的一个危险信号

 经验教训 

一个成功的实验平台不仅仅是把 A/B 测试引擎和分析仪表板放在一起。它渗透在我们作为一个公司的许多设计选择中。这里介绍了我们学到的一些重要经验。

自助服务是速度的关键

对于 ML 团队来说,实验中有一个瓶颈是特别痛苦的,那就是需要将实验的变更(如新模型、新功能等)部署到生产环境中。以前改变一个模型还需要改变后端服务代码,这就需要与后端团队协调设计、实现、测试和部署这些变更。在快速发展的环境中,这种协调总是成为快速迭代的拦路虎。

因此,我们在重新设计后端服务时就着重考虑到了实验的因素。对于大多数常见的模型和功能变更,创建一个实验只需要一个新的实验配置,并根据后端服务所需的模式来交付新模型。特别是对于我们的首页内容推荐,将多个模型和排序组合成首页的过程也是配置驱动的,可以通过实验配置进行控制。仅此一项改变,就使 ML 实验的速率提高了 5 倍。

提出重要的问题

你的实验平台不仅能帮助你做出决策,还能创造激励效果,改变你的团队精力重点。许多其他实验系统花费了大量的开发精力来创建实验和分析工具,以实现任意复杂和精细的实验。在 Tubi,我们考虑的更为本质,所有的实验工具、指标和流程都高度专注于识别对公司核心 KPI 有统计学意义影响的强信号,以不断驱动公司向前发展。实验平台使得实验在不同系统和团队之间具有可比性。它还强烈鼓励实验者优先考虑高潜在价值的想法,并与业务价值更紧密地结合。

拥抱跨平台的一致性

在 Tubi,几乎每个团队都希望进行实验,以帮助他们更快地进行迭代,做出更好的决策。这些实验在移动和 OTT 的 20 个不同平台上进行。运行这些实验的代码是用许多不同的语言编写的,从后端的 Scala 和 Elixir 到客户端的 Kotlin、Swift 和 Typescript。将所有的实验应用整合到同一个平台上需要付出不小的努力,但拥有跨平台的一致性对于公司整体上更快地迭代是至关重要的。

Tubi 的新功能往往先在一个平台上进行测试,然后再决定是否在整个 Tubi 推广。拥有一个一致的实验平台意味着我们在进行和分析实验时有信心认为移动和 OTT 说的是同一种语言。

实验引擎和 UI 的新功能不断被开发出来,使实验变得更加简单。提供 API 可以手动覆盖实验设置使实验测试更加容易。UI 功能使得创建新的实验和查看所有实验日历变得更容易。拥有一个一致的实验平台意味着我们不必为不同的团队重复实现相同的功能。

一致性确实迫使我们做出一些权衡。有一些专门的解决方案可能会让某些团队更容易,但不能跨平台/语言通用。虽然创建专门的一次性解决方案可能很诱人,但重要的是,实验平台的所有者要抵制这种诱惑,并采取为整个公司优化的方法。

结论 

我们为改善实验所做的投入对提高整个公司的生产力,帮助我们做出更好的决策产生了巨大的影响。从我们 MLOps 后台团队构建的 Popper 引擎,到数据开发团队构建的数据管线,再到数据科学团队构建的分析系统,这个实验平台是一个复杂的跨团队协作的成果。

实验 UI 和自动分析仪表盘确已帮助非专家进行实验并做出决策。健康检查和实时监控有助于发现有问题的实验,并建立对整个平台的信任。最后,我们通过关注北极星指标,推动实验团队与整体业务 KPI 更紧密地结合。最终,我们不仅能够更快地提出问题,而且更重要的是,提出关键问题。

如果你对构建更好的实验工具充满热情,不妨考虑加入我们吧!

[1] Akka: https://akka.io/

[2] PlanOut: https://facebook.github.io/planout/

[3] CUPED: https://www.exp-platform.com/Documents/2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf

[4] Benjamini-Hochberg: https://en.wikipedia.org/wiki/False_discovery_rate#Benjamini%E2%80%93Hochberg_procedure

原文作者:Chang SHE,Tubi 前 VP of Engineering

译者:Chun SHANG,Tubi Associate VP of Engineering

你可能感兴趣的:(Tubi技术分享,音视频,经验分享,scala,数据分析,abtest)