机器学习43条军规:解密谷歌机器学习工程最佳实践(下)
作者: 张相於
接上篇: 机器学习43条军规:解密谷歌机器学习工程最佳实践(上)
Human Analysis of the System
在更进一步之前,我们需要了解一些机器学习课程上不会教你的内容:如何观察分析模型,并改进它。用作者的话说,这更像是一门艺术 ,但仍然有一些规律可循。
Rule #23: You are not a typical end user.
- 规则23:你不是一个典型的终端用户。
这条规则的中心思想是说,虽然吃自己的狗食是必要的,但也不要总是从工程师的角度来衡量模型的好坏。这不仅可能不值当,而且可能看不出问题。所谓不值当,是因为工程师的时间太贵了,这个大家都懂;而所谓看不出问题,是因为工程师自己看自己开发的模型,容易看不出问题,所谓“不识庐山真面目”。
所以作者认为合理的方法是让真正的终端用户来衡量模型或产品的好坏。要么通过线上ABTest,要么通过众包的方式来做。
Rule #24: Measure the delta between models.
- 规则24:离线衡量模型之间的差异。
原文没有说是离线,但我通过上下文理解他说的应该是离线。这一条规则说的是新模型在上线之前,需要先和老模型做差异对比。所谓差异对比,指的是对于同样的输入,新旧两个模型给出的结果是否差异足够大。例如对于同一个query,两个排序模型给出的差异是否足够大。如果离线计算发现差异很小,那也没必要上线测试了,因为上线后差异肯定也大不了。如果差异比较大,那么还需要看差异是不是好的差异。通过观察差异可以得知新模型究竟对数据产生了什么影响,这种影响是好是坏。
当然,这一切的前提是你需要有一个稳定的对比系统, 起码一个模型和他自己对比的话差异应该非常小,最好是零差异。
Rule #25: When choosing models, utilitarian performance trumps predictive power.
- 规则25:当选择模型时,实用性指标比预测能力更重要。
这是一条很有用的经验。虽然我们训练模型时objective一般都是logloss,也就是说实在追求模型的预测能力。但是我们在上层应用中却可能有多种用途,例如可能会用来排序,那么这时具体的预测能力就不如排序能力重要;如果用来划定阈值然后跟根据阈值判断垃圾邮件,那么准确率就更重要。当然大多数情况下这几个指标是一致的。
除了作者说的这一点,还有一种情况是需要特别注意的,那就是我们在训练时可能会对样本做采样,导致得到的预测值整体偏高或偏低。如果这个预测值是用来直接排序的,那么这个变化关系不大,但如果有其他用处,例如和另外的值相乘,典型的如广告场景下的CTR*bid,或者电商推荐排序下的CTR*CVR,在这类场景下,预测值本身的准确性也很重要,就需要对其进行校准(calibrate),使其与采样前的样本点击率对齐。
Rule #26: Look for patterns in the measured errors, and create new features.
- 规则26:在错误中发现模式,并创建新特征。
这算是一种用来提升模型效果的通用思路。具体来说,指的是观察训练数据中模型预测错误的样本,看看是否能够通过添加额外特征来使得这条样本被模型预测正确。之所以使用训练集中的数据,是因为这部分数据是模型已经试图优化过的,这里面的错误,是模型知道自己搞错了,目前学不出来的,所以如果你给它足够好的其他特征,它或许就能把这条样本学对了。
一旦发现错误的模式,就可以在当前系统之外寻找新的特征。例如,如果你发现当前系统倾向于错误地把长文章排到后面,那么就可以加入文章长度这一特征,让系统去学习文章长度的相关性和重要性。
Rule #27: Try to quantify observed undesirable behavior.
- 规则27:尽量将观测到的负面行为量化。
如果在系统中观察到了模型没有优化到的问题,典型的例如推荐系统逼格不够这种问题,这时应该努力将这种不满意转化为具体的数字,具体来讲可以通过人工标注等方法标注出不满意的物品,然后进行统计。如果问题可以被量化,后面就可以将其用作特征、objective或者metric。整体原则就是“先量化,再优化”。
多说一句,这里的优化,不一定是用模型来优化,而是指的整体优化。比如推荐系统逼格这种问题,模型可能很难优化,但是只要能量化出来,就可以通过其他方法来尽量减少,例如单独去学习有逼格物品的特征,或者在召回阶段进行一定倾斜。
Rule #28: Be aware that identical short-term behavior does not imply identical long-term behavior.
- 规则28:要注意短期内观察到的类似行为不一定会长期存在。
假设你搞了个系统,通过学习每个具体的文档ID和query ID,计算出了每个query下每个文档的点击率。通过离线对比和ABTest,你发现这个系统的行为和当前系统的行为一毛一样,而这个系统又更简单,所以你就把这个系统上线了。后面你会发现这个系统在任何query下都不会给出任何新的文档。奇怪吗?一点都不奇怪,因为你只让它记住了之前的历史数据,它对新数据没有任何信息。
所以唯一能够衡量一个系统是否长期有效的方法,就是让它使用该模型在线上时收集到的真实数据上进行训练。当然这有难度。
往大了说,作者这条规则其实说的是个系统或者模型的泛化能力,如果一个系统或者模型不能对新数据做出很好的预测,那么无论他的离线表现如何,都不能代表它的真正能力。再换个角度来看,一个系统必须具有持续学习适应新数据的能力,才能是一个合格的机器学习系统,否则就只是个学舌的鹦鹉。
Training-Serving Skew
训练和服务之间的差异问题时一个大话题,主要原因包括训练时与服务时数据获取方式不同、训练时与服务时数据分布不同以及模型和算法之间的反馈循环等。作者说这种差异在G家的多条产品线上出现过,都产生了负面影响。但要我说这绝对不仅仅是谷歌的问题,谷歌绝对是做的比较好的了,各种中小厂里面这种问题只多不少,所以这部分的经验是非常宝贵的。解决这类问题的核心是对系统和数据的变化进行监控,确保一切差异都在监控之内,不会悄悄进入系统。
Rule #29: The best way to make sure that you train like you serve is to save the set of features used at serving time, and then pipe those features to a log to use them at training time.
- 规则29:保证服务与训练一致性的最好方法是将服务时的特征保存下来,然后通过日志将特征喂到训练过程中去。
这句话基本道出了保证差异最小化的核心套路。这种基于特征日志的方法可以极大提升效果,同时能够减少代码复杂度。谷歌的很多团队也正在往这种做法上迁移。
Rule #30: Importance weight sampled data, don’t arbitrarily drop it!
- 规则30:对采样样本做重要性赋权,不要随意丢弃!
这是作者唯一用了感叹号的一条,可想而知背后的辛酸。当我们有太多训练数据时,我们会只取其中的一部分。但这是错误的。正确的做法是,如果你给某条样本30%的采样权重,那么在训练时就给它10/3的训练权重。通过这样的重要性赋权(importance weight),整个训练结果的校准性(calibration)就还能够保证。
多说一句,这个校准性非常的重要,尤其对于广告系统,或者多个预测值相加或相乘来得到最终结果的系统。如果单个值没有校准,偏低或偏高,那么在相乘或相加之后其含义就会不正确。如果直接使用模型预测值进行排序,校准性就没那么重要,因为校准性不会影响排序,只会影响具体的值。
Rule #31: Beware that if you join data from a table at training and serving time, the data in the table may change.
- 规则31:如果你在训练时和服务时都在join一张表,那么要注意这张表的数据可能会发生变化。
比如说某张表里存着一些文档的特征,你在离线训练之前要去这个表里取这些特征用来训练,但这里就有个风险,那就是这个表里的数据在你离线取的时候和在线服务的时候数据不一样,发生了变化。最好的解决方式就是在服务端将特征记录在日志中,这样能保证数据的一致性。或者如果这张表的变化频率比较低,也可以考虑对其做小时级或天级备份,以此来减少这种差异。但要记住这种方法并不能彻底解决这个问题。
Rule #32: Reuse code between your training pipeline and your serving pipeline whenever possible.
- 规则32:尽量在训练pipeline和服务pipeline之间复用代码。
训练一般是离线批量进行的,而服务则是在线流式进行的,这两者之间虽然在处理数据的方式上存在着较大差异,但仍然有很多代码可以共享。这些代码的共享可以从代码层面介绍训练和服务之间的差异。换句话说,日志记录特征是从数据角度消除差异,那么代码复用就是从代码角度消除差异,双管齐下,效果更好。
Rule #33: If you produce a model based on the data until January 5th, test the model on the data from January 6th and after.
- 规则33:如果训练数据是1月5日之前的,那么测试数据要从1月6日开始。
这条规则的主要目的是让测试结果与线上结果更加接近,因为我们在使用模型时就是在用服务当天之前的数据训练,然后来预测当天的数据。这样得到的测试结果虽然可能会偏低,但却更加真实。
Rule #34: In binary classification for filtering (such as spam detection or determining interesting emails), make small short-term sacrifices in performance for very clean data.
- 规则34:在为过滤服务的二分类问题中(例如垃圾邮件过滤),可以为了干净的数据牺牲一些短期效果。
在过滤类的任务中,被标记为负的样本是不会展示给用户的,例如可能会把75%标记为负的样本阻拦住不展现给用户。但如果你只从展示给用户的结果中获取下次训练的样本,显然你的训练样本是有偏的。
更好的做法是使用一定比例的流量(例如1%)专门收集训练数据,在这部分流量中的用户会看到所有的样本。这样显然会影响线上的真实过滤效果,但是会收集到更好的数据,更有利于系统的长远发展。否则系统会越训练越偏,慢慢就不可用了。同时还能保证至少过滤掉74%的负样本,对系统的影响也不是很大。
但是如果你的系统会过滤掉95%或者更多的负样本,这种做法就不那么可行了。即使如此,为了准确衡量模型的效果,你仍然可以通过构造一个更小的数据集(0.1%或者更小)来测试。十万级别的样本足够给出准确的评价指标了。
Rule #35: Beware of the inherent skew in ranking problems.
- 规则35:注意排序问题中固有的数据偏置。
当新的排序算法对线上排序结果产生了重大改变时,你其实是改变了算法将来会看到的数据。这时这种偏置就会出现。这种问题有以下几种方法来解决,核心思想都是更偏重模型已经看到过的数据。
- 对覆盖更多query(或类似角色,根据业务不同)的特征给予更强的正则化。这样模型会更偏重只覆盖一部分样本的特征,而不是泛化性特征。这样会阻止爆品出现在不相关query的结果中。
- 只允许特征取正的权重值。这样任何好特征都会比“未知”特征要好。
- 不要使用只和文档相关的特征。这是第一条的极端情况,否则会导致类似哈利波特效应的情况出现,也就是一条在任何query下都受欢迎的文档不会到处都出现。去除掉只和文档相关的特征会阻止这种情况发生。
Rule #36: Avoid feedback loops with positional features.
- 规则36:使用位置特征来避免反馈回路。
大家都知道排序位置本身就会影响用户是否会对物品产生互动,例如点击。所以如果模型中没有位置特征,本来由于位置导致的影响会被算到其他特征头上去,导致模型不够准。可以用加入位置特征的方法来避免这种问题,具体来讲,在训练时加入位置特征,预测时去掉位置特征,或者给所有样本一样的位置特征。这样会让模型更正确地分配特征的权重。
需要注意的是,位置特征要保持相对独立,不要与其他特征发生关联。可以将位置相关的特征用一个函数表达,然后将其他特征用另外的函数表达,然后组合起来。具体应用中,可以通过位置特征不与任何其他特征交叉来实现这个目的。
Rule #37: Measure Training/Serving Skew.
- 规则37:衡量训练和服务之间的差异。
整体来讲有多种原因会导致这种差异,我们可以将其进行细分为以下几部分:
- 训练集和测试集之间的差异。这种差异会经常存在,而且不一定是坏事。
- 测试集和“第二天”数据间的差异。这种差异也会一直存在,而这个“第二天”数据上的表现是我们应该努力优化的,例如通过正则化。这两者之间差异如果过大,可能是因为用到了一些时间敏感的特征,导致模型效果变化明显。
- “第二天”数据和线上数据间的差异。如果同样一条样本,在训练时给出的结果和线上服务时给出的结果不一致,那么这意味着工程实现中出现了bug。
ML Phase III: Slowed Growth, Optimization Refinement, and Complex Models
一般会有一些明确的信号来标识第二阶段的尾声。首先,每月的提升会逐步降低。你开始在不同指标之间做权衡,有的上升有的下降。嗯,游戏变得有趣了。既然收益不容易获得了,机器学习就得变得更复杂了。
在前两个阶段,大部分团队都可以过得很开心,但到了这个阶段,每个团队都需要找到适合自己的路。
Rule #38: Don’t waste time on new features if unaligned objectives have become the issue.
- 规则38:如果objective没有达成一致,不要在新特征上浪费时间。
当系统整体达到一个稳定期,大家会开始关注机器学习系统优化目标以外的一些问题。这个时候,目标就不如之前那么清晰,那么如果目标没有确定下来的话,先不要在特征上浪费时间。
Rule #39: Launch decisions are a proxy for longterm product goals.
- 规则39:上线决策是长期产品目标的代理。
这句话读起来有点别扭,作者举了几个例子来说明,我觉得核心就是在讲一件事情:系统、产品甚至公司的长远发展需要通过多个指标来综合衡量,而新模型是否上线要综合考虑这些指标。所谓代理,指的就是优化这些综合指标就是在优化产品、公司的长远目标。
决策只有在所有指标都在变好的情况下才会变得简单。但常常事情没那么简单,尤其是当不同指标之间无法换算的时候,例如A系统有一百万日活和四百万日收入,B系统有两百万日活和两百万日收入,你会从A切换到B吗?或者反过来?答案是或许都不会,因为你不知道某个指标的提升是否会cover另外一个指标的下降。
关键是,没有任何一个指标能回答:“五年后我的产品在哪里”?
而每个个体,尤其是工程师们,显然更喜欢能够直接优化的目标,而这也是机器学习系统常见的场景 。现在也有一些多目标学习系统在试图解决这种问题。但仍然有很多目标无法建模为机器学习问题,比如用户为什么会来访问你的网站等等。作者说这是个AI-complete问题,也常被称为强AI问题,简单来说就是不能用某个单一算法解决的问题。
Rule #40: Keep ensembles simple.
- 规则40:ensemble策略保持简单。
什么叫简单的ensemble?作者认为,只接受其他模型的输出作为输入,不附带其他特征的ensemble,叫做简单的ensemble。换句话说,你的模型要么是单纯的ensemble模型,要么是普通的接收大量特征的基模型。
除了保持简单,ensemble模型最好还能具有一些良好的性质。例如,某个基模型的性能提升不能降低组合模型的性能。以及,基模型最好都是可解释的(例如是校准的),这样基模型的变化对上层的组合模型来说也是可解释的。同时,一个基模型预测概率值的提升不会降低组合模型的预测概率值。
Rule #41: When performance plateaus, look for qualitatively new sources of information to add rather than refining existing signals.
- 规则41:当效果进入稳定期,寻找本质上新的信息源,而不是优化已有的信号。
你加了一些用户的人口统计学特征,你加了一些文档的文字特征,等等,但是关键指标上的提升还不到1%。现在咋整?
这时就应该考虑加一些根本上不同的特征,例如用户再过去一天、一周看过的文档历史,或者另外一个数据源的数据。总之,要加入完全不同的维度的特征。此外也可以尝试使用深度学习,但同时也要调整你对ROI的预期,并且要评估增加的复杂度换来的收益是否值得。
Rule #42: Don’t expect diversity, personalization, or relevance to be as correlated with popularity as you think they are.
- 规则42:多样性,个性化或者相关性与流行度的相关性关系可能要比你想的弱很多。
多样性意味着内容或者来源的多样性;个性化意味着每个用户得到不一样的东西;相关性意味着一个query的返回结果相比其他query与这个query更相关。所以这三个指标的含义都是与普通不一样。
但问题在于普通的东西很难被打败。
如果你的衡量指标是点击、停留时长、观看数、分享数等等,你本质上是在衡量东西的 流行度 。有的团队有时会希望学到一个多样化的个性化模型。为此,会加入个性化特征和多样化特征,但是最后会发现这些特征并没有得到预期的权重。
这并不能说明多样性、个性化和相关性不重要。像前文指出,可以通过后续的处理来增加多样性或相关性。如果这时看到长期目标提升了,你就可以确定多样性/相关性是有用的。这时你就可以选择继续使用后续处理的方式,或者根据多样性和相关性直接修改要优化的objective。
Rule #43: Your friends tend to be the same across different products. Your interests tend not to be.
- 规则43:你在不同产品上的好友一般是一样的,但你的兴趣通常会不一样。
谷歌经常在不同产品上使用同样的好友关系预测模型,并且取得了很好的效果,这证明不同的产品上好友关系是可以迁移的,毕竟他们是固定的同一批人。但他们尝试将一个产品上的个性化特征使用到另外一个产品上时却常常得不到好结果。可行的做法是使用一个数据源上的原始数据来预测另外数据源上的行为,而不是使用加工后的特征。此外,用户在另一个数据源上的行为历史也会有用。
总结
从上面洋洋洒洒43条经验之谈中不难看出,大神作者认为,对于大多数机器学习应用场景来说,我们需要解决的问题大多数都是工程问题,解决这些工程问题需要的并不是复杂的理论,更多是对细节、架构、过程的仔细推敲和精致追求。而这些是我们非大神的普通人可以做到的,如果说大神做的是95分以上的系统,那么我们只要对工程架构、过程和细节做好足够的优化,我们也可以做出至少80分的系统。
End.
转载请注明来自36大数据(36dsj.com): 36大数据 » 机器学习43条军规:解密谷歌机器学习工程最佳实践(下)