编者按:此白皮书为谷歌总结的机器学习(ML)最优实践方法,浓缩了其多年技术积累与经验,尤其是 YouTube、Google Play 和 Google+ 等平台背后的 ML 算法开发、维护经历。谷歌于白皮书中总结了四十三条 ML 黄金法则,旨在帮助已经掌握了基础知识的开发者少走弯路。本文上接雷锋网(公众号:雷锋网)"谷歌机器学习白皮书全解析 43条黄金法则(一)"
3.0 机器学习第二阶段
3.1 特征工程
在进行机器学习相关实践的第一阶段,你要关注的主要问题包括以下三个方面:一是将训练数据导入系统,二是确定系统的重点关注指标,三是保证底层基础设施的稳定可靠。当这三个问题都确认无误,即已经搭建了一个端到端的可稳定运行的系统,并且针对系统本身和其中的每个单元都经过了严格测试,这时就可以进入第二阶段了。
应该说,第二阶段将更容易取得成绩。这一阶段会有许多显著的特征(feature)被导入系统,因此,导入并以直观的方式组合这些特征,将是本阶段涉及的主要任务。另外,本阶段也更适合多位工程师协同工作,共同对此前导入的训练数据进行整合和处理,推动所有的指标(metric)在本阶段取得持续性的上升。
16. 做好模型被推倒和重建的准备
不要指望从头到尾只使用一个模型,也不要指望着某一结点之后就不用重建模型了,模型的推倒和重建是机器学习过程中的必修课。另外,每加入一个新特性都必须考虑是否会拉低模型的运行效率。目前,许多团队每三个月或一年就会新建一个模型。这里我们总结了一般情况下引发模型重建的三大原因:
1) 增加新的特征
2) 打算重组旧的特征,或对旧模型正则化
3) 修订建模目标
无论如何,创建模型时多想想并没有什么坏处:例如检查训练数据是否有更合理的组织形式,考虑当前的建模方式是否便于特征的修改和重组,当前的机器学习流水线(pipeline)是否便于创建副本并检验其正确率,以及是否可以创建两到三个副本并行运行等等。最后需要指出的是,并不一定非要在一个机器学习流水线中覆盖所有特征,在下一个版本中实现也是可行的。
17. 直接以观察到的或报告的特征开始训练,而不是经过学习的特征
这一点建议或许存在一些争议,但的确能避免许多潜在的问题。这里经过学习的特征(learned feature)是指由外部系统(例如无监督的聚类系统)或模型本身(例如通过深度学习和因子模型)产生的特征。这两种情况虽然的确可以使用,但并不适合系统的第一个模型。
首先,在使用外部系统创建特征时必须要格外小心。因为外部系统的目标可能与当前系统并不相符,而且从外部系统更新当前系统的特征,其特定的含义也可能改变。
另一方面,因子模型和深度模型的主要问题是它们是非凸的(non-convex),因此它们无法保证可以最终找到或近似找到最优解,它们在每次迭代中产生的局部最小值都可能变化,而且目前无法评估这种变化对系统的影响是有益的还是有害的。通过创建没有深度特征的模型,你就可以获得很好的基准性能。在实现这一基准性能之后,你可以尝试更高阶的方法。
18. 从不同的上下文环境中提取特征
通常情况下,机器学习只占到一个大系统中的很小一部分,因此你必须要试着从不同角度审视一个用户行为。比如热门推荐这一场景,一般情况下论坛里“热门推荐”里的帖子都会有许多评论、分享和阅读量,如果利用这些统计数据对模型展开训练,然后对一个新帖子进行优化,就有可能使其成为热门帖子。另一方面,YouTube上自动播放的下一个视频也有许多选择,例如可以根据大部分用户的观看顺序推荐,或者根据用户评分推荐等。总之,如果你将一个用户行为用作模型的标记(label),那么在不同的上下文条件下审视这一行为,可能会得到更丰富的特征(feature),也就更利于模型的训练。需要注意的是这与个性化不同:个性化是确定用户是否在特定的上下文环境中喜欢某一内容,并发现哪些用户喜欢,喜欢的程度如何。
19. 尽量选择更具体的特征
在海量数据的支持下,即使学习数百万个简单的特征也比仅仅学习几个复杂的特征要容易实现。由于被检索的文本标识与规范化的查询并不会提供太多的归一化信息,只会调整头部查询中的标记排序。因此你不必担心虽然整体的数据覆盖率高达90%以上,但针对每个特征组里的单一特征却没有多少训练数据可用的情况。另外,你也可以尝试正则化的方法来增加每个特征所对应的样例数。
20. 以合理的方式组合、修改现有的特征
目前有多种方法组合、修改现有的特征,由于本文以Google工具为背景,因此在这里推荐两种TensorFlow框架已实现好的方法:“离散化”(discretizations)和“交叉”(crosses)。
离散化主要包含提取连续特征和从连续特征中创建离散特征两个部分。比如对于年龄这一连续的特征,你就可以创建这样的离散特征:当年龄小于18时结果为1,或者当年龄介于18-35之间时为1,等等。另外,不要过分考虑直方图中基本分位数的问题。
在TensorFlow的术语中,特征栏是一组相似的特征,比如{男性,女性},{美国,加拿大,墨西哥}等。这里的交叉是指将两个或多个特征栏合并,例如{男性,女性}×{美国,加拿大,墨西哥}的结果就是一个交叉(a cross),也就构成了一个新的特征栏。假设你利用TensorFlow框架创建了这样一个交叉,其中也就包含了{男性,加拿大}的特征,因此这一特征也就会出现在男性加拿大人的样例中。需要注意的是,交叉方法中合并的特征栏越多,所需要的训练数据量就越大。
如果通过交叉法生成的特征栏特别庞大,那么就可能引起过拟合。例如,假设你正在进行某种搜索,并且在查询请求和文档中都具有一个包含关键字的特征栏。那么假如你选择用交叉法组合这两个特征栏,这样得到的新特征栏就会非常庞大,它内部包含了许多特征。当这种情况发生在文本搜索场景时,有两种可行的应对方法。最常用的是点乘法(dot produc),点乘法最常见的处理方式就是统计查询请求和文档中共同的所有特征词,然后对特征离散化。另一个方法是交集(intersection),比如当且仅当关键词同时出现在文档和查询结果中时,我们才能获取所需的特征。
21. 通过线性模型学到的特征权重的数目,大致与数据量成正比
许多人都认为从一千个样例中并不能得到什么可靠的训练结果,或者由于选择了某种特定的模型,就必须获取一百万个样例,否则就没法展开模型训练。这里需要指出的是,数据量的大小是和需要训练的特征数是正相关的:
1) 假如你在处理一个搜索排名问题,文档和查询请求中包含了数百万个不同的关键词,并且有一千个被标记的样例,那么你应该用上文提到的点乘法处理这些特征。这样就能得到一千个样例,对应了十几个特征。
2) 如你有一百万个样例,那么通过正则化和特征选择的方式就可以交叉处理文档和查询请求中的特征栏,这可能会产生数百万的特征数,但再次使用正则化可以大大减少冗余特征。这样就可能得到一千万个样例,对应了十万个特征。
3) 如果你有数十亿或数百亿个样例,那同样可以通过特征选择或正则化的方法交叉处理文档和查询请求中的特征栏。这样就可能得到十亿个样例,对应了一千万个特征。
对特征数和样例来说,这些统计学上的结论并不能给出一个具体的比例关系,但却可以从数量级上给出一些指导。另外,这里推荐用户依照第28条建议来选择具体使用哪些特征。
22. 清理不需要的特征
如果你发现有些特征并没有在使用,而且将其与其他特征相结合之后也无法使用的话,就应该清理这些特征。应该保持系统的清洁,这样才能尽快尝试那些最有希望出结果的特征。而且,如果有必要,被删除的特征也可以随时找人加回来。
在考虑增删一个特征时,应该仔细排查其覆盖范围。例如你有一些个性化的特征,但只有大约8%的用户使用了该特征,那么删掉或添加这个特征就不会有太大影响。
另一方面,增删特征时也要考虑其对应的数据量。例如你有一个只覆盖了1%数据的特征,但有90%的包含这一特征的样例都通过了训练,那么这就是一个很好的特征,应该添加。
3.2 对系统的人工分析
在进入机器学习实践的第三阶段之前,关注一些课堂上不曾教授的问题也同样至关重要,比如如何检查一个模型并改进它。要说这一点是一门科学,反而不如说它是一种艺术,这里我们介绍几点反面模式(anti-patterns)。
23. 你并不是一个典型的用户
这可能是让一个团队陷入困境的最简单的方法。虽然fishfooding(只在团队内部使用原型)和dogfooding(只在公司内部使用原型)都有许多优点,但无论哪一种,开发者都应该首先确认这种方式是否符合性能要求。另一方面,应该尽量避免不好的变化,但任何看起来合理的产品策略都应该被进一步验证,例如通过非专业人士在众包平台上的问卷调查,或者请目标用户来实测。
走外部验证渠道的原因来自两个方面:一是作为开发者,你太熟悉代码。例如你可能正在分析数据的某一方面而非全局,或者投入了太多的个人感情色彩,从而引发一些偏见。二是几位工程师开一个小时的讨论会议得到的评估结果,可能远比不上直接交给众包平台来得简单和有效。
如果你真的想要获取用户反馈,那么应该采用用户体验法(user experience methodologies)。 在流程早期创建用户角色(详情见Bill Buxton的《Designing User Experiences》一书),然后进行可用性测试(详情见Steve Krug的《Do not Make Me Think》一书)。这里的用户角色涉及创建假想用户。例如,假设你的团队成员都是男性,现在要针对35岁女性用户研发一款产品,那么基于目标群体创建一个假想角色,肯定比几位25-40岁的男性开发者闭门造车的效果要好。当然,让用户实测产品并观察他们的反应也是很不错的方法。
24. 版本之间存在对等差分(symmetric difference)
将产品交付至用户之前,有时候最简单有效的做法就是评估当前版本与交付版本的差异。例如面对排名问题,你可以在两个版本间利用同一组样例进行测试,然后对比其结果。如果差异很小,那么意味着这个版本没问题。如果差异很大,那么就需要确认进行了哪些修改,为什么进行这些修改。通过查看哪些测试样例造成了这一差异,也有助于定性了解修改具体是怎样的。总之,目标是确保不同版本的模型之间的对等差分做到最小。
25. 选择模型时,性能胜过预测能力
你的模型可能会被用来预测点击率,但更关键问题是:这种预测是应用在什么场景的。如果你用它来排列文档,那么最终排名的质量显然比预测本身更重要。如果你用它来排查垃圾邮件,那么识别精度显然更重要。大多数情况下,这两类功能应该是一致的,如果他们存在不一致,则意味着系统可能存在某种小增益。因此,假如一个改进措施可以解决日志丢失的问题,但却造成了系统性能的下降,那就不要采用它。当这种情况频繁发生时,通常应该重新审视你的建模目标。
26. 从误差中查找新模式、创建新特征
假设你的模型在某个样例中预测错误。在分类任务中,这可能是误报或漏报。在排名任务中,这可能是一个正向判断弱于逆向判断的组。但更重要的是,在这个样例中机器学习系统知道它错了,需要修正。如果你此时给模型一个允许它修复的特征,那么模型将尝试自行修复这个错误。
另一方面,如果你尝试基于未出错的样例创建特征,那么该特征将很可能被系统忽略。例如,假设在谷歌Play商店的应用搜索中,有人搜索“免费游戏”,但其中一个排名靠前的搜索结果却是一款其他App,所以你为其他App创建了一个特征。但如果你将其他App的安装数最大化,即人们在搜索免费游戏时安装了其他App,那么这个其他App的特征就不会产生其应有的效果。
所以,正确的做法是一旦出现样例错误,那么应该在当前的特征集之外寻找解决方案。例如,如果你的系统降低了内容较长的帖子的排名,那就应该普遍增加帖子的长度。而且也不要拘泥于太具体的细节。例如你要增加帖子的长度,就不要猜测长度的具体含义,而应该直接添加几个相关的特征,交给模型自行处理,这才是最简单有效的方法。
27. 尝试量化观察到的异常行为
有时候团队成员会对一些没有被现有的损失函数覆盖的系统属性感到无能为力,但这时抱怨是没用的,而是应该尽一切努力将抱怨转换成实实在在的数字。例如,当有些开发者认为在谷歌Play商店的搜索结果中显示了过多的其他App,就可以选择人工识别的方法剔除这些App(这时是可以选择人工标记数据的,因为相对较小的App查询可能占了很大一部分流量)。首先要确认你的问题是可量化的,然后才可以根据这些问题创建新的特征(features)、目标(objectives)或者指标(metrics)。总之规则是:先量化,再优化。
28. 注意短期行为和长期行为的差别
假设你有一个新系统,它可以查看每个doc_id和exact_query,然后根据每个文档的每次查询行为计算其点击率。你发现它的行为几乎与当前系统的并行和A/B测试结果完全相同,而且它很简单,于是你启动了这个系统。但却没有新的应用显示,为什么?由于你的系统只基于自己的历史查询记录显示文档,所以不知道应该显示一个新的文档。
要了解一个系统在长期行为中如何工作的唯一办法,就是让它只基于当前的模型数据展开训练。这一点非常困难。雷锋网
未完待续,请见雷锋网“谷歌机器学习白皮书全解析 43 条黄金法则(三)”。谷歌白皮书原文地址:http://martin.zinkevich.org/rules_of_ml/rules_of_ml.pdf
相关阅读:
雷锋网版权文章,未经授权禁止转载。详情见。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/68114.html