XGBoost自定义soft-Fbeta损失函数无法更新问题排查
问题描述
背景
尝试为XGBoost二分类器实现自定义soft-Fbeta损失函数,直接优化Fbeta分数以替代标准logloss。
注意事项
Fbeta本身不可微,需利用模型输出的阈值化前的对数几率构建连续的TP、FP、FN来实现可微的损失。
尝试实现的代码
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
相关说明
- 即使将gradient和hessian改为常数,结果仍无变化
- 最初返回的hessian是矩阵形式,改为一维数组后仍无改善
- 模型始终预测0,完全不更新
- 修改数据集大小后logloss值完全不变
- 训练集和验证集logloss完全相同
- 使用内置logloss时模型可正常更新
核心问题
实现中存在哪些错误?是否遗漏了XGBoost自定义损失的基础规则?
问题分析与修正
1. 核心错误:混淆了logits与概率
XGBoost的自定义objective函数接收的第二个参数不是概率p,而是未经过sigmoid转换的对数几率(logits)。你的代码直接把输入当成概率计算梯度,导致梯度计算完全错误,模型无法更新。必须先将logits转换为概率:
p = 1 / (1 + np.exp(-logits))
2. 海森矩阵的要求错误
XGBoost要求hessian返回一维数组(每个样本的二阶导数,即海森矩阵的对角线元素),而不是完整的矩阵。你的代码返回的是二维矩阵,这会导致XGBoost无法正确处理梯度更新。
3. 损失函数的符号与优化方向
XGBoost是最小化损失函数,而我们的目标是最大化Fbeta分数,因此损失应定义为-Fbeta,同时梯度和hessian的计算要对应这个方向。
修正后的代码
import numpy as np import xgboost as xgb def sigmoid(x): return 1 / (1 + np.exp(-x)) def gradient(y: np.array, logits: np.array, beta: float): p = sigmoid(logits) tp = np.dot(p, y) fp = p.sum() - tp fn = y.sum() - tp # 计算soft Fbeta的梯度(针对概率p) dF_dp = (1 + beta**2) * (y * (tp + beta**2 * fn) - tp * p) / (tp + beta**2 * fn)**2 # 链式法则:dF_dlogits = dF_dp * dp_dlogits dp_dlogits = p * (1 - p) grad = dF_dp * dp_dlogits # 因为XGBoost最小化损失,损失是-Fbeta,所以梯度取负 return -grad def hessian(y: np.array, logits: np.array, beta: float): p = sigmoid(logits) tp = np.dot(p, y) fp = p.sum() - tp fn = y.sum() - tp dp_dlogits = p * (1 - p) # 计算二阶导数(针对logits) d2F_dp2 = (1 + beta**2) * ( -y * p * (tp + beta**2 * fn) + tp * y * p + tp * (tp + beta**2 * fn) * (1 - y) ) / (tp + beta**2 * fn)**3 hess = d2F_dp2 * (dp_dlogits)**2 + dF_dp * dp_dlogits * (1 - 2*p) # 损失是-Fbeta,海森取负 return -hess def f_smooth_loss(beta: float): def custom_loss(y: np.array, logits: np.array): # 确保y是float类型,避免计算错误 y = y.astype(np.float32) grad = gradient(y, logits, beta) hess = hessian(y, logits, beta) return grad, hess return custom_loss # 随机数据集 X_train = np.random.rand(100, 100) y_train = np.random.randint(0, 2, 100) X_validation = np.random.rand(1000, 100) y_validation = np.random.randint(0, 2, 1000) # 定义模型,添加eval_metric避免显示默认logloss model = xgb.XGBClassifier( objective=f_smooth_loss(5), eval_metric='logloss', learning_rate=0.1, n_estimators=10 ) # 训练 model.fit(X_train, y_train, eval_set=[(X_train, y_train), (X_validation, y_validation)])
额外说明
- 添加
eval_metric='logloss'是为了让训练过程显示logloss,若要监控Fbeta,可自定义评估函数并传入eval_metric - 确保输入的y转换为float类型,避免整数运算导致的错误
- 学习率可根据实际需求调整
内容的提问来源于stack exchange,提问作者GiacomoP
相关产品推荐
相关产品推荐

