目标函数 #

目标函数概述 #

XGBoost 的目标函数由两部分组成:损失函数和正则化项。

text
Obj(θ) = L(θ) + Ω(θ)

其中:
- L(θ):损失函数,衡量预测值与真实值的差距
- Ω(θ):正则化项,控制模型复杂度

目标函数结构 #

text
┌─────────────────────────────────────────────────────────────┐
│                    XGBoost 目标函数                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Obj = Σ L(yᵢ, ŷᵢ) + Σ Ω(fₖ)                               │
│        ─────────────   ────────────                         │
│           损失函数        正则化项                           │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  损失函数 L(y, ŷ)                                    │   │
│  │  - 分类:Log Loss、交叉熵                            │   │
│  │  - 回归:MSE、MAE                                    │   │
│  │  - 排序:NDCG、MAP                                   │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  正则化项 Ω(f)                                       │   │
│  │  Ω(f) = γT + ½λΣwⱼ²                                 │   │
│  │  - T:叶子节点数量                                   │   │
│  │  - wⱼ:叶子节点权重                                 │   │
│  │  - γ:叶子节点数惩罚系数                             │   │
│  │  - λ:L2 正则化系数                                 │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                              │
└─────────────────────────────────────────────────────────────┘

损失函数 #

分类损失 #

二分类 - Log Loss #

python
import numpy as np

def binary_log_loss(y, y_pred):
    """
    二分类对数损失
    L = -[y·log(p) + (1-y)·log(1-p)]
    """
    eps = 1e-15
    y_pred = np.clip(y_pred, eps, 1 - eps)
    return -np.mean(y * np.log(y_pred) + (1 - y) * np.log(1 - y_pred))

def binary_log_loss_gradient(y, y_pred):
    """
    一阶导数
    ∂L/∂ŷ = ŷ - y
    """
    return y_pred - y

def binary_log_loss_hessian(y, y_pred):
    """
    二阶导数
    ∂²L/∂ŷ² = ŷ(1-ŷ)
    """
    return y_pred * (1 - y_pred)

多分类 - Softmax Loss #

python
def softmax(x):
    """Softmax 函数"""
    exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
    return exp_x / np.sum(exp_x, axis=1, keepdims=True)

def multi_class_loss(y, y_pred):
    """
    多分类交叉熵损失
    L = -Σ yₖ·log(pₖ)
    """
    probs = softmax(y_pred)
    return -np.mean(np.sum(y * np.log(probs + 1e-15), axis=1))

def multi_class_gradient(y, y_pred):
    """
    一阶导数
    ∂L/∂ŷₖ = pₖ - yₖ
    """
    probs = softmax(y_pred)
    return probs - y

def multi_class_hessian(y, y_pred):
    """
    二阶导数(对角元素)
    ∂²L/∂ŷₖ² = pₖ(1-pₖ)
    """
    probs = softmax(y_pred)
    return probs * (1 - probs)

回归损失 #

均方误差(MSE) #

python
def mse_loss(y, y_pred):
    """
    均方误差
    L = ½(y - ŷ)²
    """
    return 0.5 * np.mean((y - y_pred) ** 2)

def mse_gradient(y, y_pred):
    """
    一阶导数
    ∂L/∂ŷ = ŷ - y
    """
    return y_pred - y

def mse_hessian(y, y_pred):
    """
    二阶导数
    ∂²L/∂ŷ² = 1
    """
    return np.ones_like(y)

平均绝对误差(MAE) #

python
def mae_loss(y, y_pred):
    """
    平均绝对误差
    L = |y - ŷ|
    """
    return np.mean(np.abs(y - y_pred))

def mae_gradient(y, y_pred):
    """
    一阶导数(次梯度)
    ∂L/∂ŷ = sign(ŷ - y)
    """
    return np.sign(y_pred - y)

Huber Loss #

python
def huber_loss(y, y_pred, delta=1.0):
    """
    Huber 损失
    结合 MSE 和 MAE 的优点
    """
    residual = np.abs(y - y_pred)
    mask = residual <= delta
    
    loss = np.where(
        mask,
        0.5 * (y - y_pred) ** 2,
        delta * residual - 0.5 * delta ** 2
    )
    return np.mean(loss)

def huber_gradient(y, y_pred, delta=1.0):
    """
    Huber 损失的一阶导数
    """
    residual = y_pred - y
    mask = np.abs(residual) <= delta
    
    return np.where(
        mask,
        residual,
        delta * np.sign(residual)
    )

正则化 #

正则化项 #

XGBoost 的正则化项控制模型复杂度:

text
Ω(f) = γT + ½λΣwⱼ²

其中:
- T:叶子节点数量
- wⱼ:第 j 个叶子节点的权重
- γ:叶子节点数惩罚(min_split_loss)
- λ:L2 正则化系数(reg_lambda)
- α:L1 正则化系数(reg_alpha)

正则化作用 #

python
import numpy as np
import matplotlib.pyplot as plt

def regularization_effect():
    """可视化正则化效果"""
    w = np.linspace(-3, 3, 100)
    
    fig, axes = plt.subplots(1, 3, figsize=(15, 4))
    
    # L1 正则化
    alpha = 0.5
    l1 = alpha * np.abs(w)
    axes[0].plot(w, l1)
    axes[0].set_title('L1 Regularization (α=0.5)')
    axes[0].set_xlabel('w')
    axes[0].set_ylabel('Penalty')
    
    # L2 正则化
    lambda_ = 0.5
    l2 = 0.5 * lambda_ * w ** 2
    axes[1].plot(w, l2)
    axes[1].set_title('L2 Regularization (λ=0.5)')
    axes[1].set_xlabel('w')
    axes[1].set_ylabel('Penalty')
    
    # 组合正则化
    combined = alpha * np.abs(w) + 0.5 * lambda_ * w ** 2
    axes[2].plot(w, combined)
    axes[2].set_title('Combined (α=0.5, λ=0.5)')
    axes[2].set_xlabel('w')
    axes[2].set_ylabel('Penalty')
    
    plt.tight_layout()
    plt.show()

regularization_effect()

正则化参数 #

python
import xgboost as xgb

# 正则化参数配置
params = {
    'lambda': 1.0,      # L2 正则化权重
    'alpha': 0.0,       # L1 正则化权重
    'gamma': 0.0,       # 分裂最小增益(叶子数惩罚)
    
    # 其他控制复杂度的参数
    'max_depth': 6,
    'min_child_weight': 1,
}

二阶泰勒展开 #

XGBoost 使用二阶泰勒展开近似损失函数:

泰勒展开公式 #

text
L(y, ŷ⁽ᵗ⁾) ≈ L(y, ŷ⁽ᵗ⁻¹⁾) + gᵢfₜ(xᵢ) + ½hᵢfₜ²(xᵢ)

其中:
- gᵢ = ∂L(yᵢ, ŷ⁽ᵗ⁻¹⁾)/∂ŷ⁽ᵗ⁻¹⁾  一阶导数
- hᵢ = ∂²L(yᵢ, ŷ⁽ᵗ⁻¹⁾)/∂ŷ²⁽ᵗ⁻¹⁾  二阶导数
- fₜ(x) 是第 t 棵树的预测值

Python 实现 #

python
class TaylorExpansion:
    """泰勒展开近似"""
    
    @staticmethod
    def expand(loss_value, g, h, f):
        """
        泰勒展开近似
        
        参数:
        - loss_value: 当前损失值
        - g: 一阶导数
        - h: 二阶导数
        - f: 新树的预测值
        
        返回:
        - 近似损失值
        """
        return loss_value + np.sum(g * f) + 0.5 * np.sum(h * f ** 2)
    
    @staticmethod
    def compute_gradients(y, y_pred, loss_type='mse'):
        """计算一阶和二阶导数"""
        if loss_type == 'mse':
            g = y_pred - y
            h = np.ones_like(y)
        elif loss_type == 'logistic':
            p = 1 / (1 + np.exp(-y_pred))
            g = p - y
            h = p * (1 - p)
        elif loss_type == 'poisson':
            exp_pred = np.exp(y_pred)
            g = exp_pred - y
            h = exp_pred
        else:
            raise ValueError(f"Unknown loss type: {loss_type}")
        
        return g, h

泰勒展开的优势 #

text
┌─────────────────────────────────────────────────────────────┐
│                    泰勒展开的优势                            │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 统一框架                                                 │
│     - 不同损失函数使用相同的优化方法                          │
│     - 只需计算一阶和二阶导数                                  │
│                                                              │
│  2. 高效优化                                                 │
│     - 二阶信息加速收敛                                       │
│     - 比一阶方法更快                                         │
│                                                              │
│  3. 自定义损失                                               │
│     - 用户可以自定义损失函数                                  │
│     - 只需提供一阶和二阶导数                                  │
│                                                              │
└─────────────────────────────────────────────────────────────┘

最优权重计算 #

推导过程 #

目标函数:

text
Obj = Σ [gᵢfₜ(xᵢ) + ½hᵢfₜ²(xᵢ)] + Ω(fₜ)

    = Σ [gᵢwq(xᵢ) + ½hᵢw²q(xᵢ)] + γT + ½λΣwⱼ²

    = Σⱼ [(Σi∈Iⱼ gᵢ)wⱼ + ½(Σi∈Iⱼ hᵢ + λ)wⱼ²] + γT

其中 Iⱼ 是叶子节点 j 上的样本集合

对 wⱼ 求导并令其为零:

text
∂Obj/∂wⱼ = Gⱼ + (Hⱼ + λ)wⱼ = 0

wⱼ* = -Gⱼ / (Hⱼ + λ)

其中:
- Gⱼ = Σi∈Iⱼ gᵢ  叶子节点上的一阶导数和
- Hⱼ = Σi∈Iⱼ hᵢ  叶子节点上的二阶导数和

最优目标值 #

text
Obj* = -½ Σⱼ Gⱼ²/(Hⱼ + λ) + γT

Python 实现 #

python
import numpy as np

class OptimalWeight:
    """最优权重计算"""
    
    @staticmethod
    def compute_leaf_weight(G, H, lambda_):
        """
        计算叶子节点最优权重
        
        参数:
        - G: 叶子节点上的一阶导数和
        - H: 叶子节点上的二阶导数和
        - lambda_: L2 正则化系数
        
        返回:
        - 最优权重
        """
        return -G / (H + lambda_)
    
    @staticmethod
    def compute_leaf_score(G, H, lambda_, gamma):
        """
        计算叶子节点得分
        
        参数:
        - G: 叶子节点上的一阶导数和
        - H: 叶子节点上的二阶导数和
        - lambda_: L2 正则化系数
        - gamma: 叶子节点惩罚
        
        返回:
        - 叶子节点得分
        """
        return 0.5 * G**2 / (H + lambda_) - gamma
    
    @staticmethod
    def compute_split_gain(G_left, H_left, G_right, H_right, lambda_, gamma):
        """
        计算分裂增益
        
        参数:
        - G_left, H_left: 左子树的梯度
        - G_right, H_right: 右子树的梯度
        - lambda_: L2 正则化系数
        - gamma: 分裂最小增益
        
        返回:
        - 分裂增益
        """
        gain_left = G_left**2 / (H_left + lambda_)
        gain_right = G_right**2 / (H_right + lambda_)
        gain_total = (G_left + G_right)**2 / (H_left + H_right + lambda_)
        
        return 0.5 * (gain_left + gain_right - gain_total) - gamma

完整示例 #

python
import numpy as np
import xgboost as xgb
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

# 创建数据
X, y = make_classification(n_samples=1000, n_features=10, 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)

# 训练模型
params = {
    'objective': 'binary:logistic',
    'max_depth': 3,
    'eta': 0.1,
    'lambda': 1.0,
    'alpha': 0.0,
    'gamma': 0.0
}

model = xgb.train(params, dtrain, num_boost_round=10)

# 查看树结构
tree_dump = model.get_dump(with_stats=True)[0]
print("树结构(包含统计信息):")
print(tree_dump)

分裂增益计算 #

增益公式 #

text
Gain = ½ [G_L²/(H_L+λ) + G_R²/(H_R+λ) - (G_L+G_R)²/(H_L+H_R+λ)] - γ

分裂选择 #

python
def find_best_split(X, g, h, lambda_=1.0, gamma=0.0):
    """
    找到最佳分裂点
    
    参数:
    - X: 特征矩阵
    - g: 一阶导数
    - h: 二阶导数
    - lambda_: L2 正则化系数
    - gamma: 分裂最小增益
    
    返回:
    - best_feature: 最佳特征
    - best_threshold: 最佳阈值
    - best_gain: 最佳增益
    """
    best_gain = 0
    best_feature = None
    best_threshold = None
    
    G_total = np.sum(g)
    H_total = np.sum(h)
    
    for feature in range(X.shape[1]):
        # 按特征值排序
        sorted_idx = np.argsort(X[:, feature])
        X_sorted = X[sorted_idx, feature]
        g_sorted = g[sorted_idx]
        h_sorted = h[sorted_idx]
        
        # 累计梯度
        G_left = 0
        H_left = 0
        
        for i in range(len(X_sorted) - 1):
            G_left += g_sorted[i]
            H_left += h_sorted[i]
            
            G_right = G_total - G_left
            H_right = H_total - H_left
            
            # 计算增益
            gain = 0.5 * (
                G_left**2 / (H_left + lambda_) +
                G_right**2 / (H_right + lambda_) -
                G_total**2 / (H_total + lambda_)
            ) - gamma
            
            if gain > best_gain:
                best_gain = gain
                best_feature = feature
                best_threshold = (X_sorted[i] + X_sorted[i+1]) / 2
    
    return best_feature, best_threshold, best_gain

目标函数总结 #

text
┌─────────────────────────────────────────────────────────────┐
│                    目标函数关键公式                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  目标函数:                                                   │
│  Obj = Σ L(yᵢ, ŷᵢ) + Σ Ω(fₖ)                               │
│                                                              │
│  正则化项:                                                   │
│  Ω(f) = γT + ½λΣwⱼ²                                         │
│                                                              │
│  泰勒展开:                                                   │
│  L ≈ L₀ + g·f + ½h·f²                                       │
│                                                              │
│  最优权重:                                                   │
│  wⱼ* = -Gⱼ / (Hⱼ + λ)                                      │
│                                                              │
│  最优目标值:                                                 │
│  Obj* = -½ Σⱼ Gⱼ²/(Hⱼ + λ) + γT                            │
│                                                              │
│  分裂增益:                                                   │
│  Gain = ½[G_L²/(H_L+λ) + G_R²/(H_R+λ) - G²/(H+λ)] - γ      │
│                                                              │
└─────────────────────────────────────────────────────────────┘

下一步 #

现在你已经理解了目标函数,接下来学习 数据准备 了解如何为 XGBoost 准备数据!

最后更新:2026-04-04