自定义目标函数 #

自定义目标函数概述 #

为什么需要自定义? #

text
┌─────────────────────────────────────────────────────────────┐
│                    自定义目标函数场景                        │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 特定业务需求                                             │
│     - 不平衡分类的成本敏感损失                               │
│     - 回归任务的加权损失                                     │
│     - 排序任务的自定义指标                                   │
│                                                              │
│  2. 研究创新                                                 │
│     - 新的损失函数设计                                       │
│     - 论文算法实现                                           │
│     - 实验性目标函数                                         │
│                                                              │
│  3. 特殊优化目标                                             │
│     - 分位数回归                                             │
│     - 置信区间预测                                           │
│     - 多目标优化                                             │
│                                                              │
└─────────────────────────────────────────────────────────────┘

目标函数要求 #

XGBoost 的自定义目标函数需要返回一阶和二阶梯度:

python
def custom_objective(preds, dtrain):
    """
    自定义目标函数
    
    参数:
    - preds: 模型预测值(原始输出,未经 sigmoid/softmax)
    - dtrain: DMatrix 对象
    
    返回:
    - grad: 一阶导数(梯度)
    - hess: 二阶导数(海森矩阵对角元素)
    """
    labels = dtrain.get_label()
    
    # 计算梯度和海森
    grad = ...  # 一阶导数
    hess = ...  # 二阶导数
    
    return grad, hess

常见目标函数实现 #

二分类 - Log Loss #

python
import numpy as np
import xgboost as xgb

def logloss_objective(preds, dtrain):
    """
    二分类 Log Loss 目标函数
    
    L = -[y*log(p) + (1-y)*log(1-p)]
    p = sigmoid(preds)
    
    一阶导数: ∂L/∂preds = p - y
    二阶导数: ∂²L/∂preds² = p * (1 - p)
    """
    labels = dtrain.get_label()
    
    # Sigmoid
    preds = 1.0 / (1.0 + np.exp(-preds))
    
    # 梯度和海森
    grad = preds - labels
    hess = preds * (1.0 - preds)
    
    return grad, hess

# 使用自定义目标函数
dtrain = xgb.DMatrix(X_train, label=y_train)
params = {'max_depth': 6, 'eta': 0.1}
model = xgb.train(params, dtrain, num_boost_round=100, obj=logloss_objective)

多分类 - Softmax Loss #

python
def softmax_objective(preds, dtrain, num_class):
    """
    多分类 Softmax 目标函数
    
    preds: shape = (n_samples * num_class,)
    """
    labels = dtrain.get_label()
    
    # 重塑预测值
    preds = preds.reshape(-1, num_class)
    
    # Softmax
    max_preds = np.max(preds, axis=1, keepdims=True)
    exp_preds = np.exp(preds - max_preds)
    probs = exp_preds / np.sum(exp_preds, axis=1, keepdims=True)
    
    # One-hot 编码标签
    y_onehot = np.zeros_like(probs)
    y_onehot[np.arange(len(labels)), labels.astype(int)] = 1
    
    # 梯度和海森
    grad = (probs - y_onehot).flatten()
    hess = (probs * (1 - probs)).flatten()
    
    return grad, hess

回归 - Huber Loss #

python
def huber_objective(preds, dtrain, delta=1.0):
    """
    Huber Loss 目标函数
    
    对异常值更鲁棒
    """
    labels = dtrain.get_label()
    residual = preds - labels
    
    abs_residual = np.abs(residual)
    
    # 梯度
    grad = np.where(
        abs_residual <= delta,
        residual,
        delta * np.sign(residual)
    )
    
    # 海森
    hess = np.where(
        abs_residual <= delta,
        np.ones_like(residual),
        np.zeros_like(residual)
    )
    
    return grad, hess

分位数回归 #

python
def quantile_objective(preds, dtrain, alpha=0.5):
    """
    分位数回归目标函数
    
    参数:
    - alpha: 分位数 (0.5 = 中位数)
    """
    labels = dtrain.get_label()
    residual = labels - preds
    
    # 梯度
    grad = np.where(
        residual > 0,
        -alpha,
        alpha - 1
    )
    
    # 海森(分位数损失在非零点不可导,使用常数)
    hess = np.ones_like(residual)
    
    return grad, hess

# 使用示例:预测 0.25, 0.5, 0.75 分位数
params = {'max_depth': 6, 'eta': 0.1}

# 25% 分位数
model_q25 = xgb.train(params, dtrain, num_boost_round=100, 
                      obj=lambda p, d: quantile_objective(p, d, 0.25))

# 中位数
model_q50 = xgb.train(params, dtrain, num_boost_round=100,
                      obj=lambda p, d: quantile_objective(p, d, 0.5))

# 75% 分位数
model_q75 = xgb.train(params, dtrain, num_boost_round=100,
                      obj=lambda p, d: quantile_objective(p, d, 0.75))

自定义评估指标 #

评估函数格式 #

python
def custom_eval(preds, dtrain):
    """
    自定义评估指标
    
    参数:
    - preds: 预测值
    - dtrain: DMatrix 对象
    
    返回:
    - (指标名称, 指标值)
    """
    labels = dtrain.get_label()
    
    # 计算指标
    metric_value = ...
    
    return 'metric_name', metric_value

常见评估指标 #

python
from sklearn.metrics import f1_score, precision_score, recall_score

def f1_eval(preds, dtrain):
    """F1 Score 评估"""
    labels = dtrain.get_label()
    preds_binary = (preds > 0.5).astype(int)
    f1 = f1_score(labels, preds_binary)
    return 'f1', f1

def precision_eval(preds, dtrain):
    """Precision 评估"""
    labels = dtrain.get_label()
    preds_binary = (preds > 0.5).astype(int)
    prec = precision_score(labels, preds_binary)
    return 'precision', prec

def recall_eval(preds, dtrain):
    """Recall 评估"""
    labels = dtrain.get_label()
    preds_binary = (preds > 0.5).astype(int)
    rec = recall_score(labels, preds_binary)
    return 'recall', rec

# 使用自定义评估指标
model = xgb.train(
    params,
    dtrain,
    num_boost_round=100,
    obj=logloss_objective,
    feval=f1_eval,
    evals=[(dtrain, 'train'), (dtest, 'test')]
)

自定义 AUC 评估 #

python
from sklearn.metrics import roc_auc_score

def auc_eval(preds, dtrain):
    """AUC 评估"""
    labels = dtrain.get_label()
    auc = roc_auc_score(labels, preds)
    return 'auc', auc

# 使用
model = xgb.train(
    params,
    dtrain,
    num_boost_round=100,
    evals=[(dtest, 'eval')],
    feval=auc_eval
)

高级自定义目标函数 #

成本敏感损失 #

python
def cost_sensitive_objective(preds, dtrain, cost_fp=1.0, cost_fn=5.0):
    """
    成本敏感损失函数
    
    参数:
    - cost_fp: 假阳性成本
    - cost_fn: 假阴性成本
    """
    labels = dtrain.get_label()
    
    # Sigmoid
    preds = 1.0 / (1.0 + np.exp(-preds))
    
    # 加权梯度
    weights = np.where(labels == 1, cost_fn, cost_fp)
    grad = weights * (preds - labels)
    hess = weights * preds * (1.0 - preds)
    
    return grad, hess

# 使用示例:假阴性成本更高
model = xgb.train(
    params,
    dtrain,
    num_boost_round=100,
    obj=lambda p, d: cost_sensitive_objective(p, d, cost_fp=1.0, cost_fn=5.0)
)

Focal Loss #

python
def focal_loss_objective(preds, dtrain, gamma=2.0, alpha=0.25):
    """
    Focal Loss 目标函数
    
    用于处理类别不平衡
    FL = -α(1-p)^γ * y*log(p) - (1-α)*p^γ * (1-y)*log(1-p)
    """
    labels = dtrain.get_label()
    
    # Sigmoid
    p = 1.0 / (1.0 + np.exp(-preds))
    
    # Focal Loss 梯度
    pt = np.where(labels == 1, p, 1 - p)
    at = np.where(labels == 1, alpha, 1 - alpha)
    
    grad = at * (1 - pt) ** gamma * (pt - labels)
    hess = at * (1 - pt) ** (gamma - 1) * pt * (1 - pt) * (
        gamma * (pt - labels) + (1 - pt)
    )
    
    return grad, hess

# 使用示例
model = xgb.train(
    params,
    dtrain,
    num_boost_round=100,
    obj=lambda p, d: focal_loss_objective(p, d, gamma=2.0, alpha=0.25)
)

平滑 L1 Loss #

python
def smooth_l1_objective(preds, dtrain, beta=1.0):
    """
    Smooth L1 Loss (Huber Loss 变体)
    
    更平滑的损失函数
    """
    labels = dtrain.get_label()
    diff = preds - labels
    abs_diff = np.abs(diff)
    
    # 梯度
    grad = np.where(
        abs_diff < beta,
        diff / beta,
        np.sign(diff)
    )
    
    # 海森
    hess = np.where(
        abs_diff < beta,
        np.ones_like(diff) / beta,
        np.zeros_like(diff)
    )
    
    return grad, hess

完整示例 #

自定义目标函数完整流程 #

python
import numpy as np
import xgboost as xgb
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, roc_auc_score

# 创建数据
X, y = make_classification(n_samples=10000, n_features=20, weights=[0.9, 0.1], random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test, label=y_test)

# 自定义目标函数
def weighted_logloss_objective(preds, dtrain, weight_positive=5.0):
    """加权 Log Loss"""
    labels = dtrain.get_label()
    
    p = 1.0 / (1.0 + np.exp(-preds))
    
    weights = np.where(labels == 1, weight_positive, 1.0)
    
    grad = weights * (p - labels)
    hess = weights * p * (1.0 - p)
    
    return grad, hess

# 自定义评估指标
def weighted_accuracy_eval(preds, dtrain, threshold=0.5):
    """加权准确率评估"""
    labels = dtrain.get_label()
    preds_binary = (preds > threshold).astype(int)
    
    # 计算加权准确率
    weights = np.where(labels == 1, 5.0, 1.0)
    correct = (preds_binary == labels).astype(float)
    weighted_acc = np.sum(weights * correct) / np.sum(weights)
    
    return 'weighted_acc', weighted_acc

# 训练
params = {'max_depth': 6, 'eta': 0.1}

model = xgb.train(
    params,
    dtrain,
    num_boost_round=200,
    obj=lambda p, d: weighted_logloss_objective(p, d, weight_positive=5.0),
    feval=weighted_accuracy_eval,
    evals=[(dtrain, 'train'), (dtest, 'test')],
    early_stopping_rounds=20,
    verbose_eval=20
)

# 评估
y_pred = model.predict(dtest)
y_pred_binary = (y_pred > 0.5).astype(int)

print(f"\n准确率: {accuracy_score(y_test, y_pred_binary):.4f}")
print(f"AUC: {roc_auc_score(y_test, y_pred):.4f}")

自定义目标函数最佳实践 #

python
def custom_objective_best_practices():
    """
    自定义目标函数最佳实践
    """
    practices = {
        '数学推导': [
            '确保目标函数可导',
            '正确计算一阶和二阶导数',
            '验证梯度计算正确性'
        ],
        '实现细节': [
            '处理数值稳定性问题',
            '避免除零错误',
            '使用 clip 限制数值范围'
        ],
        '调试技巧': [
            '与内置目标函数对比',
            '使用数值梯度验证',
            '监控训练过程'
        ],
        '性能优化': [
            '使用向量化计算',
            '避免循环操作',
            '利用 NumPy 广播'
        ]
    }
    
    for category, items in practices.items():
        print(f"\n{category}:")
        for item in items:
            print(f"  • {item}")

custom_objective_best_practices()

下一步 #

现在你已经掌握了自定义目标函数,接下来学习 分类任务 开始实战应用!

最后更新:2026-04-04