1、lgb.LGBMRanker
lgb.LGBMRanker是LightGBM中的一个用于排序任务的模型类。它使用基于梯度提升的算法,能够训练出一个能够对一组对象进行排名的模型。
在训练过程中,LGBMRanker会使用Pairwise-FTRL算法来优化排序的目标函数,该目标函数可以考虑到对象间的相对顺序。具体来说,Pairwise-FTRL使用类似于逻辑回归的方法,通过最大化正确的对象对之间的相对排序来训练模型。
在使用LGBMRanker时,需要将数据集转换为LightGBM的特定格式,并指定排名任务使用的目标函数和评估指标。此外,可以使用超参数调整和交叉验证来优化模型性能。
总之,LGBMRanker是一种高效、灵活的排序模型,适用于各种排序任务,例如搜索结果排序、推荐系统中的物品排序等。
2、lgb相关模型使用listwise
在使用lgb模型进行listwise训练时,需要将数据集按照样本排列顺序,以列表的形式传入模型,其中每个子列表包含了单个查询(query)的所有样本。例如,一个包含3个查询的数据集,每个查询包含5个样本,可以表示为一个包含3个子列表的列表,每个子列表包含5个样本。
具体而言,需要使用LightGBM的Dataset类来加载数据集,并设置参数group表示每个查询(query)中的样本数目,例如:
import lightgbm as lgb
train_data = lgb.Dataset(X_train, label=y_train, group=group_train)
其中,group_train为一个包含每个查询中样本数的列表。
在训练时,设置objective为’rank_xendcg’或’rank_xendcg_map’,表示使用listwise排序损失函数,例如:
x_train_group = train_metrics_df.groupby(['order']).count()['listwise_label'].values
x_valid_group = valid_metrics_df.groupby(['order']).count()['listwise_label'].values
lgb_ranker = lgb.LGBMRanker(
boosting_type='gbdt', num_leaves=63, reg_alpha=0.0, reg_lambda=1,
max_depth=-1, n_estimators=1500, subsample=0.7, colsample_bytree=0.7, subsample_freq=1,
learning_rate=0.05, min_child_weight=30, random_state=2018,
n_jobs=-1)
# objective 在 默认是LambdaRank
objective (str, callable or None, optional (default=None)) – Specify the learning task and the corresponding learning objective or a custom objective function to be used (see note below). Default: ‘regression’ for LGBMRegressor, ‘binary’ or ‘multiclass’ for LGBMClassifier, ‘lambdarank’ for LGBMRanker.
https://lightgbm.readthedocs.io/en/v3.3.2/pythonapi/lightgbm.LGBMRanker.html
lgb_ranker.fit(X_train, y_train[:,5], group=x_train_group,
eval_set=[(X_valid, y_valid[:,5])], eval_group=[x_valid_group], eval_at=[1], eval_metric=['ndcg'],
early_stopping_rounds=100)
lgb_ranker = lgb.Booster(model_file='./LGBMRanker_no_normal_-1.txt')
y_pred_train = lgb_ranker.predict(X_train)
y_pred_valid = lgb_ranker.predict(X_valid)
y_pred_test = lgb_ranker.predict(X_test)
# clf_loads = lgb_ranker.booster_.save_model("./LGBMRanker_1500_0.05_63_100rounds_max.txt")
import treelite
import pickle
model0_booster = treelite.Model.load('./LGBMRanker_no_normal_-1.txt', model_format='lightgbm')
model0_booster.export_lib(toolchain='gcc',
libpath='./LGBMRanker_listwise_nonorm.so',
verbose=True)
params = {‘objective’: ‘rank_xendcg’,
‘metric’: ‘ndcg’,
‘learning_rate’:0.1,
‘max_depth’:6,
‘num_leaves’:64,
‘feature_fraction’:0.8,
‘bagging_fraction’:0.8}
model = lgb.train(params, train_data, num_boost_round=100)
在预测时,需要将测试集按照样本排列顺序,以列表的形式传入模型的predict方法,例如:
y_pred = model.predict(X_test, group=group_test)
其中,group_test为一个包含每个查询中样本数的列表。最后,可以根据预测结果进行排序和评估。
3、“objective为’rank_xendcg’或’rank_xendcg_map’,表示使用listwise排序损失函数”,这个描述是正确的吗
是的,这个描述是正确的。在LightGBM中,'rank_xendcg’和’rank_xendcg_map’是用于listwise排序的两种常见的目标函数(即损失函数)。这些目标函数可以同时考虑查询(query)中所有样本的贡献,从而更好地优化排序性能。其中,'rank_xendcg’是针对ndcg指标的排序损失函数,'rank_xendcg_map’是针对MAP指标的排序损失函数。
使用这些目标函数进行训练时,需要将数据集按照样本排列顺序,以列表的形式传入模型,其中每个子列表包含了单个查询(query)的所有样本。在预测时,需要将测试集也按照样本排列顺序,以列表的形式传入模型的predict方法。
4、使用pairwise
如果使用pairwise排序而不是listwise排序,则可以使用’rank:pairwise’作为LightGBM的目标函数。'rank:pairwise’是一种成对排序(pairwise)损失函数,它利用样本之间的比较关系来优化排序性能。与listwise排序不同,pairwise排序仅考虑每对样本之间的贡献,因此更适用于数据集中样本数较大的情况。
在使用’rank:pairwise’进行训练时,需要将数据集按照样本排列顺序,以列表的形式传入模型,其中每个子列表包含了单个查询(query)的所有样本。每个子列表中的样本应按照其真实排序顺序排列。在预测时,也需要将测试集按照样本排列顺序,以列表的形式传入模型的predict方法。
常用listwise的实现:
①ListNet
ListNet的关键思想是将每个查询(q)下的文档集合(D)转换成一个向量(例如召回6个点,直接把6个点合并成一条语料,标签长度为6),然后用神经网络对这些向量进行排序,最终将排序结果作为损失函数的优化目标。ListNet使用的是交叉熵损失函数,对于一个查询(q),它的损失函数可以表示为:
L(q) = -∑p(i|q)log(q(i))
其中,p(i|q)表示文档i在查询q中的相对等级。交叉熵损失函数的目标是最小化预测结果与真实结果之间的差异,因此它可以用来度量排序模型的排序质量。相比Pointwise Ranking模型,ListNet考虑了文档之间的相对关系,能够更直接地优化整个列表的排序质量,
②神经网络使用NDCG加权loss的封装和手写实现
l5 = tf.constant([[0.2, 0.8]], dtype=tf.float32) # 1.2214027581601699 2.225540928492468
# l5_softmax = [0.3543436937742045, 0.6456563062257954]
true_label = tf.constant([[1, 3]], dtype=tf.float32)
lambda_weightweights = tfr.losses.DCGLambdaWeight(
gain_fn=lambda labels: tf.pow(2.0, labels) - 1.0, # [1, 7]
rank_discount_fn=lambda rank: 1.0 / tf.math.log1p(rank),
normalized=True
)
lambda_weightweights_2 = tfr.losses.DCGLambdaWeight(
gain_fn=lambda labels: tf.pow(2.0, labels) - 1.0, # [1, 7]
rank_discount_fn=lambda rank: 1.0 / tf.math.log1p(rank), # [0.91, 1.44]
normalized=False
)
loss1 = tfr.losses._softmax_loss(labels=true_label, logits=l5, lambda_weight=lambda_weightweights)
loss2 = tfr.losses._softmax_loss(labels=true_label, logits=l5, lambda_weight=lambda_weightweights_2)
loss3 = tfr.losses._softmax_loss(labels=true_label, logits=l5)
with tf.Session() as sess:
print(sess.run(loss1))
print(sess.run(loss2))# print(7 * (1/np.log1p(1)) * np.log(0.6456563062257954) + 1 * (1/np.log1p(2)) * np.log(0.3543436937742045))
print(sess.run(loss3))
idcg = 7 * 1.44 + 1 * 0.91 ≈11
0.48709634 除以idcg之后的loss
5.3624945 没有除idcg之前的loss
2.349952 listnet损失函数