决策树学习笔记
决策树学习笔记
1. 什么是决策树?
决策树(Decision Tree)是一种基本的分类与回归方法。它是一种监督学习算法,其模型呈树形结构,可以看作是基于特征对实例进行分类或回归的过程。
- 结构:
- 根节点 (Root Node): 包含样本全集。
- 内部节点 (Internal Node): 代表一个特征或属性的测试。
- 分支 (Branch) / 边 (Edge): 代表测试的输出(特征的某个值或范围)。
- 叶节点 (Leaf Node) / 终端节点 (Terminal Node): 代表最终的决策结果(类别或数值)。
- 目标: 生成一棵泛化能力强,即处理未见示例能力强的决策树。
- 决策过程: 从根节点开始,根据实例的特征值,沿着树的分支向下移动,直到到达叶节点,该叶节点的类别或值即为预测结果。
2. 决策树学习原理
决策树学习的本质是从训练数据中归纳出一组分类规则,或者说是由训练数据集估计条件概率模型。其核心思想是 “分而治之” (Divide and Conquer)。
学习过程是一个 递归地选择最优特征,并根据该特征对训练数据进行分割,使得各个子数据集有一个最好的分类的过程。
2.1 学习步骤概览
- 开始: 构建根节点,所有训练数据都放在根节点。
- 特征选择: 选择一个最优特征,按照该特征将训练数据集分割成子集,使得各个子集在当前条件下有最好的分类。
- 生成子节点: 如果某个子集已能够被基本正确分类(达到停止条件),则构建叶节点,并将这些子集分到所对应的叶节点中去。
- 递归: 如果子集不能被基本正确分类,则对这些子集选择新的最优特征,继续对其进行分割,构建相应的节点。
- 结束: 递归地进行步骤 2-4,直到所有训练数据子集都被基本正确分类,或者没有合适的特征为止。
2.2 如何选择最优特征?—— 划分选择
选择最优特征是决策树学习的关键。目标是选择一个特征进行划分后,各子集的“纯度” (Purity) 最高。纯度越高,意味着子集中的样本尽可能属于同一类别。
常用的衡量纯度的指标有:
a) 信息熵 (Information Entropy)
熵是度量随机变量不确定性的指标。熵越大,随机变量的不确定性就越大,纯度越低。
假设当前样本集合 D 中第 k 类样本所占的比例为 p_k (k=1, 2, …, |Y|,|Y|是类别总数),则 D 的信息熵定义为:
Ent(D)的值越小,D的纯度越高。
b) 信息增益 (Information Gain) - ID3 算法
信息增益表示得知特征 A 的信息而使得数据集 D 的不确定性减少的程度。选择信息增益最大的特征作为划分特征。
假设用离散特征 A 对样本集 D 进行划分,A 有 V 个可能的取值 {a¹, a², ..., aᵛ}。若使用 A 来对 D 进行划分,则会产生 V 个分支节点,其中第 v 个分支节点包含了 D 中所有在特征 A 上取值为 aᵛ 的样本,记为 Dᵛ。
我们可以计算出用特征 A 对 D 进行划分所获得的“信息增益”:
|Dᵛ| / |D|是分支v的权重,样本数越多的分支节点影响越大。Ent(Dᵛ)是分支节点v的信息熵。- ID3 算法 就是以信息增益为准则来选择划分属性的决策树算法。
缺点: 信息增益准则对可取值数目较多的特征有所偏好(例如,如果一个特征是 ID,那么每个样本一个取值,划分出的每个子集纯度都是最高的,信息增益会很大,但这没有泛化能力)。
c) 增益率 (Gain Ratio) - C4.5 算法
为了减少信息增益对多取值特征的偏好,C4.5 算法 使用“增益率”来选择最优划分特征。
增益率定义为:
其中 IV(A) 称为特征 A 的“固有值” (Intrinsic Value):
- 特征
A的可能取值数目越多(即V越大),IV(A)的值通常会越大。
注意: 增益率准则对可取值数目较少的特征有所偏好。因此 C4.5 算法并非直接选择增益率最大的特征,而是使用一个启发式:先从候选划分特征中找出信息增益高于平均水平的特征,再从中选择增益率最高的。
d) 基尼指数 (Gini Index) - CART 算法
CART (Classification and Regression Tree) 算法使用“基尼指数”来选择划分属性。
数据集 D 的纯度也可以用基尼值来度量:
Gini(D)反映了从数据集D中随机抽取两个样本,其类别标记不一致的概率。Gini(D)越小,数据集D的纯度越高。
特征 A 的基尼指数定义为 (假设 A 是离散特征,有 V 个取值):
- 选择那个使得划分后基尼指数最小的特征作为最优划分特征,即
A_* = arg min_{A} Gini_index(D, A)。
特点: CART 生成的是 二叉树。对于连续特征,它会尝试所有可能的二分点;对于离散特征,它也会找出最优的二分组合。
2.2.1 连续特征的处理
在决策树中,特征可以是离散的(类别型)或连续的。对于连续特征,决策树的算法需要确定一个分割点,即根据特征值的大小将数据划分为两部分。
-
对于连续特征 (A),先对所有样本按该特征值排序,记排序后的不同取值为
-
决策树在构建过程中会选择一个合适的分割点
t(即A ≤ t)来将数据分为两部分 -
选择
t的方法通常是通过计算划分后的纯度指标(如信息增益、增益率、基尼指数或其他标准),选出最优的 。
2.2.2 线搜索 (Line Search) 过程
线搜索(Line Search)是一种通过遍历所有可能的分割点来寻找最优分割点的过程。对于每个连续特征,我们会按照其特征值进行排序,然后计算每个分割点的纯度(例如信息增益或基尼指数),并选择使得纯度最大化的分割点。
2.2.2 二类分类中三种纯度度量的关系
-
基尼指数
其中
p和1-p分别是属于两个类别的样本的比例。当p或1-p趋近于 0 时,基尼指数会接近 0,说明数据集非常纯净。 -
熵
同样,当
p或1-p为 0 或 1 时,熵的值接近 0,表明数据集是纯净的。 -
分类误差率
它的特点是非常直观,但对于不平衡数据(例如,一个类别占大多数)可能会给出较差的评估。
熵和基尼指数都能很好地衡量不确定性,二者通常产生相似的结果。
分类误差率通常不会作为选择特征的标准,因为它在纯度较高时变化较小,无法敏感地反映数据的变化。
对二分类问题,三者随 的曲线关系为

2.3 停止条件
递归划分过程何时停止?
- 当前节点包含的样本全属于同一类别: 无需划分,该节点成为叶节点。
- 当前属性集为空,或者所有样本在所有属性上取值相同: 无法划分,将该节点标记为叶节点,类别设定为该节点所含样本最多的类别(或根据具体问题定义)。
- 当前节点包含的样本集合为空: 不能划分,标记为叶节点,类别设定为其父节点所含样本最多的类别。
2.4 缺失值的处理
在决策树学习中,处理缺失值是一个需要解决的问题。不同的算法有不同的处理方式。
2.4.1 在算法层面如何处理缺失值
有几种常见的方法来处理缺失值:
-
数据插补: 在训练决策树之前,将缺失的值使用该特征的均值、中位数或众数进行填充。
注意: scikit-learn 的决策树实现不内置对缺失值的处理,需要用户在训练前进行预处理(如使用
SimpleImputer)。 -
忽略样本: 直接忽略包含缺失值的样本。如果缺失值不多,这可能是一个简单有效的方案,但如果缺失样本较多,可能丢失大量信息。
-
基于树的缺失值处理方法(某些算法支持): 一些决策树实现(如 C4.5)允许在划分时处理缺失值。
- 计算纯度时: 仅使用该特征上非缺失的样本子集来计算信息增益(或其他纯度指标)。
- 划分样本时: 对于在当前划分特征上有缺失值的样本,可以采用以下策略分配到子节点:
- 将其分配到最有可能的分支(例如,根据已知值样本在各分支的比例)。
- 将其分配到多个子节点,并赋予不同的权重。例如,如果一个样本在该特征上缺失,且已知值样本根据该特征分成了 A、B 两个分支,已知值样本中 70%去了 A,30%去了 B,那么可以将这个缺失值样本以 0.7 的权重分到 A 分支,以 0.3 的权重分到 B 分支。
- 将“缺失”本身视为一个独立的特征取值或分支。
2.4.2 如何选择划分特征(当特征有缺失值时)
当考虑用一个有缺失值的特征进行划分时,传统的纯度计算方法需要调整:
- 计算缺失值的概率: 可以在计算纯度指标时,对特征的缺失值情况进行惩罚。例如,C4.5 在计算增益率时会考虑样本在当前特征上已知值的比例。
- 调整纯度计算: 只使用该特征上非缺失值的样本来计算纯度(如信息增益、基尼指数)。计算信息增益时,通常会乘以一个系数,这个系数等于当前节点中该特征非缺失样本的比例。
2.5 处理过拟合 (剪枝与 Bagging)
为了防止决策树 过拟合 (Overfitting),即模型在训练数据上表现很好,但在新数据上表现差,需要进行剪枝。过拟合通常是因为树生长得过于复杂,学习了训练数据中过多的噪声或特性。
a) 预剪枝 (Pre-pruning)
在决策树生成过程中,对每个节点在划分前先进行估计。若当前节点的划分不能带来决策树泛化性能提升(例如,在验证集上精度下降),则停止划分并将当前节点标记为叶节点。
- 优点: 降低过拟合风险,减少训练时间和测试时间开销。
- 缺点: 基于“贪心”本质,可能带来欠拟合风险(有些划分暂时看可能不优,但后续划分可能显著提升性能)。
常用判断条件:
- 节点内样本数量小于阈值 (
min_samples_split,min_samples_leaf)。 - 树的深度达到预设值 (
max_depth)。 - 划分后信息增益(或其他指标)的提升小于阈值。
- 划分后在独立的验证集上精度下降。
b) 后剪枝 (Post-pruning)
先从训练集生成一棵完整的决策树,然后自底向上地对非叶节点进行考察。若将该节点对应的子树替换为叶节点能带来决策树泛化性能提升,则将该子树替换为叶节点。
- 优点: 通常比预剪枝保留了更多分支,欠拟合风险小,泛化性能往往优于预剪枝决策树。
- 缺点: 训练时间开销比未剪枝和预剪枝决策树都要大得多。
常用方法:
- 降低错误剪枝 (Reduced Error Pruning, REP): 使用验证集,将子树替换为叶节点后,如果验证集错误率降低或不变,则剪枝。
- 代价复杂度剪枝 (Cost Complexity Pruning, CCP): 定义损失函数=经验熵+正则化项(树的复杂度),通过调整正则化系数
α(ccp_alpha) 来权衡模型复杂度和拟合度,生成一系列树,最后通过交叉验证选择最优子树。Scikit-learn 中常用此方法。
c) Bagging (Bootstrap Aggregating)
除了传统的预剪枝和后剪枝,我们还可以使用 Bagging (Bootstrap Aggregating) 技术来处理过拟合。Bagging 是通过从原始训练集中进行有放回抽样(Bootstrap),生成多个不同的训练集,然后基于每个训练集独立地训练一棵决策树,并将它们的预测结果进行集成(例如,分类任务投票,回归任务平均)来减少过拟合风险。
- 思想: 对训练集做多次有放回抽样,训练多棵决策树,将其预测结果取平均(回归)或投票(分类)。
- 优点: 显著降低高方差,缓解单棵树过拟合;增强了模型的稳定性;并行化简单。
- 缺点: 牺牲了模型的解释性(因为是多个树的集成)。
- 常用实现:
sklearn.ensemble.BaggingClassifier(分类)和sklearn.ensemble.BaggingRegressor(回归),以及特别针对决策树的 随机森林(Random Forest) (sklearn.ensemble.RandomForestClassifier/Regressor)。随机森林在 Bagging 的基础上进一步引入了特征随机性(在每个节点划分时只考虑特征的一个随机子集),进一步提升了性能和鲁棒性。。
2.6 二叉树和多叉树
2.6.1 二叉树与多叉树的区别
- 二叉树:每个节点最多有两个子节点(通常表示为左右子节点),即每个决策都只能有两个结果。CART 算法生成的树通常是二叉树。
- 多叉树:每个节点可以有多个子节点,表示每次决策有多个选择。ID3 和 C4.5 算法可以生成多叉树(对于离散特征,每个取值对应一个分支)。
2.6.2 选择二叉树还是多叉树
- 二叉树 在算法实现上通常更简单,且对计算机资源的消耗更少。许多库(包括 scikit-learn)默认或只支持二叉树。
- 多叉树 能处理更多的选择,但计算复杂度较高。对于离散特征,多叉划分虽然直观,但在某些情况下(如特征取值非常多)可能不够灵活。二叉树可以通过多次二分裂来模拟多叉分裂。
2.7 二分类和多分类
2.7.1 二分类
决策树在二分类问题中,通过划分使得子节点的样本尽可能只属于两个类别中的一个。可以使用信息增益、增益率或基尼指数作为纯度标准。常见的二分类问题如癌症检测、垃圾邮件分类等。
2.7.2 多分类
对于多分类问题,决策树会通过逐步划分的方式将样本集划分成多个子集。对于每个子集,决策树会重复上述过程,直到所有的样本被完全分类。可以使用 一对一 或 一对多 的方法来构建多分类树。纯度计算方法(信息熵、基尼指数等)自然地扩展到多类别情况。
3. 决策树的优缺点
3.1 优点
- 易于理解和解释: 可以可视化,符合人类的直观思维。
- 数据预处理要求低: 不需要数据归一化或标准化(但处理缺失值仍需注意)。可以同时处理数值型和类别型数据(不同算法支持度不同)。
- 能够处理多输出问题。
- 计算复杂度相对较低: 预测阶段的复杂度是 O(log₂m),m 是样本数。
- 可以验证模型: 使用统计测试来验证模型的可靠性。
- 鲁棒性: 对于缺失值不敏感(某些算法如 C4.5 可以处理,但 scikit-learn 需要预处理)。
3.2 缺点
- 容易过拟合: 尤其是在数据有噪声或样本量不足时,可能生成过于复杂的树。需要剪枝或集成方法来缓解。
- 不稳定性: 数据微小的变动可能导致生成完全不同的树。可以通过集成方法(如随机森林)改善。
- 最优决策树学习是 NP 难问题: 实际算法通常采用启发式方法(如贪心算法),只能得到局部最优解。
- 可能创建有偏的树: 如果某些类别的样本数量远多于其他类别,生成的树可能会偏向于这些数量多的类别。建议先平衡数据集。
- 对线性关系表达能力弱: 对于变量间存在复杂线性关系的问题,决策树可能需要很深的树才能拟合。
4. 使用 Scikit-learn 实现决策树 (Iris 数据集示例)
我们将使用经典的 Iris (鸢尾花) 数据集来演示如何用 Python 的 Scikit-learn 库构建决策树分类器。
目标: 根据鸢尾花的花萼长度 (Sepal Length)、花萼宽度 (Sepal Width)、花瓣长度 (Petal Length)、花瓣宽度 (Petal Width) 这四个特征来预测鸢尾花的种类 (Setosa, Versicolour, Virginica)。
4.0 决策树在 Scikit-learn 中的主要参数、属性与方法
在 Scikit-learn 中,DecisionTreeClassifier 和 DecisionTreeRegressor 类提供了多种超参数来调整决策树的构建方式。
- 参数(
DecisionTreeClassifier/DecisionTreeRegressor)criterion:用于划分的标准。
分类树可选'gini'(基尼指数)或'entropy'(信息增益)。
回归树可选'mse'(均方误差, 已废弃, 推荐使用'squared_error'),'friedman_mse'或'mae'(平均绝对误差)。
默认是分类树'gini',回归树'squared_error'。splitter:划分策略。'best'表示选择最好的特征进行划分(默认)。
'random'表示随机选择部分特征后再从中选择最好的进行划分(随机森林常用)适用于样本量非常大的情况。max_depth: 决策树的最大深度。控制树的复杂度,避免过拟合(预剪枝)。默认None,表示不限制深度。min_samples_split:内部节点再划分所需最小样本数。
较高的值有助于避免过拟合(预剪枝)。可以是整数或小数(表示比例)。默认是 2。min_samples_leaf:叶子节点最少样本数。
一个分割点只有在左右分支都满足这个最少样本数条件时,才能进行划分(预剪枝)。可以是整数或小数。默认是 1。min_weight_fraction_leaf:叶子节点最小的样本权重和,小于这个值会和兄弟节点一起被剪枝,默认为0不考虑权重,当较多样本有缺失值或样本分布类别偏差很大需要考虑max_features:每次分割考虑的最大特征数(随机森林常用)。
可以是整数、浮点数(比例)、"auto"/"sqrt"(等于sqrt(n_features))、"log2"(等于log2(n_features)) 或None(考虑所有特征)。max_leaf_nodes:最大叶子节点数,可以防止过拟合,默认Nonemin_impurity_decrease:分裂所减小的不纯度小于等于该值才会分裂min_impurity_split:建树时候早停的基尼不纯度阈值,限制决策树的增长class_weight:类别样重,指定样本各类别的权重ccp_alpha:代价复杂度剪枝参数(后剪枝)。取值越大,剪枝越强。默认是 0.0 (不剪枝)。
- 重要属性 (训练后可用)
feature_importances_: 一个 numpy 数组,表示每个特征的重要性评分(总和为 1)。重要性基于该特征在树中降低纯度(信息增益或基尼不纯度)的总量。tree_:底层的_tree.Tree对象,包含了树结构的详细信息(节点索引、左右子节点、特征索引、阈值、不纯度等)。n_features_in_: 训练时输入的特征数量。classes_: 分类树独有,训练期间遇到的类标签数组。n_classes_: 分类树独有,类别的数量。、n_outputs_: 模型输出的数量(对于多输出问题)。n_node_samples_: 每个节点中的训练样本数量数组。
- 常用方法
.fit(X, y):使用训练数据X和目标变量y训练决策树模型。.predict(X):对新数据X进行预测。分类树返回类别标签,回归树返回预测值。.predict_proba(X):分类树独有,预测每个样本属于各个类别的概率。.score(X, y):返回模型的评估分数。分类树返回在(X, y)上的准确率,回归树返回 分数。apply(X): 返回每个样本所属的叶节点索引。get_params([deep]): 获取模型的超参数。set_params(**params): 设置模型的超参数。export_text(decision_tree[, …])、将决策树导出为文本规则表示。plot_tree(decision_tree[, …]):将决策树可视化(需要 Matplotlib)。
4.1 导入所需库
1 | import pandas as pd |
4.2 加载和准备数据
1 | # 加载 Iris 数据集 |
4.3 创建和训练决策树模型
1 | # 创建决策树分类器实例 |
4.4 模型预测与评估
1 | # 使用训练好的模型对测试集进行预测 |
4.5 可视化决策树
理解决策树如何做出决策的一个好方法是将其可视化。
a) 使用 plot_tree (需要 Matplotlib)
1 | plt.figure(figsize=(20, 10)) # 设置图形大小 |
b) 使用 export_text 输出文本表示
1 | tree_rules = export_text(dt_classifier, feature_names=list(feature_names)) |
4.6 探索剪枝 (示例)
尝试添加预剪枝参数,例如限制最大深度。
1 | # 创建带预剪枝的决策树 |
剪枝后的树通常更简单,有时准确率可能会略有下降,但泛化能力可能更好(在这个简单的 Iris 数据集上可能不明显)。
5. 决策树回归
决策树不仅能用于分类问题,还能用于回归问题。**
5.1 最小二乘回归树
对于回归问题,决策树会在每个叶节点保存该节点的均值。在构建过程中,决策树将选择能够最小化节点内部 均方误差(MSE) 的特征和分割点作为划分标准。
-
目标: 通过递归划分,使得每个子集(节点)内的目标值(y)尽可能接近其平均值,从而最小化整体的平方误差。
-
损失函数: 用于衡量一个节点(数据集 D)的“不纯度”或方差,即节点内所有样本目标值与该节点目标值均值之间的平方误差之和。
其中, 是节点 D 中第 i 个样本的真实目标值, 是节点 D 中所有样本目标值的均值。
-
划分标准: 在选择特征和分割点时,回归树会遍历所有可能的特征和分割点,计算按该点划分后,左右子节点加权后的总均方误差(或称为平方误差的减少量)。选择使总均方误差最小的划分点。
其中, 是特征, 是分割点, 和 是划分后的两个子集, 和 分别是子集 和 的目标值均值。
-
实现示例: Scikit-learn 中使用
DecisionTreeRegressor类。1
2
3
4
5
6
7
8
9
10
11
12
13from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_squared_error, r2_score
# 假设 y_train_continuous 是连续的目标值
# reg = DecisionTreeRegressor(criterion='squared_error', max_depth=5, random_state=42)
# reg.fit(X_train, y_train_continuous)
# y_pred_reg = reg.predict(X_test)
# 评估回归模型
# mse = mean_squared_error(y_test_continuous, y_pred_reg)
# r2 = r2_score(y_test_continuous, y_pred_reg)
# print(f"\n回归树 MSE: {mse:.4f}, R2: {r2:.4f}") -
评估指标: 均方误差(MSE)、均方根误差(RMSE)、 分数。
5.2 分类树与回归树的区别
- 目标变量类型:
- 分类树: 离散型(类别标签)。
- 回归树: 连续型(数值)。
- 叶节点输出:
- 分类树: 叶节点通常代表一个类别标签(该节点中样本最多的类别)或类别的概率分布。
- 回归树: 叶节点代表一个预测数值,通常是该节点中所有样本目标值的均值。
- 划分标准/损失函数:
- 分类树: 使用衡量“纯度”的指标,如信息增益、增益率、基尼指数,目标是最大化纯度(最小化不纯度)。
- 回归树: 使用衡量“方差”或“误差”的指标,如均方误差(MSE)或平均绝对误差(MAE),目标是最小化节点内误差。
| 对比维度 | 分类树(Classification Tree) | 回归树(Regression Tree) |
|---|---|---|
| 目标变量类型 | 离散型(类别标签) | 连续型(数值) |
| 叶节点输出 | 类别标签(或类别概率分布) | 预测数值(如均值、中位数等) |
| 划分标准/损失函数 | 信息增益、增益率、基尼指数(衡量纯度) | 均方误差(MSE)、平均绝对误差(MAE)(衡量误差) |
| 适用问题类型 | 分类问题(如判断是否购买商品) | 回归问题(如预测房价、温度等) |
| 示例应用场景 | 客户流失预测、垃圾邮件识别 | 房价预测、销售额预测 |
6. 小结
决策树是一种强大且直观的机器学习模型。理解其核心原理(信息熵、信息增益、基尼指数)、构建过程(递归划分)以及防止过拟合的方法(剪枝)至关重要。通过 Scikit-learn 等库可以方便地实现和应用决策树解决分类和回归问题。