决策树探秘:机器学习领域的经典算法深度剖析

决策树探秘:机器学习领域的经典算法深度剖析
本文将从决策树的基本思想与构建流程入手,深入剖析常见的划分指标、剪枝策略与优缺点,并配以代码示例、图示,帮助你直观理解这一机器学习领域的经典模型。
目录
引言
决策树(Decision Tree)是机器学习中最直观、最易解释的算法之一。它以树状结构模拟人类的“逐层决策”过程,从根节点到叶节点,对样本进行分类或回归预测。由于其逻辑透明、易于可视化、无需过多参数调优,广泛应用于金融风控、医学诊断、用户行为分析等领域。
本文将深入介绍决策树的构建原理、常见划分指标(如信息增益、基尼系数)、过拟合与剪枝策略,并结合 Python 代码示例及可视化,帮助你快速掌握这门经典算法。
决策树基本原理
决策树的构建思路
节点划分
- 给定一个训练集 $(X, y)$,其中 $X \in \mathbb{R}^{n \times d}$ 表示 $n$ 个样本的 $d$ 维特征,$y$ 是对应的标签。
- 决策树通过在某个特征维度上设置阈值(threshold),将当前节点的样本集划分为左右两个子集。
- 对于分类问题,划分后期望左右子集的“纯度”(纯度越高表示同属于一个类别的样本越多)显著提升;对于回归问题,希望目标值的方差或均方误差降低。
递归生长
- 从根节点开始,依次在当前节点的样本上搜索最佳划分:选择 “最优特征+最优阈值” 使得某种准则(如信息增益、基尼系数、方差减少)最大化。
- 将样本分到左子节点与右子节点后,继续对每个子节点重复上述过程,直到满足“停止生长”的条件。停止条件可以是:当前节点样本数量过少、树的深度超过预设、划分后无法显著提升纯度等。
叶节点预测
- 对于分类树,当一个叶节点只包含某一类别样本时,该叶节点可直接标记为该类别;如果混杂多种类别,则可用多数投票决定叶节点标签。
- 对于回归树,叶节点可取对应训练样本的平均值或中位数作为预测值。
整个生长过程形成一棵二叉树,每个内部节点对应“某特征是否超过某阈值”的判断,最终路径到达叶节点即可得预测结果。
划分指标:信息增益与基尼系数
不同的指标衡量划分后节点“纯度”或“杂质”改善程度。下面介绍最常用的两种:
信息增益(Information Gain)
对于分类问题,信息熵(Entropy)定义为:
$$ H(D) = - \sum_{k=1}^K p_k \log_2 p_k, $$
其中 $p\_k$ 是数据集 $D$ 中类别 $k$ 的出现概率,$K$ 是类别总数。
若按特征 $f$、阈值 $\theta$ 将 $D$ 划分为左右子集 $D\_L$ 与 $D\_R$,则条件熵:
$$ H(D \mid f, \theta) = \frac{|D_L|}{|D|} H(D_L) \;+\; \frac{|D_R|}{|D|} H(D_R). $$
信息增益:
$$ IG(D, f, \theta) = H(D) - H(D \mid f, \theta). $$
- 在决策树构建时,遍历所有特征维度与可能阈值,选择使 $IG$ 最大的 $(f^, \theta^)$ 作为最佳划分。
基尼系数(Gini Impurity)
基尼系数衡量一个节点中随机采样两个样本,它们不属于同一类别的概率:
$$ G(D) = 1 - \sum_{k=1}^K p_k^2. $$
划分后加权基尼系数为:
$$ G(D \mid f, \theta) = \frac{|D_L|}{|D|} G(D_L) \;+\; \frac{|D_R|}{|D|} G(D_R). $$
优化目标是使划分后“基尼减少量”最大化:
$$ \Delta G = G(D) - G(D \mid f, \theta). $$
- 由于基尼系数计算无需对数运算,计算量略低于信息增益,在实践中常被 CART(Classification and Regression Tree)算法采用。
两者本质都是度量划分后节点“更纯净”的程度,信息增益和基尼系数通常会给出非常接近的划分结果。
决策树的生长与剪枝
递归划分与停止条件
递归划分流程
对当前节点数据集 $D$:
- 计算当前节点纯度(熵或基尼)。
- 对每个特征维度 $f$、对所有可能的阈值 $\theta$(通常是该特征在样本中两个相邻取值的中点)遍历,计算划分后的纯度改善。
- 选取最佳 $(f^, \theta^)$,根据 $f^* < \theta^*$ 将 $D$ 分为左右集 $D\_L$ 与 $D\_R$。
- 递归地对 $D\_L$、$D\_R$ 重复上述步骤,直到满足停止生长的条件。
常见的停止条件
- 当前节点样本数少于最小阈值(如
min_samples_split
)。 - 当前树深度超过预设最大深度(如
max_depth
)。 - 当前节点已纯净(所有样本属于同一类别或方差为 0)。
- 划分后纯度改善不足(如信息增益 < 阈值)。
- 当前节点样本数少于最小阈值(如
若无任何限制条件,树会一直生长到叶节点只剩一个样本,训练误差趋近于 0,但会导致严重过拟合。
过拟合风险与剪枝策略
过拟合风险
- 决策树模型对数据的分割非常灵活,若不加约束,容易“记住”训练集的噪声或异常值,对噪声敏感。
- 过拟合表现为训练误差很低但测试误差较高。
剪枝策略
预剪枝(Pre-Pruning)
在生长过程中就限制树的大小,例如:
- 设置最大深度
max_depth
。 - 限制划分后样本数
min_samples_split
或min_samples_leaf
。 - 阈值过滤:保证划分后信息增益或基尼减少量大于某个小阈值。
- 设置最大深度
- 优点:不需要完整生长子树,计算开销较小;
- 缺点:可能提前终止,错失更优的全局结构。
后剪枝(Post-Pruning)
先让决策树自由生长到较深,然后再依据验证集或交叉验证对叶节点进行“剪枝”:
- 从叶节点开始,自底向上逐步合并子树,将当前子树替换为叶节点,计算剪枝后在验证集上的性能。
- 若剪枝后误差降低或改善不显著,则保留剪枝。
常用方法:基于代价复杂度剪枝(Cost Complexity Pruning,也称最小化 α 修剪),对每个内部节点计算代价值:
$$ R_\alpha(T) = R(T) + \alpha \cdot |T|, $$
其中 $R(T)$ 是树在训练集或验证集上的误差,$|T|$ 是叶节点数,$\alpha$ 是正则化系数。
- 调节 $\alpha$ 可控制剪枝强度。
决策树分类示例与代码解析
下面以 Iris 数据集的两类样本为例,通过 Python 代码演示决策树的训练、决策边界可视化与树结构图解。
示例数据介绍
- 数据集:Iris(鸢尾花)数据集,包含 150 个样本、4 个特征、3 个类别。
- 简化处理:仅选取前两类(Setosa, Versicolor)和前两维特征(萼片长度、萼片宽度),构造二分类问题,方便绘制二维决策边界。
训练与可视化决策边界
下面的代码展示了:
- 加载数据并筛选;
- 划分训练集与测试集;
- 用
DecisionTreeClassifier
训练深度为 3 的决策树; - 绘制二维平面上的决策边界与训练/测试点。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
# 1. 加载 Iris 数据集,仅取前两类、前两特征
iris = datasets.load_iris()
X = iris.data[:, :2]
y = iris.target
mask = y < 2 # 仅保留类别 0(Setosa)和 1(Versicolor)
X = X[mask]
y = y[mask]
# 2. 划分训练集与测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
# 3. 训练决策树分类器(基尼系数、最大深度=3)
clf = DecisionTreeClassifier(criterion='gini', max_depth=3, random_state=42)
clf.fit(X_train, y_train)
# 4. 绘制决策边界
# 定义绘图区间
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(
np.linspace(x_min, x_max, 200),
np.linspace(y_min, y_max, 200)
)
# 预测整个网格点
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.figure(figsize=(8, 6))
plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.Paired)
# 标注训练与测试样本
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, edgecolor='k', s=50, label='训练集')
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, marker='s', edgecolor='k', s=50, label='测试集')
plt.xlabel('萼片长度 (cm)')
plt.ylabel('萼片宽度 (cm)')
plt.title('决策树决策边界 (Depth=3)')
plt.legend()
plt.grid(True)
plt.show()
解释:
DecisionTreeClassifier(criterion='gini', max_depth=3)
表示使用基尼系数作为划分指标,最大树深不超过 3。contourf
用于绘制决策边界网格,网格中每个点通过训练好的分类器预测类别。- 决策边界呈阶梯状或矩形块,反映二叉树在二维空间的一系列垂直/水平切分。
决策树结构图解
要直观查看决策树的分裂顺序与阈值,可使用 sklearn.tree.plot_tree
函数绘制树结构:
from sklearn.tree import plot_tree
plt.figure(figsize=(8, 6))
plot_tree(
clf,
feature_names=iris.feature_names[:2],
class_names=iris.target_names[:2],
filled=True,
rounded=True,
fontsize=8
)
plt.title('Decision Tree Structure')
plt.show()
图示说明:
- 每个节点显示“特征 [f] <= 阈值 [t]”、“节点样本数量”、“各类别样本数量(class counts)”以及该节点的基尼值或熵值;
filled=True
会根据类别分布自动配色,纯度越高颜色越深;- 最终叶节点标注预测的类别(多数投票结果)。
关键技术细节深入剖析
划分点(Threshold)搜索策略
候选阈值
- 对于给定特征 $f$,首先对该维度的训练样本值进行排序:$v\_1 \le v\_2 \le \dots \le v\_m$。
可能的划分阈值通常取相邻两个不同值的中点:
$$ \theta_{i} = \frac{v_i + v_{i+1}}{2}, \quad i = 1,2,\dots,m-1. $$
- 每个阈值都可将样本分为左右两部分,并计算划分后纯度改善(如基尼减少量)。
时间复杂度
- 单个特征上,排序耗时 $O(m \log m)$,遍历所有 $m-1$ 个阈值计算纯度约 $O(m)$,总计 $O(m \log m + m) \approx O(m \log m)$。
- 若当下节点样本数为 $n$,总特征维度为 $d$,则基于纯排序的划分搜索总复杂度约 $O(d , n \log n)$。
- 在实际实现中,可重用上层节点的已排序数组,并做“增量更新”,降低总体复杂度。
离散特征与缺失值
- 若特征为离散型(categorical),阈值对应的是“某一类别集合”与其补集,需判断各类别子集划分带来纯度变化,计算量急剧增多,常采用贪心或基于信息增益进行快速近似。
- 对缺失值,可在划分时将缺失样本同时分给左右子节点,再在下游节点中决定。
多分类决策树与回归树
多分类决策树
对于 $K$ 类问题,基尼系数与信息增益都可以直接推广:
$$ G(D) = 1 - \sum_{k=1}^K p_k^2,\quad H(D) = -\sum_{k=1}^K p_k \log_2 p_k. $$
- 划分后依旧根据各子集的类别分布计算加权纯度。
- 叶节点的预测标签为该叶节点中出现频率最高的类别。
回归树(Regression Tree)
- 回归问题中,目标变量连续,节点纯度用方差或平均绝对误差衡量。
均方差减少(MSE Reduction)常用:
$$ \text{Var}(D) = \frac{1}{|D|} \sum_{i \in D} (y_i - \bar{y})^2,\quad \bar{y} = \frac{1}{|D|} \sum_{i \in D} y_i. $$
划分时,计算:
$$ \Delta \text{Var} = \text{Var}(D) - \left( \frac{|D_L|}{|D|} \text{Var}(D_L) + \frac{|D_R|}{|D|} \text{Var}(D_R) \right). $$
- 叶节点预测值取训练样本的均值 $\bar{y}$。
剪枝超参数与模型选择
常见超参数
max_depth
:树的最大深度。min_samples_split
:分裂节点所需的最小样本数(只有不低于该数才允许继续分裂)。min_samples_leaf
:叶节点所需的最小样本数。max_leaf_nodes
:叶节点数量上限。ccp_alpha
:代价复杂度剪枝系数,$ \alpha > 0$ 时启用后剪枝。
交叉验证选参
- 可对上述参数做网格搜索或随机搜索,结合 5 折/10 折交叉验证,通过验证集性能(如准确率、F1)选择最佳超参数组合。
- 代价复杂度剪枝常通过
DecisionTreeClassifier(ccp_alpha=…)
设置并利用clf.cost_complexity_pruning_path(X_train, y_train)
获得不同 $\alpha$ 对应的子树性能曲线。
剪枝示例代码片段
from sklearn.tree import DecisionTreeClassifier # 获取不同 alpha 对应的子树有效节点编号 clf0 = DecisionTreeClassifier(random_state=42) clf0.fit(X_train, y_train) path = clf0.cost_complexity_pruning_path(X_train, y_train) ccp_alphas, impurities = path.ccp_alphas, path.impurities # 遍历多个 alpha,绘制精度随 alpha 变化曲线 clfs = [] for alpha in ccp_alphas: clf = DecisionTreeClassifier(random_state=42, ccp_alpha=alpha) clf.fit(X_train, y_train) clfs.append(clf) # 在验证集或交叉验证上评估 clfs,选出最佳 alpha
决策树优缺点与应用场景
优点
- 可解释性强:树状结构直观,易于可视化与理解。
- 无需太多数据预处理:对数据归一化、标准化不敏感;能自动处理数值型与分类型特征。
- 非线性建模能力:可拟合任意形状的决策边界,灵活强大。
- 处理缺失值 & 异常值:对缺失值和异常值有一定鲁棒性。
缺点
- 易过拟合:若不做剪枝或限制参数,容易产生不泛化的深树。
- 对噪声敏感:数据噪声及少数异常会显著影响树结构。
- 稳定性差:数据稍微改变就可能导致树的分裂结构大幅变化。
- 贪心算法:只做局部最优划分,可能错失全局最优树。
应用场景
- 金融风控:信用评分、欺诈检测。
- 医疗诊断:疾病风险分类。
- 营销推荐:用户分群、消费预测。
- 作为集成学习基模型:随机森林(Random Forest)、梯度提升树(Gradient Boosting Tree)等。
总结与延伸阅读
本文从决策树的基本构建思路出发,详细讲解了信息增益与基尼系数等划分指标,介绍了递归生长与剪枝策略,并结合 Iris 数据集的示例代码与可视化图解,让你直观感受决策树是如何在二维空间中划分不同类别的区域,以及树结构内部的决策逻辑。
核心要点:
- 决策树本质为一系列特征阈值判断的嵌套结构。
- 划分指标(信息增益、基尼系数)用于度量划分后节点“更纯净”的程度。
- 过深的树容易过拟合,需要使用预剪枝或后剪枝控制。
- 决策边界是分段式的矩形(或多维立方体)区域,非常适合解释,但在高维或复杂边界下需增强(如集成方式)提升效果。
延伸阅读与学习资源:
- Breiman, L., Friedman, J.H., Olshen, R.A., Stone, C.J. “Classification and Regression Trees (CART)”, 1984.
- Quinlan, J.R. “C4.5: Programs for Machine Learning”, Morgan Kaufmann, 1993.
- Hastie, T., Tibshirani, R., Friedman, J. “The Elements of Statistical Learning”, 2nd Edition, Springer, 2009.(第 9 章:树方法)
- Liu, P., 《机器学习实战:基于 Scikit-Learn 与 TensorFlow》, 人民邮电出版社,2017。
scikit-learn 官方文档 DecisionTreeClassifier 与 plot\_tree:
评论已关闭