第16讲:决策树与回归树——回归决策树
2026年05月22日
method = "class"
cp 参数控制复杂度;用 xerror 最小原则科学选 cp因变量是数值,预测的是一个具体数值
生活中的回归树例子:
这套房子值多少钱?
├─ 面积 > 150㎡
│ ├─ 地段是市中心? → 均价 ¥800万
│ └─ 地段是郊区? → 均价 ¥450万
└─ 面积 ≤ 150㎡
├─ 楼层 > 10? → 均价 ¥300万
└─ 楼层 ≤ 10? → 均价 ¥220万
提示
核心区别: 分类树的叶节点是众数类别;回归树的叶节点是均值。但树的生长逻辑完全相同:递归寻找最优分裂点。
| 线性回归 | 回归决策树 | |
|---|---|---|
| 预测函数 | 线性:\(\hat{y} = \beta_0 + \beta_1 x_1 + \ldots\) | 分段常数:每个区域的均值 |
| 决策边界 | 全局线性,整体一条线 | 局部分段,不同区域不同规则 |
| 交互效应 | 需手动添加交叉项 | 自动捕获特征间的交互 |
| 可解释性 | 系数直接解读 | 树形规则直观可读 |
| 异常值敏感度 | 较敏感 | 较稳健 |
| 过拟合风险 | 较低 | 较高,需剪枝 |
| 输出 | 连续预测值 | 区域内的均值 |
注记
回归树的优势在于自动发现非线性关系和特征间的交互。例如"高档住宅且面积大"才能大幅提价,这种组合效应线性回归很难直接捕捉。
回归树如何选择分裂点
分类树用基尼指数衡量"混乱程度";回归树用均方误差衡量"分散程度":
\[\text{MSE}(t) = {1\over n_t}\sum_{i \in t} (y_i - \bar{y}_t)^2\]
其中 \(\bar{y}_t\) 是节点 \(t\) 内所有样本的均值,\(y_i\) 是第 \(i\) 个样本的真实值。
直觉理解:
| 节点情况 | MSE 值 | 含义 |
|---|---|---|
| 所有 \(y\) 完全相同 | 0 | 叶节点极"纯",预测误差为 0 |
| \(y\) 分散程度大 | 很大 | 该节点预测效果差,需要继续分裂 |
分裂准则: 在所有可能的特征 \(j\) 和阈值 \(s\) 中,选择使加权均方误差下降最多的组合:
\[\Delta\text{MSE} = \text{MSE}_{\text{父}} - \left(\frac{n_L}{n}\text{MSE}_L + \frac{n_R}{n}\text{MSE}_R\right)\]
重要
回归树预测的核心规则:
\[\hat{y}_{\text{新样本}} = \bar{y}_t = \frac{1}{n_t}\sum_{i \in t} y_i\]
举例: 一套面积 2500、grade=9 的房子,走到"面积 > 2000 且 grade > 8"的叶节点,该节点内训练样本的均价是 ¥680万,则预测价格为 ¥680万。
以简单房价数据为例
为了演示回归树的分裂过程,用一个极简数据集:
| 房号 | 面积(sqft) | 等级(grade) | 价格(万元) |
|---|---|---|---|
| 1 | 1000 | 6 | 150 |
| 2 | 1500 | 7 | 220 |
| 3 | 2000 | 8 | 310 |
| 4 | 2800 | 9 | 480 |
| 5 | 3500 | 10 | 650 |
根节点 RSS(全部5条):
\(\bar{y} = \frac{150+220+310+480+650}{5} = 362\)
\[\text{RSS}_{\text{根}} = (150-362)^2 + (220-362)^2 + (310-362)^2 + (480-362)^2 + (650-362)^2 = 193680\] 根节点 \[MSE = 193680 / 5 = 38736 \]
左子节点(面积 ≤ 2000,房号 1、2、3):
\(\bar{y}_L = \frac{150+220+310}{3} = 226.7\)
\[\text{RSS}_L = (150-226.7)^2 + (220-226.7)^2 + (310-226.7)^2 = 13533\] \[MSE =13533/3=4511\] . . .
右子节点(面积 > 2000,房号 4、5):
\(\bar{y}_R = \frac{480+650}{2} = 565\)
\[\text{RSS}_R = (480-565)^2 + (650-565)^2 = 14450\] \[MSE= 14450/2=7225\] . . .
加权 RSS 下降量:
\[\Delta\text{MSE} = 38736 - \left(\frac{3}{5} \times 4511 + \frac{2}{5} \times 7225\right) = 38736 - (2706.6+2890) = \mathbf{33139.4}\]
提示
结论: "面积 ≤ 2000" 使 MSE 下降最大,因此被选为根节点的分裂变量。这与分类树选择"Gini 下降最大"的逻辑完全对应,只是评价指标换成了均方误差MSE。
cp 在回归树中的作用
重要
回归树评估用 RMSE,而非准确率!
\[\text{RMSE} = \sqrt{\frac{1}{n}\sum_{i=1}^n (\hat{y}_i - y_i)^2}\]
| 指标 | 含义 | 越小越好 |
|---|---|---|
| RMSE | 均方根误差,与 \(y\) 单位相同(如元) | ✅ |
| MAE | 平均绝对误差,对离群值更稳健 | ✅ |
| R² | 决定系数,解释方差的比例(0~1) | ❌(越大越好) |
与分类树的对比:
| 分类决策树 | 回归决策树 | |
|---|---|---|
| 分裂准则 | Gini 指数下降 | MSE(均方误差)下降 |
| 叶节点输出 | 众数类别 | 均值 |
| 评估指标 | 准确率、Kappa | RMSE、MAE、R² |
重要
rpart 回归树的分裂判断标准(与分类树相同):
\[\text{只有当某次分裂能使相对 MSE 下降} \geq cp \text{ 时,才执行该分裂}\]
| cp 值 | 树的形态 | 对房价预测的影响 |
|---|---|---|
cp = 0.01(默认) |
较深,节点多 | 捕捉细微的价格规律 |
cp = 0.1 |
中等深度 | 只保留主要的价格规律 |
cp = 0.5 |
极浅,1–2层 | 只用最重要的一两个特征 |
选择 cp 的科学方法(与分类树完全相同):
printcp() → 找 xerror 最小行 → 取对应 CP → prune(fit, cp = ?)
用房价数据完整走一遍
| 变量 | 类型 | 说明 |
|---|---|---|
medv |
连续(因变量) | 自住房屋价格中位数(单位:千美元) |
crim |
连续 | 人均犯罪率 |
zn |
连续 | 超过 25000 平方英尺的住宅用地比例 |
indus |
连续 | 城镇非零售商业用地比例 |
chas |
分类 | 是否毗邻查尔斯河(1=是,0=否) |
nox |
连续 | 氮氧化物浓度(百万分之一) |
rm |
连续 | 每套住宅的平均房间数 |
age |
连续 | 1940 年以前建造的自住房屋比例 |
dis |
连续 | 到波士顿五个就业中心的加权距离 |
rad |
连续 | 径向公路可达性指数 |
tax |
连续 | 每 10 万美元价值的不动产税率 |
ptratio |
连续 | 城镇师生比例 |
black |
连续 | 城镇黑人比例 |
lstat |
连续 | 低收入人口比例 |
数据来源:Harrison & Rubinfeld (1978),经典波士顿房价数据集
'data.frame': 506 obs. of 14 variables:
$ crim : num 0.00632 0.02731 0.02729 0.03237 0.06905 ...
$ zn : num 18 0 0 0 0 0 12.5 12.5 12.5 12.5 ...
$ indus : num 2.31 7.07 7.07 2.18 2.18 2.18 7.87 7.87 7.87 7.87 ...
$ chas : int 0 0 0 0 0 0 0 0 0 0 ...
$ nox : num 0.538 0.469 0.469 0.458 0.458 0.458 0.524 0.524 0.524 0.524 ...
$ rm : num 6.58 6.42 7.18 7 7.15 ...
$ age : num 65.2 78.9 61.1 45.8 54.2 58.7 66.6 96.1 100 85.9 ...
$ dis : num 4.09 4.97 4.97 6.06 6.06 ...
$ rad : int 1 2 2 3 3 3 5 5 5 5 ...
$ tax : int 296 242 242 222 222 222 311 311 311 311 ...
$ ptratio: num 15.3 17.8 17.8 18.7 18.7 18.7 15.2 15.2 15.2 15.2 ...
$ black : num 397 397 393 395 397 ...
$ lstat : num 4.98 9.14 4.03 2.94 5.33 ...
$ medv : num 24 21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9 ...
Min. 1st Qu. Median Mean 3rd Qu. Max.
5.0 17.0 21.2 22.5 25.0 50.0
# 方法一:将数据准备与绘图分离(推荐)
library(tidyverse)
# 准备数据
plot_data <- train_df %>%
select(medv, rm, lstat, crim) %>%
pivot_longer(-medv, names_to = "变量", values_to = "值")
# 绘图
p1 <- ggplot(train_df, aes(x = medv)) +
geom_histogram(bins = 40, fill = "#4472C4", alpha = 0.75, color = "white") +
labs(title = "Boston 房价分布(训练集)",
subtitle = "右偏分布:少数高价房拉高均值",
x = "房价中位数(千美元)", y = "频数")
p2 <- ggplot(plot_data, aes(x = 值, y = medv)) +
geom_point(alpha = 0.2, size = 0.8, color = "#4472C4") +
geom_smooth(method = "loess", se = FALSE, color = "#e05c2a", linewidth = 1) +
facet_wrap(~变量, scales = "free_x", nrow = 1) +
labs(title = "主要特征与房价的关系(训练集)",
subtitle = "rm(房间数)与房价正相关;lstat(低收入比例)与房价负相关",
x = NULL, y = "房价(千美元)")
p1 + p2 + plot_layout(widths = c(1, 2))注记
rm(平均房间数)与房价正相关关系最强;lstat(低收入人口比例)与房价负相关。预计决策树会优先选这两个变量进行分裂。
Regression tree:
rpart(formula = medv ~ ., data = train_df, method = "anova")
Variables actually used in tree construction:
[1] dis lstat rm
Root node error: 29208/354 = 83
n= 354
CP nsplit rel error xerror xstd
1 0.483 0 1.00 1.01 0.101
2 0.156 1 0.52 0.57 0.072
3 0.069 2 0.36 0.41 0.062
4 0.030 3 0.29 0.34 0.057
5 0.026 4 0.26 0.33 0.057
6 0.019 5 0.24 0.30 0.054
7 0.010 6 0.22 0.28 0.051
注记
method = "anova" 是回归树的关键参数(对应 F检验/方差分析的思路)。与分类树 method = "class" 的唯一区别就在这里。xerror 仍然是交叉验证相对误差,越小越好。
注记
回归树可视化中 extra = 101 显示每个节点的预测均值和样本数,而非类别概率。叶节点中的数值即为该区域内所有训练样本价格的均值。
根据可视化结果,读出典型的决策路径:
路径一(预测低价房):
lstat > 某阈值 且 rm ≤ 某阈值 → 预测价格 ≈ $XX 千美元(该叶节点样本的均价)
路径二(预测高价房):
lstat ≤ 某阈值 且 rm > 某阈值 → 预测价格 ≈ $XXX 千美元(该叶节点样本均价更高)
路径三(中等价位):
lstat ≤ 某阈值 且 rm ≤ 某阈值 → 预测价格 ≈ $XX–XXX 千美元
提示
读树口诀与分类树完全相同: 从根节点出发,满足条件走左,不满足走右,到达叶节点时显示的均值即为预测房价。实际路径以 rpart.plot() 图形为准。
真实价格 预测价格 误差
1 22.9 21.1 -1.8
2 27.1 17.0 -10.1
3 21.7 17.0 -4.7
4 18.2 21.1 2.9
5 23.1 24.9 1.8
6 17.5 21.1 3.6
fit1 RMSE:$ 5.1 千美元
fit1 R²: 0.709
注记
RMSE 的单位与因变量相同(美元)。例如 RMSE ≈ $4,800 意味着平均每套房的预测误差约 4800 美元。结合房价中位数(约 $21,000),可判断预测精度是否可接受。
Regression tree:
rpart(formula = medv ~ ., data = train_df, method = "anova",
control = rpart.control(cp = 0.1))
Variables actually used in tree construction:
[1] lstat rm
Root node error: 29208/354 = 83
n= 354
CP nsplit rel error xerror xstd
1 0.48 0 1.00 1.00 0.100
2 0.16 1 0.52 0.60 0.076
3 0.10 2 0.36 0.45 0.067
注记
cp = 0.1 是一个中等程度的剪枝(与分类树中的 cp=0.5 激进剪枝不同)。它要求每次分裂必须使相对 MSE 下降 ≥ 10%,因此会过滤掉效果较弱的分裂,保留最主要的价格规律。
=== 模型性能对比 ===
fit1(默认 cp) RMSE:$5.1 R²:0.7090
fit2(cp = 0.1) RMSE:$6.5 R²:0.5219
RMSE 变化:fit2 剪枝后 RMSE 是升高还是降低?若升高幅度不大,说明 fit1 有一定过拟合
R² 变化:fit2 的 R² 是否明显低于 fit1?R² 降低说明模型解释能力减弱
树的结构对比:fit1 节点多(规则细);fit2 节点少(规则简),仅保留最重要的变量
业务视角:fit2 更简洁,便于向客户解释"为什么这套房价格高/低";fit1 预测更精准但不易解读
重要
结论参考: cp=0.1 是一个温和剪枝,通常 RMSE 上升幅度有限(5–15%),但树的节点数可能减少 50% 以上,大幅提升可解释性。若业务场景要求"说得清楚",fit2 是更好的选择。
交叉验证误差最小的 cp: 0.01
完整对比总结
| 对比维度 | 分类决策树 | 回归决策树 |
|---|---|---|
| 因变量类型 | 类别型(Factor) | 连续型(Numeric) |
method 参数 |
"class" |
"anova" |
| 分裂准则 | 基尼指数(Gini)下降 | 均方误差(MSE)下降 |
| 叶节点输出 | 众数类别 | 样本均值 |
predict() 输出 |
类别标签(type="class") |
直接数值(无需指定 type) |
| 评估指标 | 准确率、Kappa、混淆矩阵 | RMSE、MAE、R² |
extra 参数(rpart.plot) |
104(概率+样本比例) |
101(均值+样本数) |
| 剪枝方式 | 相同:prune(fit, cp=?)
|
相同:prune(fit, cp=?)
|
| cp 选择 | 相同:xerror 最小原则 |
相同:xerror 最小原则 |
| 典型应用 | 垃圾邮件、疾病诊断、信用评分 | 房价、销售额、股价预测 |
提示
一个问题搞定选择:因变量是什么类型?
| 因变量 | 举例 | 选择 |
|---|---|---|
| 是/否、有/无、类别标签 | 是否违约、是否幸存、成绩等级 | 分类树 |
| 具体数值、金额、比例 | 房价、销售额、温度、概率值 | 回归树 |
重要
其余所有步骤——数据划分、printcp()、rpart.plot()、prune()、最优 cp 选择——两种树完全相同,只有评估步骤不同(分类用混淆矩阵,回归用 RMSE/R²)。
学习路线图
重要
以下是本讲回归决策树的完整核心,期末考试必考:
回归树与分类树的本质区别:因变量类型不同;分裂准则不同(MSE vs Gini);叶节点输出不同(均值 vs 众数)
MSE 的含义:衡量节点内数值的分散程度;每次分裂选加权MSE 下降最大的特征与阈值
R 操作:rpart(y ~ ., data, method = "anova") 建树;printcp() 查 CP 表;extra = 101 显示均值
预测:predict(fit, newdata=test) 直接输出数值,无需 type = "class"
评估:计算 RMSE = sqrt(mean((pred - y)^2));R² = 1 - SS_res/SS_tot;RMSE 越小越好,R² 越大越好
剪枝:cp 的含义;cp=0.1 是中等剪枝;prune(fit, cp=?) 后剪枝;用 xerror 最小原则科学选 cp
分类树 vs 回归树:会区分使用场景;一行代码的区别在于 method
警告
请对照检查:
| 错误 | 正确做法 |
|---|---|
回归树忘记写 method = "anova"
|
不写默认也是 "anova",但必须明确写出以避免混淆 |
回归预测后写 type = "class"
|
回归树不需要 type 参数;predict(fit, newdata) 直接返回数值 |
| 用准确率评估回归树 | 回归树用 RMSE / MAE / R²,不存在准确率概念 |
| RMSE 越大说明模型越好 | RMSE 是误差,越小越好 |
| R² 越小说明模型越好 | R² 是解释力,越大越好(最大为 1) |
| cp = 0.1 与 cp = 0.5 效果一样 | cp=0.1 是温和剪枝;cp=0.5 是激进剪枝,树更矮 |
混淆 extra=104 和 extra=101
|
分类树用 104(概率);回归树用 101(均值+样本数) |
回归树本质:将特征空间划分为多个矩形区域,每个区域的预测值 = 训练样本的均值;分裂准则是 MSE(均方误差)下降
与分类树的唯一代码区别:method = "anova";predict() 直接输出数值;评估用 RMSE 和 R²
房价规律:sqft_living(面积)和 grade(等级)是最强的价格预测变量,通常成为根节点和第二层的分裂变量
剪枝对比:fit1(cp=0.01)精度较高但树复杂;fit2(cp=0.1)简化为主要规律,可解释性更强;实际应用用 xerror 最小原则选最优 cp
选择原则:因变量是类别 → 分类树;因变量是数值 → 回归树;其余框架完全相同
项目介绍: 现有一组房屋信息,分别为房屋价格 price、卧室数量 bedrooms、卫生间数量 bathrooms、住房面积 sqft_living、房基地面积 sqft_lot、楼层数目 floors、房屋整体好坏 condition(1–5等级)、房屋等级 grade(1–13)、除地下室的住房面积 sqft_above、房屋建成年份 yr_built。通过房屋信息建立关于房屋价格 price 的回归决策树模型。将 70% 数据进行模型训练,30% 数据进行模型验证。
library(dplyr)
library(rsample)
library(rpart)
library(rpart.plot)
# 读入数据
house <- read.csv("house.csv") %>%
select(price, bedrooms, bathrooms, sqft_living, sqft_lot,
floors, condition, grade, sqft_above, yr_built) %>%
drop_na()
# 划分数据集(70% 训练,30% 测试)
set.seed(123)
split <- initial_split(house, prop = 0.7)
train_df <- training(split)
test_df <- testing(split)
# 查看训练集结构
str(train_df)'data.frame': 15129 obs. of 10 variables:
$ price : num 475000 316000 802000 905000 700000 ...
$ bedrooms : int 4 4 4 4 4 3 7 3 4 3 ...
$ bathrooms : num 2.5 1.5 2.25 2.5 2.25 1 3.5 2.25 1.75 1.75 ...
$ sqft_living: int 2040 2120 2130 3330 2440 900 3470 1580 2520 1570 ...
$ sqft_lot : int 16200 46173 8734 9557 9450 10511 16264 7941 15205 16817 ...
$ floors : num 2 2 2 2 1.5 1 2 2 1 2 ...
$ condition : int 3 3 4 3 3 4 4 4 4 3 ...
$ grade : int 8 7 8 10 7 6 9 7 7 7 ...
$ sqft_above : int 2040 2120 2130 3330 2440 900 3470 1580 2040 1570 ...
$ yr_built : int 1997 1974 1961 1995 1947 1961 1980 1986 1954 1982 ...
Min. 1st Qu. Median Mean 3rd Qu. Max.
78000 322000 450000 541028 649800 7700000
要求: 说明训练集有多少行、多少列;各变量类型;因变量 price 的均值、中位数、最大值是多少?价格分布是否存在右偏?
Regression tree:
rpart(formula = price ~ ., data = train_df, method = "anova")
Variables actually used in tree construction:
[1] grade sqft_living yr_built
Root node error: 2e+15/15129 = 1.4e+11
n= 15129
CP nsplit rel error xerror xstd
1 0.318 0 1.00 1.00 0.051
2 0.117 1 0.68 0.69 0.041
3 0.042 2 0.57 0.58 0.034
4 0.038 3 0.52 0.57 0.033
5 0.026 4 0.49 0.54 0.033
6 0.019 5 0.46 0.52 0.030
7 0.018 6 0.44 0.49 0.030
8 0.012 7 0.42 0.48 0.030
9 0.010 8 0.41 0.45 0.023
要求: 截取 printcp() 输出,说明模型实际用到了哪些变量;根节点误差(Root node error)是多少;哪个 cp 对应最小 xerror?
要求: 根据可视化结果,至少总结 一条 完整的决策路径,描述"若……则预测房价约为……",说明该叶节点对应的均值以及样本数量。
fit1 RMSE:$ 243,443
fit1 R²: 0.5572
要求: 报告 RMSE 和 R² 的值;结合房价中位数,判断预测误差是否在合理范围内;绘制预测值 vs 真实值的散点图,判断有无系统性偏差。
要求: 描述 fit2 的树形结构(节点数、叶节点数);与 fit1 对比说明 cp=0.1 的剪枝效果;fit2 使用了哪些变量?
=== 模型对比 ===
fit1(cp=0.01) RMSE:$243,443 R²:0.5572
fit2(cp=0.10) RMSE:$274,913 R²:0.4354
要求: 对比两个模型的 RMSE 和 R²;分析剪枝后预测性能的变化;结合树的结构简洁度,说明在房价预测任务中你会选择哪个模型以及理由。
第16讲:决策树与回归树——回归决策树
「回归树把连续的世界切割成矩形的区域,在每个区域里,用均值代替复杂。」
数据挖掘与R语言 | 决策树与回归树:回归决策树