今儿再来和大家聊聊XGBoost~
很重要,但是对于很多同学来说,理解起来还是有一定的困难。这篇文章咱们从一个简单案例讲起,然后给出核心原理和一个完整的案例~
什么是XGBoost?
XGBoost 是一种机器学习算法,专门用来解决分类和回归问题。它的全称是「eXtreme Gradient Boosting」,意思是“极限梯度提升”。这个名字听起来有点复杂,但其实它背后的概念不难。
我们可以把它想象成一个「团队合作」的游戏。假设有很多个「小模型」,这些小模型本身不一定特别聪明,但通过一次次合作,每个小模型都尽力弥补上一个小模型的不足,最后整个「团队」会变得非常强大。
怎么理解XGBoost的工作方式呢?
我们拿一个日常生活中的例子来说明。假设你是一位老师,想根据学生的历史考试成绩来预测他们下一次考试的分数。
第一步:先有一个基础猜测
一开始,你可能随便猜一个平均值(比如根据班上所有人的平均成绩来预测每个人的下一次成绩)。这是我们的第一个「小模型」,虽然很简单,但这是起点。
第二步:分析差距,改进预测
你发现有些学生考得比你预测的好,有些考得比你预测的差。你就把每个学生的误差记录下来,比如小明你猜他考80分,但他实际上考了85分,差了5分;小丽你猜她考75分,但她实际只考了70分,差了-5分。
第三步:用新的模型来修正误差
现在,你创建了第二个「小模型」,这个模型的任务是根据前面预测的误差来做改进。小明比我们猜的多了5分,这次你可以专门为他加5分,小丽比我们猜的少了5分,这次为她减掉5分。这样一来,你的预测变得更准确了。
继续改进:不断重复
但还没完!即便加上这些修正,可能还有一些误差。于是你继续再造一个新的「小模型」,进一步改进。每一步都在解决上一步预测的错误,直到误差越来越小。
为什么GBoost这么厉害?其实核心就是3点:
快速且高效:XGBoost 的设计非常注重效率,它用了一些聪明的技术来加快计算速度和减少内存的使用。你可以理解为它做事情既快又省钱。
防止过拟合:XGBoost 有一些内置的机制来防止它过度学习训练数据,确保它不仅在训练数据上表现好,在没见过的新数据上也能表现不错。
灵活性强:它不仅可以处理分类问题(比如预测明天是晴天还是下雨),还能处理回归问题(比如预测房价、股票价格)。它还可以处理各种数据类型,数字的、类别的、甚至缺失的数据,它都可以处理得很好。
简单来说,XGBoost 是一个「聪明的组合拳」。它用多个简单的「小模型」一起合作,每个模型都在不断改正前一个模型的错误,最终形成一个非常强大的预测模型。而且它做得又快又准确,是机器学习比赛中经常用的「王牌选手」。
你也可以把它想象成一个班级,班上每个同学(小模型)虽然有自己的弱点,但他们互相补台,最后整个班级的成绩非常优秀。XGBoost 就是通过一次次改进,使得最终的预测效果非常好。
下面,咱们从 XGBoost 的详细公式,以及一个完整的案例(不使用现成的包),并实现相关的可视化分析。
XGBoost 原理
1. 损失函数
XGBoost 的目标是通过最小化损失函数 ,来得到更好的模型。假设我们有 个样本,目标是预测 ,预测值为 ,XGBoost 使用了以下损失函数:
其中, 是损失函数(比如平方损失), 是正则化项,用来防止模型过拟合,通常形式为:
是控制叶子数量的权重 是控制叶子权重的正则化参数 是树的叶子数, 是叶子权重
2. 二阶泰勒展开
为了最小化损失函数,XGBoost 采用了二阶泰勒展开近似,将目标函数展开为一阶和二阶项。假设当前已有 颗树,接下来我们添加一颗新树 ,此时的损失函数可以表示为:
使用泰勒展开到二阶近似,可以得到:
其中:
是一阶导数(梯度) 是二阶导数(Hessian)
3. 树的分裂
当我们构建树时,分裂的目标是最大化损失函数的下降值(也就是增益)。对于给定的分裂,增益公式为:
其中 和 是左右子节点中的样本集合, 是当前节点的样本集合。
4. 叶子节点的最佳权重
对于每一个叶子节点,最佳权重可以通过下式得到:
完整案例
我们用 Python 实现一个简单的 XGBoost 案例,使用虚拟数据集,并进行数据分析~
1. 数据生成与处理
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 设置随机种子
np.random.seed(42)
# 生成虚拟数据集
X = np.random.rand(1000, 5) # 1000个样本,5个特征
true_weights = np.array([2.5, -1.7, 0.5, 1.2, -0.9])
y = X.dot(true_weights) + np.random.randn(1000) * 0.5 # 线性关系加噪声
# 创建DataFrame以便后续处理
data = pd.DataFrame(X, columns=[f'feature_{i}' for i in range(1, 6)])
data['target'] = y
# 打印数据集前5行
print(data.head())
2. 损失函数及梯度计算
# 损失函数(平方损失)
def squared_loss(y_true, y_pred):
return 0.5 * np.mean((y_true - y_pred) ** 2)
# 梯度和Hessian计算
def gradient(y_true, y_pred):
return -(y_true - y_pred)
def hessian(y_true, y_pred):
return np.ones_like(y_true)
3. XGBoost 核心训练流程
class XGBoostFromScratch:
def __init__(self, n_estimators=10, learning_rate=0.1, max_depth=3, lambda_reg=1):
self.n_estimators = n_estimators
self.learning_rate = learning_rate
self.max_depth = max_depth
self.lambda_reg = lambda_reg
self.trees = []
self.losses = [] # 新增一个用于保存每轮损失的列表
def fit(self, X, y):
n_samples = X.shape[0]
# 初始化预测值为0
y_pred = np.zeros(n_samples)
# 迭代生成树
for _ in range(self.n_estimators):
# 计算梯度和Hessian
grad = gradient(y, y_pred)
hess = hessian(y, y_pred)
# 构建一颗树并拟合
tree = self.build_tree(X, grad, hess, depth=0)
self.trees.append(tree)
# 更新预测值
y_pred += self.learning_rate * self.predict_tree(tree, X)
# 每轮迭代后计算当前的损失,并保存
current_loss = squared_loss(y, y_pred)
self.losses.append(current_loss)
def build_tree(self, X, grad, hess, depth):
# 这里我们简单处理为单层分裂的树,后续可扩展为多层
n_samples, n_features = X.shape
best_split = None
best_gain = -float('inf')
# 遍历所有特征,找到最佳分裂点
for feature_idx in range(n_features):
thresholds = np.unique(X[:, feature_idx])
for threshold in thresholds:
left_mask = X[:, feature_idx] <= threshold
right_mask = ~left_mask
if len(left_mask) == 0 or len(right_mask) == 0:
continue
# 计算增益
gain = self.compute_gain(grad, hess, left_mask, right_mask)
if gain > best_gain:
best_gain = gain
best_split = (feature_idx, threshold)
# 计算叶子节点权重
if best_split is not None:
feature_idx, threshold = best_split
left_mask = X[:, feature_idx] <= threshold
right_mask = ~left_mask
left_weight = self.compute_leaf_weight(grad[left_mask], hess[left_mask])
right_weight = self.compute_leaf_weight(grad[right_mask], hess[right_mask])
return {"split_feature": feature_idx, "threshold": threshold,
"left_weight": left_weight, "right_weight": right_weight}
else:
return None
def compute_gain(self, grad, hess, left_mask, right_mask):
G_L, H_L = np.sum(grad[left_mask]), np.sum(hess[left_mask])
G_R, H_R = np.sum(grad[right_mask]), np.sum(hess[right_mask])
gain = 0.5 * (G_L**2 / (H_L + self.lambda_reg) + G_R**2 / (H_R + self.lambda_reg))
return gain
def compute_leaf_weight(self, grad, hess):
return -np.sum(grad) / (np.sum(hess) + self.lambda_reg)
def predict_tree(self, tree, X):
predictions = np.zeros(X.shape[0])
if tree is not None:
feature_idx, threshold = tree["split_feature"], tree["threshold"]
left_mask = X[:, feature_idx] <= threshold
right_mask = ~left_mask
predictions[left_mask] = tree["left_weight"]
predictions[right_mask] = tree["right_weight"]
return predictions
def predict(self, X):
y_pred = np.zeros(X.shape[0])
for tree in self.trees:
y_pred += self.learning_rate * self.predict_tree(tree, X)
return y_pred
4. 训练与可视化分析
# 训练模型
model = XGBoostFromScratch(n_estimators=10, learning_rate=0.1)
model.fit(X, y)
# 预测结果
y_pred = model.predict(X)
# 绘制数据分析图形
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 图1:实际值与预测值对比
axes[0, 0].scatter(y, y_pred, color='blue')
axes[0, 0].set_title("Actual vs Predicted")
axes[0, 0].set_xlabel("Actual")
axes[0, 0].set_ylabel("Predicted")
# 图2:误差分布
error = y - y_pred
axes[0, 1].hist(error, bins=20, color='green')
axes[0, 1].set_title("Error Distribution")
# 图3:特征对目标值的相关性
for i in range(5):
axes[1, 0].scatter(data[f'feature_{i+1}'], data['target'], label=f'Feature {i+1}')
axes[1, 0].set_title("Feature vs Target")
axes[1, 0].legend()
# 图4:损失随迭代次数的变化
axes[1, 1].plot(range(1, len(model.losses) + 1), model.losses, color='red')
axes[1, 1].set_title("Loss vs Iterations")
axes[1, 1].set_xlabel("Iterations")
axes[1, 1].set_ylabel("Loss")
plt.tight_layout()
plt.show()
XGBoostFromScratch 类:实现了简单的 XGBoost 训练流程,包括梯度和 Hessian 计算、树的构建、叶子节点权重计算等。
图1显示实际值与预测值的关系; 图2展示误差的分布情况; 图3展示各个特征与目标值的关系; 图4展示了迭代过程中损失的变化。
这段代码实现了一个从零开始的 XGBoost,虽然简化了很多细节(例如只实现了单层树的分裂),给大家提供了一个较为完整的训练和预测流程。
最后
最近准备了16大块的内容,124个算法问题的总结,完整的机器学习小册,免费领取~
另外,今天给大家准备了关于「深度学习」的论文合集,往期核心论文汇总,分享给大家。
点击名片,回复「深度学习论文」即可~
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~