当前位置: 首页 > news >正文

数据分析中的决策树算法是如何工作的?有哪些优缺点?

决策树算法详解

1. 核心思想

决策树通过递归分裂将特征空间划分为若干矩形区域,每个区域对应一个预测值。

直观理解:二十个问题游戏 你在想一个动物? ├── 它是哺乳动物吗? │ ├── 是 → 它会汪汪叫吗? │ │ ├── 是 → 🐕 狗 │ │ └── 否 → 它会喵喵叫吗? │ │ ├── 是 → 🐱 猫 │ │ └── 否 → 🐄 牛 │ └── 否 → 它会飞吗? │ ├── 是 → 🦅 鸟 │ └── 否 → 🐟 鱼

2. 工作原理

2.1 树的结构

年龄 ≤ 35? ╱ ╲ 是 否 ╱ ╲ 收入 > 50k? 收入 > 80k? ╱ ╲ ╱ ╲ 是 否 是 否 ╱ ╲ ╱ ╲ [购买] [不买] [购买] [不买] 内部节点(Internal Node): 特征判断条件 分支(Branch): 判断结果的路径 叶子节点(Leaf Node): 预测结果(类别或数值) 根节点(Root): 第一个分裂点 深度(Depth): 从根到最深叶子的层数

2.2 构建过程(核心三步)

Step 1: 选择最优分裂特征和分裂点 Step 2: 按分裂条件将数据分成子集 Step 3: 对每个子集递归重复 Step 1~2,直到满足停止条件

关键问题:如何选择"最优"分裂?→ 用纯度度量指标


3. 分裂标准:纯度度量

3.1 信息增益(ID3 算法)

信息熵衡量数据的不确定性:

Entropy(S) = -Σ pᵢ × log₂(pᵢ) 其中 pᵢ = 第 i 类的比例 例: 6 个正例,4 个负例 Entropy = -(0.6×log₂0.6 + 0.4×log₂0.4) = -(0.6×(-0.737) + 0.4×(-1.322)) = 0.971
熵的直觉: 纯数据 [10正, 0负] → Entropy = 0 (完全确定) 混数据 [5正, 5负] → Entropy = 1 (最不确定) 偏数据 [8正, 2负] → Entropy = 0.722 (较确定)

信息增益= 分裂前的熵 - 分裂后的加权平均熵:

Gain(S, A) = Entropy(S) - Σ (|Sᵥ|/|S|) × Entropy(Sᵥ) 例: 按"年龄"分裂 分裂前: Entropy = 0.971 (6正4负) 年龄 ≤ 35: 3正3负 → Entropy = 1.0 (权重 6/10) 年龄 > 35: 3正1负 → Entropy = 0.811 (权重 4/10) 分裂后加权熵 = 0.6×1.0 + 0.4×0.811 = 0.924 信息增益 = 0.971 - 0.924 = 0.047 选择信息增益最大的特征作为分裂特征

3.2 信息增益率(C4.5 算法)

信息增益偏向取值多的特征,增益率进行修正:

SplitInfo(S, A) = -Σ (|Sᵥ|/|S|) × log₂(|Sᵥ|/|S|) ← 固有属性熵 GainRatio(S, A) = Gain(S, A) / SplitInfo(S, A) 例: 特征"用户ID"有 1000 个不同值 信息增益很高(每个值都能完美分类)→ 但无意义 SplitInfo 也很高 → 增益率被拉低 → 避免选择这类特征

3.3 基尼指数(CART 算法)

最常用的标准,scikit-learn 默认使用:

Gini(S) = 1 - Σ pᵢ² 例: 6 正 4 负 Gini = 1 - (0.6² + 0.4²) = 1 - 0.52 = 0.48 直觉: 纯数据 [10正, 0负] → Gini = 1 - 1² = 0 (最纯) 混数据 [5正, 5负] → Gini = 1 - 0.5 = 0.5 (最不纯)

基尼增益

ΔGini = Gini(分裂前) - Σ (|Sᵥ|/|S|) × Gini(Sᵥ) 选择基尼增益最大的特征和分裂点

3.4 三种标准对比

标准算法适用任务特点
信息增益ID3分类(离散特征)偏向多值特征
增益率C4.5分类(连续+离散)修正多值偏好
基尼指数CART分类+回归计算快,sklearn 默认
MSE/MAECART回归方差减少量

回归树的分裂标准

选择分裂点使子集的 MSE 最小: MSE(S) = (1/|S|) × Σ(yᵢ - ȳ)² 分裂目标: 最小化 Σ (|Sᵥ|/|S|) × MSE(Sᵥ) 叶子预测值 = 该叶子内所有样本的均值

4. 完整构建示例

4.1 分类树

数据集: 是否购买产品(10 条样本) ID 年龄 收入 学生 信用 购买 1 青 高 否 良 否 2 青 高 否 优 否 3 中 高 否 良 是 4 老 中 否 良 是 5 老 低 是 良 是 6 老 低 是 优 否 7 中 低 是 优 是 8 青 中 否 良 否 9 青 低 是 良 是 10 老 中 是 优 是 Step 1: 计算各特征的基尼增益 收入特征: 高: [1是, 2否] → Gini = 1 - (1/3)² - (2/3)² = 0.444 中: [3是, 0否] → Gini = 0 低: [3是, 1否] → Gini = 0.375 加权 Gini = 0.3×0.444 + 0.3×0 + 0.4×0.375 = 0.283 ΔGini = 0.48 - 0.283 = 0.197 学生特征: 是: [4是, 1否] → Gini = 0.32 否: [2是, 3否] → Gini = 0.48 加权 Gini = 0.5×0.32 + 0.5×0.48 = 0.40 ΔGini = 0.48 - 0.40 = 0.08 → 收入的增益最大,选收入作为根节点分裂特征 Step 2: 递归构建子树... 最终树: 收入 ╱ | ╲ 高 中 低 ╱ ╲ │ ╱ ╲ 学生? [是] 学生? 信用? ╱ ╲ ╱ ╲ ╱ ╲ [否] [是] [是] [否] [是] [否]

4.2 连续特征的分裂

年龄是连续值,如何选分裂点? 1. 将年龄排序: 22, 25, 28, 35, 42, 50, 58, 63 2. 取相邻值的中点作为候选分裂点: 23.5, 26.5, 31.5, 38.5, 46, 54, 60.5 3. 对每个候选点计算基尼增益 4. 选增益最大的点作为最终分裂点 例: 年龄 ≤ 31.5 → Gini增益 = 0.12 年龄 ≤ 38.5 → Gini增益 = 0.18 ← 最优 年龄 ≤ 46 → Gini增益 = 0.09 → 选择 38.5 作为分裂点

5. 停止条件与剪枝

5.1 停止条件(预剪枝)

构建时提前停止,防止树过长: ├── 叶子样本数 < min_samples_split(默认 2) ├── 叶子样本数 < min_samples_leaf(默认 1) ├── 树深度 ≥ max_depth(默认不限制) ├── 纯度增益 < min_impurity_decrease(默认 0) └── 叶子中样本全属于同一类
fromsklearn.treeimportDecisionTreeClassifier tree=DecisionTreeClassifier(max_depth=5,# 最大深度min_samples_split=20,# 分裂所需最少样本数min_samples_leaf=10,# 叶子最少样本数min_impurity_decrease=0.01,# 最小纯度增益max_leaf_nodes=50# 最大叶子数)

5.2 后剪枝(更优但更复杂)

先构建完整树,再从底部开始剪去不提升验证集性能的子树 代价复杂度剪枝(Cost-Complexity Pruning): Rα(T) = R(T) + α × |T| R(T) = 训练误差 |T| = 叶子节点数 α = 复杂度惩罚参数 α = 0 → 不剪枝(完整树) α → ∞ → 只剩根节点 通过交叉验证选择最优 α
# sklearn 的代价复杂度剪枝tree=DecisionTreeClassifier(random_state=42)path=tree.cost_complexity_pruning_path(X_train,y_train)ccp_alphas=path.ccp_alphas# 一系列 α 值# 对每个 α 训练树,交叉验证选最优trees=[]foralphainccp_alphas:t=DecisionTreeClassifier(ccp_alpha=alpha,random_state=42)trees.append(t)scores=[cross_val_score(t,X_train,y_train,cv=5).mean()fortintrees]best_alpha=ccp_alphas[np.argmax(scores)]

5.3 预剪枝 vs 后剪枝

维度预剪枝后剪枝
策略构建时提前停止构建完再回溯剪枝
计算量大(需建完整树)
效果可能欠拟合(视野短浅)通常更好(全局视角)
实用性简单,常用更优,sklearn 支持

6. 优缺点分析

6.1 优点

1. 高度可解释 ├── 决策路径清晰,可翻译为 if-else 规则 ├── 非技术人员也能理解 └── 适合需要解释的场景(金融风控、医疗诊断) 2. 数据预处理要求低 ├── 不需要特征标准化/归一化 ├── 天然处理混合类型特征(数值+类别) ├── 对缺失值有一定容忍度 └── 不受单调变换影响(如 log(x) 和 x 效果相同) 3. 非线性建模能力 ├── 可以捕获特征间的交互作用 ├── 可以学习复杂的决策边界 └── 不假设数据分布 4. 特征重要性 ├── 自动计算特征重要性 └── 可用于特征选择 5. 速度快 ├── 训练: O(n × m × log n) n=样本数, m=特征数 └── 预测: O(树深度) → 极快

6.2 缺点

1. 高方差(最大问题) ├── 数据微小变化 → 可能生成完全不同的树 ├── 对训练数据过拟合 └── 单棵树预测不稳定 2. 贪心算法的局限 ├── 每步选局部最优 → 不保证全局最优 ├── 可能错过更好的树结构 └── 例如: 第一步选错特征,后续全错 3. 决策边界限制 ├── 只能产生轴平行的决策边界(垂直于坐标轴) └── 对斜决策边界需要多次分裂近似 4. 类别不平衡敏感 ├── 倾向多数类 └── 需要 class_weight 或采样平衡 5. 外推能力差 ├── 无法预测训练数据范围之外的值 └── 回归树在训练范围外的预测是常数

轴平行边界问题示意

真实边界是斜线 y = -x + 5: y │ · · · · │ · · · · · = 正类 │ · · · · × = 负类 │ × × × × │ × × × × └─────────────────── x 决策树只能这样近似: y │ · · │ · · 阶梯状边界 │ · ·│ · · 需要多次分裂 │ · │· × × 才能逼近斜线 │ │× × × │ │ × × × └────────┼───────── x │ x ≤ 3?

7. 从单棵树到集成方法

单棵树的核心缺陷是高方差,集成方法是标准解决方案:

单棵树的问题 集成解决方案 ────────────────────────────────────── 高方差(不稳定) → Bagging(随机森林) 多棵树投票,降低方差 贪心局部最优 → Boosting(XGBoost/LightGBM) 逐步修正错误,降低偏差 轴平行边界 → 多棵树组合 阶梯边界叠加逼近任意边界

7.1 随机森林(Bagging)

核心思想: 训练多棵独立树,投票/平均 训练阶段: ├── 从原始数据有放回抽样 → 生成多个子集 ├── 每个子集训练一棵决策树 ├── 每次分裂随机选部分特征(√m 个) └── 树之间独立,不剪枝 预测阶段: ├── 分类: 多数投票 └── 回归: 取均值 方差降低原理: Var(平均) = Var(单棵) / N × (1 - ρ) N = 树数, ρ = 树间相关性 → 随机选特征降低 ρ → 方差大幅降低

7.2 GBDT(Boosting)

核心思想: 串行训练,每棵树修正前一棵的错误 Round 1: 训练 Tree1 → 预测 ŷ₁ Round 2: 训练 Tree2 拟合残差 (y - ŷ₁) → ŷ₂ = ŷ₁ + η×Tree2 Round 3: 训练 Tree3 拟合残差 (y - ŷ₂) → ŷ₃ = ŷ₂ + η×Tree3 ... Round N: ŷ_N = ŷ₁ + η×Tree2 + ... + η×TreeN η = 学习率(shrinkage),通常 0.01~0.3 偏差降低原理: 每轮拟合残差 → 逐步逼近真实值 → 偏差不断降低

7.3 三者对比

维度决策树随机森林GBDT
树数量1多(独立)多(串行)
树结构深树深树浅树(弱学习器)
降低方差✅ Bagging部分
降低偏差✅ Boosting
过拟合风险中(需调参)
可解释性
训练速度快(可并行)慢(串行)
预测速度极快

8. 代码实战

fromsklearn.treeimportDecisionTreeClassifier,plot_treefromsklearn.model_selectionimportcross_val_scorefromsklearn.datasetsimportload_irisimportmatplotlib.pyplotasplt# 加载数据X,y=load_iris(return_X_y=True)# 训练决策树tree=DecisionTreeClassifier(criterion='gini',# 'gini' 或 'entropy'max_depth=4,min_samples_leaf=5,random_state=42)tree.fit(X,y)# 可视化树结构plt.figure(figsize=(16,10))plot_tree(tree,feature_names=load_iris().feature_names,class_names=load_iris().target_names,filled=True)plt.show()# 特征重要性importance=pd.Series(tree.feature_importances_,index=load_iris().feature_names).sort_values(ascending=True)importance.plot(kind='barh',title='Feature Importance')# 交叉验证评估scores=cross_val_score(tree,X,y,cv=5,scoring='accuracy')print(f"Accuracy:{scores.mean():.4f}±{scores.std():.4f}")# 提取决策规则fromsklearn.treeimportexport_text rules=export_text(tree,feature_names=load_iris().feature_names)print(rules)

输出示例

|--- petal length (cm) <= 2.45 | |--- class: setosa |--- petal length (cm) > 2.45 | |--- petal width (cm) <= 1.75 | | |--- petal length (cm) <= 4.95 | | | |--- class: versicolor | | |--- petal length (cm) > 4.95 | | | |--- class: virginica | |--- petal width (cm) > 1.75 | | |--- petal length (cm) <= 4.85 | | | |--- class: virginica | | |--- petal length (cm) > 4.85 | | | |--- class: virginica

总结

决策树的本质: 递归地选择最优特征和分裂点,将特征空间划分为纯度最高的区域 核心问题与解法: 如何选分裂? → 信息增益 / 增益率 / 基尼指数 何时停止? → 预剪枝 / 后剪枝 过拟合? → 剪枝 + 集成(随机森林 / GBDT) 一句话: 决策树是可解释性最强的模型,单棵树因高方差不适合直接用于生产, 但作为随机森林和 GBDT 的基础组件,是实际应用中最核心的算法族。
http://www.jsqmd.com/news/1131916/

相关文章:

  • 数据库物理设计实战:MySQL 8.0 索引与存储引擎选择的 3 个性能基准
  • 蒙特卡洛强化学习 3 大核心实现:首次访问 vs 每次访问 vs 增量更新
  • Ubuntu 22.04 apt 源配置:3步诊断与修复 E: Unable to locate package
  • Linux LVM 根分区 (/dev/mapper) 100% 排查:3步定位MySQL日志等大文件
  • 【硬核脑洞】16位实模式最后的疯狂:我们能否在 640KB 常规内存里手搓一个 MD 模拟器?
  • QAM调制原理与Python仿真:从16-QAM到4096-QAM的误码率曲线绘制
  • Ubuntu 22.04/24.04 软件源配置:3大国内镜像站(阿里/清华/中科大)实测速度对比
  • 武汉昆仑星为企业AI可见度提升的四个变量:信源、内容矩阵、平台覆盖与复盘优化
  • YOLO26 改进 - 注意力机制 ACmix自注意力与卷积混合模型:轻量级设计融合双机制优势,实现高效特征提取与推理加速
  • Linux 进程通信 6 大机制对比:管道、消息队列、共享内存、信号量、信号、Socket
  • 个人系统的RULE和SOP是否有意义?
  • 如何用番茄小说下载器打造你的个人数字图书馆:Rust高性能工具的终极指南
  • HP LaserJet M226/M128 驱动安装 1603 错误:3 步定位与修复 HpTcpMon64.msi 故障
  • 我有的几乎全世界独一无二的东西记录
  • 记录节选 0012
  • Oracle expdp/impdp 性能调优 3 要点:并行度、压缩与网络传输优化
  • PyTorch/TensorFlow 张量运算实战:3种内积与双点积实现与性能对比
  • Windows Hello 兼容性深度解析:3 类摄像头硬件要求与驱动避坑指南
  • SQL Server 2022 GROUP BY CUBE 实战:3维度销售数据交叉分析(含完整脚本)
  • MySQL 8.0 执行计划优化:解析50题中5类高频查询的性能瓶颈
  • 强化学习蒙特卡洛方法 3 大实战误区:Blackjack 21点游戏 1000 局胜率仅 35%
  • PostgreSQL 日期计算避坑指南:时区、闰秒与interval运算的3个关键陷阱
  • InnoDB vs MyISAM 存储引擎深度对比:3大场景下的性能与特性抉择
  • RDP Wrapper 1.6.2 配置 Windows 11 多用户远程桌面:3步解决 [not supported] 错误
  • UE4/UE5 资产迁移避坑指南:3种场景避免生成冗余重定向器
  • Oracle Data Pump 性能调优 5 大参数:并行度、压缩与加密实战对比
  • Python如何使用OpenAI调用Llama模型(Llama2/Llama3/Llama3.1通用教程)
  • MySQL 日志清理与预防:4种 purge 命令与 expire_logs_days 配置详解
  • Linux 内核日志 ring buffer 大小调整:从 128KB 到 2MB 的 3 种配置方法
  • FactoryTest 可以访问 /dev/ttyUSB0 /dev/ttyS1 这两个节点,还可以读写?为什么呢?