我们首先对概念进行一下简单的介绍,然后结合例子来验证一下
两部分以红色为分界线
----------------------------------------------------------------------------------------------------------------
首先说一下 ranking即排序问题,这在信息检索等领域是需要解决的核心问题
简单来说需要解决的问题就是客户给定一个查询q,需要系统返回一个文档(document)序列,并且该序列中按照文档d与q的相关度或重要性依次进行了排序
比如现在在360浏览器中我输入了一个名为“仙剑”的查询q,会返回一些document序列d1 ,d2 , d3 ,..............等等,其中该浏览器认为《仙剑奇侠传六》官方网站这个是我最想要的,其次老胡男神参演的仙一电视剧是我第二想要的结果,接下来是仙剑的游戏.......等等
所以rank解决的问题就是
其排序的模型有两种即相关度排序模型和重要性排序模型
接下来说一下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
----------------------------------------------------------------------------------------------------------------
理解有误,还望指正
转载请标明出处谢谢