Skip to content

Latest commit

 

History

History
178 lines (138 loc) · 37.3 KB

谷歌分层实验框架论文翻译.md

File metadata and controls

178 lines (138 loc) · 37.3 KB

分层重叠实验框架:更多、更好、更快的实验

Abstract

在谷歌,实验是一个实际的咒语;我们评估几乎所有影响用户体验的变更。这些变更不仅仅包括显而易见的用户可见的变更,比如说用户接口的改动,还有一些隐性的改动,比如使用可能改变排名或内容选择的不同算法。我们对实验的强烈需求让我们面对如何运行更多实验、如何运行可产出更优决策的实验、如何更快的实验的问题。在这篇论文中,我们将描述谷歌分层实验框架,这个框架是我们解决以上问题的关键。另外,因为仅仅只是实验框架的话是不足的,我们还描述了相关的一些工具和更有效使用它的教育过程。我们通过展示整个实验环境的成功作为结论。这篇论文特别的描述了谷歌的实验系统和实验流程,我们相信这是通用的,可以应用在任何希望使用实验来提高搜索引擎或网络应用的业务上。

  1. 简介

谷歌是一家数据驱动的公司,这意味着公司的决策者希望有经验数据来驱动关于是否应该将一个改动开放给用户使用的决策。这些数据通常是通过对线上流量运行实验来收集的。在web上下文里,一个实验包括一部分流量(如入站请求)和控制对这部分流量处理逻辑的变更。用户可见的变更(如改变顶层广告的背景颜色)和不可见的变更,如测试预测广告点击/通过率的新算法,都通过实验来测试。 支持这种数据驱动方法的挑战是需要跟上创新的速度。我们希望有能力实验尽可能多的想法;可同时运行的实验数量限制导致限制业务的迭代速率,这是无法接受的。我们使用实验来测试新特性(test out new features)、探索已存在特性的特征空间(explore the space around existing features)。对于后者,实验可以学习用户反馈并优化当前运行的实验。想象一下我们决定展示什么在搜索结果页面是参数化的(page is parameterized),包括展示和算法。实验通过为参数设置不同的值(different values for the parameters)来探索参数空间,我们可以使用可量度的影响(记录用户体验、收益和其他指标)来决定设置什么值来实现更好的结果。 评估用户对UI变更响应是一种典型的实验用法,值得注意的是实验还需要用于测试算法变更。比如,有些团队希望测试新的预测广告点击转换率的机器学习算法,或者是测试对已有算法进行调参(如改变学习率或下沉率)。线下评估测试对缩小选项空间是有效的,最终这些选项必须在真实线上流量下测试,这是为了评估一个特殊的算法参数在实践中到底能产生多大的正面影响。(变更可能会影响用户行为甚至改变流量的模式alter the traffic pattern,这是线下评估无法做到的)因此,对这种机器学习算法的评估受限于实验空间和对可尝试选项的思考。 因此,我们对实验系统的设计目标是更多、更好、更快

  • 更多:我们需要同时运行更多实验。然而,我们也需要灵活性:不同实验需要不同的配置和可统计重要影响的大小。许多实验只需要改变流量的子集,比如只改变日本的流量,且需要有合理的大小。有的实验需要改变所有流量并产生大的指标影响,所以可以运行在少数流量上。
  • 更好:无效实验不允许运行在真实流量上。有效但结果坏的实验(错误或无意的产生糟糕的结果)需要尽快被发现并使其失效。标准指标需要对所有实验简单可行,这样实验的对比是公平的:例如两个实验在计算广告转换指标时需要使用相同的过滤器来移除僵尸流量
  • 更快:可以简单快速的开始一个实验;足够简单,非工程师也可以开始一个实验,不需要写代码。指标要尽快收集,这样实验可以更快的评估效果。简单的迭代需要快速生效。系统不仅要支持实验,而且可控制放量,比如,保证系统可自动对所有流量逐渐放量,并且可理解。

为了满足以上设计目标,我们不仅是需要一个实验框架来运行更多实验,还需要工具和教育过程来支持更好更快的实验。 对于实验框架,明显的解决方案是有单层实验或者是多因子实验设计。单层实验指的是每个查询最多在一个实验中,这使用起来非常简单且灵活,但规模不足。多因子实验设计在统计的文献中是很常见的,每个参数可以被独立实验;每个实验的参数值和其他实验的参数值重叠。每个查询会同时在N个实验上,N是参数的数量。虽然这种方法经过了多年的研究和实践,但是这在谷歌的系统中无法实践,因为我们有上千个无法独立修改的参数。一个简单的例子是两个参数的系统,一个参数是网页的背景颜色,另一个参数是文本框的颜色。蓝色对于两个参数而言都是有效的,但是两个参数都是蓝色时,页面将无法使用。 我们在这篇论文中提出的解决方案是将所有参数分片为子集,子集中的参数互相之间无法独立修改。参数子集和一个层关联,这个层上可以包含多个实验,流量分到分到不同的实验层是正交的。每个查询将经过N个实验,N是实验的层数。回想起来虽然这个方案听上去不新奇,但我们没有找到任何论文描述这个解决方案。 在这篇论文中,我们讨论这个分层重叠实验框架和我们采用的相关工具、过程以支持更多、更好、更快的实验,并且展示它对实现我们的目标支持的有多好。虽然本文专门针对谷歌搜索,但是适用于任何希望获取实验数据以评估变更的公司、个人,以解决支持大规模实验的普遍问题。用户反馈、数据获取可能会不同,但是论文提及的解决方案是通用的。

  1. 相关工作

实验相关的工作主要在三个方面。第一个是多因子实验的大量统计文献,我们将在第四章简单讨论。 第二个是越来越多的工作在如何运行web实验。Kohavai et al.的论文为如何控制web实验提供了调查和向导[7]。这几篇论文描述了运行和分析实验时的一些陷阱[4,9]。Krieger介绍了如何运行AB测试[8]。谷歌网站优化器帮助网站设计者运行他们的AB测试[5]。实际上,这些论文更多的集中在如何设计和评估一个基于web的实验。第一篇Kohavai的论文是最相关的,因为它讨论了多个关于如何构建实验系统的设计想法,包括怎么将流量分流到实验,为跨查询和交互提供一致性。然而,这些论文都未提及大规模的实验系统和支持运行更多、更快、更好的实验的整体实验环境。 最后是交错实验的相关工作[6],专注于特殊类型的实验设计(内部查询),用于评估排名变更。这种类型的实验可以在我们描述的试验系统中轻松运行。

  1. 背景

在讨论谷歌的实验之前,我们先描述环境因为它为我们的框架设计提供了机会和限制。注意,下文提到的谷歌、谷歌服务框架,都指的是谷歌搜索设施,无关谷歌App,Android,Chrome等。 在上层,用户通过页面浏览器发送请求与谷歌交互。对于搜索结果页面,请求达到谷歌服务框架,在返回结果页面给用户之前,可能命中多个二进制(运行在机器上的程序)。比如说,一个服务决定与查询最相关的搜索结果,另一个服务决定与查询最相关的广告,第三个服务将搜索结果、广告组合成结果网页返回给用户。一方面,模块化允许我们提高延迟(互不依赖的进程可以并行),清晰的分割搜索结果和广告,它们之间互不影响了,迭代更快(每个服务都可以独立迭代,测试付出的代价更小,发布迭代周期更快)。另一方面,模块化要求更小心的设计如果每个请求只允许经过单个实验。这可能导致饥饿(上游服务在将请求发送到下游之前分配所有流量到实验中,这将导致下游服务饥饿)和倾斜(上游服务将所有英语区流量用于服务,导致下游没有英语区的流量)。 每个服务有一个关联的二进制推送和数据推送。二进制推送指当前新代码(修复bug,性能提升,新特性等)发布到线上,这定期发生。数据推送更频繁(按需或每几小时),涉及推送更新数据给指定程序。一种类型的数据必须包括参数的默认值,这决定了程序如何运行。举个例子,控制结果展示的服务可能有一个参数用于决定广告区域顶部的背景颜色。另一个例子是预测广告点击转换率的服务有一个参数用于控制算法学习率和下沉率(控制算法迭代时的布长,影响收敛和最优解)。服务可能有上百上千个参数。新特性可能增加一个或多个参数:最简单的一个场景,一个参数开关这个新特性;更复杂的场景,有多个参数用于控制新特性是否打开。拥有分割的二进制推送和数据推送意味着如果我们可以找到正确的参数分割,我们可以快速和也可以慢速的变更服务内容(二进制推送慢,新的参数值变更快) 一个web搜索的实验(experiment)转移一部分入站的查询到另一个处理路径,并可能变更用户服务的内容。一个对照实验(control experiment)转移一部分入站查询,但是不改变用户服务的内容。我们使用数据推送来配置实验。因此,在数据推送中,有一个文件指定服务参数集的默认值。还有另一个文件提供额外的参数值来改变用户服务的内容。实验只提供特定改变的参数值,对于其他参数,使用默认值。举个例子,一个简单的实验只改变一个参数,广告的背景色,从黄色(默认值)到粉色。 通过改变参数值来改变服务内容,实验必须明确哪个流量子集被转移(diverted)。一种简单的方法是随机切分实验流量。这种方法的问题是如果实验是可见的变更(改变背景颜色),一个用户的查询可能进入实验也可能退出实验(一个用户一会命中黄色一会命中粉色),这会让用户迷惑。因此,一个通用的方法用于web实验是使用cookie进行基础的切分;cookie被用于网站追踪用户。事实上,cookie是机器/浏览器保存的,可以被简单清除;因此,虽然cookie与用户不对应,但它可以用于在多次查询中提供一致的用户体验。对于实验导流(experiment diversion),我们不转移特定一批cookie,而是对cookie去模:给定一个数值表示的cookie,对cookie取模1000,所有模为42的cookie,被分为一组用于实验导流。假设cookie设计为随机的,所有cookie取模都应该是公平的。cookie取模能在实验配置简单指定,并且很容易监测冲突:实验1用cookie取模1和2,实验2用cookie取模3和4。这两个实验理论上有相同可比的流量大小。 在数据文件中配置实验让实验可以简单快速的创建:数据文件是可读的并且能简单的编辑,这不像代码,代码实验必须由工程师创建,它们可以比代码更频繁的推送,已有参数创建实验会更快。 在开发我们的分层重叠实验框架之前,我们使用一个基础的单层实验框架。在这种框架上,每个查询最多只能在一个实验中。优先使用cookie取模来进行实验导流,然后是基于随机流量的实验。上游服务拿到一个“first dibs”的查询,如果上游服务运行了足够多的实验,那么下游服务将产生在运行实验时流量饥饿。因为有以上问题(饥饿和倾斜),单层框架确实能解决一定的问题:用起来简单、足够灵活。然而,考虑到谷歌的数据驱动文化,单层的方法没有足够的可拓展性:我们不能快速的运行足够多的实验。

  1. 重叠实验框架

这个章节中,我们描述重叠实验框架,它将尝试保留单层实验系统的优势(使用简单,迅速),并增强可扩展能力、灵活性、鲁棒性。我们还提供可控、明确的放量。 一个明显的可统计的解决方案是多因子系统,每个因子对应系统中一个可变的参数。事实上,一个请求将同时在N个实验上,每个实验改变一个不同的参数,N表示参数的数量。多因子实验被理论和实验充分验证[3]。然而,一个多因子系统对于我们复杂的环境来说是不可行的,因为并非所有的参数都是独立的,不是所有我们想要测试的参数值都可以和其他参数值放到一起很好的运行(比如粉色的文本框和粉色的背景)。换而言之,谷歌必须提供一个可用的网页。 有了这个限制,我们的想法是将参数分片到N个子集中。每个子集和一个实验层(layer)关联。每个请求将同时经过N个实验(一个实验一层)。每个实验只能改变关联实验层上的参数(实验层上的参数子集),同样的参数不允许和多层关联。 有个显而易见的问题是我们怎样对参数分片。首先,我们根据模块化的服务来分:不同服务的参数可以分到不同的子集中(解决了饥饿和倾斜的问题)。但是,同一个服务中的所有参数不需要在同一个子集中:我们可以进一步通过检查(明确哪些参数不能独立变更)或者是过去的实验经验(哪些参数是一起变更的)来对同一个服务中的参数进行分组。在图一中,我们可以在每个web服务(搜索结果服务和广告服务)上拥有一个或多个层。 事实上,我们设计的系统比简单的将参数分片到子集并和实验层关联这种做法更加灵活。为了解释这种灵活性,我们介绍多个定义。这几个关键概念将与入站流量和系统参数一起工作: 域(domain):一部分流量 层(layer):对应系统参数的子集 实验(experiment):在一部分流量更改0个或多个系统参数的参数值,从而改变请求处理的逻辑 我们可以构建域和层。域包含层,层包含实验和域。在层中构建域允许不同的参数进一步分片到不同的域里。我们从头开始解释,我们有一个包含所有流量和参数的默认域和层。在这个默认域和层中,我们可以,举例:

  • 简单将参数分为3层(图2a)。在这种情况下,每个请求将最多同时在最多3个实验中,每层一个。每个实验只能更改对应层上的参数。
  • 首先将流量分为两个域。其中一个域只有一层(非重叠域),另一个域是重叠域,其中包含三个层(图2b)。在这个场景下,每个请求首先被分到非重叠域或者是重叠域中。如果请求在非重叠域中,那么这个请求最多在一个实验中(可以修改参数空间中的所有参数)。如果请求在重叠域中,那么请求将经过三个实验,每层一个实验,每个实验只能使用关联层上的参数。

尽管这种构造看上去非常复杂,但它提供了多种便利。首先,非重叠域允许我们运行实验以改变大量不需要在一起使用的参数。然后,它允许我们有对参数有不同的分片;我们想象三个域,一个非重叠域,一个非重叠域和一个参数分片,第三个重叠域和另一个不同的参数分片。最后,这种构造让我们更充分的利用空间,这取决于哪些参数分片是最常用的,以及哪些跨层的参数实验是最常用的。注意,将当前未使用的参数从一层移动到另一个是很容易的,只要能保证参数能与原有层中的参数重叠。需要注意保证实验在不同层可以被独立导流,以基于cookie的实验为例,单层框架使用mod=f(cookie) % 1000,我们用mod = f(cookie, layer) % 1000。这种复杂架构确实增强了灵活性,但对更改配置来说付出更大,特别是域:改变分配给域的流量同时改变了实验可用的流量。举个例子,如果我们改变非重叠域的流量从10%的cookie模到15%,另外5%的cookie模来自重叠域,原本可以看到重叠域每个层的用户现在看到的是非重叠域。 一个另外的概念是启动层(launch layer)。启动层和实验层在以下方面有区别:

  • 启动层总是包含在默认域中(运行在所有流量上)
  • 启动层是一个单独的参数分片(一个参数最多同时在一个启动层和一个实验层中)
  • 为了让启动层的参数和实验层中的参数重叠时能正常运行,启动层中的实验具有不同的语义。具体来说,启动层中的实验为参数提供可选的默认值。换而言之,如果实验层中没有实验覆盖启动层的实验,可选的默认值被使用,并且启动层基本等同于实验层。但是,如果一个实验层里的实验覆盖了这个参数,那么这个实验重写参数的默认值,不管这个值是系统默认值还是启动层实验提供的值。

启动层的例子在图2c,d中展示。定义启动层的目的是实验放量时,参数变更逐渐向用户开放,不影响当前存在的实验,并可以以标准的方式持续追踪放量。启动层的常见用法是为每个发布特性创建一个新的启动层,在特性全量后删除它(新的参数值变为默认值)。最后,因为启动层的实验通常比较大,他们可以用于测试功能(feature)间的相互影响。理论上我们可以在实验层上测试实验间的相互影响(假设我们在同一层中手动开始实验或观察不同层中参数的相互影响),因为实验层中的实验比较小,那么影响会更小,难以检测。 实验和域都在一部分流量(traffic)上操作(我们称流量为分流),分流(diversion)中有分流方式(diversion types)和条件(condition)两种概念。 我们已经在章节3中描述了两种分流方式,基于cookie取模和随机分流。上面同样讨论了cookie取模的分流需要使用层id(mod=f(cookie, layer)%1000),以确保层之间正交。我们支持了两种其他的分流方式,基于uid取模和cookie-day取模。uid取模类似cookie取模,不同的是我们使用登录用户的uid而不是cookie。对于cookie-day取模来说,我们使用cookie和日期组合的方式,那么对于给定的一天,同一个用户有相同的实验,分流每天都会变。在所有场景中,都没有方法配置对特定cookie的用户进行分流。类似的,分析总是使用查询,cookie,用户进行分组聚合。注意,我们现在支持4种分流方式,我们可以支持更多其它分流方式(hash查询字符串) 使用不同的分流方式的原因是确保同一用户成功查询的一致性,揭露一些随时间推移产生的潜在影响。基于以上理由,我们分流时以以下顺序采用的分流方式:uid,cookie,cookie-day,随机。一旦一个事件满足某一分流标准,那么将不再考虑其它分流方式(图3)。虽然顺序保证了最大的一致性,举个例子,在同一层中,1%的随机分流实验的流量比1%基于cookie取模的实验的流量。极端情况下,会产生饥饿。实践表明,实验层需要有主导的分流方式,实验和控制必须使用相同的分流方式。主要影响是不同的分流方式要求不同的实验大小。(5.2.1) 在通过分流方式选择流量子集后,条件(condition)通过将特定事件分流到实验或域来提供更好的流量利用。举个例子,一个实验通过一个日本的条件仅改变来自日本的查询的服务内容。我们支持条件包括国家、语言、浏览器等。有了条件,两个实验可以用同样的取模,一个实验用日本的流量,另一个实验用英语流量。另一种条件的用法是新代码的金丝雀发布(通过二进制推送发布新代码),在大流量运行前,需要小流量场景下测试新代码,确保新代码没有bug且符合预期(金丝雀通过错误日志、实时监控来检查是否有bug)。为了支持这种使用场景,我们提供基于机器或数据中心的条件进一步限制使用流量。虽然金丝雀实验不能取代严格的测试,但他们是一种有用的补充,因为在发新版本时限制了潜在的伤害范围,并且有些场景在非真实流量下难以复现。 条件直接用于实验或域配置,允许我们在创建实验时做基于数据文件做冲突检测。正如分流方式章节提到的,一旦事件(event)符合了一个实验分流方式的标准,它将不再考虑其他分流方式,即使它不符合实验设置的条件。如果我们有对应一个特殊的cookie取模的流量,那么我们有一个不倾斜的分流。但是,考虑这个场景,两个实验实验同一个cookie取模,一个条件是日语的流量,另一个条件是英语流量。这个cookie取模的剩余流量(非日语且非英语)不能分配给其他实验。为了避免倾斜流量用于后续分流方式,不把这些流量(符合分流方式的条件但是不符合条件)分配给后续分流方式的实验是很重要的。我们通过使用倾斜id标记这种流量来阻止这种情况发生。 图3展示了决定一个请求分流到哪个域、层、实验的逻辑。所有这些逻辑都是通过打包到服务中的公共库(shared library)实现的,因此所有变更(新的条件、分流方式等)在服务发布时生效。鉴于实现的复杂性,公共库让所有服务一致实现了相同协议,共享实验的新功能。 基于这套框架,评估、发布一个典型的特性的过程是这样的:

  • 在对应服务中实现新功能(包括CR,发布,设置默认值等,正如标准的工程实践)
  • 创建一个金丝雀实验(通过数据推送)来确保功能正常运行。如果不能正常运行,需要写更多代码
  • 创建一个实验或一组实验(通过数据推送)来评估功能。注意配置实验涉及指定分流方式、相关的分流参数、条件和受影响的系统参数
  • 评估实验指标,根据结果,可能需要做额外迭代,更改实验或新建新的实验,或者添加新代码从根本上改变功能
  • 如果特性发布,走发布流程:创建一个新的发布层和发布层实验,逐渐放量,最后删除发布层并修改相关参数的默认值为发布层中设置的值
  1. 工具 & 流程化

重叠框架让我们可以更快的大规模的运行更多实验,同时评估更多变更,但只有框架是不足的。我们需要工具、研究、教育过程来支持更快的实验。在这个章节中,我们讨论几个关键工具和过程,以及它们怎么帮助我们扩大规模

5.1 工具

数据文件检查(Data File Checks): 数据文件(data file)的一个优势是它们可以自动检查错误,这导致更少错误实验的运行。我们有语法错误(所有必要的字段都是可解析的)、一致性、约束错误(id独立,是否实验在使用参数所在的层,是否有足够的流量支撑该实验,流量限制检查:是否使用了其他实验的流量;注意,随着分流条件的增加,这些检查会更复杂)、基础实验设计检查(实验是否可控,该控制是否在同一个层中,控制是否与实验使用相同的流量)的自动检查 实时监控(Real-time Monitoring):我们使用实时监控来捕获基础指标(比如广告转换率),这样可以尽快发现是否存在意料之外的异常。实验人可以设置期待的监控指标的范围,如果指标在范围之外,将触发自动告警。实验人可以调整指标范围,关闭实验,或者调整实验的参数值。实时监控不能替代小心的测试和审查,它让实验人积极的测试潜在的变化,因为错误和意外能尽快被发现。

5.2 实验设计 & 大小设计

实验设计和大小设计超出基本的数据文件检查(每个实验都必须有相同的分流控制器)

5.2.1 大小设计

如Kohavi提到的[7],实验应该有足以引起注意的统计指标变化的流量大小。在这个章节中,我们讨论如何为实验设计大小,实验启动的依赖和相关的实验大小工具 定义实验大小的有效值: N = 1 / (1 / control_queries + 1 / experiment_queries) 实践中,我们有术语control_queries和experiment_queries,通过N影响相对度量估计的方差。确定N需要:

  • 实验关心什么指标(metric)
  • 对于每个指标,实验者想要检测到什么变化(θ)(例如实验者想要检测到2%的广告转换率click through rate)
  • 对于每个指标, 一个单位(N=1)样本的标准误差(s)(standard error)。因此,大小N的实验标准误差是s/根号N

Kohavi假设实验(experiment)和对照(control)有相同的大小,例如experiment_queries=2N,因此检测要求N≥ 16(s/θ)^2。16是根据期待的置信水平(confidence level)(1−α, often 95%)和统计效果(statistical power)(1−β, often 80%) 我们重叠实验的一个好处是我们每层创建一个大对照组,它可以给多个实验共享。如果共享对照组远大于实验组,那么 1/control_queries +1/experiment_queries ≈ 1/experiment_queries,experiment_queries=N,这让实验可以更小,N≥ 10.5(s/θ)^2,统计效果1−β, 90%[2] 我们在调整实验大小遇到更大的问题是怎样估计标准误差s(standard error),特别是使用比例指标时,y/z(比如覆盖率,ad/total广告曝光率)。当分析单位和实验单位不同时,问题产生了。比如,对于覆盖率,分析单位是一个查询,但是对于基于cookie取模的实验,实验单位是一个cookie(一部分查询),我们不能假设来自同一个用户或cookie的查询是独立的。我们的方法是计算s0,每个实验单元的标准误差,并根据s0写s。举例,这里s0是基于cookie取模的标准误差,s = s0(avg queries per cookie_mod)^(1/2)。对于比例指标,我们使用delta method计算s0[11]。 图4展示cookie mod和随机流量实验中不同实验的覆盖度指标,横轴是1/N^(1/2),纵轴是标准误差,斜率是s。显然,cookie取模比查询要陡,这说明,要以相同精度测量覆盖率中的相等变化,基于cookie的实验相对于随机流量的实验需要更多流量 由于s根据指标和分流方式会有不同,我们提供一个大小衡量的工具,而不是实验者自己计算。实验者指定指标和他们希望能检测到的指标变化量,分流方式(基于cookie取模还是随机)和分流(条件,只用日语流量)。工具会告诉实验者他们的实验需要多少流量才能让他们检测到具有统计学意义的指标变化。实验者可以轻易探索需要什么实验大小。有了这个标准工具,我们可以在运行实验前确信恰当的大小。 为了为我们的大小衡量工具获取数据,我们不断进行一些均衡性试验(uniformity trials),比如,我们运行足够多的对照组和aa实验,并使用实验尺寸和分流方式。我们可以使用这些结果来经验性的测量我们指标的自然方差(natural variance),测试我们计算置信区间的准确性。

5.2.2 触发(Triggering)、日志(Logging) & 反事实(Counter-factuals)

分流指实验中的一部分流量。但是,实验可能实际上不改变所有分到流量的服务内容,而是只在部分分到的流量上触发(trigger)。举个例子,一个测试在搜索栏上展示天气的实验分到了所有的流量,但是只在这些查询的子集中展示天气信息,这个子集叫做触发集合(trigger set)。 通常,我们不能在触发集合上分流,因为决定哪些请求会触发变更需要运行时的额外处理。对额外处理的需要是不能作为条件实现的原因(信息可用的时机太晚了)。因此,记录(log)事实(factual)(实验触发时)和反事实(counter-factual)(实验本应触发when the experiment would have triggered)很重要。反事实被记录在对照组中。举个例子,在上述提到的实验中,事实(展示天气信息)被记录在实验组,反事实被记录在对照组,天气信息本应在这个查询中展示但是没有展示,因为他是对照组。这种日志对衡量实验大小和分析实验都很重要,因为包含了没有变更服务内容的请求会对测量实验影响产生稀释。限制在触发集合让实验者更精准地测量它们的影响。另外,通过聚焦在触发集上的影响,需要的流量减少了,因为有效大小取决于我们希望检测到的影响的平方的倒数(1/θ^2)

5.2.3 前&后周期

一个前周期(pre-period)是开始实验之前使用相同分流但不做任何变更。后周期(post-period)是一样的东西,但是实验之后。这两个阶段是类似的,都是对比对照组和对照组,但是使用的是实验的流量。预周期有助于确认实验分流与对照组是可比的且没有任何问题,比如垃圾邮件和机器人。后周期有利于确认运行试验是否有学习效果(learned effect)。这些技术只用于cookie取模和uid取模的实验。

5.3 快速分析

虽然上述的框架和工具已经让我们能同事进行许多实验,但实验流程不会同时加快,除非解决数据分析问题。完整的讨论实验分析工具超出了这篇论文的范围,但我们会在这里简短讨论主要的设计目标。 分析工具的主要目标是提供精准指标给实验者评估实验效果。在谷歌,我们没有组合多个指标,而是检查每一个指标用以确认用户体验的改变(用户解析页面的速度,点击的移动方式)。注意实时流量只能测量发生了什么,而不是为什么发生变化。 除了精确性和完整性,实验分析工具的其他关键设计目标包括:

  • 正确计算并展示置信区间(confidence intervals):实验者需要知道实验是否有足够的流量(置信区间太宽)或观察到的变化是否有统计学意义。我们调研了许多方法计算精准的置信区间,但是完整的讨论超出了这篇论文的范围,我们考虑了delta method approaches和一种经验主义方法来计算置信区间:将实验分割成子集并估计子集的方差。还要注意,观察多个指标和实验时必须小心,因为检查足够多,有些数值将会随机展示得显著。
  • 好的UI:使用和理解起来需要很简单。图表很有效;即使是线性图这种简单的图表可以帮助可视化,如果聚合更改是一致的。UI应该指出不正确的对比(跨层对比实验),更改对比的实验和更改时间等都必须足够简单
  • 对切片的支持:聚合数字常常具有误导性,正如改变可能不是因为指标变化(CTR),而是由于一个混合的变化(更多商业查询)。These Simpson’s paradoxes are important to spot and understand, as Kohavi mentions [4]
  • 可拓展性:必须能简单增加自定义指标,特别是新的特性,已存在的指标可能不足。

有唯一的工具用以为实验提供准确指标意味着我们有一致的实现,比如过滤器(移除潜在的机器人和垃圾邮件),这样不同的团队测量CTR是可比的。单个工具效率更高,因为只计算一次并展示给用户,而不是每个实验者分别计算

5.4 教育

虽然重叠框架和配套工具实现了技术目标:更多、更好、更快的实验,我们还需要解决人的问题。教育对促进健壮的实验是必要的。在谷歌,两个过程用于确保实验设计良好,实验结果得到理解和传播。

5.4.1 实验委员会(Experiment Council)

第一个过程我们称之为实验委员会,它由一群工程师组成,他们审查实验者提交到的实验清单。问题包括:

  • 基本实验特征(实验测试什么,假设是什么)
  • 实验构建(改变哪个参数,实验或一组实验测试什么,在哪一层)
  • 实验分流和触发器(用什么分流方式,条件是什么,什么情况下触发)
  • 实验分析(关注什么指标,需要观察到多大的指标变化)
  • 实验大小和持续时间(给定的实验流量是否足以检测到想要的指标变化,是否具有统计效果)
  • 实验设计(前后周期是否恰当,反事实记录是否正确)

第一次使用的实验者了解恰当的实验设计、大小设计、实验背后的技术细节。再来的实验者发现清单足够轻量但有效。此外,该过程是传播最佳实践的有效方法(促进实验的新工具、新指标等)。清单托管在web应用上,这对审计、存档和教育都很有用:新的实验者可以通过阅读过去的清单来理解问题。

5.4.2 解释数据

我们实施的另一个过程是讨论,实验者带着他们的实验数据和专家进行讨论。讨论的目标是:

  • 确保实验结果是有效的。有些时候,即使有实验委员会,有些地方的实现也会出错或发生意想不到的事情。在这些情况下,讨论和其他一样像是一个调试的过程。让专家熟悉整个程序的堆栈、日志、实验框架、指标和分析工具是很关键的
  • 有了有效的数据,确保所查看的指标是完整的,以便理解发生了什么。为了更全面的理解实验的影响,可以采用其他方法对数据切分和度量变化。有些实验非常复杂,需要多次跟进。
  • 给出所有结果,讨论并商定整个实验对用户体验产生积极还是消极的影响,那么决策者可以使用这些数据来确定是否发布这些变更,给出可能的改进意见,或者放弃

讨论会对实验者更好的学习如何解释实验非常有用。有经验的实验者逐渐不犯同样的错误,可以预见需要做哪些分析以获得更全面的理解。讨论会长期开放,那么未来的实验者可以在实验前加入学习。实验将文档化,那么我们能有一个知识仓库。

  1. 结果

我们在2007年3月首次发布我们的重叠实验框架。最后,整个系统是成功的,我们完成了设计目标:运行更多、更好、更快的实验

6.1 更多

我们从几个方面来说明我们在运行更多实验上的成功:一段时间内有多少实验在运行,这些实验产出多少发布结果,多少人运行实验(图5)。对于实验数量,注意我们包括了发布层的实验。对于独立个体的数量,许多实验有多个负责人或者是团队邮件清单,这些都包含在其中。不幸的是,我们没有简单的方法说明多少负责人是非工程师,但是这个数量在不断增加。对于发布的数量,我们只能展示重叠实验发布后的数量。在重叠实验之前,我们使用其他机制来发布变更,在重叠实验后,我们任然使用其他方式来发布变更,但是减少频率。所有图表的y轴都因为保密性而省略(都是线性的),但是趋势很清晰的表明我们实现了更多实验,更多发布,更多人使用整个系统运行实验。

6.2 更好

另一个测量我们整个系统、工具和教育成功的是我们运行的实验是否比以前更好。我们只有一些传闻的数据,但是我们是实验委员会、讨论会的成员,在系统发布前和发布后看过许多实验。我们观察到的是:

  • 错误的实验更少了,尽管我们仍然会遇到日志问题(反事实)或奇怪的错误/失败案例

  • 更少被遗忘的实验(开始实验的人忘了分析数据)

  • 更少关于“你怎么测量CTR” 或 “你使用了什么过滤器”这些讨论,有了统一的分析工具,讨论可以集中在分析指标而不是确认正确定义和计算指标的方法

  • 更好的明确的检查,通过前周期,确保分到实验的流量没有问题

    虽然理想情况是将更少变成没有,整体上看设计、实现、分析中的错误更少了,有更多的人做更多的实验

6.3 更快

最后一个衡量我们整个系统成功的是我们是否获得数据更快,做决策更快。在速度上,我们也没有经验数据,但是我们可以讨论关于实验速度的普遍观点。实验分为以下阶段:

  • 实现一个新功能并测试它。这是最慢的一个部分,因此我们构建了其他工具以加快原型开发和测试,将实验和生产版本分割开
  • 推送一个新的实验去服务已有的功能。这个阶段需要分钟到小时来创建实验,这取决于参数的复杂度,可忽略不计的预提交检查的时间(秒到分钟级),然后需要一段相似的时间审查。推送数据的时间取决于程序,但一般在1~2小时或一天
  • 运行实验取决于大小和获得可统计数据的时间。我们感知到几个指标发生了什么一般在实验开始的几小时内,整个持续时间取决于迭代次数、大小和紧急度
  • 分析实验的时间是变量。大多数场景下,非自定义分析最常见,或者是只需要对分析工具进行少量拓展。在这些场景下,分析非常快,一般天级别。但是,自定义分析也是存在的,这种场景所需的时间非常不确定

整体上,最耗时间的点是开发阶段,前周期的等待和自定义分析。这些是我们仍然需要优化的。

  1. 结论