最近在整理推荐相关的资料,如有错误烦请指正,不胜感激。
排序学习(Learning to Rank, LTR)是搜推算法中的重要一环,主要解决的是排序问题。传统的机器学习或深度学习方法主要解决的问题是分类或者回归问题,比如对一个样本数据预测对应的类别或者预测一个数值分值。而LTR解决的是一个排序问题,对一个list的item进行一个排序,所以LTR并不关注这个list的每个item具体得多少分值,更关注所有item的相对顺序。
本文主要对LTR具有代表性的三种排序学习方法单点法(pointwise)、配对法(pairwise)、列表法(listwise)进行阐述。三种方法并不是特定的算法,而是排序学习模型的设计思路,主要区别体现在损失函数、标签标注方式和优化方法的不同。
1 pointwise: 单点法
1.1 交叉熵损失
假设为推荐系统中的商品推荐场景:用户对于曝光sku的交互行为有:曝光未点击、点击、加购、订单等,根据重要性划分label,分别为0, 1, 2, 3。pointwise通过建模单个用户和sku之间的关系,通常转化为分类任务中的多分类问题来处理。在具体实现上,通常使用交叉熵损失函数,对每个样例单独计算损失。
其中,
- :类别数量
- :符号函数,如果样本 的真实类别等于 则取 ,否则取
- :观测样本 属于类别 的预测概率
1.2 优缺点
pointwise简单粗暴,但是也存在一些问题:
- pointwise只考虑单点的label分类,而ltr其实是一个排序任务,两个样例之间也存在相对顺序,而pointwise并没有考虑到样例之间的相关性和差异(例如负样本之间的相关性,正样本与负样本之间的差异),这也就是pairwise的优化的点;
- 由于真实场景下负样本数量是远多于正样本的,这种建模方式容易使模型倾向于对负样本进行建模,而这些样本往往是排序靠后的对用户影响小的样本,而真正影响用户体验的排序靠前的样本没有被模型更多的关注,这也就是listwise优化的点。
2 pairwise:配对法
与pointwise不同的是,pairwise考虑两两item之间的排序结果,即pairwise倾向于将不同label之间的预测得分距离拉远,相同label之间的预测得分距离拉近,建模的是相对距离;而pointwise对单个item预测值与真实label之间距离进行建模,将两者之间距离拉近,建模的是绝对距离。pairwise相当于对pointwise的样例进行两两组合,形成比较关系,当一个item比另一个item相关排序更靠前的话,就是正例,否则是负例。
Pairwise有很多的实现,比如Ranking SVM,RankNet,Frank,RankBoost等。推荐中使用较多的是贝叶斯个性化排序BPR损失。
2.1 RankNet
RankNet损失函数通过考虑两两item之间的排序结果,并期望当前user更感兴趣的item排在更靠前的位置。为实现此目标,引入一个指示函数:
其中,
- 和 分别是 item 和 的真实标签
为了便于构造loss,这里对这个函数稍加修改,将其映射成一个范围为的概率,处理方式为:,得到:
这样就能比较容易地构造交叉熵损失了,假设 item 和 的对于打分为 和 ,则:表示 排在 前面的概率,因此有损失函数如下:
通过SGD随机梯度下降算法就可以优化求解模型参数权重了。在预测的阶段,对输入的是每个item得到模型预测分值,根据item对应的分值就可进行排序结果。
为了便于下文LambdaRank和LambdaMART的理解,对损失函数进行进一步求解。
由于:
带入原loss函数,并化简有:
接下来对求偏导,有:
这里的 即为LambdaRank和LambdaMART中所提到的Lambda。
对于集合中的样本,总的loss记为:
集合I将用于RankNet的加速训练公式的推导,对于一个user或一个guery,所有相关item/doc组成的pair对,每个pair对仅在中出现一次,不能重复出现,即和等价,且。为方便起见,我们假设中的item/doc对均满足,即相关性大的item/doc下标i在前,相关性小的item/doc下标在后。故对于中所有的item/doc pair ,均满足,则有:
其中,
上式含义是:对于item/doc :我们首先找到所有相关性排在item/doc 后面的 item/doc ,并找到所有相关性排在item/doc 前面的item/doc 。对所有的求和,其组成了上式的第一项,对所有的求和,其组成了上式的第二项。由于第一项和第二项的求和符号互不关联,所以第二项中的可改为。
2.2 FRank
对于上述目标损失函数,FRank进行了一点小小的改进,试验效果取得了更好的结果,新定义的损失函数如下:
如下是loss的函数图像对比情况:
2.3 LambdaRank
RankNet 以优化逆序对数为目标,并没有考虑位置的权重,这种优化方式对 AUC 这类评价指标比较友好,但实际的排序结果与现实的排序需求不一致,现实中的排序需求更加注重头部的相关度,排序评价指标选用 NDCG 这一类的指标才更加符合实际需求。而 RankNet 这种以优化逆序对数为目的的交叉熵损失,并不能直接或者间接优化 NDCG 这样的指标。
对推荐系统来讲,显示在页面前几的的推荐结果至关重要,因为这些item会优先展示给用户。传统的RankNet的缺陷此时就暴露出来了,RankNet追求的是更少的逆序对数量,也就是整体的损失情况,而很多实际的应用中,我们往往只关心最前面的几条结果。这就是LambdaRank的出发点。
给定一个query,上图为排序结果展示,其中蓝色的线表示的是相关的item/doc,灰色的线表示不相关的item/doc,那么在左图,有13个pair对错误,而在右图中,有11个pair对错误,在RankNet,右图的排序结果要比左图好,但是在信息检索指标中,比如NDCG指标,左图的效果比右边更好。
我们知道 NDCG 是一个不连续的函数,无法直接优化,那 LambdaRank 又是如何解决这个问题的呢?
我们必须先有这样一个洞察,对于绝大多数的优化过程来说,目标函数很多时候仅仅是为了推导梯度而存在的。而如果我们直接就得到了梯度,那自然就不需要目标函数了。 于是,微软学者经过分析,就直接把 RankNet 最后得到的 Lambda 梯度拿来作为 LambdaRank 的梯度来用了,这也是 LambdaRank 中 Lambda 的含义。 LambdaRank 其实是一个经验算法,它不是通过显示定义损失函数再求梯度的方式对排序问题进行求解,而是分析排序问题需要的梯度的物理意义,直接定义梯度,即 Lambda 梯度。有了梯度,就不用关心损失函数是否连续、是否可微了,所以,微软学者直接把 NDCG 这个更完善的评价指标与 Lambda 梯度结合了起来,就形成了 LambdaRank。
可以这样对Lambda梯度进行理解:lambdaRank中的lambda其实就是 RankNet中的梯度,代表下一次迭代优化的方向和强度,累加其他所有排序项的影响得到。
可以理解为item/query在排列中移动的方向和力度。也就是说:每条item/doc移动的方向和趋势取决于其他所有与之 label 不同的item/doc。由此可见,lambdaRank的主要突破点是:分析了梯度的物理意义;绕开了损失函数,直接定义梯度。
2.4 BPR
贝叶斯个性化排序借鉴了矩阵分解的思路。
这部分准备单独开一篇文章写(未完待续)。
2.5 优缺点
基于pairwise的排序算法后面还有很多工作进行了改进,并取得了不错的效果,在这里就不一一介绍了。pairwise相对pointwise,不再假设绝对的相关性,而是有了相对的关系,但是排序的性质还没有完全在模型里得到体现。比如,如下两组排序,从pairwise来看,准确率是一致的,但是从整个排序效果来看,第二组是比第一组排序效果好的:
说明 | 理想的排序结果 | 第一组排序结果 | 第二组排序结果 |
---|---|---|---|
(p: perfec), (g: good), (b: bad) | p g g b b b b | g p g b b b b | p g b g b b b |
3 listwise: 列表法
无论是pairwise还是pointwise,都是将每个item独立看待,忽视了整体的关系。对于每一个user,我们要做的是对其所有的items按照相关性进行排序,要考虑整体的结果,这就是listwise的主要动机。listwise以优化整体的排序结果为目标,而不是仅仅关注绝对打分或者是两两之间的排序结果,从而大多数时候,listwise方法能够得到相对更好的效果。
列表法排序学习有两种基本思路。第一种称为 Measure-specific,就是直接针对 NDCG 这样的指标进行优化。目的简单明了,用什么做衡量标准,就优化什么目标。第二种称为 Non-measure specific,则是根据一个已经知道的最优排序,尝试重建这个顺序,然后来衡量这中间的差异。
代表算法有:
- 基于 Measure-specific 的 SoftRank、SVM-MAP、SoftRank、LambdaRank、LambdaMART;
- 基于 Non-measure specific 的 ListNet、ListMLE、BoltzRank。
3.1 Measure-specific: from RankNet to LambdaRank to LambdaMART
直接优化排序指标的难点在于,希望能够优化 NDCG 指标这样的 “理想” 很美好,但是现实却很残酷。NDCG、MAP 以及 AUC 这类排序标准,都是在数学的形式上的 “非连续”(Non-Continuous)和 “非可微分”(Non-Differentiable)。而绝大多数的优化算法都是基于 “连续”(Continuous)和 “可微分”(Differentiable)函数的。因此,直接优化难度比较大。
-
第一种方法是,既然直接优化有难度,那就找一个近似 NDCG 的另外一种指标。而这种替代的指标是 “连续” 和 “可微分” 的 。只要我们建立这个替代指标和 NDCG 之间的近似关系,那么就能够通过优化这个替代指标达到逼近优化 NDCG 的目的。这类的代表性算法的有 SoftRank 和 AppRank。
-
第二种方法是,尝试从数学的形式上写出一个 NDCG 等指标的 “边界”(Bound),然后优化这个边界。比如,如果推导出一个上界,那就可以通过最小化这个上界来优化 NDCG。这类的代表性算法有 SVM-MAP 和 SVM-NDCG。
-
第三种方法则是,希望从优化算法上下手,看是否能够设计出复杂的优化算法来达到优化 NDCG 等指标的目的。对于这类算法来说,算法要求的目标函数可以是 “非连续” 和 “非可微分” 的。这类的代表性算法有 AdaRank 和 RankGP。
由于推荐中使用较多的 Listwise 方法是 LambdaMART,因此主要进行LambdaMART的讲解。LambdaMART 是一种 Listwise 类型的 LTR 算法,它基于 LambdaRank 算法和 MART (Multiple Additive Regression Tree) 算法,将排序问题转化为回归决策树问题。MART 实际就是梯度提升决策树(GBDT, Gradient Boosting Decision Tree)算法。GBDT 的核心思想是在不断的迭代中,新一轮迭代产生的回归决策树模型拟合损失函数的梯度,最终将所有的回归决策树叠加得到最终的模型。LambdaMART 使用一个特殊的 Lambda 值来代替上述梯度,也就是将 LambdaRank 算法与 MART 算法加和起来。
考虑到 LambdaRank 是基于 RankNet 算法的,所以在搞清楚 LambdaMART 算法之前,我们首先需要了解 MART、RankNet 和 LambdaRank 是怎么回事。其中,RankNet已经在前文进行讲解。
3.1.2 LambdaMART
LambdaMART是Lambda和MART(Multiple Additive Regression Tree)组成,前文中对Lambda进行了介绍,根据导数公式可知,损失函数定义如下:
这里的表示的信息检索指标,比如NDCG等。损失函数的一阶导数为:
其中,
那么二阶导数为:
LambdaMART模型是由许多棵决策树,通过Boosting思想组成,每颗决策树拟合的是损失函数的梯度,其中叶子节点的权重具体求解涉及一阶梯度和二阶梯度。而在这里求解的梯度用lambda方式求解,求解步骤如下:
其实就是求解一阶和二阶导数用lambda替代,算法的逻辑和求解boosting算法逻辑一致。
3.2 Non-measure specific: ListNet & ListMLE
Non-measure specific这种思路的主要假设是,已经知道了针对某个搜索关键字的完美排序,那么怎么通过学习算法来逼近这个完美排序。我们希望缩小预测排序和完美排序之间的差距。值得注意的是,在这种思路的讨论中,优化 NDCG 等排序的指标并不是主要目的。这里面的代表有 ListNet 和 ListMLE,下面进行介绍。
3.2.1 listNET
对于单个user,假设上游传入的items共有 个,即, 对应给出的打分logits为,真实标签为,假设有 个user,那么listNET的损失函数可以构造为:
其中:
- : 对于真实标签,第 个item的Top1概率
- : 对于预测结果,第 个item的Top1概率
对于此处提及的Top1概率,接下来给出详细介绍。
首先来看Permutation Probability:我们将得分先通过 映射成概率,并进行从高到低进行重新排列,用 表示这个排列中第 个位置是哪一个item,则items任意排列对应的概率 为:
计算出所有groundtruth的排列概率,两个概率分布之间可以利用KL散度最小化差异,从而完成训练。但是,这种方法有一个严重的问题,对于一个长度为n的列表,其全排列有n!种,如果n过大,计算资源过高,显然是不合理的。为了解决这个问题,于是有了Top1概率。
接下来看Top One Probability:Top1概率指的是,某个item排在第一位的概率。对于索引为 的item,它的Top1概率就是:所有以 为第一个元素的排列的概率之和。表达式如下:
这样,我们可以直接计算Top1概率而不需要计算所有排列的概率,极大减少了计算量。可以看到,损失函数与BCE Loss相似,不同的是每个维度上的数值代表的含义不同:分类任务中的score代表的是输入属于当前维度对应类别的概率;ListNet中的score代表的是当前维度对应的item的Top1概率。
3.2.2 KL散度/JS散度
除了使用上面的交叉熵损失作为listNet的损失函数之外,还可以直接使用KL散度或者是JS散度,目的都是为了使得各个items预测的结果与真实的结果更加接近。
- KL散度:
- JS散度:
这两种方法其实本质上都是用KL散度或KL散度的变体,其实现简单,且计算速度快,但是也存在一定的局限性:
- KL散度是用来衡量两个概率的分布的差别,而真实的推荐场景中,其实只要满足预测rank的顺序与真实rank相同,它的loss就应该为0,并不需要强调概率接近;
- 计算KL损失需要提前设计一个真实的排序得分,比如:下单,加购,点击与曝光分别为3,2,1,0,然后使预测rank值尽量与这个分布相近。然而这个分布是人为定义的,不是真实的分布。
3.2.3 listMLE
在listNet中,我们是在最小化预测顺序与真实顺序之间的差异,为了实现这个目的,我们构造了概率分布,然后最小化了两个概率分布的差异。
其实还有一个更加直接的方法:我们以真实标签顺序为目标,最大化预测结果排序与目标一致的概率。也就是说,只需要定义出预测结果按照目标顺序来排列的概率,然后直接使用负对数来优化。表达式如下:
其中,
- : 表示将预测得分按照真实标签的顺序排列后的列表
对于概率 的计算,通常实验Plackett-Luce模型,接下来进行介绍:
假设用 表示目标排序结果,将得分按照真实标签的顺序排列,则得到 ,具体表达式如下:
对于概率的结果,还要使用负对数优化的主要原因是,概率连乘导致数值过小,使用对数进行缩放,加之 区间的数对数后结果为负数,因此取相反数。
3.3 优缺点
listwise的优点很好总结:Listwise将所有item按照相关性进行排序,以优化整体的排序结果作为目标,而不仅仅关注绝对打分或者两两之间的排序结果,因此在大多数推荐任务中,有着更好的效果。
但是之前面试被问到一个问题,就是listwise存在什么缺点,这个问题之前一直没有考虑过,查阅了一下,主要有以下几点:
- 计算复杂度:一些算法需要基于排列来计算 loss,从而使得训练复杂度较高,如 ListNet;
- 位置信息:位置信息并没有在 loss 中得到充分利用,可以考虑在 ListNet 和 ListMLE 的 loss 中引入位置折扣因子(这一点我不太能理解,list排序进行概率连乘的时候不就使用了位置信息吗);
- Listwise方法是直接优化排序列表,输入为单条样本为一个序列。通过构造合适的度量函数衡量当前item/doc排序和最优排序差值,优化度量函数得到排序模型。由于度量函数很多具有非连续性的性质,优化困难;
- 假设过强,例如在推荐场,所有样本都要满足订单大于点击,这样的建模可能会使模型的学习偏向极端。
参考文献
- RankNet: icml.cc/Conferences…
- FRank: www.microsoft.com/en-us/resea…
- SoftRank: www.microsoft.com/en-us/resea…
- AdaRank: www.microsoft.com/en-us/resea…
- ListNet: www.microsoft.com/en-us/resea…
- ListMLE: auai.org/uai2014/pro…
- SoftRank: www.microsoft.com/en-us/resea…
- AdaRank: www.microsoft.com/en-us/resea…
- LambdaRank: www.microsoft.com/en-us/resea…
- LambdaMART: www.microsoft.com/en-us/resea…
- ListNet: www.microsoft.com/en-us/resea…
- BPR loss: arxiv.org/ftp/arxiv/p…