亚马逊AWS官方博客

推荐系统系列之推荐系统召回阶段的深入探讨

之前我们介绍了推荐系统概览,相信大家已经对推荐系统的各个环节有了一定的了解。为了讨论方便,在本文中我们同样假设只用一种个性化推荐方法(而不是分区混合推荐方法),其他方法都作为个性化推荐的召回策略。另外,本文我们讨论的内容都是针对线上推荐系统(它要比离线推荐系统更复杂)。对于个性化推荐的三个阶段,从实际项目的实现上的复杂性以及重要性来说,我认为是这样的:召回阶段 > 重排阶段 > 排序阶段,所以我建议大家不要把最多的时间花在排序阶段。每个阶段是有所区别的,本文只关注召回阶段,重点关注的问题如下:针对当前的某个具体业务场景,是否需要召回,排序和重排三阶段?召回的范式有哪些?召回阶段应该使用哪些种类的特征?采用离线的召回结果还是做实时召回?所有的用户都走这些召回策略吗?如何评价某个召回策略的好坏?需要为了将就复杂耗时的排序模型来削弱召回阶段吗?更多细节以及更详细的内容可以参考我的Github

针对当前某个具体业务场景,是否需要召回,排序,重排三阶段?

首先,我们要从本质上看清楚这三个阶段:

阶段 本质
召回阶段 在资源受限的情况下,尽可能找到当前用户可能感兴趣的尽量大的item候选集
排序阶段 从多种不同召回策略得到的item候选集需要一个统一的打分/排序方法
重排阶段 需要对排序阶段输出的候选集进行人工运营干预来生成最后的推荐列表

另外,从对输入的item数量级的要求上,三个阶段也差别很大:

  • 对于召回阶段,或者在线上的时候直接读取保存在NoSQL中的离线预先计算好的结果,或者使用高性能的线上item向量相似度检索库来获取Top k的结果。因此把整个物品池作为输入是可以的也正是我们所想要的,而物品池的物品可能有几百万甚至上千万。
  • 排序阶段一般使用传统机器学习模型或者深度学习模型,为了让模型在有限时间内能处理完,需要排序阶段的输入item数量即召回的候选集总数量不能很大(一般是千级别)。
  • 重排阶段最后会生成推荐列表,推荐列表一般不会太长,所以这里的输入item数量也就是排序后取的top K的K数量一般是百级别(这里的输入item数量指的是从排序的结果集中选择多少,重排阶段的输入源除了排序后的结果集,在首页推荐场景下,一般还有优选物品集以及需要探索的那些长尾物品集和冷启动物品集)。

如果物品池包括的物品个数是千级别,并且排序模型对全量物品能在要求的处理延迟内处理完,那么根本不需要召回阶段了,直接全量物品排序后接着重排就可以了。从更好的终端用户体验来看,重排阶段任何时候都应该有。而是否有排序阶段,情况比较复杂,和具体的场景以及是否是长尾用户/冷启动用户有关,具体可以参考如下:

  • 对于首页推荐场景的话,一般会有完整的召回,排序,重排三个阶段,首页推荐的时候由于没有办法获得用户当前的意图,一般会使用尽可能多的召回策略,对于长尾用户和冷启动的用户,可能走单独的链路效果更好;对于长尾的用户和冷启动的用户经过召回阶段后,可能不走排序阶段会更好,在重排阶段对召回的结果进行编排以及做探索。
  • 对于详情页推荐或者类似的场景,这个时候用户有了明显的意图,因此相对首页推荐场景来说详情页推荐不会有太多召回策略。如果只有一路召回并且不考虑个性化,这个情况下可以不需要排序阶段;如果只有一路召回但要考虑个性化,则需要一个复杂的算法/模型引入相关的特征来对单路召回的topK的排序结果重新打分排序,这个时候就需要排序阶段;如果使用了多路召回,对于长尾用户和冷启动用户来说,可能不走排序阶段会更好,使用重排阶段对多路召回的结果进行编排,对于非长尾和冷启动的用户,走排序阶段。

在详情页推荐场景下,冷启动的物品可能通过某种召回策略召回了,可能也不适合走排序阶段,使用重排阶段在某些固定位置给召回的冷启动物品更多的一些曝光机会的效果可能更好。在详情页推荐的场景下,一般不会在重排阶段使用探索方式。

小结:不是每个推荐系统的业务场景都需要召回,排序,重排三个阶段!需要根据具体业务场景来判断是否需要召回,排序,重排的任意一个阶段。

召回的范式有哪些?

常用的召回策略在推荐系统概览一文中已经有详细的介绍,这里不再赘述。我们这里来介绍召回的范式,需要从三个维度来考虑:召回策略的数量;单个召回策略下是否有粒度更细分的子策略;召回策略是否分优先级,如果分优先级的话,如何对多个召回策略做融合。按照上面的三个维度,召回的范式有如下的可组合的选择:

  • 单路无子策略召回
  • 单路多个子策略逐层召回
  • 多路无子策略召回;
  • 多路多个子策略逐层召回
  • 逐策略无子策略召回
  • 逐策略多个子策略逐层召回

有些业务场景下,某个召回策略是有多个子策略的。比如对于基于地理位置的应用,可能有一路召回策略是基于地理位置的召回,子策略可以是附近召回、当前商圈召回、当前城市召回等等,子策略需要根据业务场景来设置优先级。这种情况下,按照业务逻辑来说,先做附近召回,如果附近召回的item数量不够再考虑做当前商圈召回,以此类推。

对于逐策略来说,也是给每个不同的召回策略分配了不同的优先级。召回的时候,优先对最高优先级的策略做召回,如果召回的item数量达到要求就结束召回过程,否则剩下还需要召回的item数量用第二高优先级的策略来做,以此类推。

单路无子策略召回可以用于详情页推荐的场景中。多路多个子策略逐层召回或多路无子策略召回以及逐策略多个子策略逐层召回或逐策略无子策略召回则常用于首页推荐的场景中(当然,详情页推荐用多路召回也是可以的)。

对于多路召回,在总的召回item数量确定的情况下需要给每一路分配多少item数量:

  • 不显示区分策略优先级,给每个召回策略分配相同的item数量(比如总的需要召回的item候选集是1万,可以给每个召回策略分配都是1万,最终通过一个简单的打分粗排模型来选择top 1万的打分结果)。这种方法需要用一个模型比如LR逻辑回归模型来学习每路的重要性权重,来对各路召回的结果集打分,最终选择top k的打分结果集送入下游的排序模型。

对这个模型建模的时候,特征就是一共比如30路的multi-hot特征,对于某个item的多个路的召回,那么对应的这些路就是1,其他路就是0,label就是这个item最后是否被点击/感兴趣。在推理时候,多路召回的item走一遍模型,看最后多路召回的所有不重复item的打分,取分数高的就可以。

在给LR模型积累样本的阶段,如果下游的排序模型已经上线,假设总的需要召回的item候选集是1万,并且一共是10路召回策略,那么这个阶段给每路策略固定1000个item数量(如果考虑有重复的item被召回,那么每路策略可以分配比1000更多一些);如果排序模型还没有上线(可能在推荐系统起步阶段),那么假设最后的推荐列表是100个,一共10路召回策略,那么可以给每路策略分配10个item(如果考虑有重复的item被召回,那么每路策略可以分配比10更多一些)。

  • 显示设定每个策略的优先级以及每个策略分配的可召回item的数量。根据最近一段时间统计的某个指标比如点击的召回归因结果来计算出每一路的召回比率(比如7天内整个大盘点击正样本一共1万,归因到热门召回策略的有5000条,那么热门召回策略的召回比率就是50%),然后用总的召回结果条数相乘就得到每一路召回条数(从这里可以看出,对于每一路的召回策略,设定的召回的item条数是随着时间变化的)。

召回归因复杂的地方是,从多路策略或者逐策略召回了相同的itemid,这个 itemid最后归属于哪个策略的问题,可选的有两种方法:一种是多路召回了相同的itemid,那么这个itemid的点击归因到多路上。这种方法的好处是简单直接,直觉上也是合理的。一种是根据每个策略的历史表现来确定优先级,然后重复出现的itemid会分配给高优先级的那个。那每个策略的历史表现又是如何计算的?比如可以使用上面提到的方法,也就是不显示区分策略优先级,给每个召回策略分配相同的item数量,然后用一个模型比如LR逻辑回归模型来学习每路的重要性权重。等这个LR模型训练完以后,就得到了每路策略的重要性权重,从而可以确定每路的优先级。

实际做多路召回的时候,需要考虑在召回的结果不足量(包括因为去重导致的结果不足量)的情况下用高召回率的那路来补足结果。因此,实际落地的时候一般每一路都会尽量比要求的结果多召回一些,目的是在可能需要补足的时候不用进行二次召回。

而对于逐策略召回的话,不需要考虑给每一路分配多少item的困扰。至于选择多路召回还是逐策略召回,需要具体情况具体分析。这两种方法各有优缺点,逐策略召回不用考虑每路分配多少条结果的问题,而多路召回相对来说对每路召回更公平一点,也就是可能召回效果更好(多路召回更多见一些)。两种范式的具体效果如何,最好还是做线上AB test来对比。

另外,这里会涉到一个相关的问题,即总的召回结果的item条数如何确定:这个没有什么特别的规定,确定的时候最好参考下面的两个因素,如果是做线上召回,那么线上召回的latency是否能满足要求;下游的线上排序模型是否可以在latency容许的范围内处理完这么多的item。宗旨是只要上面的两个限定条件能满足,设定的总的召回结果的item条数就应该尽可能多。

一般来说,召回的结果去重后需要进行过滤然后再送给排序阶段:因为存在比如用户把某个item加入黑名单或者显式“踩“过某个物品,为了更好的用户体验,需要维护一个这样的列表,当召回结果去重以后把命中这个list的item也去掉;另外,还可以有一个用户最近一段时间发生过行为的item列表,比如一个小时内点击过的item列表,这样当过滤掉最近发生过行为的item后,客户体验会更好。实践中可能采用bloom filter相关的算法来进行过滤,这样的性能更高。

召回阶段应该使用哪些种类的特征?

在推荐系统中,我们经常会谈到四大类特征:用户侧特征,物品侧特征,上下文特征,交叉特征 。那是否这四大类特征都适用于召回阶段呢?(这里我们不考虑那些和上下文特征比如地理位置特征强相关的应用,这些应用的召回阶段肯定是需要考虑上下文特征的)从下面的表格也能看出来,之所以要区分召回阶段和排序阶段,除了之前提到的他们的输入的item集合的量级差别很大之外,他们使用到的特征也是不同的。

特征类别 介绍 可否用在召回阶段
用户侧特征 用来刻画用户的特征,其实就是用户画像 可以用在首页推荐场景的基于用户画像的召回策略中
物品侧特征 用来刻画物品的特征,其实就是物品画像 可以用在首页推荐场景的基于物品画像的召回策略中;也可以用在详情页推荐场景的基于物品item的表示向量相似度的召回策略中
上下文特征

比如用户登录网站时用的手机操作系统类型,手机型号;

比如用户当前所在的国家;

比如用户使用的APP版本;

比如当天是否是节假日,当天属于什么季节;

比如当天是否有什么突发事件

对于不是上下文特征强相关的应用,一般不考虑在召回阶段使用上下文特征,但是在排序阶段会使用上下文特征。

(这里指的是当前上下文特征,而不是用户行为的历史上下文特征,比如我们在做实时召回模型的离线训练的时候,可以考虑用户历史点击行为序列作为一个特征,点击item时对应的某种历史上下文比如那时使用的APP版本作为另一个序列特征)

交叉特征

包括用户侧特征,物品侧特征,上下文特征这三大类特征之间的特征交叉,以及每类特征内部的特征交叉。

比如手机是IOS系统并且当前所在地是中国这样的特征;

比如性别是女性并且物品是化妆品类目这样的特征

一般来说,可能不需要在召回阶段使用交叉特征这么细粒度的特征,这类特征更适合在排序阶段中使用。

采用离线的召回结果还是做实时召回?

实时召回指的是在线上服务时对于每一个访问的用户进行重新计算来召回top K的item集合,支持做实时召回的模型一般需要把用户的最近的行为建模进去,这样该模型基于用户最新的行为可以计算得到一个新的用户的embedding向量(实时召回是用在首页推荐场景的),而那些item向量则是离线用同一个模型计算后并保存在某个高速向量检索库中比如Faiss。而离线的召回结果指的是离线把每个用户召回的top K的item集合预计算出来保存到某个NoSQL中(比如Amazon ElastiCache for Redis ),线上服务的时候直接从NoSQL中取当前用户的top K的item集合。

据我所知,实时召回的流行是从Youtube DNN召回模型(2016年,如下图所示)发布以后开始的。

Youtube DNN召回模型在离线训练完以后,作者建议把最后一个全连接层的权重矩阵[hiden_unit, item_count]做转置后得到的矩阵的每行对应一个video的output embedding,并把这些video embedding存入到一个近邻索引库中比如Faiss,而user embedding则是在线上根据最新的用户行为特征走一遍模型的前向计算取最后一个全连接层的输出的激活值作为他的embedding。Example age(样本年龄)在训练的时候可能指的是这条样本对应的用户当前这次行为发生的时间离当前训练时间的间隔(paper中作者没有对这个特征讲的很清楚,说加上这个特征模型的效果很好),在线上召回的时候该特征需要设置为0。

至于采用离线的召回结果还是做实时召回,需要根据业务场景来权衡。对于实时召回,需要更复杂的系统架构支撑,比如需要通过一个流式处理引擎来实时获得用户最近的点击序列来更新该用户的点击序列特征并喂给模型;对于离线的召回,线上系统架构实现简单,缺点是时效性没有实时召回那么好。对于某些业务场景来说 ,可能用户的行为没有那么的频繁,那么用小时级别更新的离线的召回方法就比较适合。实际项目中,经常是有一路是实时召回比如Youtube DNN召回模型,其他的是多路的离线召回。

所有的用户都走这些召回策略吗?

对于详情页推荐,召回策略是以物品item为中心,因此所有用户都可以使用同样的召回策略。

对于首页推荐,如下表:

召回策略 适用用户
基于热度/流行度的召回 适用于所有的用户
基于协同过滤的召回 不适合长尾用户和冷启动用户(由于针对的是用户-物品的交互矩阵来建模,对每个用户来说至少需要一定数量的行为,而长尾用户和冷启动用户的行为很少或者没有,所以不适合)
基于物品画像的召回

如果是基于物品整体embedding的召回,那么对于长尾用户理论上也可以使用这个策略(因为他至少有过一次对item的行为,但是由于行为还是很少,所以用户embedding的表达可能不准,召回效果可能就不好),冷启动用户不适合;

如果是基于物品内容的召回,计算过程涉及到用户的行为(至少需要一定数量的行为所谓的稠密行为),因此对于长尾用户和冷启动用户不适合。

基于用户画像的召回

用户行为画像涉及到稠密用户行为,对于长尾用户和冷启动用户不适合;

用户的基本人口统计画像是适用于是所有的用户的,前提是这些人口统计信息是真实正确的;

用户的兴趣画像一般可以应用到长尾用户(因为他至少有过一次行为,可以给他打上兴趣画像了),但是对于冷启动用户,一次行为都没有,兴趣画像可能就不适用了,当然如果该冷启动用户主动给自己打上了兴趣标签的话,兴趣画像的召回也可以用于这样的用户了。

如何评价某个召回策略的好坏?

召回阶段并不是独立存在的,它往往只是整个推荐系统流程的第一步,后面还有排序阶段和重排阶段;而且召回阶段可能还包括多个召回策略。因此对于召回阶段中的某个召回策略没有什么直接的评价,一般也是线上AB test看效果。如果使用机器学习模型来建模的话,对召回策略的评价除了前面提到的线上评价,还包括离线评价(对于比如做实时召回模型的离线评估,一般常用的离线评估指标是AUC)。

需要为了将就复杂耗时的排序模型来削弱召回阶段吗?

一定不可以这样做,不能因为下游的排序模型复杂而反过来削弱召回阶段(比如减少总的召回的item的数量)。召回阶段的重要性要大于排序阶段,因此要让排序模型来适配召回阶段,因此不能刻意去追求复杂的大的排序模型。

另外,在计算广告中复杂的大的排序模型可能并不适合在推荐系统中的排序模型用。比如美团的DPIN深度位置交叉网络模型(用来对位置做debias的模型)是用于广告中的排序模型,它是针对召回后的候选集集合以及全部曝光位置联合建模,所以对于推荐系统可能不适合。因为推荐系统的召回后的结果集一般会比较大比如千级别,而广告召回的结果集一般都很小比如不超过100,而且曝光位置的数量他们可能也有数量级的差别。

总结

推荐系统召回阶段的深入探讨到此就讲完了,本文重点讲解了召回的范式,召回阶段使用的特征种类,召回策略适用的用户群体等,相信大家现在已经对召回阶段有了更深刻的理解。我们接下来会进入到排序阶段,介绍排序任务的样本工程和排序模型的调优实践,感谢大家的耐心阅读。

本篇作者

梁宇辉

亚马逊云科技机器学习产品技术专家,负责基于亚马逊云科技的机器学习方案的咨询与设计,专注于机器学习的推广与应用,深度参与了很多真实客户的机器学习项目的构建以及优化。对于深度学习模型分布式训练,推荐系统和计算广告等领域具有丰富经验。