我们把数据分成训练集、验证集和测试集,是为了让模型从其中一部分数据中学习,在第二部分数据上进行调优,并在它从未见过的第三部分数据上得到公平的评估。

在这篇博客中,我们将学习为什么要将数据分成训练集、验证集和测试集。我们还会了解这三个集合各自的作用、在模型已经见过的数据上测试模型会出现什么问题、为什么在调优模型时仅有一个测试集是不够的、每个集合应分配多少数据、在集合之间泄露信息的常见错误,以及针对我们的使用场景何时使用哪种划分方式。

我们将涵盖以下内容:

  • 在已见数据上测试的问题
  • 什么是训练集?
  • 什么是测试集?
  • 为什么我们需要验证集?
  • 三个集合如何协同工作
  • 每个集合应分配多少数据?
  • 常见错误:数据泄露
  • 何时简单划分不够用

我是 Amit Shekhar,Outcome School 的创始人。我教导和指导过许多开发者,他们的努力为他们赢得了高薪的技术工作,我还帮助许多科技公司解决了它们的独特问题,并创建了许多被顶级公司使用的开源库。我热衷于通过开源、博客和视频分享知识。

我在 Outcome School 教授 AI 和机器学习。

让我们开始吧。

在已见数据上测试的问题

假设我们想构建一个模型,它查看一套房子并预测其价格。我们数据中的每个样本就是一套房子。输入包括房子的面积、房间数量和位置等信息。正确答案就是该房子的成交价格。

面积(平方英尺)房间数位置价格(正确答案)
12002市区50 lakh
20003郊区70 lakh
9001市区35 lakh
... ... ... ...

在这里,我们可以看到每一行就是一套房子。模型查看许多这样的行,并学习输入与价格之间的模式。

我们有一篇关于监督学习与无监督学习的详细博客,深入介绍了这种从正确答案中学习的方式。

现在,问题是:模型学习完成后,我们如何知道它学得好不好?了解这一点的最好方法是通过一个例子。

假设一位老师正在为学生准备考试。老师给学生 100 道练习题以及答案。学生们反复学习这 100 道题。

现在,到了考试当天,老师给出的正是同样的这 100 道题。

每个学生都得 100 分(满分 100 分)。

学生是真的学会了这门课,还是只是记住了答案?我们无从得知。一个背下答案的学生和一个真正理解课程的学生会得到相同的分数。

机器学习模型也会发生同样的事情。如果我们用模型学习过的同一批数据来检验它,它可能只是记住了答案。它看起来完美无缺,但在新数据上可能表现得很糟糕。

这种“记住而非学习”的问题被称为过拟合。

过拟合意味着模型把训练样本学得太死板,以至于在新样本上表现不佳。

举例来说,假设我们数据中有一套房子因为房主急于搬家而以极低的价格售出。这个低价是偶然事件,而不是规律。过拟合的模型连这种偶然事件也记住了,于是以后会错误地预测类似房子的价格偏低。

因此,在见过的数据上的分数毫无参考价值。我们需要一些模型从未见过的数据。这就是我们要拆分数据的原因。

什么是训练集?

训练集是数据中供模型学习的部分。

训练集就像模型的练习册。

模型查看训练集中的样本,做出预测,看看自己错得有多离谱,然后反复调整自己。这个调整过程称为训练。

我们有一篇关于梯度下降的详细博客,逐步解释了这个调整过程是如何运作的。

训练集通常是我们数据中最大的一部分,因为模型需要大量样本才能学好规律。

计划转型进入 AI 领域吗?

无论你今天在哪个技术领域工作,只要付出努力,你都可以转型到以下这些角色:

  • AI 工程师
  • 前沿部署工程师
  • AI Agent 工程师
  • Harness 工程师
  • 语音 AI 工程师
  • AI 架构师
  • LLM 工程师
  • LLM 推理工程师
  • 生成式 AI 工程师
  • MLOps 工程师
  • 机器学习工程师
  • 深度学习工程师

Outcome School 的 AI 与机器学习课程能带你实现这一目标。

什么是测试集?

测试集是我们锁起来、只在最后使用一次的数据部分,用来衡量最终模型的好坏。

测试集就像一场期末考试,考题是学生们从未见过的。

回到我们老师的例子:老师现在给出 100 道练习题供学生复习,但另外保留 20 道全新的题目用于考试。如果学生在这些 20 道新题上得分很高,我们就可以相信这个学生是真的理解了这门课。

同样,如果我们的模型在测试集上表现出色,我们就可以相信它在从未见过的真实世界数据上也会有良好表现。

非常重要:测试集必须保持原样,直到最后。如果我们在构建模型的过程中不断查看测试集,它就会慢慢失去作为公平考试的意义。

现在,此时我们可能会认为两个数据集就足够了:一个用于学习,一个用于测试。让我们看看为什么事实并非如此。

为什么我们需要验证集?

在构建模型时,我们必须做出许多选择。例如:

  • 模型的规模应该有多大?
  • 我们要训练它多长时间?
  • 它从每个错误中学习的速度有多快?

这些选择不是由模型自己学到的。而是我们这些构建者在训练之前设定的。这些选择被称为超参数。

超参数是我们为模型选择的设置,与模型自己学习到的值相对。

假设我们想挑选最佳的模型规模。我们尝试一个小模型、一个中等模型和一个大模型。我们需要一些数据来比较它们。

假设我们使用测试集来进行这种比较。我们在测试集上检查这三个模型,并挑选得分最好的那个。

这种方法的问题在于,我们此时已经用测试集做出了决策。我们挑选的是恰好在这些测试样本上表现最好的模型。因此,测试分数不再是一场公平的考试。它有点过于乐观,因为我们是看着测试集来选出获胜者的。

于是,验证集应运而生,来解决这个问题。

验证集是数据中单独的一部分,我们用它来比较模型和调整超参数,从而让测试集保持干净,留作最终检查之用。

验证集就像模拟考试。学生多次参加模拟考试,以了解自己的水平并调整学习方法。而真正的期末考试只进行一次。

给你的一个小提示

无论你在哪个技术领域工作,都要熟悉这些主题:

  • LLM
  • RAG
  • MCP
  • Agent
  • 微调
  • 量化

我们把它们全部整合在一个视频中:

AI 工程详解:LLM、RAG、MCP、Agent、微调与量化

无需停止阅读——把它加入书签,等有空时再看。未来的你会感谢现在的自己。

现在,让我们回到主题。

三个集合如何协同工作

现在,让我们把所有内容整合起来。假设我们的数据中有 10,000 套房屋。我们可以按如下方式划分:

   All data: 10,000 houses
   +--------------------------------------+-----------+-----------+
   |           Training set               | Validation|   Test    |
   |           7,000 houses               |   1,500   |   1,500   |
   +--------------------------------------+-----------+-----------+
     Model learns from here                Tune here   Final exam
                                                       (used once)

在这里,我们可以看到最大的一部分用于训练,两个较小的部分分别用于验证和测试。

完整的工作流程如下所示:

第 1 步:在训练集上训练模型。

第 2 步:在验证集上检查模型并记录得分。

第 3 步:更改一个超参数,例如模型规模,在训练集上重新训练,然后再次在验证集上检查。

第 4 步:多次重复第 3 步。挑选能给出最佳验证得分的设置。

第 5 步: 最后,仅对测试集检查一次所选模型。这个数字是对模型在真实世界中表现的诚实估计。

为了便于理解,我来将每个数据集的作用整理成表格。

数据集用途查看频率
训练集学习模式一直使用
验证集比较模型和调整设置多次使用
测试集诚实的最终评分只用一次,在最后

让我们看看用 Python 实现这种划分的代码,如下所示:

from sklearn.model_selection import train_test_split

# First, keep 15% aside as the test set
X_temp, X_test, y_temp, y_test = train_test_split(
    X, y, test_size=0.15, random_state=42
)

# Then, split the rest into training and validation
X_train, X_val, y_train, y_val = train_test_split(
    X_temp, y_temp, test_size=0.176, random_state=42
)

在这里,我们有:

  • X 是输入,比如面积、房间数和位置。y 是正确答案,比如价格。
  • 第一次调用取出 15% 的数据作为测试集,并将其放在一边。
  • 第二次调用将剩余的 85% 划分为训练集和验证集。取 85% 的 17.6% 大约相当于全部数据的 15% 用于验证,因此我们最终得到接近 70%、15% 和 15% 的划分。
  • random_state=42 使打乱操作可重复,因此每次运行代码时都会得到相同的划分。

要深入学习超参数调优、模型开发与训练以及评估与测试,请查看 Outcome School 的 AI 与机器学习课程。

每个集合应分配多少数据?

合适的划分比例取决于我们拥有多少数据。

当数据量较小或中等时: 常见的选择是 70% 用于训练,15% 用于验证,15% 用于测试。另一种常见选择是 80%、10% 和 10%。

当数据量非常大时: 假设我们有 1000 万个样本。即使只取 1% 也有 10 万个样本,足以获得可靠的评分。因此,我们可以使用类似 98% 用于训练、1% 用于验证、1% 用于测试的划分。而且,更多的训练样本通常能带来更好的模型。

验证集和测试集必须足够大,才能给出可信的评分。其余数据都可以用于训练。

为了便于理解,我来将这些整理成表格。

数据量训练集验证集测试集
几千个样本70%15%15%
几万个样本80%10%10%
数百万个样本98%1%1%

常见错误:数据泄露

大多数时候,我们在划分数据时会犯错,而最危险的错误叫做数据泄露。

数据泄露是指验证集或测试集中的某些信息悄悄进入了训练过程,因此评分看起来比实际更好。

这就像一个学生偷偷看过几道期末考试题。

让我们看看泄露发生的常见方式。

错误 1:重复样本。 如果同一套房子在我们的数据中出现两次,一份可能落入训练集,另一份落入测试集。模型已经见过这套房子,因此测试评分会虚高。我们必须在划分之前去除重复项。

错误 2:在拆分之前准备数据。 在训练之前,我们常常把所有数值缩放到相近的范围,例如从每个房屋面积中减去平均面积。假设我们用全部数据来计算这个平均值。这样一来,训练数据就带上了关于测试数据的一点点线索,因为这个平均值包含了测试样本。我们必须先拆分,然后只用训练集来计算这类数值。

错误 3:忽略时间。 假设我们要预测明天的销售额。如果我们随机打乱数据,模型可能会用下个月的数据训练,却用上个月的数据测试。这相当于看到了未来。在现实生活中,我们永远不会有未来的数据。因此,对于基于时间的数据,我们必须用过去的数据训练,用之后的时间段测试。

   Wrong for time data (random shuffle):
   Train: Jan, Mar, May, Jul ...   Test: Feb, Apr, Jun ...

   Right for time data (split by time):
   Train: Jan - Sep            Validation: Oct     Test: Nov - Dec

在这里可以看到,正确的方法是模型始终从过去学习,并在未来接受检验,就像现实世界一样。

错误 4:同一个人出现在两个集合中。 假设每位患者有多张照片。如果同一位患者的照片同时进入训练集和测试集,模型可能会识别出患者本人而不是疾病。我们必须把同一个人的所有照片放在同一个集合中。

错误 5:反复查看测试集。 如果每次改动后都查看测试分数,我们就会慢慢把模型调得贴合测试集。测试集就变成了第二个验证集。我们只能查看它一次。

保持更新: 订阅我们的新闻通讯,第一时间将我们最新的 AI 和机器学习博客文章送到您的邮箱。

何时简单划分不够用

有时我们的数据非常少,比如只有 500 个样本。留出 15% 用于验证,就只剩下 75 个样本来做判断,而且分数会因哪些样本碰巧落入验证集而发生很大变化。

这时就需要用到交叉验证。在交叉验证中,我们把不在测试集中的数据分成例如 5 等份。我们训练 5 次,每次由不同的部分充当验证集。然后取这 5 个分数的平均值。这样得到的分数要稳定得多。

即使使用交叉验证,我们仍然要单独留出一个测试集用于最终检验。

注意: 当某些答案很稀少时,例如只有 2% 的邮件是垃圾邮件,随机拆分可能导致测试集中几乎没有垃圾邮件。因此,我们使用分层拆分,也就是让每个集合保持与完整数据相同的答案比例。在代码中,我们可以把

stratify=y

传给

train_test_split

来实现这一点。

现在,我们应该已经理解了为什么要将数据拆分为训练集、验证集和测试集。

常见问题

什么是机器学习中的过拟合?

过拟合是指模型把训练样本学得太死板,以至于在新样本上表现不佳。它记住的是答案,而不是学到规律。如果用它学习过的同一数据来检验,它看起来完美无缺,但在新数据上可能表现得很差。

验证集和测试集有什么区别?

验证集会被多次使用,用来比较模型和调整超参数,就像模拟考试。测试集则被锁起来,只在最后使用一次,就像真正的期末考试。把两者分开意味着最终的测试分数仍然是一个诚实的估计。

我们可以多次查看测试集吗?

不可以。我们应该只查看测试集一次。如果每次改动后都查看测试分数,我们就会慢慢把模型调得适应测试集,它就变成了第二个验证集。这样测试分数会偏高,不再是一场公平的考试。

按时间排序的数据在划分前应该打乱吗?

不应该。对于按时间排序的数据,随机打乱会让模型用较晚的数据训练,却用较早的数据测试,相当于预知未来。正确的做法是按时间划分:用过去的数据训练,用接下来一段时期的数据验证,用更晚的数据测试,就像现实世界一样。

什么是分层划分?

分层划分是指每个集合保持与完整数据相同的答案比例。当某些答案很稀少时我们会使用它,例如只有 2% 的邮件是垃圾邮件时,因为随机划分可能导致测试集中几乎没有垃圾邮件。在代码中,我们把

stratify=y

传给

train_test_split

.

本次就到这里。

谢谢

Amit Shekhar Outcome School 创始人

你可以通过以下方式与我联系:

关注 Outcome School: