LGBMRegressor python 中 tweedie/Regression_l1 目标的自定义损失函数

Custom loss function of tweedie/Regression_l1 objective in LGBMRegressor python

提问人:anat 提问时间:10/26/2023 更新时间:11/14/2023 访问量:93

问:

我正在使用带有 objective=“tweedie”/objective='Regression_l1' 的 LGBMRegressor 模型,我需要创建一个自定义损失函数,该函数采用原始目标并根据业务需求对其进行一些更改。 在我开始更改之前,我正在尝试实现完全是 tweedie/Regression_l1 的自定义损失函数,以确保它是相同的。 我尝试了一些 tweedie 的常规实现,得到了不同的结果。 有人可以帮忙吗?

型号 Loss-Function LightGBM

评论


答:

1赞 VonC 10/29/2023 #1

查看LightGBM#RegressionTweedieLoss()的C++实现,您可以尝试看看是否可以在Python中模拟相同的实现,并查看结果是否不同(在根据业务需求应用更改之前)

像这样的东西(Python 中的松散翻译):

import lightgbm as lgb
import numpy as np

class CustomTweedieLoss:
    def __init__(self, rho=1.5):
        self.rho = rho

    def __call__(self, preds, train_data):
        labels = train_data.get_label()
        exp_1_score = np.exp((1 - self.rho) * preds)
        exp_2_score = np.exp((2 - self.rho) * preds)
        grad = -labels * exp_1_score + exp_2_score
        hess = -labels * (1 - self.rho) * exp_1_score + (2 - self.rho) * exp_2_score
        return grad, hess

# Usage:
objective = CustomTweedieLoss(rho=1.5)

LightGBM#RegressionL1loss()的想法相同:

import numpy as np
import lightgbm as lgb

class CustomL1Loss:
    def __init__(self, weights=None):
        self.weights = weights

    def __call__(self, preds, train_data):
        labels = train_data.get_label()
        diff = preds - labels
        grad = np.sign(diff)
        hess = np.ones_like(preds)
        if self.weights is not None:
            grad *= self.weights
            hess *= self.weights
        return grad, hess

# Usage:
# Get the weights data, if any, then:
# objective = CustomL1Loss(weights=weights_data)

评论

0赞 anat 10/29/2023
谢谢你的帮助,我之前尝试过 c++ 的实现并得到了不同的结果,但我认为它与你写的有点不同。我会尝试并报告。
0赞 anat 10/29/2023
我在 L1Loss 实现中执行了完全相同的代码减去权重并得到了不同的结果(+-100% 差异),如果我在使用 objective='Regression_l1' 的 lightgbm 实现时没有指定权重,那么是否有任何权重?
0赞 anat 10/29/2023
tweedie 损失的结果与原始实现有很大不同
0赞 VonC 10/30/2023
@anat 是的,这只是一个建议,让你尝试调整松散的 python 翻译。这个想法是:从原始的 C++ 实现开始,我的答案精确地引用了它。
0赞 anat 10/30/2023
我已经尝试了一些接近的代码实现,但没有一个接近。
-1赞 Ibrahim Iqbal 11/14/2023 #2

您似乎正在使用 objective=“tweedie” 或 objective='Regression_l1' 设置来使用 LGBMRegressor 模型,并尝试创建与这些目标的行为完全匹配的自定义损失函数。但是,在实现自定义损失函数时,您遇到了一些差异。

要创建模拟内置目标行为的自定义损失函数,请务必严格遵循目标函数的数学公式。对于 Tweedie 目标,LGBMRegressor 文档指定该目标是 Tweedie 偏差:

obj = tweedie(y, y_hat) = -2 * sum(w * (y * phi - (y_hat * phi)**(2/p) / (2/p)) / (1/p - 1))

其中 y 是真实目标值,y_hat 是预测值,w 是样本的权重,phi 是色散参数,p 是功效参数。

对于回归 L1 目标,目标是平均绝对误差:

obj = |y - y_hat|

如果您观察到结果的差异,则自定义损失函数的实现可能存在差异。确保自定义损失函数遵循 Tweedie 偏差或平均绝对误差的精确数学公式,具体取决于您要复制的目标。

下面是 Tweedie 的自定义损失函数示例:

import numpy as np

def custom_tweedie(y_true, y_pred, phi, p):
    dev = -2 * np.sum(y_true * phi - (y_pred * phi)**(2/p) / (2/p))
    return dev

# Usage:
# loss = custom_tweedie(y_true, y_pred, phi, p)

对于回归 L1,您可以使用平均绝对误差:

def custom_regression_l1(y_true, y_pred):
    return np.mean(np.abs(y_true - y_pred))

# Usage:
# loss = custom_regression_l1(y_true, y_pred)

确保参数(Tweedie 的 phi 和 p)设置正确,并与你在 LGBMRegressor 中使用的配置匹配。