目标函数 #
目标函数概述 #
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