- 作者
-
-
- 姓名
- Amit Shekhar
- 发布于
-
我们把数据分成训练集、验证集和测试集,是为了让模型从其中一部分数据中学习,在第二部分数据上进行调优,并在它从未见过的第三部分数据上得到公平的评估。
在这篇博客中,我们将学习为什么要将数据分成训练集、验证集和测试集。我们还会了解这三个集合各自的作用、在模型已经见过的数据上测试模型会出现什么问题、为什么在调优模型时仅有一个测试集是不够的、每个集合应分配多少数据、在集合之间泄露信息的常见错误,以及针对我们的使用场景何时使用哪种划分方式。
我们将涵盖以下内容:
- 在已见数据上测试的问题
- 什么是训练集?
- 什么是测试集?
- 为什么我们需要验证集?
- 三个集合如何协同工作
- 每个集合应分配多少数据?
- 常见错误:数据泄露
- 何时简单划分不够用
我是 Amit Shekhar,Outcome School 的创始人。我教导和指导过许多开发者,他们的努力为他们赢得了高薪的技术工作,我还帮助许多科技公司解决了它们的独特问题,并创建了许多被顶级公司使用的开源库。我热衷于通过开源、博客和视频分享知识。
我在 Outcome School 教授 AI 和机器学习。
让我们开始吧。
在已见数据上测试的问题
假设我们想构建一个模型,它查看一套房子并预测其价格。我们数据中的每个样本就是一套房子。输入包括房子的面积、房间数量和位置等信息。正确答案就是该房子的成交价格。
| 面积(平方英尺) | 房间数 | 位置 | 价格(正确答案) |
|---|---|---|---|
| 1200 | 2 | 市区 | 50 lakh |
| 2000 | 3 | 郊区 | 70 lakh |
| 900 | 1 | 市区 | 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: