Skip to content

Latest commit

 

History

History
386 lines (287 loc) · 11.7 KB

File metadata and controls

386 lines (287 loc) · 11.7 KB
Error in user YAML: (<unknown>): found a tab character that violate indentation while scanning a plain scalar at line 3 column 3
---
- oeasy Python 0799
- 这是 oeasy 系统化 Python 教程,从基础一步步讲,扎实、完整、不跳步。愿意花时间学,就能真正学会。
- 本教程同步发布在: 
	- 个人网站: `https://oeasy.org` 
	- 蓝桥云课: `https://www.lanqiao.cn/courses/3584` 
	- GitHub: `https://github.com/overmind1980/oeasy-python-tutorial` 
	- Gitee: `https://gitee.com/overmind1980/oeasypython` 
---

从零开始

回忆

  • 上次 我们封装了
    • 标准化器 变型后的数据
    • 随机森林分类器
  • 封装 成了 流水线对象
    • 两个操作连锁完成
    • 流水线可以直接 进行预测

图片描述

  • 啥是 随机森林决策树 数量?🤔

决策树

from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import matplotlib.pyplot as plt

X = [[1.75,1.78,70],[1.65,1.68,55],[1.83,1.85,85],[1.70,1.73,60],[1.91,1.96,95],[1.88,1.93,82],[1.98,2.11,90],[2.03,2.21,102],[2.08,2.13,111],[2.16,2.27,116]]
y = [0,0,0,0,0,1,1,1,1,1]
pipe = Pipeline([('scaler',StandardScaler()),('dt',DecisionTreeClassifier(random_state=42))])
pipe.fit(X,y)

dt = pipe.named_steps['dt']
print(dt.feature_importances_)
plt.figure(figsize=(8,6))
plot_tree(dt,feature_names=['Height','ArmSpan','Weight'],class_names=['No','Yes'],filled=True)
plt.savefig('tree.png')
plt.close()
print('Saved: tree.png')
  • 把 随机森林(RandomForestClassifier) 评估器
    • 换成了 决策树(DecisionTreeClassifier)

图片描述

翻译成中文

图片描述

  • 第一刀 为什么 切在
    • 臂展 -0.382

切分过程

  • 标准化后的特征矩阵
X_scale (10行3列):
[[-0.8923, -0.9458, -0.8634],  # 样本1:身高、臂展、体重标准化值
 [-0.8985, -1.4570, -1.6436],  # 样本2
 [-0.4123, -0.5879, -0.0832],  # 样本3
 [-1.2123, -1.1994, -1.3835],  # 样本4
 [ 0.0799,  0.0256,  0.4369],  # 样本5
 [-0.1046, -0.1789, -0.2393],  # 样本6
 [ 0.5107,  0.7413,  0.1768],  # 样本7
 [ 0.8185,  1.2526,  0.8010],  # 样本8
 [ 1.1262,  0.8436,  1.2691],  # 样本9
 [ 1.6184,  1.5593,  1.5292]]  # 样本10
  • 标准化之后的臂展特征
样本  | 标准化臂展值 | 标签(0=无潜质,1=有潜质)
1     | -0.9458      | 0
2     | -1.4570      | 0
3     | -0.5879      | 0
4     | -1.1994      | 0
5     | 0.0256       | 0
6     | -0.1789      | 1
7     | 0.7413       | 1
8     | 1.2526       | 1
9     | 0.8436       | 1
10    | 1.5593       | 1

排序

  • 对「标准化臂展值」
    • 进行从小到大排序
      • 决策树必须排序
排序后:
值:-1.4570  → 标签0
值:-1.1994  → 标签0
值:-0.9458  → 标签0
值:-0.5879  → 标签0
值:-0.1789  → 标签1
值:0.0256   → 标签0
值:0.7413   → 标签1
值:0.8436   → 标签1
值:1.2526   → 标签1
值:1.5593   → 标签1
  • 排序后
    • 标准化臂展值 + 对应标签的完整排序结果

求中点

  • 计算「所有相邻值的中点」
    • 决策树的候选阈值池
  • 决策树不会乱选阈值
    • 只会选「排序后相邻两个值的平均值」作为候选阈值
候选阈值1:(-1.4570 + -1.1994)/2 = -1.3282
候选阈值2:(-1.1994 + -0.9458)/2 = -1.0726
候选阈值3:(-0.9458 + -0.5879)/2 = -0.7669
候选阈值4:(-0.5879 + -0.1789)/2 = -0.3834 ✅ 重点!就是这个值!
候选阈值5:(-0.1789 + 0.0256)/2 = -0.0767
候选阈值6:(0.0256 + 0.7413)/2 = 0.3835
候选阈值7:(0.7413 + 0.8436)/2 = 0.7925
候选阈值8:(0.8436 + 1.2526)/2 = 1.0481
候选阈值9:(1.2526 + 1.5593)/2 = 1.4060
  • 因为只有这些中点
    • 才能把样本切成左右两部分
    • 得到 9 个候选阈值
  • 为什么 选这个值?

第一刀

  • 第一刀之前

图片描述

  • 每一刀的目标 都是要
    • 把节点的加权基尼系数降到最低
    • 第一刀切出纯净组 切分后
  • 左分支 = 4个样本
    • 全部是标签 0(无潜质)
    • Gini=0.0(绝对纯净,没有任何混杂)
  • 这个切分的牛逼之处在于
    • 一刀就把 4 个「无潜质」样本彻底提纯、完全分离出来了
    • 这是所有候选阈值里
    • 唯一能做到「单侧 Gini=0」的阈值
  • 臂展达不到的
    • 就一定没有球员潜质!

图片描述

  • 啥是基尼系数?

基尼系数

  • 基尼 是 数学家
    • 研究的 是 财富是否平均的系数

图片描述

基尼系数

  • 假设 群体有 100 人 总财富为 100 万元
基尼系数值 分配状态 具体特征示例
0 绝对平等 100人总财富100万元 → 每人1万元
累计10%人口拥有10%财富
累计50%人口拥有50%财富
0.6 高度不平等 100人总财富100万元 → 前10%人口拥有60万元(60%财富)
后50%人口仅拥有5万元(5%财富)
0.8 极端不平等 100人总财富100万元 → 前5%人口拥有80万元(80%财富)
后80%人口仅拥有5万元(5%财富)
1 绝对不平等 100人总财富100万元 → 1人独占全部100万元
其余99人财富为0

图片描述

图片描述

决策树

  • 决策树算法 在设计 “节点纯度指标” 时
    • 借用了基尼系数的数学核心逻辑
      • 通过 “概率平方和” 衡量分布的不均匀性
      • 但调整了公式以适配分类任务
      • 因此被称为 基尼不纯度(Gini Impurity)

图片描述

  • 第二刀
    • 为什么 还选 臂展
    • 为什么 在0.278

第二刀

  • 第二刀的目标 还是 要
    • 把 总节点的 加权基尼系数 降到最低

图片描述

  • 第一刀切完后右分支剩下 6 个样本
    • 只有臂展能继续切出
      • 单侧基尼 = 0 的 纯节点
      • 身高 / 体重依旧做不到
    • 而且这是能让基尼系数下降幅度最大的唯一选择
      • 没有其他特征能替代
  • 臂展 满足 某个条件
    • 一定有潜质

图片描述

  • 衡量 总的加权基尼系数

图片描述

第三刀

  • 第三刀的目标 还是 要
    • 把 总节点的 加权基尼系数 降到最低

图片描述

图片描述

  • 总节点的基尼系数
切分步骤 全局加权基尼系数 核心变化
切分前 0.5 初始最不纯状态
第1刀后 0.167 纯度大幅提升
第2刀后 0.100 纯度进一步提升
第3刀后 0 所有节点纯,分类完成
  • 最后一刀 肯定能 一分为二
    • 为什么 还选 臂展?

臂展

  • 第二刀后 剩下的左子分支

    • 共2个样本
      • 标签是[1,0]
  • 这两个样本很容易混淆

指标 无潜质 有潜质
原始身高(m) 1.91 1.88
原始体重(kg) 95 82
原始臂展(m) 1.96 1.93
  • 身高高、体重大、臂展长 的人
    • 反而没有潜质
  • 那为什么选择臂展?

臂展

  • 决策树的【特征选择优先级】

    • 从头到尾只选同一个特征到底
    • 决策树的 CART 算法有一个硬性规则
  • 能一个特征解决的问题

    • 绝对不用第二个特征
  • 臂展是唯一在全局范围内

    • 能稳定区分标签的特征
  • 那么 总结出 这个决策树之后

    • 如何评估呢?

目前决策树

  • 决策过程像一棵树
    • 不断向下 开枝散叶

图片描述

player = [1.88, 1.93, 82] 

评估过程

from sklearn.tree import DecisionTreeClassifier, export_text
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np

# 1. 原始数据训练模型
X = [[1.75,1.78,70],[1.65,1.68,55],[1.83,1.85,85],
     [1.70,1.73,60],[1.91,1.96,95],[1.88,1.93,82],
     [1.98,2.11,90],[2.03,2.21,102],[2.08,2.13,111],[2.16,2.27,116]]
y = [0,0,0,0,0,1,1,1,1,1]
pipe = Pipeline([('scaler',StandardScaler()),('dt',DecisionTreeClassifier(random_state=42))])
pipe.fit(X, y)
scaler, dt = pipe.named_steps['scaler'], pipe.named_steps['dt']

# 2. 待观察球员数据
player = [1.88, 1.93, 82]
player_std = scaler.transform(np.array(player).reshape(1,-1))[0]
a_std = player_std[1] # 球员标准化臂展值

# 3. 核心分支判断推理
print("="*60)
print("✅ 球员分支判断全过程(仅臂展维度)")
print(f"球员标准化臂展值:{a_std:.6f}")
print("-"*60)
# 第1层判断
cond1 = a_std <= -0.381518
print(f"第1层:臂展 <= -0.381518 → {cond1} → 走右分支")
# 第2层判断
cond2 = a_std <= 0.356083
print(f"第2层:臂展 <= 0.356083 → {cond2} → 走左分支")
# 第3层判断
cond3 = a_std <= -0.101738
print(f"第3层:臂展 <= -0.101738 → {cond3} → 走左分支")
# 最终结果
pred = pipe.predict(np.array(player).reshape(1,-1))[0]
print("-"*60)
print(f"最终判定:{'有潜质(1)' if pred==1 else '无潜质(0)'}")
print("="*60)
  • 结论

图片描述

  • 为啥会有决策树模型呢?

专家系统

  • 早期有专家系统

图片描述

  • 把专家的知识数字化

图片描述

  • 其实就是分类讨论
    • 一堆if-else
    • 但本质上还是人来决定具体参数

决策树模型

  • 专家系统的最大痛点是规则不全、规则冲突
    • 而决策树可以完美解决这个问题
    • 用数据自动挖掘规则
    • 补充和优化专家系统的知识库
对比维度 决策树 专家系统
核心驱动 数据驱动:从数据集里自动学习规则 知识驱动:由领域专家手动定义规则
规则来源 来自数据的统计规律,比如“身高≤1.855m”这个阈值是算法从数据中算出来的 来自专家的经验,比如“身高>2.0m臂展更长”是专家凭经验总结的
构建方式 无需人工干预,输入数据→算法训练→输出规则树,代码自动完成 需要大量人工成本:专家访谈→规则提炼→编码到知识库,过程繁琐
灵活性 数据更新后,重新训练就能生成新规则,适配新数据 规则需要手动修改,新情况出现时,若没有对应的规则就无法决策
可扩展性 数据量越大,规则可能越精准(但可能过拟合) 规则数量过多时,容易出现规则冲突(比如两条规则条件重叠但结论不同)
适用场景 有充足标注数据的场景,比如:
1. 身高体重预测臂展
2. 客户分类、风险评估
数据稀缺但专家经验丰富的场景,比如:
1. 早期医疗诊断(数据少,医生经验关键)
2. 工业设备故障排查(专家知道故障和症状的对应关系)
  • 机器学习中
    • 具体参数 由数据决定

图片描述

总结

  • 这次我们了解了决策树
    • 独木不成林

图片描述

  • 决策树 又是 怎么形成 随机森林的呢?🤔
  • 我们下次再说👋

  • 本文来自 oeasy Python 系统教程。
  • 想完整、扎实学 Python,
  • 搜索 oeasy 即可。