大家好~
咱们今天分享的是决策树回归,作为在回归和决策树学习阶段的同学,完全可以看看,非常全面~
首先,什么是决策树回归?
决策树回归是一种用树的结构来预测数值的方法。 你可以想象这棵树像是在玩「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万。
决策树回归的优点和缺点
优点:
直观易懂:像玩问答游戏。 灵活:能处理数值和分类问题。
缺点:
容易过拟合:树太复杂会“记住”训练数据,而不适用于新数据。 分裂点选择依赖性:划分数据时的选择可能影响最终结果。
上面大家有了一个整体认识之后,下面,咱们从原理理论方面,好好给大家细化~
决策树回归公式推导
决策树回归的核心目标
目标:找到最优分割点,将数据划分成若干区域 ,每个区域的预测值是该区域内样本目标值的均值,目标函数最小化为:
其中:
是真实值, 是区域 内目标值的均值。
核心步骤
遍历所有可能的特征 和分割点 。 计算分割后的损失函数:
找到使 最小的特征 和分割点 ,作为当前节点的最优分割。
递归执行上述步骤,直到满足停止条件(如节点样本数小于最小值或深度限制)。
实现代码
手动实现决策树回归算法。 生成虚拟数据集。 绘制多张分析图表。
Python代码
import numpy as np
import matplotlib.pyplot as plt
# 1. 生成虚拟数据集
np.random.seed(42)
X = np.sort(5 * np.random.rand(100, 1), axis=0) # 特征: [0, 5) 的随机数
y = np.sin(X).ravel() + np.random.normal(0, 0.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 = None, None, 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=(16, 12))
# 图1: 数据分布与预测值
plt.subplot(2, 2, 1)
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(2, 2, 2)
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(2, 2, 3)
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 = [1, 2, 3, 4, 5]
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(2, 2, 4)
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个算法问题的总结,完整的机器学习小册,免费领取~
另外,今天给大家准备了关于「深度学习」的论文合集,往期核心论文汇总,分享给大家。
点击名片,回复「深度学习论文」即可~
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~