news 2026/5/7 15:41:52

机器学习大师课 第 5 课:决策树 —— 最像人类思考的 AI 算法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
机器学习大师课 第 5 课:决策树 —— 最像人类思考的 AI 算法

课程承诺:1 个核心概念(决策树)+1 个核心思想(信息熵与信息增益)+1 段可视化实战代码。学完你能看懂 AI 的每一步决策逻辑,亲手实现一个可解释性最强的 AI 模型,这是所有工业界集成算法的基础。

本节课目标:彻底搞懂决策树如何像人一样 "做判断",理解它自动选择最优特征的数学原理,实现一个银行贷款审批 AI,并且亲眼画出 AI 的决策流程图


🧩 先回答上一课的思考题

  1. 多分类问题逻辑回归怎么处理?用 "一对多"(One-vs-Rest)策略:有 N 个类别,就训练 N 个二分类器。每个分类器判断 "是这个类别还是其他类别",最终取概率最高的那个类别。scikit-learn 的逻辑回归默认就支持多分类。

  2. 鸢尾花分类问题:直接用from sklearn.datasets import load_iris加载数据集,然后用和垃圾邮件分类完全一样的代码就能实现,准确率可以达到 95% 以上。


🧠 第一个核心概念:决策树(Decision Tree)

决策树是唯一能让人类完全看懂 AI 思考过程的算法。它的决策逻辑和人类一模一样:通过一系列 "是 / 否" 问题,一步步缩小范围,最终得出结论

最生活化的例子:今天要不要去打球?

人类的决策过程:

  1. 先看天气:下雨吗?→ 下雨就不去
  2. 如果不下雨,再看温度:太热吗?→ 太热就不去
  3. 如果温度合适,再看有没有风:风大吗?→ 风大就不去
  4. 如果都满足,就去打球

把这个过程画成一棵树,就是一个决策树:

plaintext

根节点:天气怎么样? ├─ 下雨 → 不去(叶子节点) ├─ 阴天 → 去(叶子节点) └─ 晴天 → 温度怎么样? ├─ 太热 → 不去(叶子节点) ├─ 适中 → 有没有风? │ ├─ 有风 → 不去 │ └─ 无风 → 去 └─ 凉爽 → 去

决策树的基本结构

  • 根节点:第一个要问的问题(最有区分度的特征)
  • 内部节点:中间的问题(每个节点对应一个特征)
  • 分支:问题的答案(每个分支对应特征的一个取值)
  • 叶子节点:最终的决策结果

最神奇的地方:这棵树不是人写的,是 AI 自己从数据中学习出来的!它会自动决定先问哪个问题,再问哪个问题,最终得出结论。


💡 第一个核心思想:信息熵与信息增益

现在的问题是:AI 怎么知道应该先问哪个问题?

比如在贷款审批问题中,有 "收入"、"年龄"、"是否有房" 三个特征,AI 应该先判断 "有没有房",还是先判断 "收入多少"?

答案是:哪个特征能让数据变得最 "整齐",就先问哪个

信息熵(Entropy):衡量数据的混乱程度

信息熵是一个 0 到 1 之间的数:

  • ✅ 熵 = 0:数据完全整齐,所有样本都是同一个类别(比如全是 "批准贷款")
  • ❌ 熵 = 1:数据最混乱,一半是这个类别,一半是那个类别

举个例子:

  • 10 个人,全是好人 → 熵 = 0
  • 10 个人,5 个好人 5 个坏人 → 熵 = 1
  • 10 个人,8 个好人 2 个坏人 → 熵≈0.72

信息增益(Information Gain):熵减少的量

用一个特征划分数据之后,熵减少了多少,就叫这个特征的信息增益。

  • 信息增益越大 → 用这个特征划分后,数据变得越整齐 → 这个特征的分类能力越强

决策树的核心算法

  1. 计算所有特征的信息增益
  2. 选择信息增益最大的特征作为当前节点
  3. 按照这个特征的取值把数据分成子集
  4. 对每个子集重复步骤 1-3,直到所有子集都足够整齐或者没有特征可用

这就是最经典的ID3 算法,所有决策树算法都是在这个基础上改进的。


💻 代码实战:银行贷款审批 AI

我们将训练一个 AI,根据申请人的 4 个特征,自动决定是否批准贷款。最重要的是,我们会把 AI 的决策树完整地画出来!

前置准备

先安装画图需要的库:

bash

运行

pip install scikit-learn matplotlib graphviz

注意:graphviz 需要额外安装系统软件:

  • Windows:下载安装包 https://graphviz.org/download/,安装时勾选 "添加到系统 PATH"
  • Mac:brew install graphviz
  • Linux:sudo apt-get install graphviz

完整代码

python

运行

import numpy as np import matplotlib.pyplot as plt from sklearn.tree import DecisionTreeClassifier, plot_tree from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, classification_report # ================= 核心修复代码(加这两行) ================= plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签(SimHei是黑体) plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 # ======================================================= # 1. 准备贷款审批数据集 # 特征:[年龄(岁), 月收入(千元), 是否有房(0=无,1=有), 是否有车(0=无,1=有)] X = np.array([ [25, 5, 0, 0], [30, 8, 0, 1], [35, 12, 1, 0], [40, 15, 1, 1], [45, 20, 1, 1], [22, 3, 0, 0], [28, 6, 0, 0], [32, 10, 1, 0], [38, 18, 1, 1], [50, 25, 1, 1], [20, 2, 0, 0], [26, 7, 0, 1], [33, 11, 0, 1], [36, 14, 1, 0], [42, 22, 1, 1], [24, 4, 0, 0], [29, 9, 0, 0], [31, 13, 1, 1], [39, 19, 1, 1], [48, 30, 1, 1] ]) # 标签:0=拒绝贷款,1=批准贷款 y = np.array([0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0,0,1,1,1]) # 2. 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) # 3. 创建并训练决策树模型 model = DecisionTreeClassifier(max_depth=3, random_state=42) model.fit(X_train, y_train) # 4. 模型评估 y_pred = model.predict(X_test) print(f"模型准确率:{accuracy_score(y_test, y_pred):.2f}") # 5. 查看特征重要性 print("\n各特征重要性:") features = ["年龄", "月收入", "是否有房", "是否有车"] for feature, importance in zip(features, model.feature_importances_): print(f"{feature}: {importance:.2f}") # 6. 画出完整的决策树(修复后) plt.figure(figsize=(16, 10)) plot_tree(model, feature_names=features, class_names=["拒绝", "批准"], filled=True, rounded=True, fontsize=12) plt.title("银行贷款审批决策树", fontsize=16) plt.show() # 7. 测试新的申请人 print("\n" + "="*50) applicants = [ [27, 6, 0, 0], [34, 13, 1, 0], [23, 15, 0, 1] ] for i, applicant in enumerate(applicants): result = model.predict([applicant])[0] result_text = "批准" if result == 1 else "拒绝" print(f"申请人{i+1}:{result_text}贷款")

🔍 逐行解读核心知识点

1. 特征重要性

你会看到类似这样的输出:

plaintext

各特征重要性: 年龄: 0.00 月收入: 0.25 是否有房: 0.75 是否有车: 0.00
  • AI 认为 "是否有房" 是最重要的特征,重要性占 75%
  • "年龄" 和 "是否有车" 在这个数据集中完全不重要,AI 根本没用到
  • 这就是决策树的巨大优势:可解释性极强,你能明确知道 AI 是根据什么做出的决策

2. 解读决策树图

当你运行代码后,会看到一张完整的决策树图,每个节点都包含 4 个信息:

  1. 问题:比如 "是否有房 ≤ 0.5"(意思是 "没有房子吗?")
  2. gini:基尼系数(和信息熵类似,也是衡量混乱程度的指标)
  3. samples:这个节点有多少个样本
  4. value:[拒绝的数量,批准的数量]
  5. class:这个节点的最终决策结果

你可以顺着树的分支一步步走,完全复现 AI 的决策过程!比如:

  • 没有房子 → 看月收入 ≤ 9.5 → 是 → 拒绝贷款
  • 有房子 → 看月收入 ≤ 10.5 → 否 → 批准贷款

3. 决策树的过拟合问题

决策树的最大缺点就是极其容易过拟合。如果不限制树的深度,它会一直分裂下去,直到每个叶子节点只有一个样本,训练集准确率达到 100%,但泛化能力极差。


✨ 神奇的实验:控制树的深度,观察过拟合

这是本节课最重要的实验,一定要做!

修改max_depth参数,观察效果:

python

运行

import numpy as np import matplotlib.pyplot as plt from sklearn.tree import DecisionTreeClassifier, plot_tree from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score # 设置中文字体 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # ========================================== # 1. 生成大规模模拟数据集 (200条数据) # ========================================== np.random.seed(42) # 固定随机种子,保证每次运行结果一致 n_samples = 200 # 初始化数组 X = np.zeros((n_samples, 4)) y = np.zeros(n_samples) for i in range(n_samples): # 随机生成特征 age = np.random.randint(20, 60) # 年龄 20-60 income = np.random.randint(2, 30) # 收入 2-30k house = np.random.randint(0, 2) # 是否有房 car = np.random.randint(0, 2) # 是否有车 # --- 真实逻辑 (带一点随机噪声) --- # 规则:(年龄 > 30 且 收入 > 15) 或 (有房) 或 (有车且收入>10) -> 批准(1) score = 0 if age > 30 and income > 15: score += 2 if house == 1: score += 2 if car == 1 and income > 10: score += 1 # 引入噪声 (15%的概率翻转标签,模拟现实中的误判或特殊情况) if np.random.random() < 0.15: label = 1 if score < 2 else 0 # 翻转 else: label = 1 if score >= 2 else 0 # 正常 X[i] = [age, income, house, car] y[i] = label # 特征名称 feature_names = ["年龄", "月收入", "是否有房", "是否有车"] # ========================================== # 2. 划分训练集和测试集 (80% 训练, 20% 测试) # ========================================== # 现在数据量够大,测试集会有 40 个样本,结果更具说服力 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) print(f"特征数据 X 的形状: {X.shape}") print(f"训练集样本数: {len(X_train)} (足够训练)") print(f"测试集样本数: {len(X_test)} (足够测试)") print("-" * 50) # ========================================== # 3. 实验对比 # ========================================== # 实验1:过拟合 (不限制深度,树会死记硬背噪声) # 注意:对于噪声数据,过拟合模型会试图把每个噪点都分对,导致树非常复杂 model_overfit = DecisionTreeClassifier(max_depth=None, random_state=42) model_overfit.fit(X_train, y_train) acc_train_over = accuracy_score(y_train, model_overfit.predict(X_train)) acc_test_over = accuracy_score(y_test, model_overfit.predict(X_test)) # 实验2:欠拟合 (限制深度为2,树太简单,抓不住核心规律) model_underfit = DecisionTreeClassifier(max_depth=2, random_state=42) model_underfit.fit(X_train, y_train) acc_train_under = accuracy_score(y_train, model_underfit.predict(X_train)) acc_test_under = accuracy_score(y_test, model_underfit.predict(X_test)) # 实验3:拟合良好 (限制深度为5,允许一定复杂度,但忽略微小噪声) model_good = DecisionTreeClassifier(max_depth=5, random_state=42) model_good.fit(X_train, y_train) acc_train_good = accuracy_score(y_train, model_good.predict(X_train)) acc_test_good = accuracy_score(y_test, model_good.predict(X_test)) # ========================================== # 4. 打印结果对比 # ========================================== print(f"{'模型状态':<12} | {'训练集准确率':<12} | {'测试集准确率':<12}") print("-" * 50) print(f"{'过拟合':<12} | {acc_train_over:.2f} | {acc_test_over:.2f}") print(f"{'欠拟合':<12} | {acc_train_under:.2f} | {acc_test_under:.2f}") print(f"{'拟合良好':<12} | {acc_train_good:.2f} | {acc_test_good:.2f}") print("-" * 50) # 预期结果分析: # 1. 过拟合:训练集应该是 1.00 (因为它记住了所有噪点),测试集会下降 (比如 0.85 左右)。 # 2. 欠拟合:训练集和测试集都较低 (比如 0.70 左右),因为它太简单了。 # 3. 拟合良好:测试集准确率应该是最高的,且训练集准确率略低于 1.00 (这是健康的)。 # ========================================== # 5. 可视化“拟合良好”的树 # ========================================== plt.figure(figsize=(20, 12)) plot_tree( model_good, feature_names=feature_names, class_names=["拒绝", "批准"], filled=True, rounded=True, fontsize=12, ) plt.title("拟合良好的决策树结构 (Max Depth=5)", fontsize=16) plt.show()

结果:

特征数据 X 的形状: (200, 4) 训练集样本数: 160 (足够训练) 测试集样本数: 40 (足够测试) -------------------------------------------------- 模型状态 | 训练集准确率 | 测试集准确率 -------------------------------------------------- 过拟合 | 0.99 | 0.72 欠拟合 | 0.78 | 0.85 拟合良好 | 0.90 | 0.78 --------------------------------------------------

你会发现

这组数据非常经典,它完美地展示了机器学习中“过拟合”、“欠拟合”和“拟合良好”三者的本质区别。

以下是详细解读:

1. 过拟合:死记硬背的“书呆子”

  • 表现:训练集0.99(几乎全对),测试集0.72(大幅下降)。
  • 解读
    • 模型在训练集上拿了满分,说明它把训练数据里的每一个细节(包括那些人为加入的“噪声”和“错误数据”)都背下来了。
    • 但是在测试集(没见过的题)上,它的表现甚至不如最简单的模型。
    • 结论:它泛化能力最差。它学到的不是规律,而是特例。

2. 欠拟合:过于简单的“懒人”

  • 表现:训练集0.78,测试集0.85
  • 解读
    • 模型在训练集上表现平平,说明它没有完全学会数据中的复杂规律。
    • 为什么测试集反而高?这通常是因为测试集(40个样本)里恰好包含了很多容易分类的简单样本,或者模型虽然简单,但运气好蒙对了一些。
    • 本质:它的上限很低。如果给它更复杂的难题,它就搞不定了。它虽然没犯错(因为没学深),但也错过了很多机会。

3. 拟合良好:懂得取舍的“优等生”

  • 表现:训练集0.90,测试集0.78
  • 解读
    • 这是最健康的状态。
    • 训练集 0.90:说明它学到了大部分核心规律。
    • 测试集 0.78:虽然没有达到欠拟合的 0.85(那是运气),但它保持了与训练集相对接近的水平,说明它具备了举一反三的能力
    • 结论:它既没有死记硬背(像过拟合那样),也没有浅尝辄止(像欠拟合那样)。

这组数据非常经典,它完美地展示了机器学习中“过拟合”、“欠拟合”和“拟合良好”三者的本质区别。

虽然你之前提到“拟合良好”的测试集准确率(0.78)看起来不如“欠拟合”(0.85),但这其实是小样本下的随机波动。透过数据看本质,“拟合良好”依然是最佳选择

以下是详细解读:

1. 过拟合:死记硬背的“书呆子”

  • 表现:训练集0.99(几乎全对),测试集0.72(大幅下降)。
  • 解读
    • 模型在训练集上拿了满分,说明它把训练数据里的每一个细节(包括那些人为加入的“噪声”和“错误数据”)都背下来了。
    • 但是在测试集(没见过的题)上,它的表现甚至不如最简单的模型。
    • 结论:它泛化能力最差。它学到的不是规律,而是特例。

2. 欠拟合:过于简单的“懒人”

  • 表现:训练集0.78,测试集0.85
  • 解读
    • 模型在训练集上表现平平,说明它没有完全学会数据中的复杂规律。
    • 为什么测试集反而高?这通常是因为测试集(40个样本)里恰好包含了很多容易分类的简单样本,或者模型虽然简单,但运气好蒙对了一些。
    • 本质:它的上限很低。如果给它更复杂的难题,它就搞不定了。它虽然没犯错(因为没学深),但也错过了很多机会。

3. 拟合良好:懂得取舍的“优等生”

  • 表现:训练集0.90,测试集0.78
  • 解读
    • 这是最健康的状态。
    • 训练集 0.90:说明它学到了大部分核心规律。
    • 测试集 0.78:虽然没有达到欠拟合的 0.85(那是运气),但它保持了与训练集相对接近的水平,说明它具备了举一反三的能力
    • 结论:它既没有死记硬背(像过拟合那样),也没有浅尝辄止(像欠拟合那样)

核心启示:我们应该选哪个?

答案:选“拟合良好”

虽然在这个特定的 40 个样本的测试集中,欠拟合的准确率偶然高了一点点,但在实际工程中:

  1. 过拟合是绝对要避免的(0.99 -> 0.72 的跌幅太可怕)。
  2. 拟合良好的模型具备更强的鲁棒性。随着数据量继续增加,它的表现会比欠拟合模型更稳定、更优秀。

解决决策树过拟合的方法

  • 预剪枝:在训练过程中限制树的深度、每个节点的最少样本数等(最常用)
  • 后剪枝:训练完一棵完整的树后,再剪掉一些没用的分支

📝 本节课总结

  1. 核心概念:决策树通过一系列 "是 / 否" 问题做决策,结构和人类思考完全一致
  2. 核心思想:用信息熵衡量数据的混乱程度,选择信息增益最大的特征进行分裂
  3. 核心优势:可解释性极强,能明确知道 AI 的决策依据
  4. 核心缺点:容易过拟合,需要通过限制树的深度等方法进行剪枝
  5. 你已经做到了:实现了一个可解释的贷款审批 AI,并且能看懂它的每一步决策

🎯 课后作业(必须做)

  1. 运行上面的所有代码,成功画出决策树,并且能顺着分支解释 AI 的决策过程
  2. 尝试不同的max_depthmin_samples_splitmin_samples_leaf参数,观察对模型的影响
  3. 用决策树解决鸢尾花分类问题,画出它的决策树,看看 AI 是怎么区分三种鸢尾花的
  4. 思考:单棵决策树的效果有限,有没有什么方法能提高决策树的准确率?
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/7 15:41:50

如何突破群晖NAS硬盘兼容性限制:Synology HDD db完整指南

如何突破群晖NAS硬盘兼容性限制&#xff1a;Synology HDD db完整指南 【免费下载链接】Synology_HDD_db Add your HDD, SSD and NVMe drives to your Synologys compatible drive database and a lot more 项目地址: https://gitcode.com/GitHub_Trending/sy/Synology_HDD_db…

作者头像 李华
网站建设 2026/5/7 15:40:29

GoldHEN作弊管理器:5分钟掌握PS4游戏修改终极指南

GoldHEN作弊管理器&#xff1a;5分钟掌握PS4游戏修改终极指南 【免费下载链接】GoldHEN_Cheat_Manager GoldHEN Cheats Manager 项目地址: https://gitcode.com/gh_mirrors/go/GoldHEN_Cheat_Manager 还在为PS4游戏难度太高而烦恼吗&#xff1f;想要轻松解锁《血源诅咒》…

作者头像 李华
网站建设 2026/5/7 15:35:28

创业团队如何通过 Taotoken 实现低成本多模型 AI 能力验证

创业团队如何通过 Taotoken 实现低成本多模型 AI 能力验证 对于资源有限的创业团队和独立开发者而言&#xff0c;在早期产品开发阶段&#xff0c;一项关键决策是选择合适的大语言模型来支撑核心功能。不同的模型在创意写作、代码生成、逻辑推理或成本控制上各有侧重&#xff0…

作者头像 李华
网站建设 2026/5/7 15:34:47

明日方舟MAA自动化工具终极指南:从零掌握游戏助手完整教程

明日方舟MAA自动化工具终极指南&#xff1a;从零掌握游戏助手完整教程 【免费下载链接】MaaAssistantArknights 《明日方舟》小助手&#xff0c;全日常一键长草&#xff01;| A one-click tool for the daily tasks of Arknights, supporting all clients. 项目地址: https:/…

作者头像 李华
网站建设 2026/5/7 15:33:48

抖音无水印下载工具:从零到精通的完整实战指南

抖音无水印下载工具&#xff1a;从零到精通的完整实战指南 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. 抖音…

作者头像 李华