White Paper of Machine Learning of Google

开发者的最佳机器学习实践指南

作者:雷锋网

谷歌白皮书原文地址:http://martin.zinkevich.org/rules_of_ml/rules_of_ml.pdf

编者按:此白皮书为谷歌总结的机器学习(ML)最优实践方法,浓缩了其多年技术积累与经验,尤其是 Youtube、Google Play 和 Google+ 等平台背后的 ML 算法开发、维护经历。谷歌于白皮书中总结了四十三条 ML 黄金法则,旨在帮助已经掌握了基础知识的开发者少走弯路。鉴于其珍贵程度与技术性,雷锋网逐条做了严格尊重原文的翻译。若你已学习过机器学习课程,抑或有开发 ML 模型的经验,那么应当具备足够的背景知识理解这篇文章。

术语

以下是对文中反复出现的术语的解释。

实例( Instance):做预测的对象。比如说,实例可以是一个网页,你想要把它分类为“关于猫”或者“与猫不相关”。

标记(Label):预测任务的答案。它既可以是机器学习系统生成的答案,也可以是训练数据中提供的正确答案(雷锋网注:比如监督学习中的人工标记)。举例来说,一个网页的标记可以是“关于猫”。

特征(Feature):预测任务中实例的属性。比如说,某网页可能有“包含关键词‘猫’”的特征

特征栏 (Feature Column):这是谷歌自创的术语,意为关联特征的集合。比如说,用户的所有可能居住国家的集合。一个样例的特征栏可以有一个或多个特征。特征栏可被看作是 VW 系统(微软、雅虎所用)中的命名空间,或者场( field)。

样例(Example):有标记的实例(具备特征)。

模型(Model):对预测任务的统计表达。你用样例训练模型,然后用模型做预测。

指标(Metric):你在意的数字。可被直接优化过,也可没有。

目标(Objective):你的算法试图优化的指标。

流水线(Pipeline):机器学习算法的基础设施;包括从前端收集数据,把它放入训练数据文档,训练一个或多个模型,以及把模型输出、产品化。

概览:

为了开发出好产品:

做机器学习这一行首先要摆正心态,你是一名(优秀的)工程师,不要拿专家的标准来要求自己。

事实上,你将要面对的大多数难题是工程问题(engineering problems)。即便是一个杰出的ML 专家,坐拥该级别才有的资源,其大多数收获也来自于特征而不是 ML 算法。所以,ML 开发的基本路线是:

保证可靠的端到端流水线

从制定合理的目标着手

用简单的方式,加入符合常识的特征

确保流水线始终可靠

该方法能帮你赚钱养家,并且让很多人满意。只有当无路可走、简单的技巧无法再起作用时,你才需要偏离该路线。但注意,提高复杂度会拖慢将来的产品发布。另外,当你穷尽了简单技巧,或许就到了登堂入室、探索 ML 最前沿技术的时候了。具体请看本文机器学习第三阶。

本文分为四个部分:

第一部分“1.0 做机器学习之前”,会帮你搞清楚,你创建机器学习系统的时机是否已经成熟。

第二部分“2.0 机器学习第一阶”是关于设置你的第一个流水线。

第三部分“3.0 机器学习第二阶”,关乎启动和重复,同时向流水线加入新特征。

最后一部分“4.0 机器学习第三阶”是关于达到瓶颈后怎么办。

43 条黄金法则列表:

  1. 对发布一个不含 ML 技术的产品,不要有顾虑

  2. 首先要设计和贯彻指标

  3. 在机器学习和复杂启发算法之间,选择前者

  4. 第一个模型要简单,把基础设施弄好

  5. 测试基础设施要与 ML 测试分开

  6. 复制流水线时当心数据遗落

  7. 把启发式(heuristics)变为特征,不然就对它们做外部处理

  8. 了解系统的时效性

  9. 在输出模型之前发现问题

  10. 于无声处听惊雷:注意没表现出来的故障

  11. 注意特征栏的维护者和文件

  12. 选择直接优化哪个目标时,不需要想太多

  13. 选择一个简单、可观察并且可归属(attributable)的指标来作为第一个目标

  14. 用可解释的模型开头,修补漏洞会更简单

  15. 用 policy layer(规则层)把垃圾信息过滤和质量排序分来

  16. 做好模型被推倒和重建的准备

  17. 直接以观察到的或报告的特征开始训练,而不是经过学习的特征

  18. 从不同的上下文环境中提取特征

  19. 尽量选择更具体的特征

  20. 以合理的方式组合、修改现有特征

  21. 通过线性模型学到的特征权重的数目,大致与数据量成正比

  22. 清理不需要的特征

  23. 你并不是一个典型的用户

  24. 版本之间存在对等差分(symmetric difference)

  25. 选择模型时,性能胜过预测能力

  26. 从误差中查找新模式、创建新特征

  27. 尝试量化观察到的异常行为

  28. 注意短期行为和长期行为的差别

  29. 确保训练和服务一样好的最直接办法是:保存服务时使用的特征,然后将这些特征导入日志,以便在训练中使用。

  30. 重视采样数据

  31. 注意表格中的数据可能改变

  32. 尽量在训练和服务流水线中复用代码

  33. 训练和测试的数据不能相同

  34. 在二进制分类过滤的应用场景中(例如垃圾邮件检测),不要为了纯净的数据做太大的性能牺牲

  35. 注意排序问题的固有偏差

  36. 避免具有位置特征的反馈回路

  37. 测量训练/服务偏差

  38. 如果目标之间不搭,并成为问题,就不要在新特征上浪费时间

  39. 模型发布决策是长期产品目标的代理

  40. 保证集成模型(ensemble)的简洁

  41. 当性能达到瓶颈,相比精炼现存信号,不如寻找新性质的信息源

  42. 不要期望多样性、个性化、相关性和受欢迎程度之间有紧密联系

  43. 不同产品中,你的朋友总是那一个,你的兴趣不会如此

1.0 做机器学习之前

  1. 对发布一个不含 ML 技术的产品,不要有顾虑

机器学习很酷,但要有数据。理论上,你可以把另一个相近课题的数据拿来用,调整下模型变成一个新产品。但这么做的实际效果,通常比简单的启发式算法(heuristics)还差。如果你认为机器学习能完成任务的 100%。那么启发式算法能帮你完成 50%。

比如说,若你为应用商店进行 app 排名,不妨直接利用下载率和装机量写个简单算法;若你在检测垃圾邮件,可以先把发送过垃圾邮件的地址过滤掉。也不要在人工编辑上有顾虑。如果机器学习对于你的产品不是必需的,那么在获得数据之前不要用它。

  1. 首先要设计和贯彻指标

在定义你的 ML 系统要做什么之前,要尽可能多得追踪你当前的系统。这出于以下原因:

在早期,获得系统用户的许可相对容易。

如果你认为有些东西在将来需要考虑,最好从现在起就收集历史数据。

如果你设计系统时考虑了指标的工具化( metric instrumentation),会省下将来的许多力气。你绝对不想为了指标而查找日志字符串。

有些东西会改变,有些不会。比如说,假设你想要直接优化每日活跃用户。但是,在你对系统的早期操作中,你也许会发现用户体验的大幅变化并不会显著改变这个指标。

Google+ 团队会衡量每次阅读的扩展数(expands per read)、分享、点赞、评论,以及每用户评论数、分享等等。然后他们利用这些数据计算发布消息的质量。另外要注意,能通过试验把用户分组并整合数据的试验框架非常重要,参考第 12 条。

通过更灵活地收集指标,你能用更大的视角观察系统。发现一个问题?添加一个指标来追踪它!对上一个发布版本的量化变动很兴奋?添加指标来追踪!

  1. 在机器学习和复杂启发算法之间,选择前者

一个简单的启发算法能帮助产品走向市场,而复杂启发算法难以维护。一旦你有了数据以及需要实现的目标的蓝图,就可以转去开发 ML。在大多数软件工程任务中,开发者需要不停更新开发方式,不管是启发式算法还是 ML 模型。你会发现后者更加容易更新维护(参考第 16 条)。

2.0 机器学习第一阶

2.1 你的第一条流水线

对于第一条流水线,关注你的系统基础设施。虽然,设想你将要做的种种 ML 应用很有趣;但如果你无法信任自己的流水线,你会很难搞清楚状况。

  1. 第一个模型要简单,把基础设施弄好

第一个模型为你的产品提供了最大的助力,所以它不需要花哨。而且你会遇到许多想象之外的基础设施问题。在你的新 ML 系统诞生之前,你需要决定:

如何获取学习算法的样例

对于你的系统,“好”、“坏”的定义是什么

如何把模型整合入应用。你可以实时应用模型,也可以在线下预计算模型,并把结果保存好。比如对网页预分类,然后在表格里保存结果。但有的任务可能需要对实时聊天信息进行分类。

选择简单的特征更容易保证:

这些特征正确应用于学习算法

模型学会合理的权重。

这些特征正确应用于服务器模型。

当你有了能可靠做到上述三点的系统,大部分的工作就已完成。简单模型提供给你基础的指标和行为,然后你可以用它们来测试更复杂的模型。有些团队把目标定为“中性”的首发——故意在首次发布不那么重视机器学习成果,以避免分心。

  1. 测试基础设施要与 ML 测试分开

要确保基础设施可测试,而且系统的学习部分都被包含在内,使得你能够测试所有相关物。特别是:

测试把数据导入算法。检查可填充的特征栏是不是空的。若条件允许,手工检查训练算法的输入。若可能,把流水线数据与其他地方作比较,比如 RASTA。

测试把数据导出训练算法。确保训练环境的模型与服务环境(serving environment)的模型产生同样的得分(详见第 37 条)。

ML 有不可预测的因素。所以一定要对生成训练、服务样例的代码进行测试;这样你可以在服务中载入、使用固定模型。另外,理解你的数据也十分重要。

  1. 复制流水线时当心数据遗落

我们经常复制现成的流水线来创建新流水线(例如 cargo cult 编程),但有时旧流水线遗落了新流水线需要的数据。举个例子, Google Plus What’s Hot(雷锋网按:社交软件 Google+ 的热门新闻版块) 的流水线会遗落旧帖子(因为它试图为新帖子排名)。我们复制该流水线,用于 Google Plus Stream(Google+ 流)。对于后者,旧帖子仍然有意义,但新流水线仍然会丢掉数据。

另一个常见的模式是只记录用户看过的数据。因此,当你需要对为什么用户没有看到某个信息进行建模,该数据完全没用——因为所有反例已经被丢掉了。Google Play 发生过一个类似的问题:当我们开发 Google Play 应用商城主页时,创建出的新流水线包含另外两个登录页面(Play Games Home and Play Home Home,游戏主页和家庭主页)的样例。但是,并没有能够对“样例来自于哪个主页”加以区分的特征。

  1. 把启发式(heuristics)变为特征,不然就对它们做外部处理

通常来讲,ML 试图解决的问题并不是什么新问题——一般有现成的排名、分类等各种系统。这意味着有一大堆规则和启发式算法可用。这些启发式能在你调整 ML 时起到帮助。你应该压榨出启发式算法的所有信息,这有两个原因:1. 到 ML 系统的过渡会更顺畅。2. 这些规则通常包含一大堆关于系统的直觉信息,你绝对不想把它们扔掉。有四种利用现成启发式算法的途径:

使用启发式算法预处理。如果该特征非常棒,那么这就是一个选择。举个垃圾邮件过滤器的例子,若发件人已经被加入黑名单,不要试图重新学习“加入黑名单”是啥意思。直接拦截该信息。该方法最适用于二分类任务。

创建特征。直接用启发式创建特征相当棒。比如说,如果你用启发式计算一个问题结果的相关度分值,你可以把该得分作为特征值。之后,你或许想用 ML 技术来操作数值(比如把数值转化为有限个独立值集合,或与其他特征合并),但却拿启发式生成的原始数值来开头。

挖掘启发式的原始输入。如果有面向 APP 的启发式把装机量、文字中字母数目和日期组合到一起,就得考虑把它们分开——把这些输入分开来学习。有些应用于整体的技巧可用在这里(详见第 40 条)。

修正标记。当你发现启发式抓取了标记中未包含的信息时,这是一个选择。举个例子,如果你试图最大化下载量,但却仍然想要高品质内容,那么或许最好的方案是把标记与 APP 的平均星星得分相乘。这里有很大的余地。请参考“2.3 你的第一个目标”部分。

请注意启发式为 ML 系统加入的复杂度。在新 ML 算法中加入旧启发式有助于平滑地过渡,但你需要考虑是否更简单的实现方式。

2.2 监测

总的来讲,养成处理警告(alerts)的好习惯,比如对每个提醒付诸行动,并且建立一个仪表页面(dashboard page)。

  1. 了解系统的时效性

当你的模型已经开发出来一天、一周、一季度了,它的效果分别会降低多少?该信息能帮助你理解维护任务的优先级。假设模型一天没更新,你就要损失 10% 的收入。那么你或许要考虑雇佣专人每天维护。许多广告服务系统每天都有需要处理的新广告,因此必须每日更新。再举一个例子,如果 Google Play 的搜索 ML 模型停止更新,一个月内就会造成很大的损失。Google+ What’s Hot(雷锋网注:热门推荐)的一些模型,并没有针对发布信息的身份确认机制,所以不需要频繁导出这些模型。但有身份确认机制的模型就需要非常频繁地更新。另外需注意,时效性会随时间而变化,尤其是为模型添加或移除特征栏的时候。

  1. 在输出模型之前发现问题

许多 ML 系统包含该步骤:输出模型到服务端。如果输出的模型有问题,会直接让用户们遇上。而这个环节之前的问题只是训练问题,不会影响用户体验。

在导出模型之前一定要检查,尤其要确保模型在给定数据上有合理的效果。另外,若你对数据有顾虑,不要输出该模型。许多开发团队会在模型输出前检查 ROC 曲线 (或 AUC) 下的区域。未输出的模型存在问题,可能只需要一封 email 提醒一下。但用户端模型出了问题,很可能需要你向上司、同事解释一整页。所以最好多花点时间,在影响到用户之前做到胸有成竹。

  1. 于无声处听惊雷:注意没表现出来的故障

这是一个多见于机器学习、而少见于其他系统的问题。设想一个不再更新的特定表格:机器学习系统会调整,其行为仍会有合理表现,但逐渐退化。有时候开发者会发现过期几个月的表格——这时,一个简单的更新所提高的性能,比该季度的所有发布新版本都要高。举个例子,对一个特征的取舍会因为执行情况的变化而变化:覆盖 90% 样例的特征栏可能突然降低到只覆盖 60%。Google Play 曾经就有一个过期了六个月的表格,单单更新那个表格就带来了 2% 的安装率提升。如果你对统计数据进行跟踪,并偶尔人工检查,就能减少这类失误。

  1. 注意特征栏的维护者和文件

如果系统很大、有许多特征栏,你需要知道谁创立、维护了每一个特征栏。如果你发现懂得特征栏的那个人要跳槽了,一定要确保团队里有还有人知道这些信息。虽然许多特征栏有描述名称,你仍然需要更详细的解释,知道它是什么、从哪里来、起什么作用。

2.3 你的第一个目标

你有许多关心的系统指标或度量,但 ML 算法通常只需要一个目标——算法试图优化的某个数字。这里,我要区别目标(objectives)和指标(metrics):指标是系统报告的任何数字,或许重要,或许不重要。详见第二条。

  1. 选择直接优化哪个目标时,不需要想太多

你想要赚钱,让用户满意,并且让地球更美好。有许多你关心的指标,你应该全部都去测量(见第二条)。但在 ML 初期,你会注意到它们全都有提升,即便是那些没有直接优化的也是如此。举个例子,假设你关注点击数、浏览时间和每日活跃用户。如果你优化点击数,你会看到浏览时间也在上升。

所以简简单单就好。当你能轻易地提高所有指标,不需要在不同指标之间的平衡上想太多。但也不要误解这条建议:别把目标与系统最终的健康混为一谈(详见第 39 条)。另外,如果你增加了直接优化的指标,但决定不予发布,或许有必要重新修订目标。

  1. 选择一个简单、可观察并且可归属(attributable)的指标来作为第一个目标

很多情况下你不知道真正的目标是什么——你以为你知道。但当你仔细观察数据,以及对旧系统和新 ML 系统进行分析,你意识到自己其实想要对原定目标进行修改。团队不同成员也经常无法在真正的目标上取得一致意见。ML 目标应当易于测量,并可作为“真正”目标的代理。所以最好采用简单的 ML 目标训练,然后考虑在这之上设一个 “policy layer”(规则层),允许你加入额外的逻辑(但愿是简单的逻辑)来做最终排名。

最容易建模的是,能被直接观察到、并且可归属于系统中某个行动的用户行为:

这个排名链接被点击了吗?

这个排名对象被下载了吗?

这个排名对象被 转发/回复/发 email 了吗?

这个排名对象被打分了吗?

这个显示的对象被标记为垃圾邮件/色情信息/侮辱性信息了吗?

一开始要避免对间接作用建模:

用户在第二天访问了吗?

用户的访问时间是多长?

每日活跃用户都是谁?

其实,间接作用是非常不错的指标,并且可在 A/B 测试和发布决定中使用。

最后,不要试图让 ML 搞懂:

用户对使用该产品满意吗?

用户对体验满意吗?

产品提升了用户的福祉了吗?

这如何影响公司的整体健康?

这些都很重要,但是极度困难。你应该用代理来替代:如果用户感到开心,他们会在页面停留更长时间。如果用户满意,他明天会再次访问。目前,当涉及到福祉和公司健康状态,把 ML 目标与产品本质和商业计划之间做关联需要人的判断。

  1. 用可解释的模型开头,修补漏洞会更简单

线性回归、逻辑回归、泊松回归(Poisson regression)直接被概率模型驱动,每个预测都可作为概率或期望值解释。这使得相比使用了目标、直接优化分类精度或排序效果的模型(zero­one 损失、各种 hinge 损失等等),它们修补漏洞更加简单。如果通过对比或检查产品系统,发现训练里的概率偏离了预测概率,就可能存在问题。

比如说,在线性回归、逻辑回归、泊松回归之中,有的数据子集里平均预期和平均标签相等(1-­moment 校准,或者普通校准 )。对于一个值要么是 0 要么是 1 的特征,三个特征值为 1 的样例集就会被校准。同样地,若某特征下所有样例的特征值都是 1,它们都会被校准。

对于简单的模型,处理反馈回路( feedback loops )更加容易。我们经常用这些概率预期来做决定:比如以期望值(点击概率/下载量等)为标准对发布消息进行降序排列。但要记住,当决定采用那个模型的时候,你的决定比给定模型数据的可能性( the likelihood of the data given the model )更加重要(参考第 21 条)。

  1. 用 policy layer(规则层)把垃圾信息过滤和质量排序分来

质量排序是一门高雅的艺术,而垃圾信息过滤是一场战争。对于使用你系统的人,你用来判断高质量消息的信号十分显而易见。然后,他们会据此调整他们的发布信息来获得这些属性。因此,你的质量排序应当专注于有信誉的内容——不应该让质量排序学习器退化到给垃圾信息高排名。同样的,重口味内容应当与质量排序分开。而垃圾信息过滤是另一回事了。你需要创建的特征会不断变化,对此要有心理准备。通常,你加入系统里的规则有些很显而易见(比如,若一个发布信息得到超过三个“垃圾信息”票数,不要恢复它)。任何学习到的模型需要至少每天更新。内容生产者的名誉会起到相当大的作用。

在某个层级,这两个系统的输出需要整合在一起。需要注意的是,在搜索结果里过滤垃圾信息,比过滤垃圾邮件要更加强力。 为了高质量的分类器而去除训练数据中的垃圾,已是行业标准。

3.0 机器学习第二阶段

3.1 特征工程

在进行机器学习相关实践的第一阶段,你要关注的主要问题包括以下三个方面:一是将训练数据导入系统,二是确定系统的重点关注指标,三是保证底层基础设施的稳定可靠。当这三个问题都确认无误,即已经搭建了一个端到端的可稳定运行的系统,并且针对系统本身和其中的每个单元都经过了严格测试,这时就可以进入第二阶段了。

应该说,第二阶段将更容易取得成绩。这一阶段会有许多显著的特征(feature)被导入系统,因此,导入并以直观的方式组合这些特征,将是本阶段涉及的主要任务。另外,本阶段也更适合多位工程师协同工作,共同对此前导入的训练数据进行整合和处理,推动所有的指标(metric)在本阶段取得持续性的上升。

  1. 做好模型被推倒和重建的准备

不要指望从头到尾只使用一个模型,也不要指望着某一结点之后就不用重建模型了,模型的推倒和重建是机器学习过程中的必修课。另外,每加入一个新特性都必须考虑是否会拉低模型的运行效率。目前,许多团队每三个月或一年就会新建一个模型。这里我们总结了一般情况下引发模型重建的三大原因:

1) 增加新的特征

2) 打算重组旧的特征,或对旧模型正则化

3) 修订建模目标

无论如何,创建模型时多想想并没有什么坏处:例如检查训练数据是否有更合理的组织形式,考虑当前的建模方式是否便于特征的修改和重组,当前的机器学习流水线(pipeline)是否便于创建副本并检验其正确率,以及是否可以创建两到三个副本并行运行等等。最后需要指出的是,并不一定非要在一个机器学习流水线中覆盖所有特征,在下一个版本中实现也是可行的。

  1. 直接以观察到的或报告的特征开始训练,而不是经过学习的特征

这一点建议或许存在一些争议,但的确能避免许多潜在的问题。这里经过学习的特征(learned feature)是指由外部系统(例如无监督的聚类系统)或模型本身(例如通过深度学习和因子模型)产生的特征。这两种情况虽然的确可以使用,但并不适合系统的第一个模型。

首先,在使用外部系统创建特征时必须要格外小心。因为外部系统的目标可能与当前系统并不相符,而且从外部系统更新当前系统的特征,其特定的含义也可能改变。

另一方面,因子模型和深度模型的主要问题是它们是非凸的(non-convex),因此它们无法保证可以最终找到或近似找到最优解,它们在每次迭代中产生的局部最小值都可能变化,而且目前无法评估这种变化对系统的影响是有益的还是有害的。通过创建没有深度特征的模型,你就可以获得很好的基准性能。在实现这一基准性能之后,你可以尝试更高阶的方法。

  1. 从不同的上下文环境中提取特征

通常情况下,机器学习只占到一个大系统中的很小一部分,因此你必须要试着从不同角度审视一个用户行为。比如热门推荐这一场景,一般情况下论坛里“热门推荐”里的帖子都会有许多评论、分享和阅读量,如果利用这些统计数据对模型展开训练,然后对一个新帖子进行优化,就有可能使其成为热门帖子。另一方面,YouTube上自动播放的下一个视频也有许多选择,例如可以根据大部分用户的观看顺序推荐,或者根据用户评分推荐等。总之,如果你将一个用户行为用作模型的标记(label),那么在不同的上下文条件下审视这一行为,可能会得到更丰富的特征(feature),也就更利于模型的训练。需要注意的是这与个性化不同:个性化是确定用户是否在特定的上下文环境中喜欢某一内容,并发现哪些用户喜欢,喜欢的程度如何。

  1. 尽量选择更具体的特征

在海量数据的支持下,即使学习数百万个简单的特征也比仅仅学习几个复杂的特征要容易实现。由于被检索的文本标识与规范化的查询并不会提供太多的归一化信息,只会调整头部查询中的标记排序。因此你不必担心虽然整体的数据覆盖率高达90%以上,但针对每个特征组里的单一特征却没有多少训练数据可用的情况。另外,你也可以尝试正则化的方法来增加每个特征所对应的样例数。

  1. 以合理的方式组合、修改现有的特征

目前有多种方法组合、修改现有的特征,由于本文以Google工具为背景,因此在这里推荐两种TensorFlow框架已实现好的方法:“离散化”(discretizations)和“交叉”(crosses)。

离散化主要包含提取连续特征和从连续特征中创建离散特征两个部分。比如对于年龄这一连续的特征,你就可以创建这样的离散特征:当年龄小于18时结果为1,或者当年龄介于18-35之间时为1,等等。另外,不要过分考虑直方图中基本分位数的问题。

在TensorFlow的术语中,特征栏是一组相似的特征,比如{男性,女性},{美国,加拿大,墨西哥}等。这里的交叉是指将两个或多个特征栏合并,例如{男性,女性}×{美国,加拿大,墨西哥}的结果就是一个交叉(a cross),也就构成了一个新的特征栏。假设你利用TensorFlow框架创建了这样一个交叉,其中也就包含了{男性,加拿大}的特征,因此这一特征也就会出现在男性加拿大人的样例中。需要注意的是,交叉方法中合并的特征栏越多,所需要的训练数据量就越大。

如果通过交叉法生成的特征栏特别庞大,那么就可能引起过拟合。例如,假设你正在进行某种搜索,并且在查询请求和文档中都具有一个包含关键字的特征栏。那么假如你选择用交叉法组合这两个特征栏,这样得到的新特征栏就会非常庞大,它内部包含了许多特征。当这种情况发生在文本搜索场景时,有两种可行的应对方法。最常用的是点乘法(dot produc),点乘法最常见的处理方式就是统计查询请求和文档中共同的所有特征词,然后对特征离散化。另一个方法是交集(intersection),比如当且仅当关键词同时出现在文档和查询结果中时,我们才能获取所需的特征。

  1. 通过线性模型学到的特征权重的数目,大致与数据量成正比

许多人都认为从一千个样例中并不能得到什么可靠的训练结果,或者由于选择了某种特定的模型,就必须获取一百万个样例,否则就没法展开模型训练。这里需要指出的是,数据量的大小是和需要训练的特征数是正相关的:

1) 假如你在处理一个搜索排名问题,文档和查询请求中包含了数百万个不同的关键词,并且有一千个被标记的样例,那么你应该用上文提到的点乘法处理这些特征。这样就能得到一千个样例,对应了十几个特征。

2) 如你有一百万个样例,那么通过正则化和特征选择的方式就可以交叉处理文档和查询请求中的特征栏,这可能会产生数百万的特征数,但再次使用正则化可以大大减少冗余特征。这样就可能得到一千万个样例,对应了十万个特征。

3) 如果你有数十亿或数百亿个样例,那同样可以通过特征选择或正则化的方法交叉处理文档和查询请求中的特征栏。这样就可能得到十亿个样例,对应了一千万个特征。

对特征数和样例来说,这些统计学上的结论并不能给出一个具体的比例关系,但却可以从数量级上给出一些指导。另外,这里推荐用户依照第28条建议来选择具体使用哪些特征。

  1. 清理不需要的特征

如果你发现有些特征并没有在使用,而且将其与其他特征相结合之后也无法使用的话,就应该清理这些特征。应该保持系统的清洁,这样才能尽快尝试那些最有希望出结果的特征。而且,如果有必要,被删除的特征也可以随时找人加回来。

在考虑增删一个特征时,应该仔细排查其覆盖范围。例如你有一些个性化的特征,但只有大约8%的用户使用了该特征,那么删掉或添加这个特征就不会有太大影响。

另一方面,增删特征时也要考虑其对应的数据量。例如你有一个只覆盖了1%数据的特征,但有90%的包含这一特征的样例都通过了训练,那么这就是一个很好的特征,应该添加。

3.2 对系统的人工分析

在进入机器学习实践的第三阶段之前,关注一些课堂上不曾教授的问题也同样至关重要,比如如何检查一个模型并改进它。要说这一点是一门科学,反而不如说它是一种艺术,这里我们介绍几点反面模式(anti-patterns)。

  1. 你并不是一个典型的用户

这可能是让一个团队陷入困境的最简单的方法。虽然fishfooding(只在团队内部使用原型)和dogfooding(只在公司内部使用原型)都有许多优点,但无论哪一种,开发者都应该首先确认这种方式是否符合性能要求。另一方面,应该尽量避免不好的变化,但任何看起来合理的产品策略都应该被进一步验证,例如通过非专业人士在众包平台上的问卷调查,或者请目标用户来实测。

走外部验证渠道的原因来自两个方面:一是作为开发者,你太熟悉代码。例如你可能正在分析数据的某一方面而非全局,或者投入了太多的个人感情色彩,从而引发一些偏见。二是几位工程师开一个小时的讨论会议得到的评估结果,可能远比不上直接交给众包平台来得简单和有效。

如果你真的想要获取用户反馈,那么应该采用用户体验法(user experience methodologies)。 在流程早期创建用户角色(详情见Bill Buxton的《Designing User Experiences》一书),然后进行可用性测试(详情见Steve Krug的《Do not Make Me Think》一书)。这里的用户角色涉及创建假想用户。例如,假设你的团队成员都是男性,现在要针对35岁女性用户研发一款产品,那么基于目标群体创建一个假想角色,肯定比几位25-40岁的男性开发者闭门造车的效果要好。当然,让用户实测产品并观察他们的反应也是很不错的方法。

  1. 版本之间存在对等差分(symmetric difference)

将产品交付至用户之前,有时候最简单有效的做法就是评估当前版本与交付版本的差异。例如面对排名问题,你可以在两个版本间利用同一组样例进行测试,然后对比其结果。如果差异很小,那么意味着这个版本没问题。如果差异很大,那么就需要确认进行了哪些修改,为什么进行这些修改。通过查看哪些测试样例造成了这一差异,也有助于定性了解修改具体是怎样的。总之,目标是确保不同版本的模型之间的对等差分做到最小。

  1. 选择模型时,性能胜过预测能力

你的模型可能会被用来预测点击率,但更关键问题是:这种预测是应用在什么场景的。如果你用它来排列文档,那么最终排名的质量显然比预测本身更重要。如果你用它来排查垃圾邮件,那么识别精度显然更重要。大多数情况下,这两类功能应该是一致的,如果他们存在不一致,则意味着系统可能存在某种小增益。因此,假如一个改进措施可以解决日志丢失的问题,但却造成了系统性能的下降,那就不要采用它。当这种情况频繁发生时,通常应该重新审视你的建模目标。

  1. 从误差中查找新模式、创建新特征

假设你的模型在某个样例中预测错误。在分类任务中,这可能是误报或漏报。在排名任务中,这可能是一个正向判断弱于逆向判断的组。但更重要的是,在这个样例中机器学习系统知道它错了,需要修正。如果你此时给模型一个允许它修复的特征,那么模型将尝试自行修复这个错误。

另一方面,如果你尝试基于未出错的样例创建特征,那么该特征将很可能被系统忽略。例如,假设在谷歌Play商店的应用搜索中,有人搜索“免费游戏”,但其中一个排名靠前的搜索结果却是一款其他App,所以你为其他App创建了一个特征。但如果你将其他App的安装数最大化,即人们在搜索免费游戏时安装了其他App,那么这个其他App的特征就不会产生其应有的效果。

所以,正确的做法是一旦出现样例错误,那么应该在当前的特征集之外寻找解决方案。例如,如果你的系统降低了内容较长的帖子的排名,那就应该普遍增加帖子的长度。而且也不要拘泥于太具体的细节。例如你要增加帖子的长度,就不要猜测长度的具体含义,而应该直接添加几个相关的特征,交给模型自行处理,这才是最简单有效的方法。

  1. 尝试量化观察到的异常行为

有时候团队成员会对一些没有被现有的损失函数覆盖的系统属性感到无能为力,但这时抱怨是没用的,而是应该尽一切努力将抱怨转换成实实在在的数字。例如,当有些开发者认为在谷歌Play商店的搜索结果中显示了过多的其他App,就可以选择人工识别的方法剔除这些App(这时是可以选择人工标记数据的,因为相对较小的App查询可能占了很大一部分流量)。首先要确认你的问题是可量化的,然后才可以根据这些问题创建新的特征(features)、目标(objectives)或者指标(metrics)。总之规则是:先量化,再优化。

  1. 注意短期行为和长期行为的差别

假设你有一个新系统,它可以查看每个doc_id和exact_query,然后根据每个文档的每次查询行为计算其点击率。你发现它的行为几乎与当前系统的并行和A/B测试结果完全相同,而且它很简单,于是你启动了这个系统。但却没有新的应用显示,为什么?由于你的系统只基于自己的历史查询记录显示文档,所以不知道应该显示一个新的文档。

要了解一个系统在长期行为中如何工作的唯一办法,就是让它只基于当前的模型数据展开训练。这一点非常困难。

3.3 训练服务的偏差(Training­-Serving Skew)

这里训练服务偏差是指系统在训练时的性能表现和服务中的性能表现出现差别。造成这种差别的原因可能有如下三个方面:

1) 在训练和服务中的数据处理流水线不同;

2) 在训练和服务中使用了不同的数据;

3) 模型和算法间的反馈回路引起。

我们注意到谷歌的机器学习系统也存在训练服务偏差,而且会对性能产生负面影响。这里需要说明的是:最好的解决办法就是明确地监视它,使系统和数据的改变不至于引发潜在的偏差。

  1. 确保训练和服务一样好的最直接办法是:保存服务时使用的特征,然后将这些特征导入日志,以便在训练中使用

即使你不能对每个样例都这样做,做一小部分也比什么也不做好,这样你就可以验证服务和训练之间的一致性(见规则37)。在谷歌采取了这项措施的团队有时候会对其效果感到惊讶。比如YouTube主页在服务时会切换到日志记录特征,这不仅大大提高了服务质量,而且减少了代码复杂度。目前有许多团队都已经在其基础设施上采用了这种策略。

  1. 重视采样数据

当数据太多时,有些团队可能会选择丢弃一部分以减轻负担。这是一个明显的错误:历史经验证明在训练过程中丢弃数据将引发一系列问题(详见规则6)。当然,有时候的确可以丢弃数据,比如那些从未向用户显示过的,但重要性加权却是更好的选择。重要性加权意味着,如果你决定以30%的概率对样例X进行抽样,则权重应该是3/10。值得一提的是,使用重要性加权并不影响规则14中讨论的校准属性。

  1. 注意表格中的数据可能改变

假设你通过包含文件特征的表格(表格中还可能包含评论或点击的次数)加入文件的ID信息,那么需要注意表格中的特征可能会在训练和服务的不同时间点发生一些变化,造成模型对同一文档的预测也跟着改变。避免此类问题的最简单方法是在服务时记录特征(请参阅规则32)。如果表格的变化足够缓慢的话,你可以每天或每小时都记录一次表格以获得非常接近的数据,但需要注意的是,这并不能完全解决问题。

  1. 尽量在训练和服务流水线中复用代码

首先需要明确的一点是:批处理与在线处理不同。在线处理中,你必须在每个请求到达时及时处理(例如必须为每个查询单独查找);而在批处理中,你可以组合任务(例如建立联结)。类似的,可以将服务视为在线处理过程,而训练视为批处理过程,而其中有许多代码是可以复用的。比如说,你可以创建特定于系统的对象,其中的所有联结和查询结果都以人类可读的方式存储,错误也可以被简单地测试。然后,一旦在服务或训练期间收集了所有信息,你就可以通过一种通用方法在这个特定对象和机器学习系统需要的格式之间形成互通,训练和服务的偏差也得以消除。另外,由此推知:最好不要在训练和服务期间使用不同的编程语言(因为不同的语言间几乎无法复用)。

  1. 训练和测试的数据不能相同

一般来说,最好用不同的数据对模型进行训练和测试,例如你用1月5日之前的数据训练了一个模型,那么最好用1月6日之后的数据对模型展开测试。可能模型对新数据的性能表现不如训练数据,但也不会太糟。由于可能会产生每日效应(daily effects),因此你可能无法预测平均点击率或转化率,但曲线下方的面积(表示正面样例的分数高于反面样例的可能性)应该是接近的。

  1. 在二进制分类过滤的应用场景中(例如垃圾邮件检测),不要为了纯净的数据做太大的性能牺牲

一般在过滤应用场景中,反面样例并不会对用户展示。不过假如你的过滤器在服务过程中阻止了75%的反面样例,那么你可能需要从向用户显示的实例中提取额外的训练数据并展开训练。比如说,用户将系统认可的邮件标记为垃圾邮件,那么你可能就需要从中学习。

但这种方法同时也引入了采样偏差。如果改为在服务期间将所有流量的1%标记为“暂停”,并将所有这样的样例发送给用户,那你就能收集更纯净的数据。现在你的过滤器阻止了至少74%的反面样例,这些样例可以成为训练数据。

需要注意的是,如果你的过滤器阻止了95%或更多的反面样例,那这种方法可能就不太适用。不过即使如此,如果你想衡量服务的性能,可以选择做出更细致的采样(例如0.1%或0.001%),一万个例子足以准确地估计性能。

  1. 注意排序问题的固有偏差

当你彻底改变排序算法时,一方面会引起完全不同的排序结果,另一方面也可能在很大程度上改变算法未来可能要处理的数据。这会引入一些固有偏差,因此你必须事先充分认识到这一点。以下这些方法可以有效帮你优化训练数据。

1) 对涵盖更多查询的特征进行更高的正则化,而不是那些只覆盖单一查询的特征。这样,模型将偏好于那些基于一个或几个特定查询的特征,而不是所有的特征。这种方式可以有效防止那些最常见的查询结果泄漏到不相关的查询中。需要注意的是,这与一条更传统的建议相左:更多地正则化一些具有单一值的特征栏。

2) 只允许特征具有正向权重,这样一来就能保证任何好特征都会比未知特征合适。

3) 不要选择那些只处理文档数据的特征。例如,不管搜索请求是什么,即使一个给定的应用程序是当前的热门下载,你也不会想在所有地方都显示它。没有文档特征的话,这一点会很容易做到。

  1. 避免具有位置特征的反馈回路

内容的位置会显著影响用户与它交互的可能性。很明显,如果你把一个App置顶,那它一定会更频繁地被点击。处理这类问题的一个有效方法是加入位置特征,即关于页面中的内容的位置特征。假如你用正向特征来训练模型,那模型就会更偏向“1st-position”这类的特征。因而模型对其他因素的权重就会相应地减小,例如对“1st-position = true”这种样例。在服务的时候,你可以选择不提供任何位置特征的实例,或者为所有位置特征设置相同的初始值,因为在决定以怎样的顺序显示它们之前,你具有决策权。

需要注意的是,因为训练和测试的不对称性,所以最好在一些位置特征和模型之间保持一定的分离性,这一点很重要。让模型成为位置特征函数和其他特征函数的和,是理想的状态。比如说,最好不要交叉任何文档特征和位置特征。

  1. 测量训练/服务偏差

许多情况都会引起偏差,但它们大多可以分为如下三类:

1) 训练数据和测试数据的性能之间的差异。一般来说,这总是存在的,但并不会太严重。

2) 测试数据的性能与“第二天数据”(next-day data)之间的差异。同样,这也会一直存在。你可以不同程度地正则化以最大限度地提高第二天的性能(next-day performance)。然而,如果在测试数据和第二天数据之间存在很大的性能下降,这有可能意味着某些特征是时间敏感的,而且整个模型的性能也会跟着下降。

3) “第二天数据”和实时数据的性能之间的差异。如果你将模型应用于训练数据的样例,也应用于相同的服务样例,则它们应该给出完全相同的结果(详见规则5)。因此,这里的差异可能是指工程误差。

4.0 机器学习第三阶

4.1 减慢的增速,精细优化和复杂模型

第二阶段将要结束的时候,一定会有些信号。首先,你每月的收益开始降低。你开始要在指标之间做牺牲:一些试验中有的上升有的下降。从此情况变得更有趣。由于更难产生效益,机器学习不得不变得更复杂。

警告:这部分有许多开放式的实践法则。我们亲眼看着很多团队走过第一阶段和第二阶段的幸福期——一旦到达第三阶段,开发团队就不得不找出他们自己的路。

  1. 如果目标之间不搭,并成为问题,就不要在新特征上浪费时间

当达到度量瓶颈,你的团队开始关注 ML 系统目标范围之外的问题。如同之前提到的,如果产品目标没有包括在算法目标之内,你就得修改其中一个。比如说,你也许优化的是点击数、点赞或者下载量,但发布决策部分依赖于人类评估者。

  1. 模型发布决策是长期产品目标的代理

(雷锋网注:谷歌工程师在这里举了个例子)Alice 有一个关于降低安装预测的逻辑损失的想法。她加入一个特征。逻辑损失下降。当她实时测试时,安装量上升了。但在公司的发布会议上,有人指出每日活跃用户数降低了 5%。团队决定不发布该模型。Alice 很失望,但意识到发布决策取决于多个标准,其中只有一部分能够被 ML 直接优化。

事实是,现实世界并不是网络游戏:没有“攻击值”和“血量”来衡量产品的健康。团队需要利用收集的数据,来试图预测将来系统的表现会怎样。他们需要操心用户黏性、每日活跃用户、每月活跃用户、收入和广告主的收益。这些 A/B 测试中的指标,实际上只是长期目标的代理:让用户满意、增加用户、让合作方满意还有利润;即便这时你还可以考虑高品质、有使用价值的产品的代理,以及五年后一个繁荣的企业的代理。

做出发布决策变得容易的唯一一种情况是:所有指标都变好了(起码没有变差的)。如果团队在复杂 ML 算法和简单启发式算法之间有的选择;如果简单的启发式算法在这些指标上做得更好;那么应当选择后者。另外,所有指标数值并没有明确的排序。更具体的,考虑以下两种情形:

谷歌机器学习白皮书全解析 43条黄金法则(四)

雷锋网注:标题栏(自左至右)为试验,每日活跃用户以及每日收入

如果现有系统是 A ,团队不会想要转移到 B。如果现有系统是 B,团队也不会想要转到 A。这看起来与理性决策相抵触:但是,对指标变化的预期情形或许会发生,或许不会。因此任意一种改变都有相当大的风险。每一个指标覆盖了一些团队所关注的风险。但没有指标能覆盖团队的首要关切——“我的产品在五年后会怎样?”

另一方面,个体倾向于选择能直接优化的目标。大多数 ML 工具喜欢这样的环境。这样的环境下,一个能快速创建新特征的工程师能稳定输出一系列产品发布。有一种叫“多目标学习”(multi­objective learning)的机器学习开始解决这一问题。比如说,可以制定一个在每个指标上有下限的约束满意度问题(constraint satisfaction problem),然后优化指标的一些线性组合。但即便那时,也不是所有指标都能轻易表达为 ML 目标:如果一个文件被点击,或者 APP 被安装,这是因为有内容被展示出来。但搞清楚用户为什么访问你的页面就更加难了。如何预测一个页面在将来是否成功,是一项 AI­-complete 问题(雷锋网注:意味着完成它的难度相当于解决 AI 问题),与计算机视觉和自然语言处理一样难。

  1. 保证集成模型(ensemble)的简洁

接收原始特征、直接对内容排序的统一模型,是最容易理解、最容易修补漏洞的模型。但是,一个集成模型(一个把其他模型得分组合在一起的“模型”)的效果会更好。为保持简洁,每个模型应该要么是一个只接收其他模型的输入的集成模型,要么是一个有多种特征的基础模型,但不能两者皆是。如果你有单独训练、基于其它模型的模型,把它们组合到一起会导致不好的行为。

只用简单模型来集成:那些只把基础模型的输入作为输出、进行接收的模型。你或许想要为这些集成模型强加上属性。比如,基础模型生成得分的提高,不应该降低集成模型的分数。另外,如果连入模型在语义上可解释(比如校准了的)会更好,这样其下层模型不会与集成模型混淆。再者,强行让下层分类器预测的概率升高,不会降低集成模型的预测概率。

  1. 当性能达到瓶颈,相比精炼现存信号,不如寻找新性质(qualitatively)的信息源

你已经加入了一些关于用户的人口统计信息,还有文件中的词语。你经历了模板探索,和正则化(regularization)调参。但连续几个季度的发布,你都没有看到核心指标有超过 1% 的提升。现在怎么办?

你已经到了为不同寻常(雷锋网注:很不一样)的特征,创建基础设施的时候了。比如用户昨天、上周、去年检索的文档,或是另一种属性的数据。为你的公司使用维基数据(wikidata)实体或者一些内部的东西(比如谷歌的知识图,Google’s knowledge graph)。你或许需要使用深度学习。开始调整你对投资回报的期望,并作出相应努力。如同所有工程项目,你需要平衡新增加的特征与提高的复杂度。

  1. 不要期望多样性、个性化、相关性和受欢迎程度之间有紧密联系

一系列内容的多样性能意味着许多东西,内容来源的多样性最为普遍。个性化意味着每个用户得到属于他们自己的结果。相关性意味着一个特定检索的结果,对应它比对应其他检索更合适。因此,这三个属性的定义都有别于“标准”。

但标准更难被打败。

注意:如果你的系统在统计点击量、耗费时间、浏览数、点赞数、分享数等等,你事实上在衡量内容的受欢迎程度。有团队试图学习具备多样性的个性化模型。为个性化,他们加入允许系统进行个性化的特征(有的特征代表用户兴趣),或者加入多样性(表示该文档与其它返回文档有相同特征的特征,比如作者和内容),然后发现这些特征比他们预想的得到更低的权重(有时是不同的信号)。

这不意味着多样性、个性化和相关性就不重要。如同上个法则所指出的,你可以通过后处理来提高多样性或相关性。如果你看到长期目标的进步,那么你可以宣布在受欢迎程度之外,多样性和相关性是有价值的。你可以继续采用后处理,或者直接根据多样性或相关性修改目标。

  1. 不同产品中,你的朋友总是同一个,你的兴趣不会如此

谷歌的 ML 团队 常常把一个预测某产品联系紧密程度(the closeness of a connection in one product)的模型,应用在另一个产品上,然后发现效果很好。另一方面,我见过好几个在产品线的个性化特征上苦苦挣扎的团队。是的,之前看起来它应该能奏效。但现在看来它不会了。有时候起作用的是——用某属性的原始数据来预测另一个属性的行为。即便知道某用户存在另一个属性能凑效的历史,也要记住这一点。比如说,两个产品上用户活动的存在或许就自身说明了问题。

全文结束。感谢您对雷锋网(公众号:雷锋网)的支持。

雷锋网版权文章,未经授权禁止转载。详情见转载须知。