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`
----
上次核心原理
- 样本随机抽样
- 特征随机选择
- 独立预测+投票:
- 「少数服从多数」
-
如果 只有这10个人的数据
- 不但要 拟合(fitting)
- 还要 测试验证
- 该如何做呢?
- 把10条数据切分成
- 训练集 8条
- 测试集 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) # 测试集准确率
- 为什么要分 训练集 和 数据集?
- 如果把全部数据(10条) 都用来训练模型
- 模型会把这10条球员的「身高/臂展/体重」和「是否有潜质」的规律死记硬背下来
- 用这份数据再去测试模型
- 准确率一定是
100% - 看起来模型特别好
- 模型会把这10条球员的「身高/臂展/体重」和「是否有潜质」的规律死记硬背下来
- 但这是「作弊」
- 因为模型只是记住了已知数据,遇到新的球员数据,预测准确率会暴跌
- 简单说:不拆分,你永远不知道模型好不好用,只能知道模型「记数据」的能力好不好
✅ 拆分的本质逻辑:
训练集 → 让模型学习规律(只给模型看8条球员数据,让它学「什么样的身体条件=有潜质」)
测试集 → 让模型考试打分(把剩下2条模型从没见过的球员数据给它,看它能不能判断对)
测试集的准确率,才是模型的「真实分数」!
-
用的是随机森林
- 哪怕样本少也不容易过拟合
- 但这个原则通用所有模型
-
过拟合
- 模型把训练数据的噪音、偶然规律当成了「通用规律」
- 比如:训练集里刚好「身高1.88的球员都有潜质」
- 模型就死记「1.88=有潜质」
- 但新球员身高1.87、其他条件更好
- 模型反而判断错
-
测试集就是检验模型有没有「学歪」的照妖镜
- 训练集准确率高、测试集准确率也高 → 模型学好了
- 训练集满分、测试集低分 → 模型学歪了(过拟合)
-
不管是比赛/工作/论文
- 只要做机器学习建模
- 数据集必须拆分训练+测试
- 这是共识
-
不拆分的模型
- 别人直接判定为「无效建模」
- 因为没有任何参考价值
-
为什么要这样拆分?
- 参数 不是随便加的
- 你的数据只有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
- 训练集8条,测试集2条?
原始数据一共只有 10条样本 这个比例是「小样本专属最优比例」:
test_size=0.2代表- 测试集占20%(2条)
- 训练集占80%(8条)
- ✅ 为什么不能设0.3/0.5?
- 如果测试集设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条」;
-
你第二次运行代码
- 训练集又变成了另一批
- 测试集也变了
-
每次运行的「训练/测试准确率」都不一样
- 你根本没法判断模型到底好不好
-
- 固定随机拆分的结果
- 保证每次运行代码
- 拆分出的训练集和测试集都是一模一样的
- 结果可复现
- 可对比
补充:42是行业通用值 换成1/10/2026都可以 只要固定就行
- 分层抽样[重中之重]
- 代码里最关键的参数】
- 这是份代码里最不能少的参数
- 没有之一!
- 先看你的标签分布:
y = [0,0,0,0,0,1,1,1,1,1] # 0=无潜质 5条,1=有潜质 5条 → 标签比例 1:1- ❌ 如果不加
stratify=y- 会发生什么?
- 随机拆分时
- 大概率会出现「极端情况」
- 比如:
- 训练集8条全是「0」
- 测试集2条全是「1」;
- 模型训练时只学了「无潜质」的规律
- 测试时让它判断「有潜质」
- 准确率直接为0;
- 这个结果完全是拆分导致的
- 不是模型的问题
- 模型评估彻底失真
- 强制让训练集和测试集的「标签比例」和原始数据完全一致!
- 原始y
- 0占50%
- 1占50%;
- 训练集(8条)
- 0占4条 : 1占4条
- 50%:50%;
- 测试集(2条):0占1条,1占1条 → 50%:50%;
- 这样拆分的测试集,才能公平的检验模型,评估结果100%真实。
- 原始y
✅ 结论:只要你的标签是「分类标签」(0/1)
且各类别数量差不多 必须加 stratify=y!
保证标签平衡
- stratify 这个词怎么来的?
- Stratus
- 源自拉丁语 sternere 的过去分词 stratus
- 意为“铺开”、“铺平”或“散布”
- 演变:stratum 指代“铺盖物”或“层”
- 现代英语单词 Stratum(地层/阶层)
- 后缀:-fy
- 源自拉丁语动词后缀 -ficare
- 由 facere(做、使成为)演变而来
- 含义: 表示“使……化”或“使成为……”
-
Stratify 的字面意思是“使形成层状”
- (To make into layers)
- 地质学(岩石分层)
- 社会学(社会阶级分层)
- 生物学(种子催芽处理)
- (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)-
流水线的内部逻辑:
- 调用
fit时- 只对
X_train执行:标准化器fit_transform(X_train) - 模型训练
- 只对
- 调用
score(X_test, y_test)时- 自动对
X_test执行:标准化器transform(X_test) - 模型预测
- 自动对
- 全程
- 标准化器只见过训练集
- 测试集的信息完全隔离
- 从根源杜绝数据泄露。
- 调用
-
✅ 总结:拆分数据集 + 流水线 = 建模的「双保险」
- 这两个组合在一起
- 就是规范、正确、无坑的最优写法
- 这份代码
- 就是小样本机器学习建模的标准答案了 ✔️
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)-
为什么要分训练集/测试集?
- 检验模型真实能力 防止模型
死记硬背 - 避免过拟合 判断模型是否学对了规律
- 行业通用标准 建模必做步骤
- 检验模型真实能力 防止模型
-
参数原理
| 参数名 | 参数值 | 含义 & 核心作用 |
|---|---|---|
stratify |
y |
分层抽样 强制训练集/测试集的标签(0/1)比例 与原始数据完全一致 避免极端拆分、评估失真 |
test_size |
0.2 |
测试集占比20% 训练集8条、测试集2条 兼顾模型学习规律+公平测试 小样本黄金比例 |
random_state |
42 |
固定随机种子 锁定数据拆分结果 代码可复现 评估结果可对比 |
-
为什么要用流水线?
- 彻底杜绝数据泄露
- 标准化和模型一体化封装
- 代码简洁无坑
- 工业级最优写法
-
但是测试集
- 只有两个特征值
- 对应标签0 和 1
- 蒙对一个
- 就有 50%的正确率?
- 这正确率 靠得住吗?
- 只有两个特征值
-
在数据集不变的情况下
- 让 模型 更靠谱呢?
-
我们下次再说👋
- 本文来自 oeasy Python 系统教程。
- 想完整、扎实学 Python,
- 搜索 oeasy 即可。



