Skip to content

Latest commit

 

History

History
434 lines (339 loc) · 13.9 KB

File metadata and controls

434 lines (339 loc) · 13.9 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 0801
- 这是 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` 
---

从零开始

回忆

  • 上次核心原理

    1. 样本随机抽样
    2. 特征随机选择
    3. 独立预测+投票
      • 「少数服从多数」
  • 如果 只有这10个人的数据

    • 不但要 拟合(fitting)
    • 还要 测试验证

图片描述

  • 该如何做呢?

训练集 / 测试集切分

图片描述

  • 把10条数据切分成
    1. 训练集 8条
    2. 测试集 2条
# 训练集80% + 测试集20%,分层抽样保证0/1标签比例一致,固定随机种子结果可复现
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)
  • 测试的数据 不会提前泄露(leakage) 给 评估器(estimator)
    • 保证 模型的 客观

代码

from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
import numpy as np

# 原始数据集:身高、臂展、体重 | 标签0=无潜质 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]
feature_names = ['身高','臂展','体重']

# ===================== 1、数据集切分 核心步骤 =====================
# 训练集80% + 测试集20%,分层抽样保证0/1标签比例一致,固定随机种子结果可复现
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# ===================== 2、构建流水线+模型训练 =====================
# 流水线封装:标准化 → 随机森林,杜绝数据泄露,无需手动处理标准化
pipe_model = Pipeline([
    ("标准化", StandardScaler()),
    ("随机森林", RandomForestClassifier(n_estimators=100, random_state=42))
])
# 仅使用训练集训练模型(机器学习核心准则)
pipe_model.fit(X_train, y_train)

# ===================== 3、模型效果评估(训练集+测试集) =====================
train_acc = pipe_model.score(X_train, y_train)  # 训练集准确率
test_acc  = pipe_model.score(X_test, y_test)   # 测试集准确率
feat_imp  = pipe_model['随机森林'].feature_importances_ # 特征重要性

# ===================== 结果汇总输出 =====================
print("="*70)
print("随机森林模型-球员潜质评估 训练&验证报告")
print("="*70)
print(f"训练集样本数:{len(X_train)} | 测试集样本数:{len(X_test)}")
print(f"训练集预测准确率:{train_acc:.4f}")
print(f"测试集预测准确率:{test_acc:.4f}")
print(f"特征重要性:身高={feat_imp[0]:.3f} | 臂展={feat_imp[1]:.3f} | 体重={feat_imp[2]:.3f}")
print("="*70)

结果

图片描述

  • 根据 训练集 训练出来 之后
    • 在测试集上 进行验证
    • 因为 测试集 都有标签
    • 所以可以得到准确率
train_acc = pipe_model.score(X_train, y_train)  # 训练集准确率
test_acc  = pipe_model.score(X_test, y_test)   # 测试集准确率
  • 为什么要分 训练集 和 数据集?

✅ 原因1:最核心 → 为了检验模型的「真实泛化能力」,防止自欺欺人

  • 如果把全部数据(10条) 都用来训练模型
    • 模型会把这10条球员的「身高/臂展/体重」和「是否有潜质」的规律死记硬背下来
      • 用这份数据再去测试模型
      • 准确率一定是 100%
      • 看起来模型特别好
  • 但这是「作弊」
    • 因为模型只是记住了已知数据,遇到新的球员数据,预测准确率会暴跌
    • 简单说:不拆分,你永远不知道模型好不好用,只能知道模型「记数据」的能力好不好

✅ 拆分的本质逻辑:

训练集 → 让模型学习规律(只给模型看8条球员数据,让它学「什么样的身体条件=有潜质」)

测试集 → 让模型考试打分(把剩下2条模型从没见过的球员数据给它,看它能不能判断对)

测试集的准确率,才是模型的「真实分数」!

✅ 原因2:避免「过拟合」

  • 用的是随机森林

    • 哪怕样本少也不容易过拟合
    • 但这个原则通用所有模型
  • 过拟合

    • 模型把训练数据的噪音、偶然规律当成了「通用规律」
    • 比如:训练集里刚好「身高1.88的球员都有潜质」
    • 模型就死记「1.88=有潜质」
    • 但新球员身高1.87、其他条件更好
    • 模型反而判断错
  • 测试集就是检验模型有没有「学歪」的照妖镜

    • 训练集准确率高、测试集准确率也高 → 模型学好了
    • 训练集满分、测试集低分 → 模型学歪了(过拟合)

✅ 原因3:工业级建模的「硬性标准」

  • 不管是比赛/工作/论文

    • 只要做机器学习建模
    • 数据集必须拆分训练+测试
    • 这是共识
  • 不拆分的模型

    • 别人直接判定为「无效建模」
    • 因为没有任何参考价值
  • 为什么要这样拆分

8:2

  • 参数 不是随便加的
    • 你的数据只有10条
    • 这个拆分方式是量身定制的
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)
  • X, y之外 3个参数
    • test_size=0.2,
    • random_state=42
    • stratify=y

✅ 1. 为什么 test_size=0.2

  • 训练集8条,测试集2条?

原始数据一共只有 10条样本 这个比例是「小样本专属最优比例」:

  • test_size=0.2 代表
    • 测试集占20%(2条)
    • 训练集占80%(8条)
  • ✅ 为什么不能设0.3/0.5?

3:7

  • 如果测试集设30%就是3条
    • 训练集只剩7条 →

样本太少

模型学不到足够的规律 如果设50% 训练集和测试集各5条 两边都不够用

  • ✅ 通用规则:样本越少,测试集比例要越小

    • 如果你的数据有1000条
    • 就可以设test_size=0.2/0.3
    • 样本越多
    • 比例越灵活
  • 为什么 random_state=42 → 固定随机种子?

固定随机 拆分结果

  • train_test_split随机拆分数据的
  • 不加这个参数的后果:
    • 你第一次运行代码

      • 训练集是「第1、3、5、7、8、9、2、6条」
      • 测试集是「4、10条」;
    • 你第二次运行代码

      • 训练集又变成了另一批
      • 测试集也变了
    • 每次运行的「训练/测试准确率」都不一样

      • 你根本没法判断模型到底好不好

✅ 2. random_state=42 的作用

  • 固定随机拆分的结果
    • 保证每次运行代码
    • 拆分出的训练集和测试集都是一模一样的
    • 结果可复现
    • 可对比

补充:42是行业通用值 换成1/10/2026都可以 只要固定就行

✅ 3. 为什么 stratify=y

  • 分层抽样[重中之重]
    • 代码里最关键的参数】
    • 这是份代码里最不能少的参数
    • 没有之一!
  • 先看你的标签分布:
y = [0,0,0,0,0,1,1,1,1,1] # 0=无潜质 5条,1=有潜质 5条 → 标签比例 1:1
  • ❌ 如果不加 stratify=y
    • 会发生什么?
  • 随机拆分时
    • 大概率会出现「极端情况」
    • 比如:
      • 训练集8条全是「0」
      • 测试集2条全是「1」;
    • 模型训练时只学了「无潜质」的规律
      • 测试时让它判断「有潜质」
      • 准确率直接为0;
  • 这个结果完全是拆分导致的
    • 不是模型的问题
    • 模型评估彻底失真

✅ 加了 stratify=y 会怎么样?

  • 强制让训练集和测试集的「标签比例」和原始数据完全一致
    • 原始y
      • 0占50%
      • 1占50%;
    • 训练集(8条)
      • 0占4条 : 1占4条
      • 50%:50%;
    • 测试集(2条):0占1条,1占1条 → 50%:50%;
    • 这样拆分的测试集,才能公平的检验模型,评估结果100%真实。

✅ 结论:只要你的标签是「分类标签」(0/1)

且各类别数量差不多 必须加 stratify=y

保证标签平衡

  • stratify 这个词怎么来的?

Stratify词根

  • Stratus
    • 源自拉丁语 sternere 的过去分词 stratus
    • 意为“铺开”、“铺平”或“散布”
    • 演变:stratum 指代“铺盖物”或“层”
    • 现代英语单词 Stratum(地层/阶层)
  • 后缀:-fy
    • 源自拉丁语动词后缀 -ficare
    • 由 facere(做、使成为)演变而来
    • 含义: 表示“使……化”或“使成为……”

图片描述

  • Stratify 的字面意思是“使形成层状”

    • (To make into layers)
      • 地质学(岩石分层)
      • 社会学(社会阶级分层)
      • 生物学(种子催芽处理)
  • 为什么要继续用「流水线Pipeline」?

和拆分数据集强绑定的核心

  • 代码里 拆分+流水线是「黄金组合」
    • 缺一不可
    • 刚好一起讲透
    • 为什么我一直坚持给你用流水线
  • 数据标准化(StandardScaler)
    • 这是对数据的预处理
    • 这里有一个机器学习的铁律,绝对不能违反

❌ 绝对不能用「包含测试集的数据」做标准化的拟合(fit)!

✅ 正确做法:标准化的规则,只能从训练集里学,测试集和新数据,都必须用训练集的规则做转换。

新手最常踩的坑

  • 测试集的信息提前被标准化器学习到了
    • 测试集的准确率会虚高
    • 模型的真实能力被高估
scaler = StandardScaler()
X_std = scaler.fit_transform(X) # ❌ 用了全部数据拟合,包含测试集,数据泄露!
X_train, X_test = train_test_split(X_std, test_size=0.2)
  • 用流水线的绝对优势(完美规避所有问题)
pipe_model = Pipeline([("标准化", StandardScaler()), ("随机森林", RandomForestClassifier())])
pipe_model.fit(X_train, y_train)
  • 流水线的内部逻辑:

    1. 调用fit
      • 只对 X_train 执行:标准化器fit_transform(X_train)
      • 模型训练
    2. 调用score(X_test, y_test)
      • 自动对 X_test 执行:标准化器transform(X_test)
      • 模型预测
    3. 全程
      • 标准化器只见过训练集
      • 测试集的信息完全隔离
      • 从根源杜绝数据泄露
  • ✅ 总结:拆分数据集 + 流水线 = 建模的「双保险」

    • 这两个组合在一起
    • 就是规范、正确、无坑的最优写法

总结

  • 这份代码
    • 就是小样本机器学习建模的标准答案了 ✔️
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split

# 特征数据:身高、臂展、体重
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]]
# 标签:0=无潜质 1=有潜质 (0和1各5条,比例1:1)
y = [0,0,0,0,0,1,1,1,1,1]

# ===================== 数据集拆分 核心 =====================
# test_size=0.2 小样本最优比例:训练8条,测试2条
# random_state=42 固定随机种子,结果可复现
# stratify=y 分层抽样,保证训练/测试集标签比例和原数据一致,避免极端拆分
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# ===================== 流水线建模 核心 =====================
# 流水线封装:标准化→随机森林,杜绝数据泄露,无需手动处理数据格式
pipe_model = Pipeline([
    ("数据标准化", StandardScaler()),
    ("随机森林分类器", RandomForestClassifier(n_estimators=100, random_state=42))
])
# 仅用训练集训练模型(机器学习铁律:测试集全程不参与训练)
pipe_model.fit(X_train, y_train)

# ===================== 模型验证 =====================
train_acc = pipe_model.score(X_train, y_train)  # 训练集准确率
test_acc  = pipe_model.score(X_test, y_test)   # 测试集准确率
feat_imp  = pipe_model['随机森林分类器'].feature_importances_

# 结果输出
print("="*70)
print("随机森林模型-球员潜质评估 训练&验证报告")
print("="*70)
print(f"训练集样本数:{len(X_train)} | 测试集样本数:{len(X_test)}")
print(f"训练集准确率:{train_acc:.4f}")
print(f"测试集准确率:{test_acc:.4f}")
print(f"特征重要性:身高={feat_imp[0]:.3f} | 臂展={feat_imp[1]:.3f} | 体重={feat_imp[2]:.3f}")
print("="*70)

总结

  • 为什么要分训练集/测试集?

    1. 检验模型真实能力 防止模型死记硬背
    2. 避免过拟合 判断模型是否学对了规律
    3. 行业通用标准 建模必做步骤
  • 参数原理

参数名 参数值 含义 & 核心作用
stratify y 分层抽样
强制训练集/测试集的标签(0/1)比例
与原始数据完全一致
避免极端拆分、评估失真
test_size 0.2 测试集占比20%
训练集8条、测试集2条
兼顾模型学习规律+公平测试
小样本黄金比例
random_state 42 固定随机种子
锁定数据拆分结果
代码可复现
评估结果可对比
  • 为什么要用流水线?

    • 彻底杜绝数据泄露
    • 标准化和模型一体化封装
    • 代码简洁无坑
    • 工业级最优写法
  • 但是测试集

    • 只有两个特征值
      • 对应标签0 和 1
    • 蒙对一个
      • 就有 50%的正确率?
    • 这正确率 靠得住吗?
  • 在数据集不变的情况下

    • 让 模型 更靠谱呢?
  • 我们下次再说👋


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