课程承诺1 个核心概念决策树1 个核心思想信息熵与信息增益1 段可视化实战代码。学完你能看懂 AI 的每一步决策逻辑亲手实现一个可解释性最强的 AI 模型这是所有工业界集成算法的基础。本节课目标彻底搞懂决策树如何像人一样 做判断理解它自动选择最优特征的数学原理实现一个银行贷款审批 AI并且亲眼画出 AI 的决策流程图。 先回答上一课的思考题多分类问题逻辑回归怎么处理用 一对多One-vs-Rest策略有 N 个类别就训练 N 个二分类器。每个分类器判断 是这个类别还是其他类别最终取概率最高的那个类别。scikit-learn 的逻辑回归默认就支持多分类。鸢尾花分类问题直接用from sklearn.datasets import load_iris加载数据集然后用和垃圾邮件分类完全一样的代码就能实现准确率可以达到 95% 以上。 第一个核心概念决策树Decision Tree决策树是唯一能让人类完全看懂 AI 思考过程的算法。它的决策逻辑和人类一模一样通过一系列 是 / 否 问题一步步缩小范围最终得出结论。最生活化的例子今天要不要去打球人类的决策过程先看天气下雨吗→ 下雨就不去如果不下雨再看温度太热吗→ 太热就不去如果温度合适再看有没有风风大吗→ 风大就不去如果都满足就去打球把这个过程画成一棵树就是一个决策树plaintext根节点天气怎么样 ├─ 下雨 → 不去叶子节点 ├─ 阴天 → 去叶子节点 └─ 晴天 → 温度怎么样 ├─ 太热 → 不去叶子节点 ├─ 适中 → 有没有风 │ ├─ 有风 → 不去 │ └─ 无风 → 去 └─ 凉爽 → 去决策树的基本结构根节点第一个要问的问题最有区分度的特征内部节点中间的问题每个节点对应一个特征分支问题的答案每个分支对应特征的一个取值叶子节点最终的决策结果最神奇的地方这棵树不是人写的是 AI 自己从数据中学习出来的它会自动决定先问哪个问题再问哪个问题最终得出结论。 第一个核心思想信息熵与信息增益现在的问题是AI 怎么知道应该先问哪个问题比如在贷款审批问题中有 收入、年龄、是否有房 三个特征AI 应该先判断 有没有房还是先判断 收入多少答案是哪个特征能让数据变得最 整齐就先问哪个。信息熵Entropy衡量数据的混乱程度信息熵是一个 0 到 1 之间的数✅ 熵 0数据完全整齐所有样本都是同一个类别比如全是 批准贷款❌ 熵 1数据最混乱一半是这个类别一半是那个类别举个例子10 个人全是好人 → 熵 010 个人5 个好人 5 个坏人 → 熵 110 个人8 个好人 2 个坏人 → 熵≈0.72信息增益Information Gain熵减少的量用一个特征划分数据之后熵减少了多少就叫这个特征的信息增益。信息增益越大 → 用这个特征划分后数据变得越整齐 → 这个特征的分类能力越强决策树的核心算法计算所有特征的信息增益选择信息增益最大的特征作为当前节点按照这个特征的取值把数据分成子集对每个子集重复步骤 1-3直到所有子集都足够整齐或者没有特征可用这就是最经典的ID3 算法所有决策树算法都是在这个基础上改进的。 代码实战银行贷款审批 AI我们将训练一个 AI根据申请人的 4 个特征自动决定是否批准贷款。最重要的是我们会把 AI 的决策树完整地画出来前置准备先安装画图需要的库bash运行pip install scikit-learn matplotlib graphviz注意graphviz 需要额外安装系统软件Windows下载安装包 https://graphviz.org/download/安装时勾选 添加到系统 PATHMacbrew install graphvizLinuxsudo 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_size0.3, random_state42) # 3. 创建并训练决策树模型 model DecisionTreeClassifier(max_depth3, random_state42) 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_namesfeatures, class_names[拒绝, 批准], filledTrue, roundedTrue, fontsize12) plt.title(银行贷款审批决策树, fontsize16) 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申请人{i1}{result_text}贷款) 逐行解读核心知识点1. 特征重要性你会看到类似这样的输出plaintext各特征重要性 年龄: 0.00 月收入: 0.25 是否有房: 0.75 是否有车: 0.00AI 认为 是否有房 是最重要的特征重要性占 75%年龄 和 是否有车 在这个数据集中完全不重要AI 根本没用到这就是决策树的巨大优势可解释性极强你能明确知道 AI 是根据什么做出的决策2. 解读决策树图当你运行代码后会看到一张完整的决策树图每个节点都包含 4 个信息问题比如 是否有房 ≤ 0.5意思是 没有房子吗gini基尼系数和信息熵类似也是衡量混乱程度的指标samples这个节点有多少个样本value[拒绝的数量批准的数量]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_size0.2, random_state42 ) print(f特征数据 X 的形状: {X.shape}) print(f训练集样本数: {len(X_train)} (足够训练)) print(f测试集样本数: {len(X_test)} (足够测试)) print(- * 50) # # 3. 实验对比 # # 实验1过拟合 (不限制深度树会死记硬背噪声) # 注意对于噪声数据过拟合模型会试图把每个噪点都分对导致树非常复杂 model_overfit DecisionTreeClassifier(max_depthNone, random_state42) 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_depth2, random_state42) 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_depth5, random_state42) 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_namesfeature_names, class_names[拒绝, 批准], filledTrue, roundedTrue, fontsize12, ) plt.title(拟合良好的决策树结构 (Max Depth5), fontsize16) 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 个样本的测试集中欠拟合的准确率偶然高了一点点但在实际工程中过拟合是绝对要避免的0.99 - 0.72 的跌幅太可怕。拟合良好的模型具备更强的鲁棒性。随着数据量继续增加它的表现会比欠拟合模型更稳定、更优秀。解决决策树过拟合的方法预剪枝在训练过程中限制树的深度、每个节点的最少样本数等最常用后剪枝训练完一棵完整的树后再剪掉一些没用的分支 本节课总结核心概念决策树通过一系列 是 / 否 问题做决策结构和人类思考完全一致核心思想用信息熵衡量数据的混乱程度选择信息增益最大的特征进行分裂核心优势可解释性极强能明确知道 AI 的决策依据核心缺点容易过拟合需要通过限制树的深度等方法进行剪枝你已经做到了实现了一个可解释的贷款审批 AI并且能看懂它的每一步决策 课后作业必须做运行上面的所有代码成功画出决策树并且能顺着分支解释 AI 的决策过程尝试不同的max_depth、min_samples_split、min_samples_leaf参数观察对模型的影响用决策树解决鸢尾花分类问题画出它的决策树看看 AI 是怎么区分三种鸢尾花的思考单棵决策树的效果有限有没有什么方法能提高决策树的准确率