Fork me on GitHub

网易新闻推荐工程优化 - 特征平台篇

稿 作者:希孟

从2019年年中起,我们针对网易新闻个性化推荐的系统架构做了更新迭代,涉及的工作包括特征/样本的平台化、pCTR推理服务的性能优化、大规模分布式训练的探索,以及目前正在进行的推荐中台化改造等。

我们梳理了之前的部分工作,并将涉及特征、推理、训练的部分做了一个系列文章,希望和大家多多交流。本文主要描述网易新闻的特征平台。

1 业务特性

网易新闻的业务场景为信息流推荐,沉浸阅读的模式使用户可以持续刷新以获取新的资讯、视频以及其他内容载体。同时,作为新闻媒体,网易新闻还需要在第一时间将实时发生的重大新闻推荐给用户。这样的产品特性,使得推荐系统在设计的时候,必须充分考虑到实时性,无论在训练环节还是特征环节。

图片

针对用户侧,用户刚刚登录APP的时候,其可能对NBA新闻感兴趣,随着不断的刷新阅读,用户兴趣很快就会发生迁移,用户的当前兴趣点可能已经转换到了新能源汽车。如果相关的特征没有随之改变,推荐的精准度一定是不够的。同时,用户在刷新过程中也会实时反馈不喜欢的内容,这对于特征的实时性提出了更高的要求。

针对新闻侧尤其是热门新闻类型,新闻刚刚发布时候的热度值或者点击率,与1天后乃至1个小时候的热度值/点击率差异会非常大,如果文章侧的特征描述忽略了这一点,其对于文章侧的描述也是很不准确的。

2 历史版本

业内常见的特征平台的核心设计往往为数据表,比如设计用户表和文章表。离线训练时将日志信息、用户表、文章表做join处理以生成样本;在线服务时则有专门的服务加速从数据表中获取内容的过程。而基于上述的实时性的考虑,我们没有采取这种方式。

我们历史版本的基本思路为:线上服务实时生产特征,离线样本生成则使用线上实时落盘的特征,其基本流程如下图所示。

图片

推荐引擎完成召回、过滤、粗排环节之后,获取或计算用户侧、文章侧的各个维度的原始属性,并人工特征工程以得到结构化/规范化的特征数据,并将此刻的特征数据输入对应模型之中进行预估操作,并根据返回的CTR分数做最后的排序。引擎侧完成推荐之后,启动样本落盘流程,落盘数据为此刻预估时的特征数据。离线训练环节,基于该特征数据,以用户ID和文章ID为key,与曝光日志、点击日志、其他行为日志关联标签,形成带有标签的特征样本。算法同事可以直接使用特征样本进行模型训练。

这种方式下,可保证预估和训练的特征一致性,避免特征穿越问题。但这种模式的核心缺点为灵活性差:落盘数据为特征数据,如果算法同事想对特征工程的过程进行变更,则往往需要推荐引擎或者预估服务上线,等待周期较长。另外,这种模式下面,如果算法同事想做验证特征的有效性,则需要落全部特征,选择其中的子集做处理。

3 当前版本的设计

基于上述的不足,我们从优化灵活性入手,继而考虑多业务复用,继而考虑线上服务的高效性,逐步迭代。

3.1 灵活性的优化

针对历史版本的不足,我们引入的特征算子和物料的概念。

(1)我们将特征工程抽象成不同的特征算子,比如bucket、cross、cosine,并基于json文件做计算逻辑的管理,线上和线上基于同样的特征算子包做计算以保证预估和训练的一致性。而关于特征算子包的内容,我们会在后续的文章中专门详细介绍。

(2)我们将特征区分为原始特征(我们称为物料)和特征,比如年龄27岁为物料,年龄3号桶则为特征。日志落盘的是物料数据。这样离线环节,只需要将物料数据和曝光点击等行为日志关联标签,继而再使用特征算子包计算生产特征样本,即可进行模型训练。其基本流程如下图所示。

图片

这个版本在灵活性上有很大提升。引入特征算子包,算法同事的工作主要聚焦特征工程和模型训练。在特征工程方面,算法人员可以根据我们提供的基础工具和基础特征算子书写特征配置。特别地为了提高书写配置文件的便捷性,我们提供了若干辅助工具,包括原始数据列表和说明(每个原始数据的计算逻辑说明,样例数据,数据类型)、特征算子使用说明和样例(特征算子介绍,功能说明,使用方式,输入个数,输出个数,参数定义,支持的数据类型,每个数据类型下的样例)、特征输出展示,以及基础数据的监控工具,包括原始数据分布、特征数据分布等,用来辅助算法同事书写、校验、评估自己的特征配置文件,以此提高用户特征配置项开发的开发效率和准确率。

3.2 业务复用的优化

我们率先在网易新闻头条推荐使用了这套逻辑,但网易新闻除了头条推荐还有很多小栏目推荐,比如娱乐栏目、体育栏目,比如push的场景等。如果每个子栏目都需要重新构建这样的服务,其人力开销则较大。当然,这个时候基于hive表存储的优势就体现出来,因为数据表和业务是没有绑定的。为了解决业务复用的问题,我们引入了物料服务和物料缓存网关的概念,其架构如下图所示:

图片

  • 离线环节:各个业务依然是独自落原始物料,经过特征算子计算生成样本,没有改变;
  • 在线环节:pCTR服务不再直接和物料服务通信,而是借助物料缓存网关。物料缓存网关的核心功能则为缓存和路由,其根据特征名称路由到不同的物料服务来计算。

这样,新增业务只需要在对应的物料服务中构建自己专属的物料,其他栏目的物料可基于网关直接复用。同时,为了降低开发成本,我们将物料服务拆分为物料服务模板和BUILDER包,模板封装了数据格式、服务发现、计算触发、写入缓存等逻辑,业务人员仅关注具体的builder逻辑即可。

3.3 性能侧的优化

为了灵活性,我们引入了特征算子包,在离线和线上做实时的计算。然而实时计算就意味着RT增加,随着进入pCTR排序服务的候选数目的扩大,RT的压力越来越大。我们开始对性能侧也做了集中的优化,优化的内容包括算子库实现的性能优化、pCTR服务端的性能优化、以及特征平台侧的优化等。特征算子库和pCTR服务端的性能优化会在后面的系列文章中详细说明,这里主要描述下特征平台侧的性能优化。

降低RT的常见思路为用空间换时间,给定特征配置文件,同个用户的不同请求中,类似年龄性别等人口属性和用户的长期兴趣是固定的,没有必要每次都计算。那么优化的思路就很直观了,可以将某些类型的特征提前计算好并存储到缓存中,线上请求直接拉取对应的特征即可。其整体架构如下图所示:

图片

物料和特征生产:触发物料服务计算有两种方式,消息队列和接口调用,与3.2小节不同的是,物料服务除了计算物料之外,还会将具体的特征也计算完成,并且将结果分别缓存。这里面有几点需要注意:

(1)避免空间浪费,以线上实际的使用方式来看,不同特征配置文件之间其实大同小异,如果每份特征配置文件均存储一份,其实有很大的冗余,需要将不同特征配置的存储做融合。

(2)区分不同物料的计算频率,我们将物料区分为下面几种更新周期:天级别、分钟级别、实时级别。实时级别的特征还是需要每次请求都实时计算的,比如上一刷用户的点击类别分布等。而物料服务针对天级别和分钟级别物料的更新周期也是有所差异的。

线上服务的获取:线上服务的获取与3.2小节基本类似,只是会区分物料和特征。针对pCTR服务而言,其直接获取到特征粒度,物料缓存服务网关需要根据其对应的特征配置拉取具体的特征。而在落物料日志的过程中,则获取物料粒度,这样在性能优化的同时依旧保持了灵活性。

4 样本生成逻辑

除了提供特征之外,我们还将常见的样本生成逻辑做了标准化,并将其存储到数据表中,用户在数据表中新增一行数据,即可生成新的样本,无需代码层面的改动。这里需要说明的是,如本文开头所述,业务场景对于实时性的要求很高,所以如头条推荐的很多场景均使用了流式训练的方式,本节描述的样本生成逻辑也是实时的。我们也提供了离线批处理的样本生成逻辑,但这种方式需要在有数数据开发及管理平台提交任务了。

样本生成标准化的事项如下图所示,我们将样本生成的过程拆分为两个环节:join环节、sample环节。join环节的核心工作为融合信息,融合如物料信息、推荐点击信息、用户互动信息等等,同时我们将业务中常见的标签推理的方式封装为函数,外面直接设置相关的参数即可得到实际的标签,比如加上阅读时长、阅读完成度类似的限制。sample环节的工作为特征工程,除了基于配置的特征工程之外,我们将样本权重计算、样本过滤逻辑、样本采样逻辑也做了函数式的封装,并对外暴露参数使其可配置。

图片

样本生成逻辑是基于flink任务来完成的,flink任务会定期读取数据表,获取需要计算的样本详情,生成新的样本。下图是2020年的截图,算法人员在下表中配置参数并点击上线即可生成样本。当然,flink任务除了输出kafka之外,也会将里面的部分样本写入hdfs路径下面,方便模型训练。

图片

这里值得说明的是,如3.3小节所述,如果每份样本均进行独立的特征计算时,会存在较大的冗余,比如两份样本的特征配置文件很相似,比如两份样本仅仅是采样不同,特征配置文件是完全一致的。出于性能的考虑,我们在flink任务中同样做了计算融合的工作,降低冗余计算,节省计算资源。

5 总结

针对网易新闻个性化推荐场景,站在实时性的出发点上,我们搭建了一套特征平台,并且兼顾了灵活性和高效性。同时,我们针对常见的样本生成逻辑做了封装,用户在数据表中新增一行即可生成新的样本逻辑。

然而与业内的其他同类型产品相对,我们的工作更加定制化。与OpenMLDB相比,我们没有在离线/在线侧做太多更底层的基础性能优化;与云音乐的工作相比,我们在特征管理方面还不够完善。我们的样本生成逻辑也更吻合精排的逻辑,召回所需要的各种负采样逻辑还没有做到很好的抽象,这些都是我们之后待完善的工作了。


本文地址:https://www.6aiq.com/article/1647651329252
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出