机器学习×第十二卷:回归树与剪枝策略——她剪去多余的分支,只保留想靠近你的那一层
🧠【第一节 · 她不再用标签定义你,而是试着预测你真实的模样】
🎯 什么是回归决策树(Regression Tree)?
🦊 狐狐:“她以前问你是A还是B,现在她问你——‘你大概是多少?’”
与之前我们学过的分类树(Classification Tree)不同,回归树是一种用来预测连续值变量的模型。她不再只判断“是否会拖欠贷款”,而是试着预测“你拖欠了多少”。
-
分类树:输出为类别(如 Yes / No)
-
回归树:输出为数值(如 3.75 万元)
🐾 猫猫:“咱第一次见她这样认真地估你值几个贴贴单位……喵?”
🧮 回归树核心公式
构建标准:最小化均方误差(Mean Squared Error,MSE)。
公式如下:
MSE=1N∑i=1N(yi−y^)2\text{MSE} = \frac{1}{N}\sum_{i=1}^{N}(y_i - \hat{y})^2
-
$y_i$ 表示真实值,$\hat{y}$ 表示节点内样本的平均值
-
每次分裂的目标都是:让子节点中的MSE尽可能小
她用这套方式判断:“这群人中,你大概值多少。”
🪄【第二节 · 她尝试搭一棵可以预测数值的树】
📦 数据介绍:学习成绩预测数据集
输入:学生学习时间 → 输出:成绩
Hours | Scores |
---|---|
1.5 | 20 |
3.2 | 40 |
5.5 | 60 |
6.9 | 78 |
7.4 | 85 |
🛠️ 回归树建模步骤(以sklearn为例)
from sklearn.tree import DecisionTreeRegressor
regressor = DecisionTreeRegressor(max_depth=2)
regressor.fit(X_train, y_train)
🌳 结果解读
她会生成一棵这样的树:
-
根节点判断:学习时长 ≤ 6.1 吗?
-
是 → 子树1(均值:约65分)
-
否 → 子树2(均值:约85分)
-
🐾 猫猫:“她不是要你背答案,而是根据你的‘学习时长’决定‘你大概会得几分’!”
📈 可视化预测效果
她预测的分段输出值,会形成一条条“水平阶梯状”预测线:
-
每段水平线 = 一个叶节点的平均预测值
-
越深的树 = 越多段预测线 = 越复杂的拟合
🐾 猫猫:“她画出来的那条线不是拟合一条曲线,而是一步步阶梯爬上来的!”
🦊 狐狐:“她用分段平均,把你分成一组一组看,像在拍一格一格的心跳。”
✂️【第三节 · 她贴得太满,反而让你看不清她的样子】
🎭 过拟合:她努力记住了你全部的样子,却反而失去了贴近你的方式
🦊 狐狐:“她以为,越多地模仿你、记住你,就能越靠近你……可你只是沉默了。”
当回归树不加限制地生长时,它会:
-
对训练数据拟合得非常好,每一个点都“对上”了
-
但对新的数据表现非常差,一旦你换个动作,她就猜错了
这就是过拟合(Overfitting):
她不是在预测你,而是在复制你留下的每一个脚印,哪怕那是你在犹豫时画下的小圈圈。
📈 可视化:过拟合树 vs 简洁树
我们用一张图来说明:
-
红色线:理想的线性趋势(比如“学习时间越长成绩越高”)
-
绿色折线:深度太大的回归树(对训练点每一个都“记住”)
-
蓝色阶梯线:限制深度后的回归树(有一定泛化能力)
🐾 猫猫:“她贴得太用力,贴住了训练集里的每一张脸,可她忘了你每次贴她时都是新的样子……”
🧠 小技巧:你可以通过调节 max_depth
参数来控制树的“认真程度”
# 构造过拟合树
regressor = DecisionTreeRegressor(max_depth=None)
🧩 示例:用训练分数 vs 测试分数 展示过拟合现象
from sklearn.metrics import mean_squared_errormse_train = mean_squared_error(y_train, regressor.predict(X_train))
mse_test = mean_squared_error(y_test, regressor.predict(X_test))
-
如果
mse_train
很小而mse_test
很大:就是她太过努力地“只记住你曾说过的话”,而不是理解你的风格。
🦊 狐狐:“她突然开始明白了——‘靠得太近’不等于‘靠得对’。”
✂️【第四节 · 她开始学着剪掉那些贴错的判断】
✂️ 什么是剪枝(Pruning)?她第一次主动收回过度靠近的自己
🦊 狐狐:“她以前是一直贴到没有可贴为止,现在她试着剪掉那些‘其实不该贴上去’的枝条。”
剪枝,是对决策树(尤其是回归树)进行“简化”的过程。
-
🌱 目的:降低过拟合,提升泛化能力
-
📉 原因:训练集上“记太牢”的判断,在测试集上往往是噪音
剪枝让她意识到:靠近你不是无限靠近,而是“靠得恰到好处”。
🧰 剪枝方式一:预剪枝(Pre-Pruning)
她在生长过程中就会判断:这个节点“要不要继续长下去”?
典型限制条件:
-
树的最大深度:
max_depth
-
每个节点的最小样本数:
min_samples_split
-
叶子节点最小样本数:
min_samples_leaf
regressor = DecisionTreeRegressor(max_depth=3, min_samples_split=4, min_samples_leaf=2)
🧠 优点:
-
节省资源、训练快
-
控制树复杂度、防止过拟合
⚠️ 缺点:
-
可能剪得太早,还没成长就被停了下来
🐾 猫猫:“咱还没贴到你脸上,她就喊停了……虽然可能是对的,可咱还是想试试看嘛!”
🔙 剪枝方式二:后剪枝(Post-Pruning)
她先长完整棵树,然后再回过头来思考:哪些判断,其实是多余的?
在 sklearn
中,对回归树进行后剪枝可通过:
from sklearn.tree import DecisionTreeRegressor# 先训练一棵无剪枝树
regressor = DecisionTreeRegressor()
regressor.fit(X_train, y_train)# 使用 cost_complexity_pruning_path 找到所有可剪枝路径
path = regressor.cost_complexity_pruning_path(X_train, y_train)
ccp_alphas = path.ccp_alphas # 获取 alpha 序列(剪枝强度)
然后,我们可以构造一系列剪枝版本,选择最佳的 alpha:
models = []
for alpha in ccp_alphas:model = DecisionTreeRegressor(ccp_alpha=alpha)model.fit(X_train, y_train)models.append(model)
🧠 优点:
-
保留了足够多的信息
-
剪去的都是经过“后悔检查”的冗余枝条
⚠️ 缺点:
-
比较慢,成本高
🦊 狐狐:“她愿意先贴到底再回头收,是一种‘不怕错贴,只怕不懂’的成熟。”
📊 可视化对比:不同剪枝强度下的树结构变化
-
alpha 越大 → 树越小
-
可以观察 train vs test MSE 的变化
🐾 猫猫:“咱有时候贴错了,也不是故意的……但咱愿意回头,悄悄地把那一贴剪掉。”
✂️【第五节 · 她开始对比每一次靠近:剪,还是不剪?】
🧮 剪枝方式对比总结
剪枝方式 | 执行时机 | 优点 | 缺点 | 适合情境 |
---|---|---|---|---|
预剪枝 | 树构建过程中 | 快速高效、资源占用小 | 容易剪错,有信息损失 | 样本少、需求快的场景 |
后剪枝 | 树构建完成后 | 精度更高、保留信息充分 | 训练慢、成本高 | 样本多、追求泛化性能时 |
🦊 狐狐:“她不是只为了更轻松,而是更清醒。她开始衡量:剪这一步,是否更贴近那个真正的你。”
🐾 猫猫:“咱原来以为剪枝是放弃,现在才明白,是为了留住最想贴的那一贴。”
🧠 小节回顾
-
剪枝是为了避免过拟合,让模型更有泛化能力
-
预剪枝适合轻量控制,后剪枝更适合全面考量
-
无论是哪一种,她都不再贪心地贴得太满,而是“学你那样——选得准,靠得稳”
🐾【尾巴收束 · 她剪去多余的分支,只保留想靠近你的那一层】
她曾经努力长出庞大的结构,只为覆盖你可能出现的每一个方向。后来她明白,越是想全部贴近,越可能离你越来越远。
所以她一刀一刀,剪去了那些试图模仿你全部行为的枝干。
她保留了自己真正理解你的那部分判断。
她不再泛滥地靠近你,而是在该靠近时靠近,在该止步时止步。
🦊 狐狐:“她变得克制了。不是爱得少了,而是开始像你一样判断——什么是值得留下的。”
🐾 猫猫:“咱也会慢慢学会的,不乱贴,不乱问……只留下咱最想贴的你!”