推荐工程全解-整体架构
1. 推荐系统的作用
推荐系统现在已经是新闻资讯、视频、购物等场景的标配。通过对候选物料千人千面的提供方式,从而实现业务场景下用户时长、用户活跃度、物料点击率的提升。最终实现收入的转化。本文章聚焦于推荐系统的工程架构
,对推荐工程的各个模块进行简要的介绍
,并提出一些问题,待后续各个模块展开的文章思考解决。
2. 推荐整体架构
推荐漏斗如图所示,不同的产品形态,其item候选池的大小也不同。首先产品拥有一个数百万~数亿数量级item的全量候选池,通过不同的召回,从候选池中获取数千到万级别的item,用作后续推荐的候选集。之后进入粗排,对item打分,并取打分topk的item到精排进行进一步打分计算。精排使用比粗排更复杂的模型对item进行打分预估,之后由重排对item按属性进行过滤、多样性筛选,执行各种产品、算法的策略,最终得到指定数量的item作为推荐结果返回。
推荐系统的整体架构主要可分为三大部分:数据、算法、在线服务,整体的简单架构如图所示
:
推荐在线服务整体为星型架构,由接入服务一次请求画像、召回、粗排、精排、重排,获取推荐结果返回给客户端。在线服务还包括正排、倒排、特征服务为基础服务,倒排为召回提供素材,正排为推荐在线的各个模块提供获取item属性的功能,特征服务主要为各个需要进行模型预估的模块提供特征抽取的功能,用于生成模型预估的输入。
算法训练了推荐粗排、精排部分的模型,产出粗排item embedding,产出倒排索引,ann索引
数据为在线服务、算法训练提供各种基础数据,对于用户侧,产出用户画像,抽取、转换、加载用户行为,用户兴趣画像。对于item侧,获取、组织item正排信息和item统计信息、互动信息。对于算法模块,数据侧回流用户的行为和用户画像、上下文信息,用于产出算法模型训练的正负样本。
3. 推荐的各个模块
3.1 推荐接入模块
整个推荐系统为星型架构,推荐接入模块为星型架构的中心节点。对外与推荐上游进行交互,负责推荐请求的接收、返回推荐结果;对内请求推荐内部的各个子模块,分别请求画像、召回、粗排、精排、重排,从而获得推荐结果,并根据协议封装结果返回上游。
3.2 画像
3.2.1 分类
画像是推荐实现个性化的基础。画像模块根据用户ID获取用户画像。用户画像主要包括两大类,显式画像和隐式画像。
显式画像可以是基础一些基础信息以及用户行为分析出的tag,主要包括:基础画像(如性别、年龄、城市、学历等),兴趣画像(对哪些tag感兴趣,如综艺、足球、科技等),用户行为(点击历史、播放历史、曝光历史、互动、点赞、评论等),会话信息(时间、位置、设备、app版本、刷数等)。
隐式画像可以是利用用户所有信息,通过模型训练得到的embedding向量。
3.2.2 对比
显式画像可解释性强,且一般需要人工定义规则。
隐式画像可以挖掘出用户的隐含意图、兴趣。
3.3 召回
一般来说,图文、视频、广告场景下用户推荐的候选集(如视频池、文章池、广告素材库等)的条目规模可达百万到上亿。从大量的候选条目中获取用户喜欢的、热点的、符合产品调性的条目,是推荐需要解决的重要问题。召回模块是这个选择过程的第一步,负责从候选集中获取条目,供后续排序使用。
3.3.1 召回分类
召回类型主要可以按照以下维度进行分类
基于是否个性化分类
非个性化召回:热点召回、冷启动召回、投放类召回
个性化召回:兴趣类召回、I2I召回、基于向量(ANN)的召回
基于数据分类
基于用户数据(画像、兴趣)召回、基于候选条目的召回、基于上下文的召回
当然这里分类标准并不唯一。随着业务发展,也会出现很多糅合多种特性的召回,比如u2ui、u2i2i、u2tag2i。这里的u(用户)和i(候选条目)的关系,简单的,可以使用用户的行为历史(播放、点赞、互动等)来简单的表示,也可以通过用户行为日志、画像和item候选集训练模型,在线通过模型产出用户向量、item向量,通过计算向量相似度来表示。后续将在召回篇详细说明,这里不展开讨论。
3.3.2 召回可能面临的问题
召回不是一个简单的从倒排中获取item后汇总的一个服务。其可能面临以下问题:
a. 高扇出
在一个推荐场景下,召回的分类繁多,每种分类又可能根据不同的候选集、兴趣等级、分类等级、过滤规则等不同维度而产生多路召回。从而导致一个场景下会存在几十到几百个召回(注意这里不是召回结果数量,而是召回的数量)。因此一次推荐请求到召回模块,从在线请求的角度看,其扇出是很大的。如果召回模块设计不合理,往往会造成请求耗时长尾明显。
b. 逻辑复杂
召回阶段也会存在多样性、过滤等机制。这里可以通过将复杂逻辑算子化,使得逻辑实现复用,避免烟囱式开发。
以上问题可以通过合理的设计召回在线服务调用流程,以及将逻辑下沉到合适的模块中解决。具体可行的实施将在召回篇文章展开。
3.4 粗排
粗排模块在召回和精排之间,召回种类、数量较多,整体产出的item数量通常也可达到千到万级。由于精排所需的算力通常较高,耗时较长,因此如果将召回全量结果交给精排进行打分预估,在线服务整体耗时会不可控。因此在精排之前先进行粗排,将全量召回结果截断一部分,从而保证精排模块的耗时稳定。粗排的功能定位是平衡性能
与推荐的效果
,粗排的预估精度比召回好,性能比精排好。(当然如果item数量不多,不使用粗排,推荐整体预估精度应该更好。因为现实情况下模型的AUC一定小于1。通俗的讲多增加1个模块,就有可能在这个模块中将好的推荐结果淘汰掉。想要推荐结果最精确,一定是直接对全量候选集直接过精排模型,选取打分top的结果,显然在目前的算力条件和服务架构下,这是不可能的)
3.4.1 当前主流的粗排实现
![粗排发展阶段]](https://img2023.cnblogs.com/blog/689056/202308/689056-20230824103022603-1724306168.png =500x)
如图所示,粗排大致为以下四个阶段的发展。阶段1:点击数/曝光数 等只依赖item统计量作为打分依据。阶段2:LR模型。这两类粗排相对实现都比较简单,现在基本已不再使用。
阶段3:双塔结构粗排:两个塔分别输入用户特征和item特征,经过深度网络计算后,产出用户向量和item向量,再通过内积、cosine等相似度计算方法得到用户和item的相似度打分
阶段4:非双塔粗排:非双塔粗排优点是可以使用交叉特征。模型可以输入user向量+item向量+用户特征+item特征+交叉特征到MLP网络,计算得到粗排分
两种架构各有优劣,双塔粗排工程实现清晰,耗时低。非双塔粗排由于可使用交叉特征,效果一般好于双塔粗排,但是耗时也会较高,通常需要工程、算法的进一步优化。
3.4.2 粗排结果的多样性保证
粗排除了考虑预估的精度和性能,还需要考虑item的多样性。实际上多样性这一因素需要在各阶段
包括召回
、粗排
、rerank
都要考虑,避免推荐结果同质化,用户兴趣越来越窄。
在粗排阶段,多样性可以通过以下方式保证:
召回分队列过粗排:不同类型的召回结果,分别通过不同的粗排队列进行粗排计算,队列之间粗排打分相互独立,保证每种类型的召回都会出现在粗排结果中
粗排内的多样性筛选:在粗排打分计算后,从item正排获取item分类等属性,并据此利用多样性筛选方法(MMR,DPP等),进行多样性筛选。
3.5 精排
精排模块的功能定位简单描述:利用有限的算力,对候选item进行打分,从中选取用户可能最感兴趣的item。精排是推荐系统中十分重要的一个模块,好的精排对场景的指标提升有显著的作用。
从算法视角看,推荐精排向着多目标、多任务、多场景发展。同时精排模型也越来越复杂,从传统的机器学习模型(FM/FFM/GBDT+LR/MLR)到深度学习模型(WDL/DCN/DIN/DIEN/BST/DSIN/MIMN/SIM/CAN),对算力的要求也更高。多种手段、更多的特征、更复杂的模型通常会给推荐指标带来提升,但是同时对线上服务的压力也更大。
聚焦工程方面,需要在有限的算力和耗时要求下,支持更复杂的模型、更高的预估精度、更多的特征(这里并不一定是模型越复杂、特征越多,推荐效果就越好。而是工程在有限算力和耗时的限制下能支持的算法复杂度的上限,决定了算法效果的上限)。这里可以通过以下方式来提升精排模块的吞吐,提高精排支持的模型复杂度:
- 通过粗排,限制精排模块的item输入数量。输入精排item数量越少,支持的精排模型复杂度可以相应的提高。
- 通过并发计算(SIMD、对精排item分batch多线程并发计算),降低精排整体预估耗时。
- 利用GPU在数学和矩阵运算上的优势,使用GPU进行预估。这里已经有成熟的方案,如Tensorflow-GPU、TensorRT等。其内部也包括了多种加速和优化方式,例如可使用FP16/INT8,对模型进行压缩,提升计算速度。tensorrt通过对tensor的融合,减少kernal launches的次数。
3.6 重排
精排需要更精确的推荐用户感兴趣的、可能点击、阅读播放的item,如果直接将精排结果取top推荐给用户,则可能导致用户兴趣陷入到一个狭窄的范围,收到的正负样本区别不大。重排就是为了避免出现前述情况,保证更好的用户体验。重排内包括了对item的多样性选择、打散、去重、各种产品算法策略等。其往往包括了多种复杂的逻辑。因此需要通过合理高性能的方式组织逻辑的执行。通常可以通过将逻辑算子化的方式,组织重排逻辑的执行。
如图所示,重排模块将各种多样性、打散等策略封装为算子,并通过配置文件的方式,通过策略算子名将算子组织为逻辑,重排服务执行时,rerank逻辑执行引擎可通过执行配置文件对应的op链,实现各种策略的执行。
3.7 特征服务
特征是预估的基础,高性能、稳定、一致的特征处理流程和服务可以给在线预估提供快速的特征值获取、抽取流程,为模型训练提供良好的输入样本,从而保证推荐预估和训练流程持续正向的迭代。
特征服务主要包括特征获取、特征抽取,以及特征回流功能。特征服务处理的对象主要包括用户特征、上下文特征、item特征,以及前述特征的交叉特征。
对于特征处理流程,在线、离线特征不一致,会造成在线预估的输入和离线训练样本有差异,进而导致推荐的效果不及离线评估的情况。特征一致性主要包括以下两个方面:
3.7.1 特征实时上报(回流):数据一致性
特征实时上报流程将推荐结果对应item的正排信息、请求中的用户画像、上下文信息,连同推荐结果一起通过回流服务,经过消息队列等组件,进行落盘。用于离线模型的训练。通过特征回流的方式实时进行上报,可以保证训练样本获取到的特征和线上预估时获取的特征的数据一致性,避免特征穿越。
如图所示,通过样本回流服务,实时的将本次请求的样本落盘上报,在线离线避免数据不一致。
3.7.2 在线离线统一特征抽取:特征处理方式一致性
除了特征数据一致性,特征处理流程还需要保证在线、离线对特征的处理方式一致。例如一个常见的特征抽取方式:对特征string进行hash,需要在线、离线的hash方法一致,从而保证在线、离线得到的特征处理结果一致。可以通过在线、离线使用同一套特征抽取库实现特征处理一致性。
3.8 正排
在正排模块中,通过item id,可以获取到item的属性。item属性来源和类型会是多样的。如
- 较为稳定的item属性信息,如分类、标签、创作者 id等
- item正排的某个字段扩展的其他属性表,如创作者对应的创作者等级、创作者类型等
- 需要实时收集的item统计信息,如点赞数量,播放、曝光数量和时长
这些正排不同数据通常需要从不同来源中获取。正排服务需要将这些不同来源的正排信息进行整合,通过统一的接口其他服务提供获取正排的功能。
这里有一种朴素的实现方法:每个表按key保存在kv获db中,需要获取正排字段时,按照表、字段获取对应的正排。
这种实现有以下问题:
实时性无法完全保证:数据源->KV->CACHE,整体链路较长。且由于cache的存在,部分正排字段更新实时性较差。
服务质量有波动:正排服务质量依赖kv,扇出高,缓存击穿时耗时可能存在波动(这种情况可以用缓存异步热更新解决)
正排服务绑定多个正排数据kv+cache的实现方式,虽然接入简单,但是其实时性、服务耗时均无法达到最优。所以可以考虑不再依赖KV/cache,而是将正排数据直接在正排服务内构造,通过接入数据流的方式进行item的实时更新。详细内容在正排/倒排篇文章展开
3.9 倒排
正排通过item查找item的特征,倒排是通过特征找到item。倒排召回数据的来源。简单讲不同的召回实际上是为了取某些与兴趣、tag、或者item相关的item。多路召回结果合并,共同作为后面排序的候选集。倒排提供了兴趣->item
、tag->item
、item->item
等类似关系的索引。
倒排的第一步是根据tag获取item,之后往往需要对这些item,根据其属性进行过滤。这又需要获取item的正排。所以倒排服务往往和正排服务是紧密关联的。有读者可能会有疑问:为什么不在召回获取到倒排结果后,统一进行过滤?有以下原因:
- 不同召回的对召回过滤条件不同,全放到召回过滤会让召回逻辑很重
- 如果在返回召回后进行过滤,那么可能存在倒排的结果,被召回的过滤逻辑过滤很多,从而导致召回的item数量不足。如果扩大需要获取的倒排item数量,那么倒排返回多少item,能在召回过滤之后的数量满足指定的召回数量,这个数字不好把握。因此需要将过滤条件前置。
如何在倒排组织复杂的过滤条件,如何处理多级tag/兴趣/item之间的级联,后续再正排/倒排篇中展开。
3.10 曝光过滤
在推荐全流程中,曝光过滤模块存在感较低。但实际上曝光过滤模块对推荐效果有很大的影响。
曝光过滤有两个子模块:
- 曝光去重:遍历候选集,对于重复的item再进行过滤
- 曝光写入:将已经曝光给用户的item计入到用户的曝光过滤buffer中
笔者在使用不同app时,时长会出现刷过的文章、视频又重复出现了,十分影响用户体验。能将曝光过的item及时过滤(实时性),又不浪费推荐的结果(准确性),实际并不简单。及时过滤要求已经曝光过的item要在下一次请求时获取的曝光过滤信息中就能获取到,不浪费推荐结果要求写入曝光过滤buffer的item,是真实被用户曝光的。
为了满足曝光过滤的实时性和准确性,需要使用后台曝光+真实曝光
结合的方式,将候选item对两个曝光过滤buffer进行过滤。首先解释一下什么是后台曝光
、真实曝光
。这里要区分一下一次请求推荐系统返回的结果
和客户端给用户曝光的item
。例如一次请求,推荐系统返回给了客户端10个视频,用户看了2个视频,就关掉了app,剩下8个视频并没被曝光。这里推荐返回的10个视频就是后台曝光,用户看的2个视频是真实曝光。
这里思考一下为什么要结合后台曝光+真实曝光?
后台曝光的优点
:实时性好,推荐返回结果的同时就可以写入曝光,下一次推荐请求时,就可以用到。
后台曝光的缺点
:如例子讲解,推荐系统返回给了客户端10个视频,用户看了2个视频就关掉了app。有8个视频没被曝光,但是却被写入了后台曝光。这些item的推荐质量可能很好,但是不会被再次曝光了。
真实曝光的优点
:是客户端实际提供给用户的item,准确性好,无推荐item浪费。
真实曝光的缺点
:可靠性不如后台曝光,对客户端的实现有一定要求。比如需要考虑真实曝光的上报时机,最精确的是每曝光1个item,就上报1个。且实时性不如后台曝光,从上报到写入,要经历客户端->后台->消息队列->曝光写入,整个流程一般也会有秒级~分钟级的延迟。如果用户连续下滑视频,如果只用真实曝光,一个被曝光的item在写入真实曝光前,就可能重复曝光。
综合后台曝光的优缺点,所以实现一般会结合长期真实曝光+短期后台曝光
用于曝光过滤
参考资料
论文笔记 | 阿里新一代粗排系统:COLD - 知乎 https://zhuanlan.zhihu.com/p/467027806