作者:黄永刚
ML Phase II: 特征工程
第一阶段介绍了机器学习的一个周期,为学习系统获取训练数据,通过有趣的引导设计指标,创建一个服务框架。在有了一个完整系统之后,就进入了第一阶段。
第二阶段有很多比较容易的东西。任务就是将大量丰富的特征搞进系统中。因此,机器学习的第二阶段就是获取尽可能多的特征并将其有意的组合。第二阶段,所有的指标应该任然在提升。很多东西开始开发,很多工程师将一起花费大量的时间处理数据,以便于你能够创建出非常优秀的学习系统。
Rule #16:
不要尝试一次做出完美的模型,或者打算停止模型迭代。因此需要考虑是否当前增加的信息会延缓未来的迭代。很多团队花费一个季度在建立一个模型上,或者数年时间都在进行模型迭代。建立新模型很必要,原因如下: 1. 你不断发掘出新的特征。 2. 你可能调整了正则,改变了原来特征的处理方式。 3. 你可能调整了目标。
无论如何,多给模型一些关注总是好的额,查找一些数据并回馈给旧模型或许帮助你找到新的信息,所以在建立模型的时候,就要考虑添加或移除特征或者重新组合特征的难度如何。考虑重建原来pipeline的一个新复制版本难度大与否,验证其正确性难不难。考虑并行运行两三个副本的难度大不大。最后,暂不考虑从35个中抽取16个特征是不是就进行模型更新。这是下一个阶段干的事情。
Rule #17: 开始时,使用可直接观察或可以记录的特征作为学习特征的反面。
这个看就有争论了,但是它可以避免很多容易犯的错误。首先,首先阐述一下学习特征是什么。学习特征是从外部系统获得的特征(例如通过聚类获得的)或者通过模型自身获得(因子模型或深度学习)。这些都很有用,但是也存在很多问题,所以在第一个模型中就不要使用它。
如果使用外部系统生成特征,要认识到那个系统有它自己的目标。外部系统的目标或许是以周期性与当前的系统目标相关。如果你利用的是外部系统的一个副本,那么它很快就过时了。如果你更新了这个特征,那么它代表的意思可能已经发生了变化。
因子模型和深度模型最大的问题是它们是非凸的。因此不能保证找到近似最优解,或许每次迭代找到的都是一个不同的局部最小。这种变化就导致了很大程度上很难判定其对于你的系统影响是有意义的还是随机的噪声。如果不利用深度特征,你可得到一个较好性能的基准模型。有了这个基准之后,你就可以尝试更加复杂的方法了。
Rule #18: 从不同的场景中找到特征
通常一个机器学习系统是一个大的业务场景的很小的一部分。例如,判断一个post是不是应该上热榜,很多人在这个post上热榜之前可能对这个post,赞、转发、评论。如果提供这些信息给学习器,可能提高了所优化场景中没有数据post的权重。YouTube的 ‘自动播放下一个’ 功能就利用了来自YouTube搜索中的数据(一个视频看过之后又看了哪个视频,对此数据进行频率统计,即看了又看)你也可以使用明显的用户评分。最后,如果你使用用户行为作为标签,用户行为在不同场景中的释义将会是很好的特征。这些特征将会将其他场景信息带入这个场景中。记住这不是个性化:找到喜欢这个内容的人群,然后判断其喜欢的程度。
Rule #20: 用人能够理解的方式对已存特征进行改写和组合创建新的特征。
有很多的方法修改和组合特征。机器学习系统像TensorFlow允许你使用transformations预处理数据。两个标准方法:离散化和交叉。
离散化是将连续性特征离散,创建出以值为标签的离散特征。如年龄是连续特征。使用一个特征表示年龄小于18岁,18到35岁的为另一个特征等等。不要过多的考虑这个边界,可以参考基础的分位数。
交叉是将多个特征进行交叉组合。特征列,在TensorFlow的terminology中是一组同质的特征。(如{男、女},{美国、加拿大、墨西哥}等)。特征交叉出新特征,如{男、女}*{美国、加拿大、墨西哥}。新特征包含这样的值{男、加拿大}。如果你使用TensorFlow做特征交叉,这个特征{男、加拿大}表示男性加拿大人。使用多个基本特征形成的交叉特征将需要大量的数据来训练模型。
交叉会产生大量的特征,有可能会过拟合。例如,你在做搜索排序,有查询词的向量和文档词向量。如果采用交叉组合,将会产生大量的特征(参考Rule #21)。当处理文本问题,这里有两个变种。严格一点大多应用点乘。点乘的最简单形式是计算查询和文档共用词汇的数量。这个特征也可以被离散化。另一种变种是使用交集:只使用那些 同时出现在查询和文档中的词作为特征。
Rule #21: 从线性模型中可学到的特征权重数目大体和拥有的数据量成比例
统计学习理论中对于模型复杂度的描述是很有意思的,这些知识是基础,你需要知道。我以前说过,人们怀疑可以从一千个样本中学到任何东西,或者最多需要的样本数量不会超过一百万。这是由于这些人陷入了特定的学习方法中了。其实这个的关键在于调整你的学习方法适应于你的数据量的大小: 1. 假设你从事搜索排序系统,在文档和查询中有上百万的不同词汇,而且你又1000个标记样本,那么你可以使用文档和查询的词频-逆文频率与六个其他人工特征的点乘。1000样本,12个特征。 2. 如果有100万样本,获取文档和查询特征的交集,使用正则化和可能的特征选择。获取的数百万特征在正则化的作用下就会变得很少。1000万样本或许只会获得10万特征。 3. 如果有数十亿的样本,交叉文档词和查询字符的特征,并使用特征选择和正则化。10亿样本只会用到1000万的特征。
统计学习理论很少给出精确的边界,但是对于起点给出了很好的指引。最后使用Rule #28去决定使用哪些特征吧!
Rule #22: 清除掉你不在使用的特征
不用的特征就是技术债务。如果发现没有使用的特征,并将其和其他特恒组合之后仍然不起作用,那么将其从你结构里面清楚出去。保持结构整洁以便于能够尽快的寻找到有价值的特征。
添加或保留哪些特征,要多考虑特征的覆盖率。这个特征在多少的样例里面出现?例如,考虑个人属性,如果只有8%的用户有这些特征,那么添加这些特征的效果不会很好。
同时,一些特征或许对于权重有很重要的作用。如一个特征只有1%的覆盖率,但是有这个特征的数据90%都是正样例,那这个特征就是很好的。
人工分析系统
进入机器学习第三阶段之前,学习一下:怎么看一个模型,并改进它。这是非常重要的,而且所有机器学习相关课程中不会讲述这些东西。这是优点偏艺术的成分,因此,告诉你一些反面模式帮助你少跳坑。
Rule #23 你不是典型的最终用户
很多团队容易陷入这个陷阱里面。员工应该自己看产品原型的性能是否正确。当产品明显变差时,就应该果断抛弃掉;当产品原型看起来比较合理的时候,就需要做进一步的测试。可以花钱请一些非行业内人士回答问题或者对用户做在线实验。
这样做有两个原因。第一,你太熟悉业务了。你可能看的角度和大众不同,或者会对产品存在情绪上的代入感。第二,你的时间太宝贵了。你能想象9个工程师坐在一个会议室里面讨论这些东西的代价有多大。
如果你真的需要用户的反馈,就使用调查用户体验方法。在早期的时候创造一个用户意像,并做可用性测试。用户意像是假设一个用户。例如,如果你的团队里面都是男性,那么设计一个35岁的女性意像,看它会做出什么样的选择。将你的站点(本地或者远程)给真实的人看看他们的反应,对于可用性可能会有不同的观点。
Rule #24 衡量模型之间的差异
在展示给其他人看之前,最简单或许最有用的方法就是,计算不同模型在产品系统中结果输出的差异大小。例如,对于排序问题,将两个模型使用同一个查询样例在整个系统中跑一遍,比较一下结果的加权大小。如果差异非常小,那你完全不用在跑实验就可以告诉别人它们的变化不大;如果两者差异比较大,那就要考考确认一下变化的好坏了。认真查看差异比较大的情况能够帮助你理解变化在质量上是什么样子地。然而一定要确保系统是稳定的。确认模型和自身比较的对称误差(symmetric difference)很低,理想情况为0.
Rule #25: 选择模型的时候,实用效果最关键
你的模型可能做的是点击率预测。然而,最终关键的问题是你要拿这个预测值做什么。如果你用来对文档进行排序,那么最终排序的质量才是关键,而不是预测本身。如果你用来判断文档是不是垃圾内容而选择是不是将其屏蔽掉,那选择‘阈值’的准确度就比预测本身重要。大多数情况这两者应该都是一致的。当它们不一致的时候,通常获得收益也很小。因此,模型的改变使得损失函数减小,但是却降低了系统的性能,那就换个特征吧。当这种情况经常发生的时候,那就需要你再次审视一下你的模型目标是不是恰当的。
假设你得到一个模型做错的样例。在分类中,应该是假阳或者假阴(false positive or false negative, FP or FN)。在排序中,是一对样例,其中正样本的排名低于负样本。看待这个一种重要的观点是:机器学习系统知道它是错的,但若你能提供机会,它自己会修正过来。如果你给模型喂进一些特征,那么模型自己会尝试修正。
另外,如果你尝试基于一些样例创造一些特这,然而这个样例系统比不将其视作错误,那么这个特征将被忽略掉。例如,在Play应用搜索中,有人搜索了‘免费游戏’。假设靠前的结果都是相关度比较低的 gag app,那么你创建一个特征来表达gag app.如果你正在优化应用安装数量,当人们搜索‘免费游戏’的手就会安装gag app,这个特征‘gag app’并没有取得你想要的效果。
一旦你发现模型错误的样例,那你就从当前特征集合之外找找有哪些趋势。例如,如果系统看上去会将posts较长的权重降低。那么将这个长度作为特征加入里面。不要太关注你所添加特征的细节了。如果你将要将post的长度加入里面,不要尝试去猜长度怎么定义才好,你只需要扔一堆特征进去,让模型自己去找哪个有用。这个才是你获得想要结果最简单的方法。
Rule #27: 量化不理想行为
你的团队成员当遇到一些他们不愿看到且没有被已存的损失函数捕获的系统情况时,可能变得沮丧。此时,无论做什么都要将这种抱怨转变成坚实的数字。例如,如果他们认为在Play搜索中有太多的‘gag apps’,应该使用专人来识别gag apps。(在这种情况下你可以灵活的使用人工标注的数据,因为这些查询部分可能占了很大一部分流量)。如果你的问题可以衡量,那就开始将其作为特征、目标或者指标。总结一句话,“量化第一,优化第二”。
Rule #28: 短期行为相同不意味长期行为相同
假设你有一个新系统做文档审计的,之后在计算每个查询对于每个文档的点击率。你会发现它的行为和当前线上系统不论在任何测试中都是相同的。鉴于它比较简单,你直接使用它。然而你慢慢发现新上架的应用不会出现在这里。为啥呢?那是因为你的系统仅仅是基于历史查询记录来展示文档,没有办法处理新文档的情况。
唯一可以理解系统在长期表现如何的方法是,将模型上线产生记录,在来训练。这就比较扯了。
训练和上线服务之间的偏差
训练性能和服务性能之间的差异可能由一下原因引起: - 训练和服务时的在处理数据的流程上存在不一致的地方 - 训练和服务时数据发生了变化 - 模型和算法之间形成了回馈环流 我们在Google的机器学习系统中见到过这种偏差,对系统产生了负面影响。最好的解决办法就是做监控确保系统和数据的变化导致的偏差能够被注意到。
Rule #29: 确保训练和服务一致的最好方法就是保存服务中的特征集,在将其用在训练当中
即便不对每一个样本都这样做,只选择一部分也是可以的,只要能够确认服务和训练的一致性就行(参考Rule #37)。有的时候Google的团队们应用这种方法获得了惊人的结果。YouTube主页通过应用这个方法质量上获得很大提高,同时也减小了代码的复杂度。很多团队都在往这种结构上转。
Rule #30: 数据采样使用的权重可不能随意丢掉
当你有太多的数据时,倾向于使用1-12忽略13-99.这是错误的,在过去很多的团队在训练中丢弃数据导致了很多问题。虽然从来没有展示给用户的数据可以被丢弃,但是可以被其他用来做重要性衡量。重要性衡量指的是如果你对X进行30%的采样,它将给这个数据一个10/3的权重。有重要性衡量,在Rule #14中谈论的属性矫正就是可以做的。
Rule #31 如果要合并训练和服务时段的数据,这个数据可能会发生变化
假设一要合并一个包含文档特征的表格,如评论和点击数。在训练阶段和服务阶段,特征可能是不同的。模型对于相同文档的预测可能在训练和服务时不同。最简单的避免这个的方式就是记录服务时段的特征(参考Rule #32)。如果表格变化的比较缓慢,你可以每小时或每天对最近的数据做快照。注意这依然没有解决掉这个问题。
Rule #32 尽可能在训练流程和服务流程中重用代码
批处理和实时处理是不同的。实时处理中当有请求达到,就要处理。然而在批处理中,你可以将这些任务集中起来一起处理。服务阶段,你做的是实时处理,而训练做的是批处理。要重用代码还有一些其他事情要做。例如,你可以创建一个独特的系统对象,请求的结果或者合并的结果以人可读的方式存储,而且保证错误可以很容易的被测试出来。之后,一旦你将所有信息汇总起来,无论是训练阶段的还是服务阶段的,你就可以使用通用的方法对该人可读的对象和系统需要的格式之间建立转化桥梁,此时无论系统需要什么格式都可以容易的处理了。这种方法从根源上减少了训练和服务的偏差。一个推论,不要尝试在训练和服务中使用两种编程语言,否则重用代码就成了几乎不可能的事情。
Rule #33 如果模型试验使用的数据是截止5号的,那么就使用6号以后的数据对其进行测试
通常,模型评估使用的数据是训练数据之后的,这样可以很好的反映出在生产环境中系统的表现。如果模型试验使用的数据是截止5号的,那么就使用6号以后的数据对其进行测试。可以预知在心数据上的表现不会很好,但是它也不应该太差。或许由于日期的影响,你可能不能预测出平均点击率或者转化率,但是AUC(area under the curve),代表正样例的评分高于负样例的似然性,应该是比较接近的。
Rule #34: 二值分类的过滤中(垃圾邮件或感兴趣邮件),为了获得干净的数据需要做出微小的牺牲
在过滤中,标记为负的样本不会呈现给用户。假设模型可以在服务中过滤75%的负样本。你可能想获得用户的其他样例作为训练数据。例如,如果用户将邮件标记为垃圾邮件,而你没能过滤掉,你可能像从这个学习到一些东西。
但是这种方法引入了样本偏差。你可以将你需要过滤掉的1%样本作为‘held out’,并将其依旧发送给用户,这样你就可以获得更加干净的数据了。而你的过滤器至少还能过滤74%的负样本。这些‘held out’样本就可以变成你的训练数据了。
如果过滤器有95%及以上的过滤比例,这个方法看起来可行性就不高了。即便如此,如果你想评估服务质量的性能,你可以使用更小的样本比例(比方说0。1%或0.001%)。有一万个样本做估计足以获取到相当精准的结果了。
Rule #35: 排序问题中的固有偏差
当你转变算法足够激进的时候,不同的结果就会出现。这样你已经改变了算法未来会用到的数据。这种固有偏差就会出现,在设计模型的时候就应该加以考虑。这里有几个方法。这些方法更偏爱模型已经看到过的数据: 1. 对于覆盖更多查询的特征给予给高的正则系数,覆盖面小的特征则相反。该方法更偏爱那些泛化性不是很好的特征(即比较独特的特征)。这个方法可以阻止非常流行的结果泄入不相关的查询当中。注意,传统的建议是对于具有更多独特值的特征给予更高的正则系数,这里和它不一样。 2. 将特征权重限制在正数范围内。因此好的特征降会由于‘unkown’特征(0权重特征)。 3. 不要只获取文本特征。这是#1的一个拓展。例如对于任何查询,有一些app都是热门,你肯定不希望任何地方都展示它。1
Rule #36: 避免位置特征的回馈循环
内容呈现的位置很大程度像影响着用户的交互的喜好度。如果你将一个APP放在第一个的位置,那它就会获得更多的点击,你会认为这个APP本身受到更多的偏爱和点击。一种解决办法是加入位置特征。将位置特征加入模型一起训练,模型自身会学习这个权重,例如,第一位的获得较高的权重。因此你的模型对于位于第一位的样本的其他因子会给予更小的权重。之后,再服务阶段,你不会给出位置权重,或者都是默认值,那是因为对候选进行评定之后才会决定展示的次序(译注:不要因果倒置)。
将位置特征和其他模型特征分开来是很重要的,这是由于训练和测试的不对称性决定的。最好的方式是对其他特征函数和位置特征函数求和。还有不要和位置特征做特征交叉。
Rule #37: 评估训练或服务偏差
这里有些事情在很多场景中会导致偏差。而且可以将其分成几个部分: 1. 训练数据和holdout数据的性能差异通常会一直存在,也不会一直都是坏的。 2. holdout数据和‘新一期’数据之间的性能差异也会一直存在。你应该调整正则化来最大化‘新一期’数据上的性能。但是,如果holdout和‘新一期’数据上的性能差异比较大,那表明了其中一些特征对时间太过敏感了,很可能降低了模型的性能。 3. 同样,‘新一期’数据上的性能和实时数据的也存在差异。如果你对训练数据和服务阶段的同一个样本进行模型预测,它们应该给出完全相同的数据。因此,如果存在差异,很大程度上表明工程商存在错误。
reference: - http://feisky.xyz/machine-learning/resources/rules_of_ml.html - Rules of Machine Learning: Best Practices for ML Engineering
- 不愿将流行的app到处展示的原因是要保证所有用户想要的app都是可见的。例如,如果有人搜索‘bird watching app’,他们可能下载了‘愤怒的小鸟’,但是那不是他们真正的意图。展示这种APP或许可以提高下载率,但是却忽略了用户最终的满意度。 ↩