You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XGBoost中多类别Focal Loss无法有效训练问题排查

XGBoost自定义多类别Focal Loss后预测概率始终均等的问题

我有一个包含60个特征变量、最后一列为4分类目标的数据集,由于数据极度不平衡,希望在XGBoost中实现自定义多类别Focal Loss损失函数。参考XGBoost官方文档编写了如下目标函数,并用SymPy验证了梯度与海森矩阵的正确性:

import numpy as np
import xgboost
from scipy.special import log_softmax, softmax
from sklearn.utils import compute_sample_weight
from sklearn.model_selection import train_test_split

num_classes = 4

def focal_loss_multiclass_objective(inputs: np.ndarray, dtrain: xgboost.DMatrix,
                                alpha=1, gamma=2, num_classes=4):
    # 将logits转换为概率和对数概率
    log_prob = log_softmax(inputs)
    prob = np.exp(log_prob)
    print(f"prob: {prob}")

    # 对正确类别进行独热编码
    targets = dtrain.get_label().astype(np.int64)
    targets_one_hot = np.identity(num_classes)[targets]

    # 梯度计算
    grad = -alpha*targets_one_hot*((1-prob)**(gamma-1)) * (-gamma*log_prob
                                                         + (1-prob)/(prob+1e-6))
    print(f"grad: {grad}")

    # 海森矩阵计算
    hess = -alpha*targets_one_hot*(gamma*(gamma-1)*((1-prob)**(gamma-2)) * log_prob
                                      - 2*gamma*((1-prob)**(gamma-1))/(prob+1e-6) 
                                      - ((1-prob)**gamma)/(prob+1e-6)**2)
    print("hess: ", hess)

    return grad, hess

训练过程中,划分了训练集与测试集,使用平衡样本权重构建DMatrix,并设置了合理的模型参数:

X = df.iloc[:, 0:60]
y = df.iloc[:, -1]

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 样本权重:平衡类别权重
sample_weights = compute_sample_weight('balanced', y_train)
print(np.unique(sample_weights))

# 创建DMatrix对象
dtrain = xgboost.DMatrix(X_train, label=y_train, weight=sample_weights)
dtest = xgboost.DMatrix(X_test, label=y_test)

params = {
    'num_class': num_classes,
    #'disable_default_eval_metric': True,
    'eval_metric': 'mlogloss',
    'eta': 0.3,
    'max_depth': 6,
    'subsample': 0.8,
    'colsample_bytree': 0.8,
    'tree_method': 'auto'
}


# 训练模型
model = xgboost.train(params,
                      dtrain,
                      num_boost_round=100,
                      obj=focal_loss_multiclass_objective)

但训练完成后,模型对测试集的预测概率始终为每类25%,无法有效完成训练,请问问题出在哪里?

预测代码如下:

y_pred_logits = model.predict(dtest, output_margin=True)
y_prob = softmax(y_pred_logits, axis=1)
print(y_pred_logits)
print(y_prob)

问题原因与修复方案

1. 梯度/海森矩阵维度不匹配(核心问题)

XGBoost多分类场景下,输入的inputs是(n_samples, num_classes)的二维数组,但XGBoost要求返回的梯度和海森矩阵必须是扁平化的一维数组(将每个样本的num_classes个梯度值按顺序拼接)。当前代码直接返回二维数组,导致XGBoost无法正确解析梯度信息,模型无法更新,始终停留在初始的均等概率状态。

修复:在返回前将梯度和海森矩阵扁平化:

return grad.flatten(), hess.flatten()

2. Focal Loss公式推导不完整

当前代码仅计算了正确类别的梯度,忽略了其他类别的梯度项。多类别Focal Loss的梯度需要覆盖所有类别:

  • 对于正确类别$t$,梯度需反映该类预测概率的误差
  • 对于非正确类别$j$,梯度需体现其对正确类别概率的影响

建议重新推导标准多类别Focal Loss的梯度与海森矩阵,确保所有类别的梯度都被正确计算。

3. 样本权重与Loss参数重复应用

你同时在DMatrix中设置了weight=sample_weights,又在自定义Loss中保留了alpha参数,这会导致权重被重复叠加,可能使梯度值异常,干扰模型收敛。建议二选一:

  • 去掉自定义Loss中的alpha,仅使用DMatrix的样本权重
  • 将alpha替换为类别专属权重数组,而非全局统一值

4. 训练策略优化

修复上述问题后,可补充以下优化:

  • 添加早停策略,避免过拟合或迭代不足:
watchlist = [(dtrain, 'train'), (dtest, 'test')]
model = xgboost.train(params,
                      dtrain,
                      num_boost_round=500,
                      obj=focal_loss_multiclass_objective,
                      evals=watchlist,
                      early_stopping_rounds=50)
  • 暂时关闭默认评估指标(开启disable_default_eval_metric=True),避免自定义Loss与默认指标的逻辑冲突

内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 19:07:39