增加训练数据是否值得,先看同一个模型在不同样本量下的训练分数和验证分数。验证分数随样本量继续上升,收集同分布、标签可靠的数据可能有收益;如果已经趋于平稳,应同时检查模型容量、特征信息和标签质量。一次曲线不够证明哪种改动必然有效。
learning_curve 输出的三个量分别是什么
scikit-learn 的 learning_curve 在交叉验证的每个训练折里取不同大小的训练子集,重新拟合模型,分别计算训练子集分数与该折验证集分数。这里把返回值中的 test_scores 命名为 valid,因为它属于开发阶段的验证折;最终留出的测试集仍然封存。

返回的 sizes 是实际参与拟合的行数;train 和 valid 每行对应一个样本量,每列对应一个验证折。设置 scoring="accuracy" 后两者都表示准确率。类别严重不平衡时,应按任务改用宏平均 F1、召回率等指标,并重新解释曲线。
准备环境与两个可比较的模型
下面示例在 Windows、Python 3.11.15、CPU 环境实际运行。先安装 Python 3.11,在新建的空文件夹中打开 PowerShell,把完整代码保存为 06-learning-curve.py。创建独立环境并执行:
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install numpy==2.4.6 scikit-learn==1.9.1
.\.venv\Scripts\python.exe 06-learning-curve.py
最后一行在保存代码后执行。macOS 或 Linux 把 Python 路径换成 ./.venv/bin/python。示例不需要 GPU、外部模型下载或付费 API;安装依赖需要联网。不同库版本可能带来数值末位差异,先核对数据行数、标签顺序和输出结构。
本例用人工生成的 600 行二分类数据;留出 120 行作最终测试,只在剩余 480 行上画学习曲线。五折划分时每个训练折有 384 行,因此 train_sizes=1.0 对应 384 行。逻辑回归用 Pipeline 把缩放限制在每个训练子集内;决策树不限制深度,用于观察训练集记忆与验证表现的差距。
执行完整示例
把下面完整代码保存为 06-learning-curve.py,先在固定的教学数据上运行,再用你自己的开发集替换 X_dev 和 y_dev。最终测试集保留到模型和方案确定以后再使用。
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, StratifiedKFold, learning_curve
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
# 人工生成的教学数据;最终测试集不用于学习曲线。
X, y = make_classification(n_samples=600, n_features=12, n_informative=6,
n_redundant=2, class_sep=0.8, flip_y=0.08,
random_state=42)
X_dev, X_test, y_dev, y_test = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42)
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
models = {
'logistic': make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000)),
'tree': DecisionTreeClassifier(random_state=42),
}
print('development/test rows:', len(y_dev), len(y_test))
for name, model in models.items():
sizes, train, valid = learning_curve(
model, X_dev, y_dev, train_sizes=[0.1, 0.3, 0.6, 1.0],
cv=cv, scoring='accuracy', shuffle=True, random_state=42,
n_jobs=1, error_score='raise')
assert train.shape == valid.shape == (4, 5)
assert np.isfinite(train).all() and np.isfinite(valid).all()
print(name, 'rows train_mean valid_mean valid_std gap')
for n, tr, va in zip(sizes, train, valid):
print(n, f'{tr.mean():.3f}', f'{va.mean():.3f}',
f'{va.std():.3f}', f'{tr.mean()-va.mean():.3f}')
print('held-out test not evaluated')
这个示例同时给折间划分和训练前缀抽样设置了随机种子。StratifiedKFold 尽量保持折间类别比例;learning_curve(shuffle=True) 则避免直接拿原始顺序的前几行当训练子集。二者处理的是不同步骤。最小子集仍可能缺少某类,不能因为外层分层就省掉检查。
运行结果与正确读法
development/test rows: 480 120
logistic rows train_mean valid_mean valid_std gap
38 0.874 0.754 0.045 0.120
115 0.802 0.771 0.022 0.031
230 0.806 0.775 0.029 0.031
384 0.784 0.775 0.037 0.009
tree rows train_mean valid_mean valid_std gap
38 1.000 0.690 0.063 0.310
115 1.000 0.729 0.021 0.271
230 1.000 0.744 0.024 0.256
384 1.000 0.725 0.035 0.275
held-out test not evaluated
先确认 development/test rows 为 480 和 120,再核对每个模型都有四行样本量,训练与验证结果的矩阵形状均为 (4, 5),且所有分数都是有限数。
| 本例观察 | 能据此采取的下一步 |
|---|---|
| 逻辑回归验证均分从 0.754 升至 0.775,后两档相同到三位小数 | 保留现有基线,另查特征、标签和错误样本;小幅收益小于折间波动时,不承诺继续加数据一定提升 |
| 决策树训练均分始终 1.000,验证分数明显更低 | 尝试限制深度或增加最小叶节点样本数,在同一开发划分重新比较 |
| 决策树验证均分先上升,最后降至 0.725 | 记录不单调现象;增大样本量后表现变差也是合法结果,需要重复划分和错误分析 |
这里的 gap 是训练均分减验证均分,并非诊断阈值。训练分数与验证分数都低时,可能是模型约束太强,也可能是特征没有目标信息、标注有误或任务本身困难;只看曲线不能区分这些原因。
替换真实数据前,处理这些边界
学习曲线给出当前数据、当前划分和当前模型下的经验线索,不能证明新增任意数据一定有效,也不能把交叉验证的标准差解释成置信区间。
每个验证折必须独立于训练过程。客户多次出现、同设备连续记录、同作者文本等有分组关系的数据应按组划分;时间序列应按时间验证,不套用本例随机分层。如果预先在全量数据上做标准化、词表拟合或特征选择,曲线会包含数据泄漏。
代码用 error_score="raise" 和有限数检查,遇到单类别拟合、非法输入或数值异常会直接报错。先修复数据与划分;不要把缺失分数填成零后继续比较。运行时间约随模型数、样本量档位和折数增长,本例是 2×4×5 次拟合,不代表大数据任务的耗时。
完成真实数据的曲线后,写下一条可检验的假设,例如“新增的少数类样本能提高开发集召回率”,再单独验证。模型和方案定下来后才使用封存测试集,避免把多次挑选得到的最佳验证分数当最终性能。
相关官方资料
本文读取官方资料的日期为 2026 年 10 月 1 日;示例输出来自上面注明的本地环境。
- learning_curve API:返回矩阵、样本量含义、shuffle 与 error_score 的行为。
- scikit-learn 常见陷阱:训练与评估隔离、预处理泄漏及 Pipeline 的使用。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30578.html