如何使用 LightGBM.LGBMRanker 执行交叉验证,同时保持组在一起?

问题描述 投票:0回答:1

我遇到了搜索问题,我有一个查询和网址数据集。对于给定的查询,每对(查询、网址)都有一个相关性(目标),即一个应保留网址顺序的浮点数。 我想对我的 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
执行交叉验证,并将各组保持在一起?

machine-learning scikit-learn cross-validation ranking lightgbm
1个回答
0
投票

我想对我的 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
关键字参数传递。

© www.soinside.com 2019 - 2024. All rights reserved.