为 LightGBM 提供额外的自定义指标以实现提前停止

Provide Additional Custom Metric to LightGBM for Early Stopping

我 运行 使用训练 API 在 LightGBM 中进行二元分类,并希望停止在自定义指标上,同时仍跟踪一个或多个内置指标。不过,尚不清楚这是否可行。

在这里我们可以禁用默认的 binary_logloss 指标,只跟踪我们的自定义指标:

import lightgbm as lgb

def my_eval_metric(...):
    ...

d_train = lgb.Dataset(...)
d_validate = lgb.Dataset(...)

params = {
    "objective": "binary",
    "metric": "custom",
}

evals_result = {}

model = lgb.train(
    params,
    d_train,
    valid_sets=[d_validate],
    feval=my_eval_metric,
    early_stopping_rounds=10,
    evals_result=evals_result,
)

如果我们让 metric 成为默认值,我们也会跟踪 binary_logloss,但我们会停止在 两个 指标上,而不仅仅是我们的自定义指标公制:

params = {
    "objective": "binary",
    # "metric": "custom",
}

我们可以在 params 中设置 first_metric_only ,但现在我们将在 binary_logloss 上仅 停止,显然,这是第一个公制:

params = {
    "objective": "binary",
    "first_metric_only": True,
}

其他可能有效但看起来很痛苦的事情:

  1. 在 sklearn API 中,您可以指定一个评估指标列表,这些指标散布在自定义指标的可调用对象和内置指标的字符串中;但是,我不想切换到 sklearn API.
  2. 我可以重新实现 binary_logloss 并将其作为自定义评估指标与我的其他自定义指标一起传递到列表中并使用 first_metric_only;但是,似乎我不应该那样做。

不起作用的东西:

  1. feval=[my_eval_metric, 'binary_logloss']lgb.train 调用中。抱怨字符串不可调用。
  2. metric: [my_eval_metric, 'binary_logloss']params 集合中。警告 Unknown parameter: my_eval_metric,然后在训练以 ValueError: For early stopping, at least one dataset and eval metric is required for evaluation.
  3. 开始时出错

我是否遗漏了一些明显的东西,或者这是 LightGBM 中的一个小洞 API?

这是 3.2.1 版。在 3.0.0 版本上,似乎完全不可能在训练中通过多个自定义评估指标 API。我不确定那里的 sklearn API。

如果您问 “如何根据自定义评估指标函数执行提前停止?”,可以通过将参数 metric 设置为字符串 "None"。这将导致 LightGBM 跳过基于 objective 函数(在您的示例中为 binary_logloss)的默认评估指标,并且仅对您在 feval 中提供的自定义指标函数执行提前停止.

下面的示例在 Python 3.8.8 上使用 lightgbm==3.2.1scikit-learn==0.24.1 重现了此行为。

import lightgbm as lgb
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=42)

dtrain = lgb.Dataset(
    data=X_train,
    label=y_train
)

dvalid = lgb.Dataset(
    data=X_test,
    label=y_test,
    reference=dtrain
)

def _constant_metric(dy_true, dy_pred):
    """An eval metric that always returns the same value"""
    metric_name = 'constant_metric'
    value = 0.708
    is_higher_better = False
    return metric_name, value, is_higher_better

evals_result = {}

model = lgb.train(
    params={
        "objective": "binary",
        "metric": "None",
        "num_iterations": 100,
        "first_metric_only": True,
        "verbose": 0,
        "num_leaves": 8
    },
    train_set=dtrain,
    valid_sets=[dvalid],
    feval=_constant_metric,
    early_stopping_rounds=5,
    evals_result=evals_result,
)

您可以在日志中看到,我提供的自定义度量函数是针对验证集进行评估的,并且在连续 early_stopping_rounds 轮训练没有改进后停止。

[LightGBM] [Warning] Auto-choosing col-wise multi-threading, the overhead of testing was 0.000846 seconds.
You can set `force_col_wise=true` to remove the overhead.
[1] valid_0's constant_metric: 0.708
Training until validation scores don't improve for 5 rounds
[2] valid_0's constant_metric: 0.708
[3] valid_0's constant_metric: 0.708
[4] valid_0's constant_metric: 0.708
[5] valid_0's constant_metric: 0.708
[6] valid_0's constant_metric: 0.708
Early stopping, best iteration is:
[1] valid_0's constant_metric: 0.708
Evaluated only: constant_metric

如果您问 “我如何向 lgb.train() 提供内置指标和自定义评估函数的组合并评估所有指标,但仅将自定义指标用于早期停止?”...那么是的,从 lightgbm 3.2.1 开始不支持。