在机器学习中,模型从数据中学习规律。然而,直接将全部数据交给模型训练,并不能可靠地判断模型是否真正有效。一个模型即使在训练数据上表现优异,也可能只是“记住”了已有样本,而非学会了可推广到新样本的规律。为了评估模型在未见数据上的性能、避免因过拟合而产生误导性结论,必须采用合理的数据划分策略。
这就引出了监督学习中的核心问题:完整的数据集通常需要拆分为不同角色,分别用于训练、验证和测试。其中最基本、最常见的划分方式,是区分训练集与测试集。
一、数据集
1、数据集的基本含义
数据集(Dataset)是指围绕某个问题收集而来的、由多个样本构成的数据整体。
例如:
• 在房价预测中,数据集是一批房屋及其价格记录
• 在垃圾邮件识别中,数据集是一批邮件及其标签
• 在手写数字识别中,数据集是一批数字图像及其类别标签
图 1 数据集由多个样本构成
图 1 表明,数据集是许多个样本组成的整体;而在监督学习中,这个整体通常最终会进一步拆分为不同用途的数据子集。
在监督学习中,数据集通常表示为输入特征 X 与目标 y 的组合。
数据量固然重要,但若模型既用某批数据学习,又用同一批数据证明自身效果,则得到的评价往往过于乐观。
模型可能仅对已见数据拟合良好,而对新数据表现不佳。因此,数据集既为模型提供学习材料,也为模型评估提供依据;这两种角色通常需要在数据划分中分开承担。
在 Scikit-learn 中,一个监督学习数据集最常见的基本形式是:
• X:特征矩阵
• y:目标变量
延伸阅读:
《Scikit-learn:特征矩阵与目标变量》
《Scikit-learn:数据集》
二、训练集
训练集(Training Set)是专门用于让模型学习规律的那部分数据。
模型在训练阶段利用训练集中的样本和标签调整参数、建立结构,形成对任务的初步认识。
训练集的作用,不是单纯“喂数据”,而是为模型提供一个学习环境。
例如:
• 在线性回归中,训练集帮助模型学习回归系数
• 在逻辑回归中,训练集帮助模型学习分类边界
• 在决策树中,训练集帮助模型生长划分结构
• 在 K 近邻中,训练集样本本身构成了预测时参考的邻居集合
训练集是模型形成内部参数或结构的依据。
模型在训练集上表现良好,只能说明其对已见数据拟合得不错。模型可能只是学到了训练样本中的特殊性而非一般规律。
因此,训练集不能同时承担“学习”和“最终证明模型可靠”两种职责。
三、测试集
测试集(Test Set)是指在训练阶段不让模型接触、留到训练完成后再用于评估模型表现的那部分数据。
其意义在于模拟“未来的新样本”,帮助判断模型在未见数据上的表现。
测试集的作用,不是让模型继续学习,而是让模型“接受检验”。
如果模型学到了真正可推广的规律,其在测试集上通常也会保持良好表现;反之,若只是记住了训练集,测试集上的表现会明显变差。
因此,测试集承担评估模型泛化能力的角色。
若模型在训练过程中已经见过测试集,则测试集失去“新数据”的意义,测试结果不再能真实反映模型面对未知样本时的能力。一旦测试数据以任何方式参与了模型构建,评估就不再独立。
四、为什么训练集和测试集必须分开
若同一份数据既用于训练又用于评估,模型相当于做了一场“提前看过答案的考试”,所得分数通常偏高。这就是过拟合风险:模型可能对训练样本拟合得很好,但无法有效推广到新样本。
把训练集与测试集分开,本质上解决了“学习”和“检验”角色混淆的问题:
• 训练集负责让模型学习
• 测试集负责检验模型是否真正有效
图 2 训练集与测试集的基本关系
这种分离使我们能够更合理地估计模型在未见数据上的表现。
在机器学习中,测试集表现通常比训练集表现更能反映模型的泛化能力:
• 训练误差反映模型对已见数据的拟合程度
• 测试误差更接近模型对未见数据的适应能力
五、验证集:何时需要第三部分数据
在更完整的机器学习流程中,除了训练集和测试集,有时还需要验证集(Validation Set)。
验证集通常用于:
• 调整超参数
• 比较不同模型
• 选择更合适的特征处理方式
如果模型选择、参数调整和最终评估都在同一批测试数据上完成,那么测试集就会被“用脏”,失去作为最终独立评估依据的作用。
当样本量不大时,再单独切出一部分验证集会显著减少训练数据。
因此实践中常采用交叉验证(Cross Validation)来承担验证集的角色,从而在有限数据下更稳健地评估模型。
可以把三者粗略理解为:
• 训练集:让模型学习
• 验证集:帮助选择模型与参数
• 测试集:给最终模型做独立评估
这三者不能随意混用,否则对模型的评价会越来越乐观,而越来越不可靠。
六、数据划分工具:train_test_split
train_test_split 是 Scikit-learn 中最常用的数据划分工具之一,用于将数组或矩阵随机拆分为训练子集和测试子集。
其典型用法为:
X_train, X_test, y_train, y_test = train_test_split(X, y, ...)常用参数说明:
• X:特征矩阵
• y:目标变量
• test_size:测试集比例或样本数
• train_size:训练集比例或样本数
• random_state:随机种子,便于结果复现
• shuffle:是否在划分前打乱数据
• stratify:按类别分层划分,常用于分类任务中保持类别比例一致
其中,stratify 有助于使训练集和测试集中的类别分布更接近整体数据分布。
在教学、实验和调试中,往往希望多次运行得到相同的数据划分结果,此时需要固定 random_state。这不会使划分“更科学”,但能让实验更可复现。
七、Python 示例:用鸢尾花数据集观察数据划分
下面用 Scikit-learn 自带的鸢尾花数据集,直观展示“完整数据集如何被拆分成训练集和测试集”。
完整代码如下:
import pandas as pdfrom sklearn.datasets import load_irisfrom sklearn.model_selection import train_test_split # 1. 加载数据iris = load_iris()X = iris.datay = iris.target # 2. 划分训练集和测试集X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y) # 3. 输出形状信息print("原始数据集 X 的形状:", X.shape)print("训练集 X_train 的形状:", X_train.shape)print("测试集 X_test 的形状:", X_test.shape) print("原始标签 y 的形状:", y.shape)print("训练标签 y_train 的形状:", y_train.shape)print("测试标签 y_test 的形状:", y_test.shape) # 4. 查看每个集合中的类别分布print("\n原始数据集中各类别样本数:")print(pd.Series(y).value_counts().sort_index()) print("\n训练集中各类别样本数:")print(pd.Series(y_train).value_counts().sort_index()) print("\n测试集中各类别样本数:")print(pd.Series(y_test).value_counts().sort_index())这段代码展示了三个关键事实:
第一,完整数据集会被拆成训练部分和测试部分。
第二,X 与 y 会同步拆分,因此样本与标签的一一对应关系不会被破坏。
第三,由于设置了 stratify=y,训练集和测试集中的类别分布会尽量与原始数据集保持一致。
从结果上看,鸢尾花数据集原本有 150 个样本,如果 test_size=0.2,那么通常会得到:
• 120 个训练样本
• 30 个测试样本
这就是最典型的“80% 训练,20% 测试”的基础划分方式。
八、Python 示例:为什么不能只看训练集结果
下面再给出一个最小监督学习例子,说明为什么训练集和测试集必须分开使用。
from sklearn.datasets import load_irisfrom sklearn.model_selection import train_test_splitfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.metrics import accuracy_score # 1. 加载数据iris = load_iris()X = iris.datay = iris.target # 2. 划分训练集与测试集X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y) # 3. 训练模型model = KNeighborsClassifier(n_neighbors=5)model.fit(X_train, y_train) # 4. 分别在训练集和测试集上预测y_train_pred = model.predict(X_train)y_test_pred = model.predict(X_test) # 5. 比较训练集准确率与测试集准确率train_acc = accuracy_score(y_train, y_train_pred)test_acc = accuracy_score(y_test, y_test_pred) print("训练集准确率:", train_acc)print("测试集准确率:", test_acc)运行这段代码时,常常会看到训练集准确率不低于测试集准确率;测试集准确率有时会略低,这反映了模型在未见数据上的表现通常更接近真实应用情形。
这正说明:模型在已见数据上的表现,通常会比在未见数据上的表现更乐观。也正因为如此,我们真正关心的是估计模型在未见数据上的泛化表现,而不是只在训练样本上打分。
九、常见误解与澄清
1、误以为“测试集只是可有可无的一部分数据”
不是。测试集的核心作用是独立评估模型在未见数据上的表现。如果没有测试集,模型评价就容易失真。
2、误以为“测试集越大越好”
也不是。测试集越大,训练集就越小。测试集太小会让评估不稳定,训练集太小又会影响模型学习效果,因此比例通常需要折中,可通过 test_size 和 train_size 灵活控制这一点。
3、误以为“训练集和测试集只要随便切开就行”
对于分类任务,若类别分布不平衡,随便切分可能会导致训练集和测试集的类别比例失衡。因此常常需要使用 stratify 做分层划分。
4、误以为“测试集也可以顺便拿来调参数”
如果测试集被反复用于调参,它就不再是独立评估依据。更规范的做法是使用验证集或交叉验证来做模型选择,把测试集留到最后一步。
📘 小结
数据集是样本构成的整体,训练集负责让模型学习规律,测试集负责检验模型在未见数据上的表现。把数据划分为训练集和测试集,并不是形式上的流程,而是为了避免模型评价过于乐观,从而更合理地估计其泛化能力。真正理解数据集、训练集与测试集,意味着开始明白:机器学习不仅要训练模型,更要用正确的数据角色分工来判断模型是否可靠。
“点赞有美意,赞赏是鼓励”