社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  机器学习算法

【机器学习】突破最强算法模型,XGBoost!!

机器学习初学者 • 9 月前 • 411 次点击  

简单来说,XGBoost 是一种非常非常强大的机器学习算法,全称叫做 Extreme Gradient Boosting,它是提升树(Boosting Trees)的一种实现。

下面我们用一个生活中的简单例子来帮助理解。

假设你是一个班级的班主任,你要根据学生的学习情况预测他们期末考试的成绩。你不想靠自己的直觉,所以你决定让一群老师帮你。

第一步:请一个老师来打分

你请来了第一位数学老师。数学老师根据学生们的平时表现打了一个分数(可能依据作业、上课表现等)。但这位老师的预测并不完美,有些学生的分数偏高,有些偏低。

第二步:再请一位老师来补充

你意识到第一位老师的打分有误差,于是你请第二位老师来专门纠正第一位老师的错误。比如,第一位老师给某个学生打了80分,但你觉得这个学生应该拿85分,那第二位老师就给这个学生增加5分,纠正这个误差。

第三步:继续请更多的老师

但是,第二位老师也不可能完美修正所有错误。于是你继续请第三位老师,专门纠正前面两位老师都没有修正好的部分。你可以不断地请新的老师,每个老师都根据之前所有老师的打分结果进行微调。

总的来说,每个新来的老师不会重新打分,而是根据之前的结果专注纠正误差。最终,所有老师的打分结果加在一起,就能得到一个越来越准确的成绩预测。

这就是Boosting的基本思路——逐步改进预测,每一步都在修正前面的错误。

那么 XGBoost 是如何做的呢?

  • XGBoost 使用的是一种叫做“决策树”的模型。可以理解为,每个老师不是凭感觉打分,而是根据学生表现做出逻辑判断,比如“如果上课积极性高,成绩增加10分;如果作业完成度低,成绩减少5分”。

  • 极端(Extreme):XGBoost 进行了很多优化,使得它比传统的提升树算法(比如经典的Gradient Boosting)运行得更快、性能更强、处理更复杂的数据。

  • 加权修正:每次纠正时,XGBoost 会根据错误的大小来决定纠正的幅度。那些预测误差大的学生会得到更多的关注。

一个具体例子:

假设我们要预测小明的期末成绩:

  1. 第一棵树:根据小明的上课参与度、作业完成情况等,预测他能考 70分

  2. 第二棵树:发现第一棵树的预测有误差,小明其实应该得 75分,于是第二棵树来修正这个错误,增加了 5分

  3. 第三棵树:可能还有些小的误差,第三棵树会再进行微调,比如再给小明加 2分,最终得出小明的预测成绩是 77分

最终,你会得到一个由多棵树组成的模型,每棵树都专注于改进前面树的错误,最后的结果是这几棵树的预测结果的累加

下面,咱们从原理和案例,给大家进行一个细致的说明~

原理和案例

我们可以从其背后的 数学推理 开始,随后用一个具体案例展示如何从零实现 XGBoost,并且使用 Kaggle 数据集

在实现时我们将不依赖已有的机器学习库,手动计算和实现所有过程,深入理解其背后原理。

XGBoost 的数学原理

XGBoost 的核心是基于提升树(Boosting Trees)的思想,而提升树算法的目的是通过一系列决策树的逐步改进来优化预测结果。

1. 基本思想:加法模型

XGBoost 使用加法模型来逐步改进预测结果。假设目标变量为 ,输入数据为 ,我们构建多个弱学习器 (如决策树)来预测目标值 。模型的总体预测可以表示为:

其中  是树的数量。

每一棵树  通过逐步调整前面树的错误,来改进预测。

2. 目标函数

为了衡量模型的好坏,XGBoost 定义了一个目标函数:

其中:

  •  是损失函数(如平方误差),用来衡量模型的预测值  与真实值  的差距。
  •  是正则化项,用来控制模型复杂度,避免过拟合。对于一棵树 ,正则化项通常定义为:

其中  是树的叶子数量, 是第  个叶子的权重, 和  是正则化参数。

3. 梯度提升(Gradient Boosting)

每一棵新树的目标是最小化之前模型的误差。为了达到这个目标,XGBoost 使用梯度下降的思想来优化树的结构和权重。

损失函数的二阶泰勒展开(利用一阶梯度和二阶导数)如下:

其中  是一阶梯度, 是二阶导数(即 Hessian)。

新的目标函数(忽略常数项)变为:

通过这个损失函数,我们可以逐步优化每棵树的结构。

4. 树的分裂

为了找到最佳的分裂点(即如何划分数据),XGBoost 通过计算每个分裂点的 增益 来选择最佳分裂。增益表示分裂后带来的误差减少量,增益公式为:

其中:

  •  和  是左子节点和右子节点的梯度和。
  •  和  是左子节点和右子节点的 Hessian 和。

找到分裂点后,可以继续递归构建决策树,直到满足一定的停止条件。

实现 XGBoost 案例

数据集选择

我们将使用 Kaggle 上的经典 "House Prices" 数据集。这个数据集包含房屋的各种特征,目标是预测房价。

实现步骤

  1. 数据预处理:加载并清洗数据。
  2. 构建弱学习器(决策树):基于每次残差进行训练。
  3. 计算梯度和 Hessian:用来指导树的生长和分裂。
  4. 优化每棵树:通过增益公式选择最佳分裂点。
  5. 模型预测:累积所有树的预测结果。
  6. 绘制图形:可视化残差和模型预测效果。

代码实现

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 加载数据集
data = pd.read_csv('house_prices.csv')  # 请确保你已经下载了Kaggle数据集

# 选择一些特征并处理缺失值
features = ['OverallQual''GrLivArea''GarageCars''GarageArea''TotalBsmtSF']
X = data[features].fillna(0).values
y = data['SalePrice'].values

# 定义损失函数的梯度和Hessian
def gradient(y_true, y_pred):
    return y_pred - y_true

def hessian(y_true, y_pred):
    return np.ones_like(y_true)

# 构建决策树类
class XGBoostTree:
    def __init__(self, max_depth=3, lambda_reg=1):
        self.max_depth = max_depth
        self.lambda_reg = lambda_reg
        self.tree = {}

    def fit(self, X, g, h):
        self.tree = self._build_tree(X, g, h, depth=0)

    def _build_tree(self, X, g, h, depth):
        if depth == self.max_depth or len(X) <= 1:
            weight = -np.sum(g) / (np.sum(h) + self.lambda_reg)
            return weight

        best_gain = 0
        best_split = None

        for feature in range(X.shape[1]):
            for threshold in np.unique(X[:, feature]):
                left_idx = X[:, feature] <= threshold
                right_idx = X[:, feature] > threshold

                if len(X[left_idx]) == 0 or len(X[right_idx]) == 0:
                    continue

                G_L, G_R = np.sum(g[left_idx]), np.sum(g[right_idx])
                H_L, H_R = np.sum(h[left_idx]), np.sum(h[right_idx])

                gain = 0.5 * ((G_L**2 / (H_L + self.lambda_reg)) +
                              (G_R**2 / (H_R + self.lambda_reg)) -
                              ((G_L + G_R)**2 / (H_L + H_R + self.lambda_reg)))

                if gain > best_gain:
                    best_gain = gain
                    best_split = (feature, threshold)

        if best_split is None:
            weight = -np.sum(g) / (np.sum(h) + self.lambda_reg)
            return weight

        feature, threshold = best_split
        left_idx = X[:, feature] <= threshold
        right_idx = X[:, feature] > threshold

        return {
            'feature': feature,
            'threshold': threshold,
            'left': self._build_tree(X[left_idx], g[left_idx], h[left_idx], depth + 1),
            'right': self._build_tree(X[right_idx], g[right_idx], h[right_idx], depth + 1)
        }

    def predict(self, X):
        return np.array([self._predict_one(row, self.tree) for row in X])

    def _predict_one(self, x, tree):
        if isinstance(tree, dict):
            feature = tree['feature']
            threshold = tree['threshold']
            if x[feature] <= threshold:
                return self._predict_one(x, tree['left'])
            else:
                return self._predict_one(x, tree['right'])
        else:
            return tree

# XGBoost模型类
class XGBoostModel:
    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 = []

    def fit(self, X, y):
        y_pred = np.zeros_like(y, dtype=np.float64)
        for i in range(self.n_estimators):
            g = gradient(y, y_pred)
            h = hessian(y, y_pred)

            tree = XGBoostTree(max_depth=self.max_depth, lambda_reg=self.lambda_reg)
            tree.fit(X, g, h)

            update = tree.predict(X)
            y_pred += self.learning_rate * update

            self.trees.append(tree)

    def predict(self, X):
        y_pred = np.zeros(X.shape[0])
        for tree in self.trees:
            y_pred += self.learning_rate * tree.predict(X)
        return y_pred

# 训练模型
model = XGBoostModel(n_estimators=50, learning_rate=0.1, max_depth=3, lambda_reg=1)
model.fit(X, y)

# 预测和可视化
y_pred = model.predict(X)

plt.figure(figsize=(105))

# 可视化预测值与真实值的关系
plt.subplot(121)
plt.scatter(y, y_pred, color='blue', alpha=0.5)
plt.title('Predicted vs Actual Sale Prices')
plt.xlabel('Actual Sale Prices')
plt.ylabel('Predicted Sale Prices')

# 可视化残差
residuals = y - y_pred
plt.subplot(122)
plt.hist(residuals, bins=50, color='red', alpha=0.7)
plt.title('Residuals Distribution')
plt.xlabel('Residuals')
plt.ylabel('Frequency')

plt.show()

注意点:

  1. 梯度和 Hessian:我们定义了损失函数的梯度和 Hessian,作为树生长的依据。
  2. XGBoostTree 类:实现了单棵决策树的构建,通过增益公式选择最佳分裂点。
  3. XGBoostModel 类:逐步训练多个决策树,并累积其预测结果。
  4. 可视化:通过散点图显示预测值与真实值的对比,并通过直方图展示残差分布。

这个案例,我们手动实现了 XGBoost 模型的核心原理,并且基于 Kaggle 的房价预测数据集进行了模型训练和结果可视化。

希望可以帮助到大家!

最后

大家有问题可以直接在评论区留言即可~

喜欢本文的朋友可以收藏、点赞、转发起来!
往期精彩回顾




  • 交流群

欢迎加入机器学习爱好者微信群一起和同行交流,目前有机器学习交流群、博士群、博士申报交流、CV、NLP等微信群,请扫描下面的微信号加群,备注:”昵称-学校/公司-研究方向“,例如:”张小明-浙大-CV“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~(也可以加入机器学习交流qq群772479961


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/174105
 
411 次点击