XGBoost - 学习排序
XGBoost 是各种 LTR 应用中最常见的选择,例如推荐系统增强、点击率预测和 SEO。在本节中,我们将介绍各种目标函数,引导您完成数据准备步骤,并提供一些如何训练模型的示例。
什么是学习排序?
在开始之前,我们将简单解释什么是排序。排序是监督机器学习的一个子集。它不是预测单个数据点的结果,而是在接收一组数据点和一个查询后评估一系列数据点,这将其与更常见的分类和回归情况区分开来。
通常,搜索引擎使用排序来识别最相关的结果。它还可以用于建议内容,根据之前的购买提供相关的建议,或者,就像它对我一样,识别在下一场比赛中获胜概率最高的马匹。
XGBoost 提供三种用于排序的目标函数:逐点、成对和列表。这三种目标函数各有优缺点,它们代表了确定项目组等级的不同方法。许多资源对它们进行了详细的描述,但我们将重点介绍这里的主要内容。
逐点法
此技术独立处理每个查询文档对。您只需为每个查询-文档对赋予一个分数,并构建一个可以预测相关性分数的模型。
考虑以下情况,我们有一组查询-文档对的数据集,每对的相关性分数介于 1 和 5 之间。可以通过训练回归模型来预测每对的相关性分数。
逐点法是一种很好的入门方法,因为它除了易于使用之外,出乎意料地强大且难以超越。此方法可以在 XGBoost 中与任何典型的回归或分类目标函数一起使用。请记住要调整数据集标签中的任何不平衡。
成对法
成对方法评估文档对,并做出决定以最小化顺序错误的对数。它被 RankNet 等算法使用。
这种方法每次处理一个查询和两个文档,并调整预测的相关性分数,以便最相关的文档的分数高于最不相关的文档。
与逐点法相比,它同时考虑查询和单个文档。在这里,我们希望精确地模拟文档相对于查询的相对顺序。
XGBoost 为此策略提供了一些目标函数:
rank:pairwise: 这是原始的成对损失函数(也称为 RankNet),它将 LambdaRank 与 MART(多加性回归树)结合起来,也称为 LambdaMART。
rank:ndcg: NDCG 代表归一化折损累积增益。它是业界最流行的排序质量指标之一,因为它同时考虑了给定查询的文档的相对顺序和每个页面的相关性分数。此目标函数使用从 NDCG 指标创建的替代梯度来优化模型。
rank:map: MAP 代表平均平均精度。当相关性分数为二进制(0 或 1)时,使用此较短的排序质量指标。如果 MAP 用作评估指标,则通常需要使用此目标函数。
列表法
即使 XGBoost 不使用列表法,为了完整起见,仍然有必要讨论它。它考虑特定查询的整个文档集,试图一次优化列表的顺序。
由于它分析所有文档的相对顺序,它是对逐点和成对过程的改进,并可能产生更好的结果。列表法损失函数的一个例子是 ListMLE。
交叉验证是一种有用的技术,用于在尝试选择最佳目标函数时确定适合您问题的理想目标函数。逐点法完全无效。
使用 XGBoost 进行学习排序
我们将了解如何使用 XGBoost(一个强大的机器学习框架)准备数据并构建学习排序 (LTR) 模型。我们将使用来自微软的 MSLR-WEB10K 真实世界数据集,该数据集在学习排序社区中很流行。此数据集中查询-文档对的相关性等级显示文档与用户查询的匹配程度。
步骤 1:准备数据
学习排序是搜索引擎根据相关性对结果进行排序的策略。此集中给出了:用户试图查找什么?显示了用户的搜索结果。此外,每个文档的相关性分数(4 为最高相关性级别)显示了其与查询的相关性级别。分数范围为 0 到 4。
在创建模型之前,我们必须导入一些用于数据处理的关键库:
import pandas as pd import numpy as np from sklearn.datasets import load_svmlight_file
这些库使数据处理更容易。NumPy 用于数值计算,Pandas 用于数据操作。使用 sklearn.datasets 中的 load_svmlight_file 方法加载文本格式的大型多特征数据集。
之后我们将加载我们的训练和验证数据集:
train = load_svmlight_file(str(data_path / '/Python/vali.txt'), query_id=True) valid = load_svmlight_file(str(data_path / '/Python/test.txt'), query_id=True)
我们的模型的训练和测试数据存储在 train 和 valid 中。每个数据集都包含三个组成部分:目标向量(相关性分数)、特征矩阵(表示文档特性)和查询 ID(用于将文档分组到同一个查询下)。
现在我们将将其解包到变量中以便轻松使用:
X_train, y_train, qid_train = train X_valid, y_valid, qid_valid = valid
现在,X_train 和 X_valid 是特征矩阵,y_train 和 y_valid 是相关性分数,qid_train 和 qid_valid 用于按查询对文档进行分组。我们可以使用这种方法来复制排序任务。
步骤 2:训练 XGBRanker
在训练过程中,我们构建一个模型来根据文档与查询的相关性对文档进行排序。通过导入 XGBoost 库,我们将首先创建 XGBRanker 类,该类旨在用于涉及学习排序的任务。
import xgboost as xgb model = xgb.XGBRanker(tree_method="hist", objective="rank:ndcg")
这里:
tree_method="hist" 是一种快速生成决策树的方法。
objective="rank:ndcg" 设置旨在优化归一化折损累积增益 (NDCG),这是一个常用在排序任务中的统计量。
接下来,我们将模型拟合到训练数据:
model.fit(X_train, y_train, qid=qid_train)
在这种情况下,我们传递查询 ID (qid_train)、特征矩阵 (X_train) 和相关性分数 (y_train)。使用查询 ID 将属于同一查询的文档分组是排序过程中的一个重要步骤。
步骤 3:预测相关性分数
模型训练完成后,我们可以使用模型进行预测。对于排序,通常每次预测一个查询的相关性很有帮助。假设我们想预测验证集中第一个查询的结果:
X_query = X_valid[qid_valid == qids[0]]
这里,X_query 包含与初始查询相关的所有文档。现在,我们可以通过应用以下方法来预测它们的相关性分数:
y_pred = model.predict(X_query)
计算出的分数将显示每个文档相对于其他文档的相关性。通过对这些预测进行排序,可以对文档进行评分;分数越高表示相关性越高。
步骤 4:使用 NDCG 评估模型
我们使用一种称为归一化折损累积增益或 NDCG 的指标来评估我们的排序系统的有效性。此统计量评估排序在多大程度上准确地反映了文档的实际重要性。以下是 NDCG 计算函数:
def ndcg(y_score, y_true, k): order = np.argsort(y_score)[::-1] y_true = np.take(y_true, order[:k]) gain = 2 ** y_true - 1 discounts = np.log2(np.arange(len(y_true)) + 2) return np.sum(gain / discounts)
通过比较预期分数 (y_score) 和真实相关性分数 (y_true),此函数计算 NDCG。我们遍历验证集中的每个查询并计算 NDCG 分数:
ndcg_ = list()
qids = np.unique(qid_valid)
for i, qid in enumerate(qids):
y = y_valid[qid_valid == qid]
if np.sum(y) == 0:
continue
p = model.predict(X_valid[qid_valid == qid])
idcg = ndcg(y, y, k=10)
ndcg_.append(ndcg(p, y, k=10) / idcg)
最后,我们计算每个查询的平均 NDCG 分数:
np.mean(ndcg_)
因此,我们剩下的只是一个代表模型整体性能的单个分数。NDCG 分数越高表示排序质量越高。