XGBoost 中的自定义损失未更新

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

背景

我正在尝试对 XGBoost 二元分类器使用自定义损失函数。

这个想法是在 XGBoost 中实现 soft-Fbeta 损失,我在here读到了相关内容。简单地说:不使用标准的对数损失,而是使用直接优化 Fbeta 分数的损失函数。

注意事项

当然,Fbeta本身是不可微分的,所以不能直接开箱即用。然而,我们的想法是使用概率(因此,在阈值化之前)来创建某种连续的 TP、FP 和 FN。在参考的 Medium 文章中查找更多详细信息。

尝试

我的尝试如下(受到几个不同人的启发)。

import numpy as np
import xgboost as xgb

def gradient(y: np.array, p: np.array, beta: float):

    """Compute the gradient of the loss function. y is the true label, p
    the probability predicted by the model """
    
    # Define the denominator
    D = p.sum() + beta**2 * y.sum() 
    
    # Compute the gradient
    grad = (1 + beta**2) * y / D - (1 + beta**2) * (np.dot(p, y)) / D**2 
        
    return grad

def hessian(y: np.array, p: np.array, beta: float):

    """Compute the Hessian of the loss function. y is the true label, p
    the probability predicted by the model """
    
    # Define the denominator
    D = p.sum() + beta**2 * y.sum() 
    
    # Tensor sum y_i + y_j
    tensor_sum = y + y[:, None]
    
    # Compute the hessian
    hess = (1 + beta**2) / D**2 * (-tensor_sum + 2*np.dot(p, y) / D)
    
    return hess

def f_smooth_loss(beta: float):
    
    """ Custom loss function for maximising F score"""
    def custom_loss(y: np.array, p: np.array):
                
        # Actual custom loss
        b = beta
        
        # Compute grad
        grad = - gradient(y, p, b)
        
        # Compute hessian
        hess = - hessian(y, p, b)
                  
        return grad, hess
        
    return custom_loss

# Random train dataset
X_train = np.random.rand(100, 100)
y_train = np.random.randint(0, 2, 100)

# Random validation dataset
X_validation = np.random.rand(1000, 100)
y_validation = np.random.randint(0, 2, 1000)

# Define a classifier trying to maximise F5 score
model = xgb.XGBClassifier(objective=f_smooth_loss(5))

# Fit
model.fit(X_train, y_train,  eval_set=[(X_train, y_train), (X_validation, y_validation)])

输出

模型运行,但输出显然卡住了,无论如何:

[0] validation_0-logloss:0.69315    validation_1-logloss:0.69315
[1] validation_0-logloss:0.69315    validation_1-logloss:0.69315
[2] validation_0-logloss:0.69315    validation_1-logloss:0.69315
[3] validation_0-logloss:0.69315    validation_1-logloss:0.69315

评论

  1. 我的导数可能不正确,即使我仔细检查了它们。然而,即使将 grad 和 hess 更改为常数,也没有任何变化。

  2. 这里的 Hessian 矩阵是一个矩阵(这将是它的数学定义),但我认为 XGBoost 需要一个一维数组(我认为它是对角线)。然而,由于第 1 点,即使我将其更改为一维数组,也不会发生任何变化

  3. 本质上,这个模型总是预测零,并且根本不更新。

  4. 更改(假)数据集的大小不会导致对数损失发生任何变化(甚至,数字完全相同)。

  5. 奇怪的是,验证和训练中的对数损失是相同的,这是另一个信号,表明某处存在严重错误。

  6. 如果我切换到标准对数损失(内置),它会更新(输出是随机的,因为数据集是随机的)。

问题

我的实施有什么问题? XGB 文档非常难以解读,我真的无法判断我是否缺少一个简单的构建块。

python xgboost loss-function xgbclassifier
2个回答
3
投票

问题是,按照docs,自定义损失函数需要以下参数作为输入:


....


def f_smooth_loss(beta: float):
    
    """ Custom loss function for maximising F score"""
    def custom_loss(
        predt: np.ndarray,
        dtrain: xgb.DMatrix
    ) -> Tuple[np.ndarray, np.ndarray]:
                
        # Actual custom loss
        b = beta
        
        # Compute grad
        grad = - gradient(dtrain, predt, b)
        
        # Compute hessian
        hess = - hessian(dtrain, predt, b)
                  
        return grad, hess
        
    return custom_los


更新:根据引用的文档似乎您需要在类的

.train()
中传递函数,而不是在初始化模型时传递函数,例如:

xgb.train({'tree_method': 'hist', 'seed': 1994},  # any other tree method is fine.
           dtrain=dtrain,
           num_boost_round=10,
           obj=f_smooth_loss(5))

另外,请注意

.fit()
方法是 XGBoost 的包装器,作为与其他 sklearn 对象(例如 sklearn.pipeline)交互的接口,因此它可能缺少此功能,因此最好使用本机方法
.train()


-1
投票

请将分类器从

objective=f_smooth_loss(5)
更改为
scoring=f_smooth_loss(5)
:

model = xgb.XGBClassifier(scoring = f_smooth_loss(5))
© www.soinside.com 2019 - 2024. All rights reserved.