我遇到了搜索问题,我有一个查询和网址数据集。对于给定的查询,每对(查询、网址)都有一个相关性(目标),即一个应保留网址顺序的浮点数。 我想对我的 lightgbm.LGBMRanker 模型进行交叉验证,目标为 ndcg。
我浏览了文档,发现将实例保持在相同的位置非常重要
group
,因为实例实际上是一个包含所有关联 URL 的查询。
然而,我对此有一个问题,因为我收到以下错误:
ValueError: Computing NDCG is only meaningful when there is more than 1 document. Got 1 instead.
我使用了调试器,虽然我的数据集中没有任何大小小于
2
的组,但我在 _feval
函数中存在较小的组,这意味着 cv()
函数实际上并未保留一起分组。
在 lightgbm.cv 中,我没有看到
LGBMRanker中使用的
group
参数的迹象。
但我可以看到函数 lightbm.cv 精确地表示了Values passed through params take precedence over those supplied via arguments
。我的理解是这个值被传递给 cv 函数的底层模型。
这是我到目前为止的代码:
def eval_model(
self,
model: lightgbm.LGBMRanker,
k_fold: int = 3,
seed: int = 42,
):
"""Evaluates with NDCG"""
def _feval(y_pred: np.ndarray, lgb_dataset: lightgbm.basic.Dataset):
y_true = lgb_dataset.get_label()
serp_sizes = lgb_dataset.get_group()
ndcg_values = []
start = 0
for size in serp_sizes:
end = start + size
y_true_serp, y_pred_serp = y_true[start:end], y_pred[start:end]
ndcg_serp = sklearn.metrics.ndcg_score(
[y_true_serp], [y_pred_serp], k=10
)
ndcg_values.append(ndcg_serp)
start = end
eval_name = "my-ndcg"
eval_result = np.mean(ndcg_values)
greater_is_better = True
return eval_name, eval_result, greater_is_better
lgb_dataset = lightgbm.Dataset(data=self.X, label=self.y, group=self.serp_sizes)
cv_results = lightgbm.cv(
params={**model.get_params(), "group": self.serp_sizes},
train_set=lgb_dataset,
num_boost_round=1_000,
nfold=k_fold,
stratified=False,
seed=seed,
feval=_feval,
)
ndcg = np.mean(cv_results["my-ndcg"])
return ndcg
我的错误/误解在哪里? 是否有一个简单的解决方法可以使用
lightgbm.LGBMRanker
执行交叉验证,并将各组保持在一起?
我想对我的 lightgbm.LGBMRanker 模型进行交叉验证,目标为 ndcg。
从
lightgbm==4.1.0
(撰写本文时的最新版本)开始,lightgbm.sklearn.LGBMRanker
无法与 scikit-learn
的交叉验证 API 一起使用。
也无法传递给
lightgbm.cv()
。
在 lightgbm.cv 中,我没有看到 LGBMRanker 中使用的组参数的迹象
如 LightGBM 文档 (link) 中所述,
lightgbm.cv()
期望传递一个 lightgbm.Dataset
对象。
group
是 Dataset
对象的属性。
要对 LightGBM 学习排序模型进行交叉验证,请使用
lightgbm.cv()
而不是 lightgbm.sklearn.LGBMRanker()
。
这是一个使用 3.11.7 和
lightgbm==4.1.0
的最小的、可重现的示例。
import lightgbm as lgb
import numpy as np
import requests
from sklearn.datasets import load_svmlight_file
from tempfile import NamedTemporaryFile
# get training data from LightGBM examples
data_url = "https://raw.githubusercontent.com/microsoft/LightGBM/master/examples/lambdarank"
with NamedTemporaryFile(mode="w") as f:
train_data_raw = requests.get(f"{data_url}/rank.train").text
f.write(train_data_raw)
X, y = load_svmlight_file(f.name)
group = np.loadtxt(f"{data_url}/rank.train.query")
# create a LightGBM Dataset
dtrain = lgb.Dataset(
data=X,
label=y,
group=group
)
# perform LambdaRank 3-fold cross-validation with 1 set of hyperparameters
cv_results = lgb.cv(
train_set=dtrain,
params={
"objective": "lambdarank",
"eval_at": 2,
"num_iterations": 10
},
nfold=3,
return_cvbooster=True
)
# check metrics
np.round(cv_results["valid ndcg@2-mean"], 3)
# array([0.593, 0.597, 0.64 , 0.632, 0.64 , 0.636, 0.655, 0.655, 0.653, 0.669])
lightgbm.cv()
在创建交叉验证折叠时将正确保留查询组。
通过参数传递的值优先于通过参数提供的值
在 LightGBM 的文档中,“param”特指 https://lightgbm.readthedocs.io/en/v4.1.0/Parameters.html.
中描述的配置您引用的声明不适用于
group
、init_score
和 label
等数据,并且这些内容不应通过任何 LightGBM 接口中的 params
关键字参数传递。