从ranknet到lamdarank,再到lamdamart

learn2rank目前基本两个分支,1是神经网络学派ranknet,lamdarank,另一个是决策树学派如gbrank,lamdamart

05年提出ranknet,算分模块是简单的全连接网络,loss函数是预测概率之家的pair-wise关系和真实lablel的pair-wise关系的逻辑回归。

预测概率的pair-wise关系是两个相减然后求个sigmoid,如下图:

真实概率如下)(S_ij表示,i 比 j 相关,S_ij是1,反之是 -1,如果label一样是0):

最后的loss是

然后可以化简为:

具体推倒过程见:

下面展示了当Sij分别取1,0,-1的时候cost function以si-sj为变量的示意图:

当两个相关性不同的文档算出来的模型分数相同时,损失函数的值大于0,仍会对这对pair做惩罚,使他们的排序位置区分开。

 

lamdarank想要解决的一个问题是,目前loss只会优化pair之间的关系,但是rank的目的是希望把最好好的结果往上promote,pair-wise没有考虑到这一点,使得最好的结果在中间位置也会loss非常小:

如:

每个线条表示文档,蓝色表示相关文档,灰色表示不相关文档,RankNet以pairwise error的方式计算cost,左图的cost为13,右图通过把第一个相关文档下调3个位置,第二个文档上条5个位置,将cost降为11,但是像NDCG或者ERR等评价指标只关注top k个结果的排序,在优化过程中下调前面相关文档的位置不是我们想要得到的结果。图 1右图左边黑色的箭头表示RankNet下一轮的调序方向和强度,但我们真正需要的是右边红色箭头代表的方向和强度,即更关注靠前位置的相关文档的排序位置的提升。LambdaRank正是基于这个思想演化而来,其中Lambda指的就是红色箭头,代表下一次迭代优化的方向和强度,也就是梯度。

 

于是lamdarank在每个pair-wise loss乘以了一个ndcg的变化值,作为最终的loss

 

我的理解:如果是 i,j是预测对了,由于log项非常小,所以整体loss小,但是如果预测错了,会根据ndcg的差对loss加权,一般label比较大的权重也大

 

最后说说lamdamart,由于神经网络系列的模型都是可以直接求梯度的,所以很直接的用梯度下降就可以求解。

首先回忆以下,梯度下降算法。(https://www.jianshu.com/p/c7e642877b0e

model =f

y=f(wx)

我们的目标是学习函数f的w,使得y=f(wx),通俗的话是,学习一个函数,拟合训练集合。

求解的方法一般是通过最小化loss,loss=(y-f(wx))来进行的。所以最终我们关注的函数是Loss函数,同样Loss函数的变量是w,使用梯度下降法来求解的话,是

w=w-lr*grad

grad就是Loss关于w的梯度。神经网络是比较容易求解梯度的,方便的地方就是,Loss-f-w,Loss可以直接对w求导,通过w的梯度下降就可以达到优化Loss的目标。

但是mart就不容易了,mart的loss到模型就可以求导的,但是模型到w是不可以求导的,所以没法直接loss导w的求导,进行梯度下降来做。

只能通过loss-f这一步进行梯度下降。

我们直接loss对模型可以直接求导的,那么我们就用model(i) = model(i-1) - Loss(梯度)。来求解最终的model就行了。由于树是ensemble的方式,实际使用的过程是model(i) = model(i-1)+(-loss(梯度)),即优化的时候,是加上loss的负梯度。

所以就非常直观了,mart没有w一说,核心就是每一步求解一个model。这一步的model的预测的目标,预测-loss的梯度。

第i次迭代的模型,等于i-1次的模型加上i次的模型。第i次的模型的目标就是Loss的负梯度。

(有的人就说,不是每个model来预测当前的残差吗,注意残差是model的预测和线性回归目标的残差,loss一般是问题的时候(如分类),loss的梯度就是残差了)

以二分类为例的话,Loss函数是如图,

,那么Loss的梯度(残差)就是下图。其实还是比较直观的,当前函数F(x)就是之前的树的总和。梯度的公式里基本就是y和之前的模型,也是为什么叫残差的原因。

最后看流程图

 

 

用model预测当前的残差,那到底怎么做呢。其实就是一个对于所有样本,预测的值尽可能接近目标值。如5个样本的值分别是,(1,2,3,4,5)

目标就是预测5个样本的值尽可能接近每个样本的值,平方差最小。

现在说一下,mart是如何预测的,一般情况下,当前叶子节点的平均值作为属于这个叶子节点样本的预测值,开始时,都在根节点,每个样本都是随机初始化一个值,这个时候的预测的是随机值的平方,之后开始分裂,然后左右子树的各自的均值就是左右子树的预测值。

 参考gbdt基本原理(https://www.cnblogs.com/rocketfan/p/4324605.html),就是每次分裂的时候使用左叶子节点值(预测值)的和除以左叶子节点数目,加上右叶子节点值的和除以右叶子节点数目。看哪个特征分裂后这个值大就用这个特征进行分裂。分裂终止的条件就是根据你的设置叶子节点数目,或者分裂到叶子节点的样本数目少于多少等等。

如图,y是需要预测的值,也就是梯度。分裂的时候就是需要左边梯度的值平方和,右边叶子的平方和。

一般情况下,遍历所有特征,对一个特征的所有分裂点,计算上述公式,选择最大的值作为当前特征和分裂点。

对于lightgbm来说,为了加速训练,对特征进行bin化,比如特征是,1,2,3,30,100。由于1,2,3特征比较接近,划分一个bin内,可以划分3个bin。这样计算的时候,通过bin的维度来做,能较少遍历的次数,尽快找到分裂点。对于每个分裂点计算的时候,也使用直方图的存储方式,每个点,存储这这个bin的样本树,和样本的值的平方和。这样遍历的时候,通过直接取直方图的中的值就可以很快的计算上述公式。

 

分布式是样本分布到多台机器,每个机器一部分样本,应该是每个机器搞一个直方图,sever维护一个总的

并行化指的多个特征一起算,然后加速

 

 

 

lightgbm分裂是所有叶子节点一起比较,选择一个最优的叶子节点进行分裂,所以往深的方向优化。xgboost是所有叶子节点都分类,是一个完全二叉树

posted @ 2017-11-24 15:56  dmesg  阅读(1401)  评论(0编辑  收藏  举报