训练和使用偏差
训练使用偏差是指模型在训练上的表现和在线上实际使用的表现不一致。这可能是以下原因带来的:
- 训练和线上,处理数据的方式不同
- 训练和线上,数据变了
- 模型和算法的反馈循环
在 Google 的实际机器学习应用中,我们发现这种训练和使用偏差的确对系统整体有负面影响。最好的解决办法是可以去监控这种偏差,以免系统和数据的变更引入更大偏差时候我们发现不了。
Rule 29 - 让训练更接近使用的最佳方式是在线上把一堆特征用日志保存下来以便在训练时候使用
即使你没发每个样本都这么做,至少抽样来搞,这样你就可以调整训练和使用的一致性(见 Rule #37)。在 Google,这么做的团队往往会收获惊喜。YouTube 的首页业务采用这种日志化特征后,质量有重大改善,而且代码复杂度也下降了。在这片文章写就时,很多其他团队也在进行类似改造。
Rule 30 - 给抽样后的数据乘上权重,不要直接扔掉!
当文件太多的时候,很可能你保留前12个文件,把剩下的13-99号文件丢了。这不对:因为在训练阶段直接扔文件在 Google 的许多团队中都造成过问题(参见 Rule #6)。 虽然用户没有见过的数据可以丢,但给剩下的数据乘上权重至关重要。意思是当你做30%抽样的时候,给一个10/3的权重。使用权重后,Rule#14中讨论的特征校准就能够起作用。
Rule 31 - 在把线上和训练阶段的数据 join一个表时候要多加小心,表可能会变化
比如说你把 doc id 和这些 id的 feature离线数据(比如评论数、点击量等)join 起来。对于训练和线上,这表的内容是不同的,那么你模型在训练和线上对于同一个 doc id 的预测也会不同。避免这种情况发生的最简单方式就是在线上把特征用日志保存下来(见 Rule #32)。如果表的更新比较慢,你也可以按照小时或者天级做快照。但是请记住,这也不能100%解决问题。
Rule 32 - 在可能的时候在训练和线上尽可能复用代码
批量处理和在线预测不同。在线预测,你必须在每个请求过来时候立即处理。(比如你必须对每个 query 做单独的查询),而在批处理时候,你可以合并任务(比如做一个 join)。服务阶段是在线预测,训练阶段是离线批处理。但是,有些情况下你可以在两者之间重用代码。比如,你可以创建一个系统专用的对象,这样在 join 或者是单独处理的时候,系统都能以一种非常易读的方式去存储,错误也能够及早被测试找到。然后,一旦你收集到了足够的信息,用一个通用的方法把这种人类易读的对象转化为模型可用的数据。这样就排除了一个训练服务偏差的源头。作为必然的结果,你最好别在训练和线上服务阶段用两种编程语言,这样会使得复用代码几乎不可能。
Rule 33 - 如果你用1月5号之前的数据训练的系统,那么用1月6号和之后的数据去测试系统
总得来说,测试数据的时间不要和训练数据的时间重叠,这样才能体现模型的预测能力。如果你用1月5号之前的数据训练的系统,那么用1月6号和之后的数据去测试系统。你可以预期模型在新数据的表现不会想训练时候那么好,但是也不应该太遭。也许会有天级效应,你也许不会去预测平均点击率或者转化率,但是曲线之下的空间(这里应该是指 AUC),应该会比较接近。
Rule 34 - 对于过滤场景的二分类问题(比如垃圾检测或者重要邮件检测),用小牺牲换取干净的数据
在一个过滤任务中,被标为负的样本是不会再展示给用户的。(Eric: 这里作者的意思应该是被分类器判别为有问题的样本,在实际应用中应该是"正样本")假如在线上你的分类器挡住了75%的正样本,你可能想用剩下展示给用户的东西作为训练集进一步完善你的分类器。比如你分类器漏掉的数据中,用户标记为垃圾邮件的做为新的正样本,并以此进行训练。但是这种方法就引入了抽样bias。你可以通过在线上保留1%小流量的方式来获取更加干净的样本,就是简单的对这个小流量不做任何过滤,直接发给用户。这样做的话,你至少还是可以拦截74%的垃圾邮件。注意,如果你的过滤器好到可以把95%以上的正样本都找出来,那么这个方法就没那么有用了。即便如此,如果你想度量线上表现,那么做一个更小的抽样(比如0.1%或者0.001%)。一万来个样本足够把模型表现体现出来了。
Eric: 对于过滤类的二分类问题,其实过滤行为本质上造成了 bias(即影响了不同样本给用户的展现机会)。对于非过滤的二分类问题,其实也一样的。比如 rank 中的展现位置同样会引入 bias,对于这种 bias 的处理,作者建议是留一个小流量。我们的实践是用轮展的方式来消除展现 bias,然后把轮展拿到的点击特征引入到模型中。
Rule 35 - 注意排序问题中的固有偏差
如果你的排序算法换得足够频繁,那么模型看到的数据就会足够多样。那么这种偏差就随之而来了,你必须围绕这种变差了设计模型。这里有一些关于如何调整你模型已经见过的数据的方法。
- 在覆盖更多query的特征上,正则化更多一些(即惩罚这些特征的权重),在只覆盖一个或几个的特征上,正则化少一些。通过这种方式, 模型就会偏向于使用那些覆盖少量query的特征,而非所有 query 都高的特征。这种方式可以避免全局都很受欢迎的结果传播到不相关的 query中去。注意这和高度正则化那些独立值的特征建议是相悖的。
Eric: 最后一句没看懂.
只使用那些权值为正的特征。因此,只要特征值非空,就有贡献。
不要只用语意特征(即和query无关的特征,比如全局 CTR 这种)。这是 Rule #1的一个特例。即使一个超热门app,你也不想让他出现在各种 query 下啊4。不只用语意特征的化,就很容易实现这个目标。
4 - 不要让一个热门 app 到处出现,主要还是因为想保持多样 app 的可触达性。 比如有人搜索"看鸟app",如果你推"愤怒的小鸟",他们也许也会下载,但是这肯定不是用户的初衷。展现这样的热门结果,也许会提升安装量,但是用户的需求并没有得到真正满足。
Eric: 想想我们的实体全局Entity CTR吧... 他的确跟我们的整体点击率带来了很大的贡献,不过确实也引入和很多的相关性问题。Google做得干脆多了。
Rule 36 - 避免位置相关特征的反馈循环
内容的位置能够显著影响用户行为。放首位的 app 会被点的更多,那么他就一定更"好"吗?加入位置特征是应对这种情况的一个选择,比如直接加入展现位置作为特征。带着位置特征进行训练,模型就会学到位置特征的权重,比如"1号位置"特征的权重会很高。然后在线上,你不带位置特征给任何样本,或者只给默认值,因为这时候排第几还没确定呢。注意因为在训练和测试时候位置特征和其他特征的处理方式不同,所以你最好把他们分开。理想情况下干脆给位置特征和非位置特征建立俩模型(这里应该是指非线性模型,因为线性模型天然就是加和)。而且,不要把位置特征和其他非位置特征做交叉。
Rule 37 - 度量训练使用偏差
大体上,有以下原因会造成偏差:
训练和测试数据的偏差。总的来说这个偏差会一直存在,但也不总是坏事。
Eric: 比如利用这种偏差来消除位置bias就不错。
留存数据和"第二天"数据的偏差。同样,这种偏差也会一直存在。你应该调整正则方式,去最大化第二天模型的表现。 然而留存数据和第二天数据的效果如果差得太多,也许意味着有时间敏感的特征存在,模型的表现下降。
"第二天"数据和实时数据的偏差。如果你给同一个样本到训练期的模型与线上模型,他们应该预测结果完全一样(参见 Rule #5),如果不同很可能工程上引入了错误。