一个强大算法模型,决策树回归!!

文摘   2024-12-20 10:08   甘肃  

大家好~

咱们今天分享的是决策树回归,作为在回归和决策树学习阶段的同学,完全可以看看,非常全面~

首先,什么是决策树回归?

  • 决策树回归是一种用的结构来预测数值的方法。
  • 你可以想象这棵树像是在玩「20 问题」:通过一系列有条件的问题(比如“是”或“不是”)一步步缩小范围,最终得出一个数值结果。

区别是,20 问题是回答“对还是错”,而决策树回归是回答“最终的数值预测是多少”。

全文内容,非常详细!~ 

文末已经给大家准备好了本文的 PDF版本 方便学习使用~

下面,咱们就从非常简单的方面和大家聊聊~

决策树长啥样?

这棵树:

  • 根节点:树的起点,所有数据都在这里。
  • 内部节点:每个节点是一个条件,比如“房子的面积 > 100 平米?”。
  • 叶子节点:树的终点,每片叶子代表一个预测值(比如“房价是500万”)。

它是怎么工作的?

决策树回归的工作流程分三步:

1. 划分数据

  • 把数据分成一堆“小块”,每一块数据的值差不多。
  • 比如,我们有一堆房价数据,决策树会问:“按照房子面积划分行不行?”然后再细分,直到每一块数据的房价差不多。

2. 设定预测值

  • 每个“叶子节点”的预测值是这一块数据的平均值。
  • 比如,这片叶子里有5个房子,它们的房价分别是480万、500万、520万、510万和490万,叶子节点的预测值就是它们的平均值 = 500万。

3. 对新数据做预测

  • 用同样的问题路径,把新数据分到某个叶子节点,返回这个节点的平均值作为预测。

举个例子:预测房价

已有数据(训练数据):

  • 房子面积(平方米) 和 房价(万元)
    • 80平 -> 300万
    • 100平 -> 400万
    • 120平 -> 450万
    • 150平 -> 500万
    • 200平 -> 800万

决策树回归过程:

决策树的第一个问题可能是:

  • “房子面积 > 130平?”
    • :去右边分支(房子面积更大的那类)。
    • :去左边分支(房子面积较小的那类)。

接着继续分:

  • 左边分支里再问:“房子面积 > 90平?”

    • :这片叶子包含 [100平, 120平],平均房价是 (400+450)/2 = 425万。
    • :这片叶子只包含 [80平],预测值是300万。
  • 右边分支里再问:“房子面积 > 170平?”

    • :这片叶子只有 [200平],预测值是800万。
    • :这片叶子只有 [150平],预测值是500万。

预测新数据:

假设你有个新房子,面积是110平。

  • 决策树会问:
    • “面积 > 130平?” → 否,去左边。
    • “面积 > 90平?” → 是,去 [100平, 120平] 的叶子。
  • 预测房价 = 425万。

决策树回归的优点和缺点

优点:

  • 直观易懂:像玩问答游戏。
  • 灵活:能处理数值和分类问题。

缺点:

  • 容易过拟合:树太复杂会“记住”训练数据,而不适用于新数据。
  • 分裂点选择依赖性:划分数据时的选择可能影响最终结果。

上面大家有了一个整体认识之后,下面,咱们从原理理论方面,好好给大家细化~

决策树回归公式推导

决策树回归的核心目标

目标:找到最优分割点,将数据划分成若干区域 ,每个区域的预测值是该区域内样本目标值的均值,目标函数最小化为:

其中:

  •  是真实值,
  •  是区域  内目标值的均值。

核心步骤

  1. 遍历所有可能的特征  和分割点 
  2. 计算分割后的损失函数:
  1. 找到使  最小的特征  和分割点 ,作为当前节点的最优分割。

  2. 递归执行上述步骤,直到满足停止条件(如节点样本数小于最小值或深度限制)。

实现代码

  1. 手动实现决策树回归算法。
  2. 生成虚拟数据集。
  3. 绘制多张分析图表。

Python代码

import numpy as np
import matplotlib.pyplot as plt

# 1. 生成虚拟数据集
np.random.seed(42)
X = np.sort(5 * np.random.rand(1001), axis=0)  # 特征: [0, 5) 的随机数
y = np.sin(X).ravel() + np.random.normal(00.2, X.shape[0])  # 加噪音的目标值

# 2. 决策树回归手动实现
class DecisionTreeRegressor:
    def __init__(self, min_samples_split=10, max_depth=5):
        self.min_samples_split = min_samples_split
        self.max_depth = max_depth
        self.tree = None

    def fit(self, X, y):
        def split(X, y, feature_index, threshold):
            left_mask = X[:, feature_index] <= threshold
            right_mask = ~left_mask
            return (X[left_mask], y[left_mask]), (X[right_mask], y[right_mask])

        def find_best_split(X, y):
            best_feature, best_threshold, best_loss = NoneNone, float('inf')
            for feature_index in range(X.shape[1]):
                thresholds = np.unique(X[:, feature_index])
                for threshold in thresholds:
                    (X_left, y_left), (X_right, y_right) = split(X, y, feature_index, threshold)
                    if len(y_left) < self.min_samples_split or len(y_right) < self.min_samples_split:
                        continue
                    left_mean, right_mean = np.mean(y_left), np.mean(y_right)
                    loss = np.sum((y_left - left_mean)**2) + np.sum((y_right - right_mean)**2)
                    if loss < best_loss:
                        best_feature, best_threshold, best_loss = feature_index, threshold, loss
            return best_feature, best_threshold

        def grow_tree(X, y, depth):
            if depth >= self.max_depth or len(y) < self.min_samples_split:
                return np.mean(y)
            feature_index, threshold = find_best_split(X, y)
            if feature_index is None:
                return np.mean(y)
            (X_left, y_left), (X_right, y_right) = split(X, y, feature_index, threshold)
            return {
                'feature_index': feature_index,
                'threshold': threshold,
                'left': grow_tree(X_left, y_left, depth + 1),
                'right': grow_tree(X_right, y_right, depth + 1),
            }

        self.tree = grow_tree(X, y, 0)

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

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

# 3. 训练模型
tree = DecisionTreeRegressor(min_samples_split=5, max_depth=4)
tree.fit(X, y)
y_pred = tree.predict(X)

# 4. 可视化分析
plt.figure(figsize=(1612))

# 图1: 数据分布与预测值
plt.subplot(221)
plt.scatter(X, y, color="blue", label="True Values")
plt.plot(X, y_pred, color="red", label="Predicted Values", linewidth=2)
plt.title("Data Distribution and Predictions")
plt.xlabel("Feature X")
plt.ylabel("Target Value y")
plt.legend()

# 图2: 残差分布
plt.subplot(222)
residuals = y - y_pred
plt.hist(residuals, bins=20, color="orange", edgecolor="black")
plt.title("Residual Distribution")
plt.xlabel("Residuals")
plt.ylabel("Frequency")

# 图3: 决策树分割示意图
plt.subplot(223)
plt.scatter(X, y, color="blue", label="True Values")
plt.plot(X, y_pred, color="red", linewidth=2)
plt.axvline(x=tree.tree['threshold'], color="green", linestyle="--", label="Split Point")
plt.title("Decision Tree Split Visualization")
plt.xlabel("Feature X")
plt.ylabel("Target Value y")
plt.legend()

# 图4: 深度对拟合效果的影响
max_depths = [12345]
mse_list = []
for depth in max_depths:
    tree = DecisionTreeRegressor(min_samples_split=5, max_depth=depth)
    tree.fit(X, y)
    y_pred = tree.predict(X)
    mse = np.mean((y - y_pred)**2)
    mse_list.append(mse)

plt.subplot(224)
plt.plot(max_depths, mse_list, marker="o", color="purple")
plt.title("Effect of Depth on Fitting")
plt.xlabel("Max Tree Depth")
plt.ylabel("Mean Squared Error (MSE)")

plt.tight_layout()
plt.show()
  • 虚拟数据生成:使用正弦函数加噪音模拟非线性数据。
  • 决策树手动实现:手动实现了分裂、损失计算、树的生长和递归预测。

  • 图1:展示真实值与预测值对比。
  • 图2:分析残差的分布,评估模型效果。
  • 图3:可视化决策树分割点的影响。
  • 图4:展示不同深度对模型拟合的影响(防止过拟合或欠拟合)。

有任何问题,大家评论区留言~

最后

需要本文 PDF 的同学,扫码备注「文章PDF」即可!

最近准备了16大块的内容,124个算法问题的总结,完整的机器学习小册,免费领取~

另外,今天给大家准备了关于「深度学习」的论文合集,往期核心论文汇总,分享给大家。

点击名片,回复「深度学习论文」即可~

如果你对类似于这样的文章感兴趣。

欢迎关注、点赞、转发~

麦当的生态学笔记
用于文献信息解读和分享,统计分析相关方法的传递。
 最新文章