决策树:集成模型的基础
使用 rpart 构建并可视化决策树,理解偏差与方差之间的权衡
决策树:集成模型的基础 是 CoddyKit 上的免费 R Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 R Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 R Academy 课程共包含 4 节课。
决策树如何进行划分
决策树会递归地将特征空间划分为矩形区域。在每个节点,算法都会搜索所有特征和所有可能的划分点,寻找能够最好地区分目标变量的划分。最终得到的是一棵由 if-else 规则组成的树。
library(rpart)
# Fit a classification tree
tree <- rpart(
Species ~ .,
data = iris,
method = 'class' # use 'anova' for regression
)
print(tree)GINI 与熵划分准则
划分准则用于衡量节点的不纯度。基尼不纯度衡量随机选择的元素被错误分类的概率。熵(信息增益)衡量信息无序程度的减少量。两者通常会生成相似的树;GINI 的计算速度更快,也是 rpart 的默认准则。
# Default: Gini impurity (parms = list(split = 'gini'))
tree_gini <- rpart(Species ~ ., data = iris, method = 'class')
# Using information gain (entropy)
tree_entropy <- rpart(
Species ~ ., data = iris, method = 'class',
parms = list(split = 'information')
)
cat('Gini root split:', tree_gini$frame$var[1])
cat('Entropy root split:', tree_entropy$frame$var[1])printcp() — 复杂度表
printcp(tree) 用于打印复杂度参数(CP)表。每一行显示一个树大小(分裂次数)、该树在训练数据上的相对误差,以及交叉验证误差(xerror)。CP 表用于找出最佳剪枝级别。
tree <- rpart(medv ~ ., data = MASS::Boston, method = 'anova',
control = rpart.control(minsplit = 5, cp = 0.001))
printcp(tree)
# Identify the CP with minimum cross-validated error
best_cp <- tree$cptable[
which.min(tree$cptable[, 'xerror']),
'CP'
]
cat('Best CP:', best_cp)prune() — 修剪树
prune(tree, cp) 会将树修剪回 cp 指定的复杂度级别。剪枝会合并预测价值很小的分支,从而防止过拟合。标准做法是:先找出使 CV 误差最小的 CP,再进行剪枝。
best_cp <- tree$cptable[
which.min(tree$cptable[, 'xerror']), 'CP'
]
pruned_tree <- prune(tree, cp = best_cp)
cat('Original tree nodes:', nrow(tree$frame))
cat('Pruned tree nodes:', nrow(pruned_tree$frame))rpart.plot() — 可视化树
rpart.plot 包中的 rpart.plot(tree) 会生成清晰、带颜色的决策树可视化图。每个内部节点显示分裂规则;每个叶节点显示预测类别和训练样本所占的比例。
library(rpart.plot)
tree <- rpart(Species ~ ., data = iris, method = 'class')
pruned <- prune(tree, cp = 0.02)
rpart.plot(
pruned,
type = 4, # split labels on branches
extra = 104, # show class + probability
fallen.leaves = TRUE
)偏差—方差权衡
深且未经剪枝的树具有较低偏差(几乎可以完美拟合训练数据),但具有较高方差(数据稍有变化就可能生成完全不同的树)。较浅的树或经过剪枝的树偏差较高,但方差较低。最佳树需要在这两种误差来源之间取得平衡。
随机森林和提升等集成方法会直接应对这种权衡。
# Deep tree = low bias, high variance (overfits)
deep_tree <- rpart(medv ~ ., data = MASS::Boston,
control = rpart.control(minsplit = 2, cp = 0))
# Shallow tree = high bias, low variance (underfits)
shallow_tree <- rpart(medv ~ ., data = MASS::Boston,
control = rpart.control(maxdepth = 2))
cat('Deep nodes:', nrow(deep_tree$frame))
cat('Shallow nodes:', nrow(shallow_tree$frame))决策树过拟合
未经剪枝的树可以通过记住每个训练样本,将训练误差降为零。当在未见过的数据上评估同一棵树时,其性能会大幅下降。这是监督学习中过拟合的典型例子。
set.seed(42)
train_idx <- sample(nrow(MASS::Boston), 400)
train_bos <- MASS::Boston[train_idx, ]
test_bos <- MASS::Boston[-train_idx, ]
# Fully grown tree
full <- rpart(medv ~ ., data = train_bos,
control = rpart.control(cp = 0, minsplit = 2))
train_pred <- predict(full, train_bos)
test_pred <- predict(full, test_bos)
cat('Train RMSE:', sqrt(mean((train_pred - train_bos$medv)^2)))
cat('Test RMSE:', sqrt(mean((test_pred - test_bos$medv)^2)))来自 rpart 的变量重要性
rpart 会为每个预测变量记录 variable.importance:该变量在所有分裂中带来的分裂准则总改进量。这可以快速显示哪些特征主导了模型的决策。
tree <- rpart(medv ~ ., data = MASS::Boston, method = 'anova')
# Variable importance (sorted)
imp <- sort(tree$variable.importance, decreasing = TRUE)
print(imp)
# Quick barplot
barplot(imp, las = 2, main = 'Variable Importance',
col = 'steelblue', cex.names = 0.8)从树到集成
单棵决策树并不稳定:对数据重新采样会生成差异很大的树。集成方法利用了这种不稳定性:
- 装袋法 / 随机森林:在自助采样样本上训练许多树,然后取平均值。
- 提升法:按顺序构建树,每棵树都纠正前一棵树的误差。
- 这两种方法都能降低方差,同时保留树的表达能力。
# Demonstrating instability of a single tree
set.seed(1); t1 <- rpart(medv ~ ., data = MASS::Boston[sample(506, 400), ])
set.seed(2); t2 <- rpart(medv ~ ., data = MASS::Boston[sample(506, 400), ])
# Root split may differ between trees
cat('Tree 1 root split:', t1$frame$var[1])
cat('Tree 2 root split:', t2$frame$var[1])rpart 控制参数
rpart.control() 控制树的生长方式。主要参数包括:cp(复杂度惩罚)、minsplit(尝试分裂所需的最少观测数)、minbucket(叶节点的最小大小)以及 maxdepth。理解这些参数对于调节基于树的模型至关重要。
ctrl <- rpart.control(
cp = 0.005, # complexity penalty
minsplit = 20, # min obs to try a split
minbucket = 7, # min obs in any leaf
maxdepth = 10 # max tree depth
)
tree <- rpart(medv ~ ., data = MASS::Boston,
method = 'anova', control = ctrl)
printcp(tree)评估树的性能
剪枝后,在留出的测试集上评估这棵树。对于回归,计算 RMSE 和 R 平方;对于分类,计算准确率和混淆矩阵。将这些指标与基准模型进行比较,以了解单棵树所带来的价值。
pruned_tree <- prune(tree, cp = best_cp)
test_pred <- predict(pruned_tree, newdata = test_bos)
rmse <- sqrt(mean((test_pred - test_bos$medv)^2))
ss_res <- sum((test_pred - test_bos$medv)^2)
ss_tot <- sum((test_bos$medv - mean(test_bos$medv))^2)
r2 <- 1 - ss_res / ss_tot
cat('RMSE:', round(rmse, 3))
cat('R2:', round(r2, 3))快速检查
以下哪项最准确地描述了调用 prune(tree, cp = best_cp) 的目的?
决策树回顾
《决策树——集成方法的基础》要点:
- 树会递归地划分特征空间;分裂使用基尼或熵准则。
rpart(y ~ ., data, method)用于拟合树;printcp()显示复杂度表。- 找出交叉验证误差最小的 CP,然后执行
prune(tree, cp)。 rpart.plot()用于可视化树的结构。- 深树会过拟合(低偏差、高方差);浅树会欠拟合。
tree$variable.importance根据预测变量带来的分裂总改进量对其排序。- 集成方法(随机森林、提升法)可以克服单棵树的不稳定性。
# Standard rpart workflow
tree <- rpart(y ~ ., data = train, method = 'anova',
control = rpart.control(cp = 0.001))
best_cp <- tree$cptable[which.min(tree$cptable[, 'xerror']), 'CP']
pruned <- prune(tree, cp = best_cp)
test_pred <- predict(pruned, newdata = test)
rmse <- sqrt(mean((test_pred - test$y)^2))
cat('Pruned Tree RMSE:', rmse)常见问题解答
「决策树:集成模型的基础」课时是免费的吗?
是的 — 「决策树:集成模型的基础」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 R Academy 课程的其余内容,请升级到 CoddyKit PRO。 R Academy 课程共包含 4 节课。
「决策树:集成模型的基础」这节课中我会学到什么?
使用 rpart 构建并可视化决策树,理解偏差与方差之间的权衡 你通过在浏览器中直接运行的动手代码来练习 R Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 R Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 R Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「决策树:集成模型的基础」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 R Academy 课中编写并运行代码吗?
能。每节 R Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 决策树:集成模型的基础
- 使用 ranger 构建随机森林
- 使用 xgboost 进行梯度提升
- 特征重要性与模型解读