从L2R开始理解一下xgboost的 'objective': 'rank:pairwise'参数

我们首先对概念进行一下简单的介绍,然后结合例子来验证一下

两部分以红色为分界线

----------------------------------------------------------------------------------------------------------------

首先说一下 ranking即排序问题,这在信息检索等领域是需要解决的核心问题

简单来说需要解决的问题就是客户给定一个查询q,需要系统返回一个文档(document)序列,并且该序列中按照文档d与q的相关度或重要性依次进行了排序

比如现在在360浏览器中我输入了一个名为“仙剑”的查询q,会返回一些document序列d1 ,d2 , d3 ,..............等等,其中该浏览器认为《仙剑奇侠传六》官方网站这个是我最想要的,其次老胡男神参演的仙一电视剧是我第二想要的结果,接下来是仙剑的游戏.......等等

所以rank解决的问题就是q\rightarrow \left \{ d_{1},d_{2},d_{3} ,................\right \}

其排序的模型有两种即相关度排序模型和重要性排序模型

接下来说一下pairwise:

它是伴随L2R的出现而出现的,L2R全称Learning to Rank即将器学习的技术很好的应用到了排序中,关于为什么要将机器学习应用到rangking问题当中也很好理解,传统的rank模型只能够考虑单一模型即相关度排序模型或重要性排序模型,即使可以将两者进行组合使用,但怎么组合以及怎么设置参数都是很大的挑战

而机器学习恰好善于处理这种问题,以及现在流行的深度学习善于进行抽象学习。

怎么结合呢?思路也非常简单:

可以将现有排序模型(ranking system)的输出作为机器学习的输入特征,然后训练一个机器学习的模型(learning system)

该learning system的贡献就是:反作用ranking system参数,使之更加准确的评分(个人觉得思想上有点像深度学习中的Actor-Critic),

那么在众多机器学习模型中到底选用哪个?机器学习模型中的训练数据具体是什么样子?话句话说数据的label是什么呢?

注意:这里之所以说到label是因为我们在L2R问题中采用的都是有监督的机器学习(以及深度学习)模型

L2R的训练数据可以有三种形式:对于每个查询,各个文档的绝对相关值(非常相关,比较相关,不相关,等等);对于每个查询,两两文档之间的相对相关值(文档1比文档2相关,文档4比文档3相关,等等);对于每个查询,所有文档的按相关度排序的列表(文档1>文档2>文档3)。这三种形式的训练数据之间可以相互转换

针对以上问题可以将L2R算法归为三大类:

PointWise,PairWise,ListWise

PointWise:只考虑给定查询下,单个文档的绝对相关度,而不考虑其他文档和给定查询的相关度。亦即给定查询q的一个真实文档序列,我们只需要考虑单个文档di和该查询的相关程度ci,亦即输入数据应该是如下的形式:

图片来源:http://www.cnblogs.com/kemaswill/archive/2013/06/01/3109497.html

这里可以采用机器学习的分类模型,那么其label就可以简单粗暴的设为其真实的相关度,还可以采用回归模型,其实这里ranking system就可以直接看出是一个learning system

PairWise:可以看到上述方法没有考虑到排序的一些特征,比如文档之间的排序结果针对的是给定查询下的文档集合,而PairWise则是考虑了任意两个相关度不同的文档之间的相对相关度

图片来源:http://www.cnblogs.com/kemaswill/archive/2013/06/01/3109497.html

它对应的机器学习就是二分类模型,二分类模型有很多啦,其中就包括下面的xgboost,它是将文档序列中的文档两两组合(一对,这也是pair命名的原因,当然对这一对的要求就是得分不能一样)这个作为二分类模型的输入,那么该二分类模型的label是什么呢?那就是+1和-1,他们是依据什么规则来标注训练集的呢?很简单就是看看这一对文档组合中第一个文档得分是否比第二个高,是的话就是+1否的话就是-1,当然啦,这个规则还可以改成别的,但最后作用给ranking system就是使其更评分精确。

Listwise:直接考虑给定查询下的文档集合的整体序列,直接优化模型输出的文档序列,使得其尽可能接近真实文档序列。

更多细节参考:http://www.cnblogs.com/kemaswill/archive/2013/06/01/3109497.html

----------------------------------------------------------------------------------------------------------------

下面回归到xgboost中objective 设为rank:pairwise的问题

xgboost就是一种二分类模型,当将objective设为rank:pairwise意思就是说采用评分机制进行训练

官网介绍–set XGBoost to do ranking task by minimizing the pairwise loss

注意当我们在具体使用xgboost的时候,如果采用了rank:pairwise的设置,其实我们输入的训练集中label在一定意义上可以看成是数据真实的得分,当使用model.predict进行预测是输出的相关度,但是其范围可能有点难理解,比如我们输入的训练集的label是0和1,那么看成得分后含义就是训练集的数据中只有两种得分0和1,那么我们用测试集去测试会发现测试集的得分有负数有正数,含义就是正数得分高,负数得分低。

这里测试集输出的相关度并不能完全等同与得分,比如我们将label设为100和10即普遍提高得分,会发现测试集输出的相关度并没有普遍提高(下面有例子),所以两者并不是完全等同,有人也将其理解为AUC,这里有负值,显然这样理解不是很恰当,它只是代表和正样本有多大的相关度,负值就是相关度低,正数就是相关度高。至于怎么确定哪一类是正样本呢?可以简单的理解为分数高的就是正样本,即二分类中label标签大的那一类就是正样本,一般来说二分类问题label是0和1或者-1和1,但是为了理解我们还是会举一些比较不太恰当的例子。

下面看个例子

我们产生两个DataFrame作为数据集,分别为train和test

这里是二分类问题,即以y=x这条直线作为分界线,位于其上的label为1,其下的label为0

GetLabel:获取label

DropDuplicate:将分界线上的点移动到分解线外

import pandas as pd
import xgboost as xgb
import random
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler

def GetLabel(row):
    if row.y-row.x<0:
        return 0
    else:
        return 1
def DropDuplicate(row):
    if row.x==row.y:
        return row.y+1
    else:
        return row.y
train = pd.DataFrame({
    'x': [random.randrange(20) for i in range(1000)],
    'y': [random.randrange(20) for i in range(1000)],
})
train.y = train.apply(DropDuplicate,axis=1)
train['label'] = train.apply(GetLabel,axis=1)


test = pd.DataFrame({
    'x': [random.randrange(20) for i in range(30)],
    'y': [random.randrange(20) for i in range(30)],
})
test.y = test.apply(DropDuplicate,axis=1)
test['label'] = test.apply(GetLabel,axis=1)

#可视化
train_0 = train[train.label==0]
train_1 = train[train.label==1]
test_0 = test[test.label==0]
test_1 = test[test.label==1]
ax1 = plt.subplot(211)
ax2 = plt.subplot(212)
ax1.scatter(train_0.x, train_0.y, s=30, c='r' ,edgecolors= 'white')
ax1.scatter(train_1.x, train_1.y, s=30, c='b' ,edgecolors= 'white')
ax2.scatter(test_0.x, test_0.y, s=30, c='r' ,edgecolors= 'white')
ax2.scatter(test_1.x, test_1.y, s=30, c='b' ,edgecolors= 'white')
plt.show()

上图为训练集,下图为测试集

下面我们就将'objective':这个超参数设置为'rank:pairwise'来训练出model进而预测:

注意这里我们就不对其它超参数进行优化啦,即这里不讨论优化问题,即对每一个超参数随便设一个常用的值

train_data = train[['x','y']]
train_label = train[['label']]
test_data = test[['x','y']]
test_compare = test
dataset1 = xgb.DMatrix(train_data,label=train_label)
dataset2 = xgb.DMatrix(test_data)


params={'booster':'gbtree',
        'objective': 'rank:pairwise',
        'eval_metric':'auc',
        'gamma':0.1,
        'min_child_weight':1.1,
        'max_depth':5,
        'lambda':10,
        'subsample':0.7,
        'colsample_bytree':0.7,
        'colsample_bylevel':0.7,
        'eta': 0.01,
        'tree_method':'exact',
        'seed':0,
        'nthread':12
        }
model = xgb.train(params,dataset1,num_boost_round=100)
print('-------------model train done-----------')
test_compare['labelpre'] = model.predict(dataset2)
print(test_compare)
-------------model train done-----------
     x   y  label  labelpre
0    7   2      0  0.365744
1    7   8      1  0.529373
2   16   3      0 -0.136610
3    3  11      1  0.884958
4    6  11      1  0.744852
5    7   3      0  0.368131
6    6  16      1  0.883681
7   18   3      0 -0.146025
8   10  19      1  0.892916
9    1  19      1  1.187794
10   4  17      1  0.998889
11  19   9      0  0.139502
12   1  17      1  1.187794
13   5   0      0  0.433676
14   1   7      1  0.747284
15   8   6      0  0.319912
16   8   4      0  0.263565
17   8   2      0  0.215807
18  14   8      0  0.203753
19   2   6      1  0.677499
20  13   3      0  0.060727
21  15  10      0  0.393613
22   4   0      0  0.381093
23  12  18      1  0.682859
24   9   4      0  0.242880
25   3   0      0  0.516810
26   8  15      1  0.677383
27  10  19      1  0.892916
28   1   6      1  0.680136
29  19  15      0  0.313164

按常理来说labelpre中的值应该是0和1两种值,但显然这里不是,甚至出现了负值,其实这里labelpre值的含义就是得分,负值代表的就是得分低,正数就是得分高,那他和类别有什么关系呢?我们可以简单的这样想,我们的类别不是1和0吗?,那么1的那一边就是代表得分高的正样本,得分越高代表和其相关度越高,我们来看一下得分最低的是-0.146025它的坐标是 ( 18 ,3 ),从坐标图上面也可以看出,它是负样本且在右下角,相对其他负样本来说它离分界线最远,所以得分最低,那么得分最高的是1.187794这里有两个坐标(1 ,19)和(1 ,17),可能会说不对呀,他们到y=x的距离不一样呀,(1 ,19)得分应该比(1 ,17)高呀,首先该模型并不是以距离为优化目标的,其次我们还没有对其它参数进行调优,出现该结果应该可以理解,但至少有一旦可以看到那就是,它们两个都是在最左上方。

我们再来试一下预测概率函数predict_proba

test_compare['labelproba'] = model.predict_proba(dataset2)
print(test_compare)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-39-fc318bfff067> in <module>()
----> 1 test_compare['labelproba'] = model.predict_proba(dataset2)
      2 print(test_compare)

AttributeError: 'Booster' object has no attribute 'predict_proba'

会发现报错了,意思是没有predict_proba,所以不能用。

为了进一步直观的理解我们现在从新设置原始数据的label,即从新设置原始数据的得分,上面的例子对应的是左上方得分高,右下方得分低,现在我们换一下,而且我们不用0和1啦,我们用1和8

import pandas as pd
import xgboost as xgb
import random
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler

def GetLabel(row):
    if row.y-row.x<0:
        return 8
    else:
        return 1
def DropDuplicate(row):
    if row.x==row.y:
        return row.y+1
    else:
        return row.y
train = pd.DataFrame({
    'x': [random.randrange(20) for i in range(1000)],
    'y': [random.randrange(20) for i in range(1000)],
})
train.y = train.apply(DropDuplicate,axis=1)
train['label'] = train.apply(GetLabel,axis=1)


test = pd.DataFrame({
    'x': [random.randrange(20) for i in range(30)],
    'y': [random.randrange(20) for i in range(30)],
})
test.y = test.apply(DropDuplicate,axis=1)
test['label'] = test.apply(GetLabel,axis=1)

#可视化
train_0 = train[train.label==8]
train_1 = train[train.label==1]
test_0 = test[test.label==8]
test_1 = test[test.label==1]
ax1 = plt.subplot(211)
ax2 = plt.subplot(212)
ax1.scatter(train_0.x, train_0.y, s=30, c='r' ,edgecolors= 'white')
ax1.scatter(train_1.x, train_1.y, s=30, c='b' ,edgecolors= 'white')
ax2.scatter(test_0.x, test_0.y, s=30, c='r' ,edgecolors= 'white')
ax2.scatter(test_1.x, test_1.y, s=30, c='b' ,edgecolors= 'white')
plt.show()

train_data = train[['x','y']]
train_label = train[['label']]
test_data = test[['x','y']]
test_compare = test
dataset1 = xgb.DMatrix(train_data,label=train_label)
dataset2 = xgb.DMatrix(test_data)


params={'booster':'gbtree',
        'objective': 'rank:pairwise',
        'eval_metric':'auc',
        'gamma':0.1,
        'min_child_weight':1.1,
        'max_depth':5,
        'lambda':10,
        'subsample':0.7,
        'colsample_bytree':0.7,
        'colsample_bylevel':0.7,
        'eta': 0.01,
        'tree_method':'exact',
        'seed':0,
        'nthread':12
        }
model = xgb.train(params,dataset1,num_boost_round=100)
print('-------------model train done3-----------')
test_compare['labelpre'] = model.predict(dataset2)
print(test_compare)
-------------model train done3-----------
     x   y  label  labelpre
0   18  13      8  0.791034
1    6   9      1  0.296516
2    3  17      1 -0.038643
3   17   0      8  1.135239
4    4  13      1  0.277415
5    6  10      1  0.256797
6   10  17      1  0.309266
7    6   0      8  0.644986
8    4  14      1  0.139423
9    3  13      1  0.199601
10  10  13      1  0.547510
11   9   1      8  0.781545
12  18  11      8  0.742983
13  19   2      8  1.129871
14  10  11      1  0.499458
15  13  18      1  0.259553
16  14  11      8  0.602178
17  16  12      8  0.664406
18  16  14      8  0.592564
19   0   2      1  0.424442
20   9  16      1  0.226197
21  10  12      1  0.481360
22  19   7      8  0.973531
23   0  17      1 -0.152639
24   6  14      1  0.162790
25   6  11      1  0.252730
26   3  11      1  0.151550
27  14  16      1  0.439086
28   0  17      1 -0.152639
29  17   4      8  1.073145

会发现得分最高的是1.135239位于右下方,得分最低的位于左上方。

另一个问题就是

假设现在我们就是想用xgb模型解决二分类问题,并且将设置rank:pairwise(事实上该参数就是解决二分类问题的)。

那我们要想的结果是概率,那么现在怎么办呢?

可以使用MinMaxScaler将labelpre进行归一化,即归到[0,1]区间就可以了

针对上面的第一个例子就是:

test_compare.labelpre = MinMaxScaler().fit_transform(test_compare.labelpre.values.reshape(-1, 1))
print(test_compare)
  x   y  label  labelpre
0    7   2      0  0.383687
1    7   8      1  0.506364
2   16   3      0  0.007059
3    3  11      1  0.772956
4    6  11      1  0.667914
5    7   3      0  0.385476
6    6  16      1  0.771998
7   18   3      0  0.000000
8   10  19      1  0.778922
9    1  19      1  1.000000
10   4  17      1  0.858372
11  19   9      0  0.214067
12   1  17      1  1.000000
13   5   0      0  0.434617
14   1   7      1  0.669737
15   8   6      0  0.349326
16   8   4      0  0.307081
17   8   2      0  0.271275
18  14   8      0  0.262238
19   2   6      1  0.617418
20  13   3      0  0.155007
21  15  10      0  0.404581
22   4   0      0  0.395194
23  12  18      1  0.621437
24   9   4      0  0.291573
25   3   0      0  0.496946
26   8  15      1  0.617331
27  10  19      1  0.778922
28   1   6      1  0.619395
29  19  15      0  0.344266

对应着得分可以看到,得分最低的对应的概率就是0,得分最高的对应概率就是1

----------------------------------------------------------------------------------------------------------------

理解有误,还望指正

转载请标明出处谢谢

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值