我正在尝试为二元分类实现自己的损失函数。首先,我想重现二元目标的完全行为。特别是,我希望实现以下几点:
- 两个函数的损失具有相同的尺度
- 训练和验证的斜率相似
- predict_proba(X) 返回概率
以下代码并未实现这些要求:
import sklearn.datasetsimport lightgbm as lgbimport numpy as npX, 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, hessmodel = 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 时,斜率甚至不平滑:
此外,对于 loglikelihood,必须对 model.predict_proba(X) 应用 sigmoid 函数才能获得概率(正如我从 https://github.com/Microsoft/LightGBM/issues/2136 中了解到的)。
是否有可能使用自定义损失函数获得相同的行为?有谁了解这些差异的来源?
回答:
查看每个案例中 model.predict_proba(X)
的输出,我们可以看到内置的 binary_logloss 模型返回概率,而自定义模型返回 logit。
内置的评估函数以概率作为输入。为了适应自定义目标,我们需要一个自定义评估函数,该函数将 logit 作为输入。
以下是如何编写这一点。我已经更改了 sigmoid 计算,以便在 logit 为大负数时不会溢出。
def loglikelihood(labels, logits): #数值稳定的 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, hessdef my_eval(labels, logits): #数值稳定的 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)
现在,结果是相同的。