排序算法(Learn to rank)的一些看法

回来自我隔离期,出不了小区加上倒春寒阴天;疯与快疯之间,重读了微软研究院Learn to Rank几篇经典论文,参考的看了CSDN上不少博主的观点。总觉得对于文章,有些思路上的点没有点透;尝试从排序更根本思路去讲解排序类算法为何如此、以及如此演进。

思路:

排序从冒泡法说起——打分、参考比较、决策

冒泡排序时候每个容器中默认是一个数,所以没有从特征到打分这个步骤

冒泡排序时候两个数据大小比对,其实是用我们认为认定的一套数字随比随大的标准;这个参考物就是我们规定好的那些数的顺序,只是我们日用忽视了这个参考比较过程

冒泡排序当符合我们认为的数的大小顺序就认为是对的(1)不需要调整两数顺序,不符合则认为是不对的(-1)调整两数顺序

Ranknet根据query对Document排序步骤和原本思路和冒泡排序如出一撤:

根据文本、用户特征、query特征利用一个函数对Document打分:F(特征、用户、query)

利用打好序列标签的训练样本做参考,比较打完分的文本顺序

决策,Ranknet通过分类的思路解决,如果打完分的两个Documeng排序和大号标签训练样本顺序是一致则为1,否则为0

这就是Learn to Rank算法的最朴素思路、也是后续lambdaRank、lambdaMART、深度学习模型TF-Ranking的建模框架。

多说一句其实大部分现在看起来高深莫测的技术,其实最朴素的思路都来自于我们了无生趣的生活中;好的想法都是简单朴实但是都是框架级别的存在,后续的变化不过是框架里填入什么东西后者小改动什么东西。

问题:

1、Ranknet只考虑到了两两比较文档对之间序列关系(给的是全局排序,它只学了俩俩关系,视野格局不够)

2、lamdaRank引入NDCG或ERR全局序列参数,但NDCG、ERR是非凸在进行梯度求解时候计算复杂

3、lambdaMART通过MART多项式来你和含NDCG或ERR的lambda梯度解决梯度非凸问题,但对于图像、文本、稀疏特征打分效果不够好

改进:

1、Ranknet梯度和deltaNDCG相乘,引入NDCG全局排序因子

2、用凸函数来拟合梯度,比如用MART多相似来拟合

3、引入对文本、图像、稀疏特征处理能力强的深度学习,替代MART或者浅层线性模型

所有的解决对策,都来源于对问题的认识。看到问题才会有对策,难得在于往往牵一发动全身,如何在简单和有效中tade off。

到此其实文章就可以结束了,因为对于learn to rank具体技术细节的文章有太多,具体可见下文参考。但出于文章完整性考虑,我简单介绍Ranknet、lambdaRank、lambdaMART。

    • 2. RANKNET

    • 2.1 算法基础定义

      RankNet解决如下搜索排序问题:给定query集合,每个query都对应着一个文档集合,如何对每个query返回排序后的文档集合。可以想象这样的场景:某位高考生在得知自己的成绩后,准备报考志愿。听说最近西湖大学办得不错,所以就想到网上搜搜关于西湖大学的资料。他打开一个搜索引擎,输入“西湖大学”四个字,然后点击“搜索”,页面从上到下显示了10条搜索结果,他认为排在上面的肯定比下面的相关,所以就开始从上往下一个个地浏览。所以RankNet的目标就是对所有query,都能将其返回的文档按照相关性进行排序。

      RankNet网络将输入query的特征向量𝑥∈ℝ𝑛x∈Rn映射为一个实数𝑓(𝑥)∈ℝf(x)∈R。RankNet采用pairwise的方法进行模型训练。具体地,给定特定query下的两个文档𝑈𝑖Ui和𝑈𝑗Uj,其特征向量分别为𝑥𝑖xi和𝑥𝑗xj,经过RankNet进行前向计算得到对应的分数为𝑠𝑖=𝑓(𝑥𝑖)si=f(xi)和𝑠𝑗=𝑓(𝑥𝑗)sj=f(xj)。用𝑈𝑖⊳𝑈𝑗Ui⊳Uj表示𝑈𝑖Ui比𝑈𝑗Uj排序更靠前(如对某个query来说,𝑈𝑖Ui被标记为“good”,𝑈𝑗Uj被标记为“bad”)。继而可以用下面的公式来表示𝑈𝑖Ui应该比𝑈𝑗Uj排序更靠前的概率:

      𝑃𝑖𝑗≡𝑃(𝑈𝑖⊳𝑈𝑗)≡11+𝑒−𝜎(𝑠𝑖−𝑠𝑗)

      这个概率实际上就是深度学习中经常使用的sigmoid函数,参数𝜎σ决定sigmoid函数的形状。对于特定的query,定义𝑆𝑖𝑗∈{0,±1}Sij∈{0,±1}为文档𝑖i和文档𝑗j被标记的标签之间的关联,即

       

      𝑆𝑖𝑗=⎧⎩⎨⎪⎪10−1文档𝑖比文档𝑗更相关文档𝑖和文档𝑗相关性一致文档𝑗比文档𝑖更相关Sij={1文档i比文档j更相关0文档i和文档j相关性一致−1文档j比文档i更相关

      定义𝑃⎯⎯⎯⎯𝑖𝑗=12(1+𝑆𝑖𝑗)P¯ij=12(1+Sij)表示𝑈𝑖Ui应该比𝑈𝑗Uj排序更靠前的已知概率,则可以用交叉熵定义优化目标的损失函数:

      𝐶=−𝑃⎯⎯⎯⎯𝑖𝑗𝑙𝑜𝑔𝑃𝑖𝑗−(1−𝑃⎯⎯⎯⎯𝑖𝑗)𝑙𝑜𝑔(1−𝑃𝑖𝑗)C=−P¯ijlogPij−(1−P¯ij)log(1−Pij)

      如果不太熟悉什么是交叉熵,可以参考宗成庆老师的《统计自然语言处理》2.2节“信息论基本概念”,里面将熵、联合熵、互信息、相对熵、交叉熵和困惑度等概念都讲得相当清楚。

      结合以上多个公式,可以改写损失函数𝐶C为:

      𝐶=12(1−𝑆𝑖𝑗)𝜎(𝑠𝑖−𝑠𝑗)+𝑙𝑜𝑔(1+𝑒−𝜎(𝑠𝑖−𝑠𝑗))C=12(1−Sij)σ(si−sj)+log(1+e−σ(si−sj))

      对于𝑆𝑖𝑗=1Sij=1,

      𝐶=𝑙𝑜𝑔(1+𝑒−𝜎(𝑠𝑖−𝑠𝑗))C=log(1+e−σ(si−sj))

      然而对于𝑆𝑖𝑗=−1Sij=−1,

      𝐶=𝑙𝑜𝑔(1+𝑒−𝜎(𝑠𝑗−𝑠𝑖))C=log(1+e−σ(sj−si))

      可以看出损失函数𝐶C具有对称性,也即交换𝑖i和𝑗j的位置,损失函数的值不变。

      分析损失函数𝐶C的趋势发现,如果对文档𝑈𝑖Ui和𝑈𝑗Uj的打分可以正确地拟合标记的标签,则𝐶C趋向于0,否则𝐶C趋向于线性函数。具体地,假如𝑆𝑖𝑗=1Sij=1,也即𝑈𝑖Ui应该比𝑈𝑗Uj排序高,如果𝑠𝑖>𝑠𝑗si>sj,则拟合的分数可以正确排序文档𝑖i和文档𝑗j,

      lim𝑠𝑖−𝑠𝑗→∞𝐶=lim𝑠𝑖−𝑠𝑗→∞𝑙𝑜𝑔(1+𝑒−𝜎(𝑠𝑖−𝑠𝑗))=𝑙𝑜𝑔1=0limsi−sj→∞C=limsi−sj→∞log(1+e−σ(si−sj))=log1=0

      如果𝑠𝑖<𝑠𝑗si<sj,则拟合的分数不能正确排序文档𝑖i和文档𝑗j,

      lim𝑠𝑖−𝑠𝑗→∞𝐶=lim𝑠𝑖−𝑠𝑗→∞𝑙𝑜𝑔(1+𝑒−𝜎(𝑠𝑖−𝑠𝑗))=𝑙𝑜𝑔(𝑒−𝜎(𝑠𝑖−𝑠𝑗))=−𝜎(𝑠𝑖−𝑠𝑗)limsi−sj→∞C=limsi−sj→∞log(1+e−σ(si−sj))=log(e−σ(si−sj))=−σ(si−sj)

      利用神经网络对模型进行训练,目前最有效的方法就是反向传播算法。反向传播算法中最核心部分就是损失函数对模型参数的求导,然后可以使用下面的公式对模型参数进行迭代更新:

      𝑤𝑘←𝑤𝑘−𝜂∂𝐶∂𝑤𝑘=𝑤𝑘−𝜂(∂𝐶∂𝑠𝑖∂𝑠𝑖∂𝑤𝑘+∂𝐶∂𝑠𝑗∂𝑠𝑗∂𝑤𝑘)wk←wk−η∂C∂wk=wk−η(∂C∂si∂si∂wk+∂C∂sj∂sj∂wk)

      损失函数𝐶C对𝑠𝑖si和𝑠𝑗sj的偏导数为:

      ∂𝐶∂𝑠𝑖=𝜎(12(1−𝑆𝑖𝑗)−11+𝑒𝜎(𝑠𝑖−𝑠𝑗))=−∂𝐶∂𝑠𝑗∂C∂si=σ(12(1−Sij)−11+eσ(si−sj))=−∂C∂sj

      𝑠𝑖si和𝑠𝑗sj对𝑤𝑘wk的偏导数可根据神经网络求偏导数的方式求得。求得了损失函数𝐶C对神经网络模型参数𝑤𝑘wk的偏导数之后,就可以使用梯度下降算法对其更新。这里的学习率𝜂η也是一个正数,因为𝜂η需要满足下面的不等式:

      𝛿𝐶=∑𝑘∂𝐶∂𝑤𝑘𝛿𝑤𝑘=∑𝑘∂𝐶∂𝑤𝑘(−𝜂∂𝐶∂𝑤𝑘)=−𝜂∑𝑘(∂𝐶∂𝑤𝑘)2<0

    • 2.2 RankNet分解形式:加速RankNet训练过程

      2.1节中定义的RankNet,对于每一个文档对(𝑈𝑖(Ui,𝑈𝑗)Uj)都将计算损失函数对神经网络的参数𝑤𝑘wk的偏导数,然后更新模型参数𝑤𝑘wk。这样做的缺点在于,对模型参数更新慢,耗时长。所以本节讲解如何通过分解组合的方式加快这一训练过程。

      对于给定的文档对𝑈𝑖Ui和𝑈𝑗Uj,损失函数𝐶C对参数𝑤𝑘wk的偏导数为:

      ∂𝐶∂𝑤𝑘=∂𝐶∂𝑠𝑖∂𝑠𝑖∂𝑤𝑘+∂𝐶∂𝑠𝑗∂𝑠𝑗∂𝑤𝑘=𝜎(12(1−𝑆𝑖𝑗)−11+𝑒𝜎(𝑠𝑖−𝑠𝑗))(∂𝑠𝑖∂𝑤𝑘−∂𝑠𝑗∂𝑤𝑘)=𝜆𝑖𝑗(∂𝑠𝑖∂𝑤𝑘−∂𝑠𝑗∂𝑤𝑘)∂C∂wk=∂C∂si∂si∂wk+∂C∂sj∂sj∂wk=σ(12(1−Sij)−11+eσ(si−sj))(∂si∂wk−∂sj∂wk)=λij(∂si∂wk−∂sj∂wk)

      其中:

      𝜆𝑖𝑗=∂𝐶(𝑠𝑖−𝑠𝑗)∂𝑠𝑖=𝜎(12(1−𝑆𝑖𝑗)−11+𝑒𝜎(𝑠𝑖−𝑠𝑗))λij=∂C(si−sj)∂si=σ(12(1−Sij)−11+eσ(si−sj))

      定义𝐼I为索引对{𝑖,𝑗}{i,j}的集合,在不损失信息量的情况下,可以将集合𝐼I中的索引对都转换成满足𝑈𝑖⊳𝑈𝑗Ui⊳Uj的形式。另外集合𝐼I中的索引对还应该满足最多只出现一次的条件。在此基础上,累加权重参数𝑤𝑘wk的更新量:

      𝛿𝑤𝑘=−𝜂∑(𝑖,𝑗)∈𝐼(𝜆𝑖𝑗∂𝑠𝑖∂𝑤𝑘−𝜆𝑖𝑗∂𝑠𝑗∂𝑤𝑘)=−𝜂∑𝑖𝜆𝑖∂𝑠𝑖∂𝑤𝑘δwk=−η∑(i,j)∈I(λij∂si∂wk−λij∂sj∂wk)=−η∑iλi∂si∂wk

      其中:

      𝜆𝑖=∑𝑗:{𝑖,𝑗}∈𝐼𝜆𝑖𝑗−∑𝑗:{𝑗,𝑖}∈𝐼𝜆𝑖𝑗λi=∑j:{i,j}∈Iλij−∑j:{j,i}∈Iλij

      𝜆𝑖λi可以看成是作用在排序文档上的力,其正负代表了方向,长度代表了力的大小。最初的实现是对每个文档对,都计算一遍梯度并且更新神经网络的参数值,而这里则是将同一个query下的所有文档对进行叠加,然后更新一次网络的权重参数。这种分解组合形式实际上就是一种小批量学习方法,不仅可以加快迭代速度,还可以为后面使用非连续的梯度模型打下基础。

    • 3.LAMBDARANK

    • 3.1 为什么需要LambdaRank

      先看一张论文原文中的图,如下所示。这是一组用二元等级相关性进行排序的链接地址,其中浅灰色代表链接与query不相关,深蓝色代表链接与query相关。 对于左边来说,总的pairwise误差为13,而右边总的pairwise误差为11。但是大多数情况下我们更期望能得到左边的结果。这说明最基本的pairwise误差计算方式并不能很好地模拟用户对搜索引擎的期望。右边黑色箭头代表RankNet计算出的梯度大小,红色箭头是期望的梯度大小。NDCG和ERR在计算误差时,排名越靠前权重越大,可以很好地解决RankNet计算误差时的缺点。但是NDCG和ERR均是不可导的函数,如何加入到RankNet的梯度计算中去?

      图1

       

      3.2  LambdaRank定义

      RankNet中的𝜆𝑖𝑗λij可以看成是𝑈𝑖Ui和𝑈𝑗Uj中间的作用力,如果𝑈𝑖⊳𝑈𝑗Ui⊳Uj,则𝑈𝑗Uj会给予𝑈𝑖Ui向上的大小为|𝜆𝑖𝑗||λij|的推动力,而对应地𝑈𝑖Ui会给予𝑈𝑗Uj向下的大小为|𝜆𝑖𝑗||λij|的推动力。如何将NDCG等类似更关注排名靠前的搜索结果的评价指标加入到排序结果之间的推动力中去呢?实验表明,直接用|Δ𝑁𝐷𝐶𝐺||ΔNDCG|乘以原来的𝜆𝑖𝑗λij就可以得到很好的效果,也即:

       

      𝜆𝑖𝑗=∂𝐶(𝑠𝑖−𝑠𝑗)∂𝑠𝑖=−𝜎1+𝑒𝜎(𝑠𝑖−𝑠𝑗)|Δ𝑁𝐷𝐶𝐺|λij=∂C(si−sj)∂si=−σ1+eσ(si−sj)|ΔNDCG|

      其中|Δ𝑁𝐷𝐶𝐺||ΔNDCG|是交换排序结果𝑈𝑖Ui和𝑈𝑗Uj得到的NDCG差值。NDCG倾向于将排名高并且相关性高的文档更快地向上推动,而排名地而且相关性较低的文档较慢地向上推动。

      另外还可以将|Δ𝑁𝐷𝐶𝐺||ΔNDCG|替换成其他的评价指标。

4. LambdaMART

4.1 逻辑回归+MART进行二分类

了解了MART之后,下面举一个MART实际应用的例子:使用MART和逻辑回归进行二分类。用于分类的样本𝑥𝑖∈ℝ𝑛xi∈Rn,标签𝑦𝑖∈{±1}yi∈{±1},拟合函数𝐹(𝑥)F(x)。为了简化表示,我们表示条件概率如下:

 

𝑃+≡𝑃(𝑦=1|𝑥)P+≡P(y=1|x)

 

𝑃−≡𝑃(𝑦=−1|𝑥)P−≡P(y=−1|x)

用交叉熵表示损失函数:

𝐿(𝑦,𝐹)=−𝑦𝑙𝑜𝑔(𝑃+)−(1−𝑦)𝑙𝑜𝑔(𝑃−)L(y,F)=−ylog(P+)−(1−y)log(P−)

逻辑回归使用对数机率(属于正例概率/属于负例概率)进行建模,

𝐹𝑛(𝑥)=12𝑙𝑜𝑔(𝑃+𝑃−)Fn(x)=12log(P+P−)

𝑃+=11+𝑒−2𝜎𝐹𝑛(𝑥)P+=11+e−2σFn(x)

𝑃−=1−𝑃+=11+𝑒2𝜎𝐹𝑛(𝑥)P−=1−P+=11+e2σFn(x)

将𝑃+P+和𝑃−P−带入𝐿(𝑦,𝐹)L(y,F)中,得到:

𝐿(𝑦,𝐹𝑛)=𝑙𝑜𝑔(1+𝑒−2𝑦𝜎𝐹𝑛)L(y,Fn)=log(1+e−2yσFn)

𝑅𝑗𝑚Rjm表示落入第𝑚m棵树的第𝑗j个叶子节点中的样例集合,可以通过下式对该叶子节点的值进行优化:

𝛾𝑗𝑚=𝑎𝑟𝑔min𝛾∑𝑥𝑖∈𝑅𝑗𝑚log(1+𝑒−2𝜎𝑦𝑖(𝐹𝑚−1(𝑥𝑖)+𝛾))γjm=argminγ∑xi∈Rjmlog⁡(1+e−2σyi(Fm−1(xi)+γ))

上式可以使用Newton-Raphson方法按照下面的公式进行迭代求解:

𝛾𝑛+1=𝛾𝑛−𝑔′(𝛾𝑛)𝑔″(𝛾𝑛)γn+1=γn−g′(γn)g″(γn)

4.2 LambdaMART基本定义

LambdaMART基于MART,优化𝜆λ梯度。根据上面的定义,对于任意𝑈𝑖Ui和𝑈𝑗Uj,有:

𝜆𝑖𝑗=∂𝐶(𝑠𝑖−𝑠𝑗)∂𝑠𝑖=−𝜎|Δ𝑍𝑖𝑗|1+𝑒𝜎(𝑠𝑖−𝑠𝑗)λij=∂C(si−sj)∂si=−σ|ΔZij|1+eσ(si−sj)

|Δ𝑍𝑖𝑗||ΔZij|表示交换𝑈𝑖Ui和𝑈𝑗Uj的位置产生的评价指标差值,𝑍Z可以是𝑁𝐷𝐶𝐺NDCG或者𝐸𝑅𝑅ERR等。对于特定𝑈𝑖Ui,累加其他所有排序项的影响,得到:

𝜆𝑖=∑𝑗:{𝑖,𝑗}∈𝐼𝜆𝑖𝑗−∑𝑗:{𝑗,𝑖}∈𝐼𝜆𝑖𝑗λi=∑j:{i,j}∈Iλij−∑j:{j,i}∈Iλij

 为了简化表示:

∑{𝑖,𝑗}⇌𝐼𝜆𝑖𝑗=∑𝑗:{𝑖,𝑗}∈𝐼𝜆𝑖𝑗−∑𝑗:{𝑗,𝑖}∈𝐼𝜆𝑖𝑗∑{i,j}⇌Iλij=∑j:{i,j}∈Iλij−∑j:{j,i}∈Iλij

于是我们可以更新损失函数:

∂𝐶∂𝑠𝑖=∑𝑗:{𝑖,𝑗}∈𝐼−𝜎|Δ𝑍𝑖𝑗|1+𝑒𝜎(𝑠𝑖−𝑠𝑗)=∑𝑗:{𝑖,𝑗}∈𝐼−𝜎|Δ𝑍𝑖𝑗|𝜌𝑖𝑗∂C∂si=∑j:{i,j}∈I−σ|ΔZij|1+eσ(si−sj)=∑j:{i,j}∈I−σ|ΔZij|ρij

其中,我们定义:

𝜌𝑖𝑗=11+𝑒𝜎(𝑠𝑖−𝑠𝑗)=−𝜆𝑖𝑗𝜎|Δ𝑍𝑖𝑗|ρij=11+eσ(si−sj)=−λijσ|ΔZij|

然后可以得到:

∂2𝐶∂𝑠2𝑖=∑{𝑖,𝑗}⇌𝐼𝜎2|Δ𝑍𝑖𝑗|𝜌𝑖𝑗(1−𝜌𝑖𝑗)∂2C∂si2=∑{i,j}⇌Iσ2|ΔZij|ρij(1−ρij)

所以我们可以用下面的公式计算第𝑚m棵树的第𝑘k个叶子节点上的值:

𝛾𝑘𝑚=∑𝑥𝑖∈𝑅𝑘𝑚∂𝐶∂𝑠𝑖∑𝑥𝑖∈𝑅𝑘𝑚∂2𝐶∂𝑠2𝑖=−∑𝑥𝑖∈𝑅𝑘𝑚∑{𝑖,𝑗}⇌𝐼|Δ𝑍𝑖𝑗|𝜌𝑖𝑗∑𝑥𝑖∈𝑅𝑘𝑚∑{𝑖,𝑗}⇌𝐼|Δ𝑍𝑖𝑗|𝜎𝜌𝑖𝑗(1−𝜌𝑖𝑗)

  • 5. 参考

https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/MSR-TR-2010-82.pdf

https://www.microsoft.com/en-us/research/wp-content/uploads/2005/08/icml_ranking.pdf

https://liam.page/uploads/slides/lambdamart.pdf

https://www.cnblogs.com/genyuan/p/9788294.html

https://ai.googleblog.com/2018/12/tf-ranking-scalable-tensorflow-library.html

https://github.com/shiba24/learning2rank

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章