召回综述

8/2/2021 召回综述

# 假设物品库数量达到百万级别,如何设计方法从这个数量级别的物品中推荐给用户top10的物品,同时可以减少计算的压力?

在推荐系统的排序层,一般会使用复杂模型,利用多特征进行精准排序,而如果直接对百万量级的候选集进行逐一推断的话,则计算资源和延迟都是在线服务过程无法忍受的。

因此可以加入召回过程,利用少量的特征和简单的模型或规则进行候选集的快速筛选,减少精准排序阶段的时间开销。

# 推荐系统中为什么要有召回?在推荐系统中召回和排序有什么异同?

首先,在实际应用中,推荐算法往往是在线上使用,可用的设备资源和响应时间都是有限的。而整个物品集的规模往往十分庞大,在线上对大量物品进行排序是对性能的较大挑战,很难实现。召回可以视为一个粗排序的过程,这个过程的主要目的是在有限的资源条件下提供尽可能准确的一个小候选集,从而减轻排序阶段的计算负担和耗时。

其次,即使资源和时间足够对整个物品集进行扫描,先使用较为简单的方法进行召回往往也是比较有利的,先进行召回意味着可以排除大部分无关物品,从而允许在排序阶段对更小的候选集使用更多的特征和更复杂的模型,以提高排序的准确率。

召回层一般待计算的候选集合大、速度快、模型简单、特征较少,尽量让用户感兴趣的物品在这个阶段能够快速被召回,即保证相关物品的召回率。工业界主流的召回方式是采用多路召回策略,采用多个简单策略叠加的方式得到最终的召回结果。

排序层的首要目标是得到精准的排序结果,需处理的物品数量少,可利用多特征,使用比较复杂的模型。

# 排序为什么比召回更受关注?

排序

  • 排序,特别是精排,处于整个链条的最后一环,方便直接对业务指标发力。比如优化时长,只要排序模型里,样本加个权就可以了。
  • 排序由于候选集较小,所以有时间使用复杂模型,各种NN,各种Attention,能加的,都给它加上,逼格顿时高大上了许多。
  • 用于排序的基础设施更加完善,特征抽取、指标监控、线上服务都相对成熟。

召回

  • 一个系统中不可能只有一路召回,平日里相互竞争,召回结果重合度高,导致单路召回对大盘的影响有限;好处是,相互冗余,有几路出问题,也不至于让排序饿肚子。
  • 召回处于整体推荐链条的前端,其结果经过粗排、精排两次筛选,作用于最终业务指标时,影响力就大大减弱了。
  • 受限于巨大的候选集和实时性要求,召回模型的复杂度受限,不能上太复杂的模型。平日里,最简单的基于统计的策略,比如ItemCF/UserCF表现就很不错,改进的动力也不那么急迫。
  • 用于召回的基础设施不完善。比如,接下来,我们会提到,召回建模时,需要一些压根没有曝光过的样本。而线上很多数据流,设计时只考虑了排序的要求,线上的未曝光样本,线下不可能获取到。

# 召回模型有什么显著区别于排序模型的特点?

  • 特征选择上

召回面对的候选item是百万级、千万级,一般此类召回模型都是双塔型的,以便单独生成user embedding和item embedding,喂入的特征禁止含有user/item之间的交叉特征

  1. user特征喂入user tower得到user embedding,item特征喂入item tower得到item embedding。
  2. 离线时,先将item embedding喂入FAISS建立索引,
  3. 线上召回时,拿user embedding去FAISS里进行top-k近邻搜索,找到与其最接受的item embedding
  • 样本选择上
  1. 正样本没有太多的争议,以内容推荐为例,选“用户点击”的item为正样本。最多考虑一下用户停留时长,将“用户误点击”排除在外
  2. 负样本的选择比较有讲究。如果说排序是特征的艺术,那么召回就是(负)样本的艺术。
    • 原则之一就是不能(只)拿“曝光未点击”做负样本,负样本的绝大部分应该由“随机负采样”生成。
    • 原则之二就是要打压热门item
  • 损失函数上

因为绝大多数负样本是通过随机采样生成的,含有一定的噪声,因此召回不适合采用CTR预估常用的pointwise cross-entropy loss,而经常采用pairwise loss,比如margin-based bpr loss或hinge loss。

因此喂入模型的样本,区别于排序中常见的<user, item, label>,而是三元组<user, item+, item->,预测的目标是MatchScore(user, item+)要远高于MatchScore(user, item-)

# 推荐系统中有哪些场景是无法获得真负样本的?如何解决?

召回。召回的候选集一般是百万级、千万级,其中绝大多数item都从未给用户曝光过,虽然没有点击,但是你不能说用户就一定不喜欢。

个性化推送。App内部的推荐,我们可以根据埋点,比较容易获知用户滑过、忽略了某些推荐内容,所以比较有信心拿那些item作为负样本。但是在推送场景下,我们很难知道用户未点击的item是用户真的不喜欢,还是压根没看见(像我对于大多数推送,就是瞟了一眼桌面上的手机,这种“忽略”行为是无法埋点的)。所以,你也不能放心地将所有未点击的item都当负样本。

在这种无法获得“真负”样本的场景下,一般我们通过随机采样来获得负样本。但是,随机采样毕竟引入了噪声,这时,再用CTR预估这种要求“绝对准确性”的算法,就不合适了。所以,在召回或个性化推送场景下,我们一般采用Pairwise LearningToRank(LTR)建模排序的“相对准确性”。就好比,

  • 由于负样本中的噪声,让我预测user不喜欢item-的精确程度,我信心不足;

  • 但是由于item-是从库中几百万的item中抽样得到的,大概率和用户兴趣八杆子打不着,让我预测“user对item+,要比user对item-,喜欢得多一点”,信心更加充足一些。

# 请简述基于embedding的召回方法,优势是什么?

在YouTube推荐系统中利用深度学习网络生成Embedding作为召回层的方法,再加上可使用局部敏感哈希进行快速的Embedding的召回方法在效果和速度上均不逊色于多路召回。

而且,多路召回中使用”兴趣标签“、”热门度“、”流行趋势“、”物品属性“等信息都可以作为Embedding召回方法中的附加信息(side information)融合进最终的Embedding向量中,就相当于在利用Embedding召回的过程中,考虑到了多路召回的多种策略。

Embedding召回的另一个优势在于评分的连续性,多路召回中不同召回策略产生的相似度、热度等分值并不具备可比性,无法据此决定每个召回策略放回候选集的大小,Embeddding召回可以把Embedding召回可以把Embedding间的相似度作为唯一的判断标准,因此可以随意限定召回的候选集大小。

# Facebook的EBR算法是如何选择正负样本的?

来自于《Embedding-based Retrieval in Facebook Search》

正样本:使用的是u2i召回的典型思路,即user与其观看过的视频,在向量空间中是相近的。

负样本:绝大部分负样本依然是通过随机采样得到的。论文明确提出了,召回算法不应该拿“曝光未点击”做负样本。

本文最大的贡献是提供了两种挑选hard negative的方案。Facebook的EBR与百度Mobius的作法非常相似,都是用上一版本的召回模型筛选出"没那么相似"的<user,item>对,作为额外负样本,来增强训练下一版本召回模型。具体做法上,又分online和offline两个版本

  • 在线筛选

    • 假如一个batch有n个正样本对,,那么的hard negative是利用上一轮迭代得到的召回模型,评估与同一个batch的除之外的所有的匹配度,再选择一个与最相似的作为hard negative。
    • 文章还提到,一个正样本最多配置2个这样的hard negative,配置多了反而会有负向效果。
    • 缺点是仅仅采用一个batch中的item作为hard negative的候选集,规模太小,可能还不足够hard。
  • 离线筛选

    • 拿当前的召回模型,为每个候选item生成item embedding,灌入FAISS
    • 拿当前的召回模型,为每个user生成user embedding,在FAISS中检索出top K条近邻item
    • 这top K条近邻item中,排名靠前的是positive,排名靠后的是easy negative,只有中间区域(Facebook的经验是101-500)的item可以作为hard negative。
    • 将hard negative与随机采样得到的easy negative混合。毕竟线上召回时,候选库里还是以easy negative为主,所以作者将比例维持在easy:hard=100:1
    • 拿增强后的负样本,训练下一版召回模型。

# 召回为什么要求具有隔离user与item特征的解耦性?具体怎么解耦?

召回需要面对的是百万、千万级别的海量候选item,如果让每个user与每个候选item都计算交叉统计特征,都过一遍DNN那样的复杂操作,是无法满足线上的实时性要求的。

在特征上,尽管交叉特征的信号很强,但是召回不允许使用“交叉统计特征”。

在模型上,禁止user feature与item feature出现DNN那样的多层交叉,二者必须独立发展,i.e., user子模型,利用user特征,生成user embedding;item子模型,利用item特征,生成item embedding。唯一一次user与item的交叉,只允许出现在最后拿user embedding与item embedding做点积计算匹配得分的时候。

只有这样,才能允许我们

  • 离线时,在user未知的情况下,独立生成item embedding灌入faiss;
  • 在线时,能独立生成user embedding,避免与每个候选item进行“计算交叉特征”和“通过DNN”这样复杂耗时的操作

# 在召回场景下,为什么往往采用Pairwise LearningToRank来构建排序的相对准确性?

排序阶段经常遵循"CTR预估"的方式

  • 样本上,是两条样本
  • loss上使用binary cross-entropy这样的pointwise loss

能够这么做的前提是,其中的是“曝光过但未点击”的“真负”样本,label的准确性允许我们使用pointwise loss追求“绝对准确性”。

但是在召回场景下,以上前提并不成立。以常见的u2i召回为例,绝大多数item从未给user曝光过,我们再从中随机采样一部分作为负样本,这个negative label是存在噪声的。在这种情况下,再照搬排序使用binary cross-entropy loss追求“预估值”与“label”之间的“绝对准确性”,就有点强人所难了。所以,召回算法往往采用Pairwise LearningToRank(LTR),建模“排序的相对准确性”:

# 优化召回Pairwise LearningToRank使用的损失函数可以有哪几种形式?

召回中的样本往往是的三元组形式

模型的优化目标是,针对同一个user,与他的匹配程度,要远远高于,与他的匹配程度。所以Loss中没有label,而存在的匹配分与的匹配分相互比较的形式。

为了实现Pairwise LTR,有几种Loss可供选择。

一种是sampled softmax loss。

这种loss将召回看成一个超大规模的多分类问题,优化的目标是使,user选中item+的概率最高。

user选中item+的概率=,其中是user embeddding,代表item embedding,代表item候选集。

为使以上概率达到最大,要求分子,即user与item+的匹配度尽可能大;而分母,即user与除了item+之外得的item的匹配度之和,尽可能小。体现出上文所说的“不与label比较,而是匹配得分相互比较”的特点。

但是,由于计算牵扯到整个候选item集合,计算量大到不现实。所以实际有花的是sampled softmax loss,即从中随机采样若干,近似替代计算完整的分母。

另一种loss是margin hinge loss,

优化目标是:user与正样本item的匹配程度,要比,user与负样本item的匹配程度,高出一定的阈值。

因为margin hinge loss多出一个超参margin需要调节,因此主要使用如下的BPR Loss。

其思想是计算"给user召回时,将item+排在item-前面的概率",

因为<user, item+, item->的ground-truth label永远是1,所以将喂入binary cross-entropy loss的公式,就有

# 如何评估召回的效果?

最好肯定是线上ab,但是ps资源和线上流量都有限,以下是一些离线评估召回方法:

  • 首先auc高并不代表召回的好,实际上好的召回可能auc低一些,但是会召回出更符合真实分布的内容,实际工作中auc当作参考就好。
  • 拿Top K召回结果与用户实际点击做交集并计算precision/recall,感觉现在大多都是用的这个方法,但是我总感觉极端条件下N路召回全都100%准确率召回率,那其实5路变成1路就好了,而且完全在拟合精排,又陷入了无探索的困境,因为召回的结果未被用户点击,未必说明用户不喜欢,而可能是由于该召回结果压根没有给用户曝光过。
  • 召回diff率。其实我现在还比较喜欢拿这个来预估要不要上线,因为diff高才有bagging一路召回的价值(当然还有是否保送,精排是否能排出来的问题)
  • 人肉debug:最近搞了一个模型,自己经常会拿组里同学的uid来打印召回结果,然后人肉评估一下靠不靠谱。虽然有点蠢,但是确实能帮我验证模型/机制做没做对,也能有个摸底的效果。

# 为什么Word2Vec训练中, 需要对负采样权重开3/4次幂?

https://zhuanlan.zhihu.com/p/144563199