好久没写博客了,今天写一点推荐系统周边设施的东西。

特征管理

特征商店会存储特征元数据,比如特征的计算逻辑、血缘关系、数据类型。 一般来说,这些元数据用于管理特征的生命周期、计算任务和使用方式。

离线训练数据生成

为了保证线上线下数据的一致性,推荐系统的训练数据通常有两个数据流Join得到:

  1. 在Ranking中即实时打点:数据流以traceId为Key,排序时特征为Value。
  2. 客户端日志:记录了traceId和事件类型(曝光、点击、分享等)

由于客户端日志必然晚于服务端日志,因此两个数据流Join时需要一定的窗口。

训练数据扩展

但是作为调参工程师,我们必然会遇到需要的特征没有记录在实时打点中,导致训练时缺少相关数据的情况,这个时候,就需要想办法来处理这个问题。

按照Uber的方法,我们可以把特征分为三类:离线特征、实时特征、RPC特征

对于离线特征:我们可以使用spark读取数据仓库中的历史数据,天为单位进行生成历史数据,然后放在一个分区的Hive表中。

对于实时特征:基于kappa的思想,我们可以在flink中编写实时特征计算逻辑,然后在启动重跑一段时间以前的历史数据,并记录这个过程中特征的每一次变化,将其输出到Kafak中去,这样我们也就有一个特征在历史时间段中的值。(这里我们最好有一个服务化的Flink平台,来进行任务的添加、删除、修改等工作)

这里,特征的计算任务就可以通过特征元数据库进行管理。

截下来,我们就可以通过带时间戳的Join来完成训练数据和特征数据的拼接,并将特征回写到训练数据中去了。 需要注意的是,为了保证线上线下数据的一致性,我们需要引入一定的延时机制来模型客户端日志的延迟。

最后对于来自外部系统的RPC特征:就没有什么好办法了,我们只能在线上添加这个特征的打点,然后跑上一段时间来得到有这个特征的训练数据了。

这里推荐一个开源的项目可以完成类似的工作: https://github.com/feast-dev/feast

在线特征推送

特征的线上存储可以使用KV数据库比如Redis,数据的来源和上面训练数据的扩展可以使用同一套代码,只需要在计算时根据元数据配置来决定是否推送上线。

另外,这里一般会做很多工程上的很优化,比如把多个特征作为一个特征组存在一个key里减少请求的次数,使用一些算法(比如XXHash32)对过长的特征名(比如spu\$realtime\$orders_last_2w\$spu_id)进行压缩。