前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用XGB learning to rank

用XGB learning to rank

作者头像
炼丹笔记
发布2021-07-16 12:13:37
1.7K0
发布2021-07-16 12:13:37
举报
文章被收录于专栏:炼丹笔记

说到learning to rank,大家应该都比较熟悉,但是说到用XGB做learning to rank,很少有人实现过.举个例子,比方说赛马,我们可以基于马的个头,体重,历史战绩等信息,建立XGB模型,进行二分类,最后选择赢的概率最高的马.这样做并没有问题,但是考虑到马是否能跑赢,和对手紧密相关,不管我选的马能力如何,只要他能跑赢同场比赛其他马即可,这就是排序.

Learning To Rank

学习排序其实也是有监督学习的一部分,和多分类和回归模型相比,我们并不是要预估一条样本的概率,而是预估很多个样本点的顺序.排序经常被用于搜索引擎,还有一些购物建议.在搜索框里搜任何一条query,top 3展示的一定是最相关的,越往后翻页相关度越低.

回到XGBoost,有3个目标函数,Point Wise,Pairwise和Listwise,这3种方法都可以用来排序,每个方法都有其优缺点.对于pointwise而言,每次仅仅考虑一个样本,预估的是每一条和query的相关性,基于此进行排序.Pairwise是每次取一对样本,预估这一对样本的先后顺序,不断重复预估一对对样本,从而得到某条query下完整的排序.Listwise同时考虑多个样本,找到最优顺序.

Point Wise虽然简单,但是存在不少问题.比如说赛马场景,马的输赢取决于对手.再比如搜索场景,我们确实可以预估每个query到每个document的点击率做为排序依据,但是点击率要考虑rank,例如排的越靠前的document点击率上占据优势,这些point-wise模型很难考虑进去.基于此,我们需要做learning to rank的模型.

建模

首先要明确的是训练数据,训练数据必须包含一列query id,该id下指明哪些样本要放到一起进行排序.同时特别需要注意的是,在训练集和测试集进行拆分时,需要按query id进行分割,如果直接随机拆分,同一个query id下的数据就会被分开,这样会导致模型出问题.我们可以用如下代码进行拆分.

代码语言:javascript
复制
from sklearn.model_selection import GroupShuffleSplit
gss = GroupShuffleSplit(test_size=.40, 
                        n_splits=1, 
                        random_state = 7)
                        .split(df, groups=df['query_id'])
X_train_inds, X_test_inds = next(gss)

train_data= df.iloc[X_train_inds]
X_train = train_data.loc[:, ~train_data.columns.isin(['id','rank'])]
y_train = train_data.loc[:, train_data.columns.isin(['rank'])]

# 模型需要输入按query_id排序后的样本
# 并且需要给定每个query_id下样本的数量
groups = train_data.groupby('id').size().to_frame('size')['size'].to_numpy()

test_data= df.iloc[X_test_inds]

#We need to keep the id for later predictions
X_test = test_data.loc[:, ~test_data.columns.isin(['rank'])]
y_test = test_data.loc[:, test_data.columns.isin(['rank'])]

我们的数据格式应该如下所示,如果数据长这样,那么我们上述代码中的groups就是[3, 4]:

然后我们就可以建模了,可以用XGBRanker训练排序模型,在这个场景下,我们无法自定义objective,也无法自定义mertic了.

代码语言:javascript
复制
import xgboost as xgb

model = xgb.XGBRanker(  
    tree_method='gpu_hist',
    booster='gbtree',
    objective='rank:pairwise',
    random_state=42, 
    learning_rate=0.1,
    colsample_bytree=0.9, 
    eta=0.05, 
    max_depth=6, 
    n_estimators=110, 
    subsample=0.75 
    )

model.fit(X_train, y_train, group=groups, verbose=True)

训练完后我们就可以进行预估,因为预估方法并不会输入groups,所以我们需要做一些特殊处理:

代码语言:javascript
复制
def predict(model, df):
    return model.predict(df.loc[:, ~df.columns.isin(['id'])])

predictions = (data.groupby('id')
               .apply(lambda x: predict(model, x)))

这里选择了"rank:pairwise"作为loss,看官方文档还有其他rank loss可供尝试:

pair wise 方法相比pointwise有优势,可以学习到一些顺序。但是pairwise也有缺点:

1.只能给出排序,并不能给出有多好,好多少.比如在搜索场景下,可能一条与query相关的doc都没,pointwise可以通过卡阈值得到这个信息,但是rank方式就不能区分.

2.当一个query下有很多doc,会产生大量的pairs。

3.对噪声的label 非常敏感。

感兴趣的赶紧尝试起来吧!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 炼丹笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档