如何在 LightGBM 中实现与二进制 objective 具有相同行为的自定义 logloss?
How to implement custom logloss with identical behavior to binary objective in LightGBM?
我正在尝试为二进制分类实现我自己的损失函数。首先,我想重现二进制 objective 的确切行为。特别是,我想要:
- 两个函数的损失具有相同的规模
- 训练和验证斜率相似
- predict_proba(X) returns 概率
None 下面的代码就是这种情况:
import sklearn.datasets
import lightgbm as lgb
import numpy as np
X, y = sklearn.datasets.load_iris(return_X_y=True)
X, y = X[y <= 1], y[y <= 1]
def loglikelihood(labels, preds):
preds = 1. / (1. + np.exp(-preds))
grad = preds - labels
hess = preds * (1. - preds)
return grad, hess
model = lgb.LGBMClassifier(objective=loglikelihood) # or "binary"
model.fit(X, y, eval_set=[(X, y)], eval_metric="binary_logloss")
lgb.plot_metric(model.evals_result_)
与objective="binary":
使用 objective=loglikelihood 斜率甚至不平滑:
此外,必须将 sigmoid 应用于 model.predict_proba(X) 以获得对数似然的概率(正如我从 https://github.com/Microsoft/LightGBM/issues/2136 中得出的那样)。
是否可以使用自定义损失函数获得相同的行为?有人知道所有这些差异从何而来吗?
在每种情况下查看 model.predict_proba(X)
的输出,我们可以看到内置 binary_logloss 模型 returns 概率,而自定义模型 returns logits .
内置的评估函数以概率作为输入。为了适应自定义 objective,我们需要一个自定义评估函数,它将 logits 作为输入。
这里是你如何写这个。我已经更改了 sigmoid 计算,这样如果 logit 是一个大的负数,它就不会溢出。
def loglikelihood(labels, logits):
#numerically stable sigmoid:
preds = np.where(logits >= 0,
1. / (1. + np.exp(-logits)),
np.exp(logits) / (1. + np.exp(logits)))
grad = preds - labels
hess = preds * (1. - preds)
return grad, hess
def my_eval(labels, logits):
#numerically stable logsigmoid:
logsigmoid = np.where(logits >= 0,
-np.log(1 + np.exp(-logits)),
logits - np.log(1 + np.exp(logits)))
loss = (-logsigmoid + logits * (1 - labels)).mean()
return "error", loss, False
model1 = lgb.LGBMClassifier(objective='binary')
model1.fit(X, y, eval_set=[(X, y)], eval_metric="binary_logloss")
model2 = lgb.LGBMClassifier(objective=loglikelihood)
model2.fit(X, y, eval_set=[(X, y)], eval_metric=my_eval)
现在结果是一样的。
我正在尝试为二进制分类实现我自己的损失函数。首先,我想重现二进制 objective 的确切行为。特别是,我想要:
- 两个函数的损失具有相同的规模
- 训练和验证斜率相似
- predict_proba(X) returns 概率
None 下面的代码就是这种情况:
import sklearn.datasets
import lightgbm as lgb
import numpy as np
X, y = sklearn.datasets.load_iris(return_X_y=True)
X, y = X[y <= 1], y[y <= 1]
def loglikelihood(labels, preds):
preds = 1. / (1. + np.exp(-preds))
grad = preds - labels
hess = preds * (1. - preds)
return grad, hess
model = lgb.LGBMClassifier(objective=loglikelihood) # or "binary"
model.fit(X, y, eval_set=[(X, y)], eval_metric="binary_logloss")
lgb.plot_metric(model.evals_result_)
与objective="binary":
使用 objective=loglikelihood 斜率甚至不平滑:
此外,必须将 sigmoid 应用于 model.predict_proba(X) 以获得对数似然的概率(正如我从 https://github.com/Microsoft/LightGBM/issues/2136 中得出的那样)。
是否可以使用自定义损失函数获得相同的行为?有人知道所有这些差异从何而来吗?
在每种情况下查看 model.predict_proba(X)
的输出,我们可以看到内置 binary_logloss 模型 returns 概率,而自定义模型 returns logits .
内置的评估函数以概率作为输入。为了适应自定义 objective,我们需要一个自定义评估函数,它将 logits 作为输入。
这里是你如何写这个。我已经更改了 sigmoid 计算,这样如果 logit 是一个大的负数,它就不会溢出。
def loglikelihood(labels, logits):
#numerically stable sigmoid:
preds = np.where(logits >= 0,
1. / (1. + np.exp(-logits)),
np.exp(logits) / (1. + np.exp(logits)))
grad = preds - labels
hess = preds * (1. - preds)
return grad, hess
def my_eval(labels, logits):
#numerically stable logsigmoid:
logsigmoid = np.where(logits >= 0,
-np.log(1 + np.exp(-logits)),
logits - np.log(1 + np.exp(logits)))
loss = (-logsigmoid + logits * (1 - labels)).mean()
return "error", loss, False
model1 = lgb.LGBMClassifier(objective='binary')
model1.fit(X, y, eval_set=[(X, y)], eval_metric="binary_logloss")
model2 = lgb.LGBMClassifier(objective=loglikelihood)
model2.fit(X, y, eval_set=[(X, y)], eval_metric=my_eval)
现在结果是一样的。