如何(以及为何)创建好的验证集

技术
作者

Rachel Thomas

发布日期

2017年11月13日

一个司空见惯的场景:一个看似令人印象深刻的机器学习模型在实际部署时却完全失败。其后果包括现在对机器学习持怀疑态度、不愿再次尝试的领导者们。这种情况为何会发生?

导致开发结果与生产结果之间脱节的最可能原因之一是验证集选择不当(甚至更糟,根本没有验证集)。根据数据的性质,选择验证集可能是最重要的步骤。虽然 sklearn 提供了 train_test_split 方法,但该方法只是随机选取数据的子集,这对于许多现实世界的问题来说是一个糟糕的选择。

训练集验证集测试集的定义可能相当微妙,并且术语有时使用不一致。在深度学习社区,“测试时推理”(test-time inference)通常用来指在生产环境中对数据进行评估,但这并非测试集的技术定义。如上所述,sklearn 有 train_test_split 方法,但没有 train_validation_test_split。Kaggle 只提供训练集和测试集,但要取得好成绩,你需要将他们的训练集分割成你自己的验证集和训练集。此外,事实证明 Kaggle 的测试集实际上又细分为两个子集。许多初学者感到困惑也就不足为奇了!我将在下文讨论这些细节。

首先,什么是“验证集”?

创建机器学习模型时,最终目标是使其在新数据上准确,而不仅仅是在你用于构建模型的数据上准确。考虑下面针对一组数据的 3 个不同模型的示例

欠拟合与过拟合
来源:吴恩达的 Coursera 机器学习课程

对于图示数据点,最右边模型(蓝色曲线几乎完美地穿过红色点)的误差最低,但这并不是最好的选择。这是为什么?如果你收集一些新的数据点,它们很可能不会落在右边图表中的那条曲线上,而会更接近中间图表中的曲线。

其核心思想是

  • 训练集用于训练给定的模型
  • 验证集用于在不同模型之间进行选择(例如,随机森林还是神经网络更适合你的问题?你想要一个有40棵树还是50棵树的随机森林?)
  • 测试集告诉你做得如何。如果你尝试了很多不同的模型,可能只是偶然在验证集上得到一个表现良好的模型,而测试集有助于确保这不是偶然情况。

验证集和测试集的一个关键特性是它们必须代表你未来将看到的新数据。这听起来像是一个不可能完成的任务!顾名思义,你还没有看到这些数据。但你仍然知道关于它的一些事情。

随机子集何时不够好?

查看一些示例会很有启发性。虽然其中许多示例来自 Kaggle 竞赛,但它们代表了你在工作中会遇到的问题。

时间序列

如果你的数据是时间序列,随机选择数据子集既会太容易(你可以看到你要预测日期之前和之后的数据),又不能代表大多数商业用例(在这些用例中,你使用历史数据来构建未来使用的模型)。如果你的数据包含日期,并且你正在构建一个将来使用的模型,你会希望选择具有最新日期的连续部分作为验证集(例如,可用数据的最后两周或最后一个月)。

假设你想将下面的时间序列数据分割成训练集和验证集

时间序列数据

随机子集是一个糟糕的选择(太容易填补空白,并且不能指示你在生产环境中需要什么)

你的训练集的一个糟糕选择

使用较早的数据作为训练集(较晚的数据作为验证集)

你的训练集的一个更好选择

Kaggle 目前正在举办一项竞赛,内容是预测厄瓜多尔连锁杂货店的销售额。Kaggle 的“训练数据”从 2013 年 1 月 1 日到 2017 年 8 月 15 日,测试数据则涵盖 2017 年 8 月 16 日到 2017 年 8 月 31 日。一个好的方法是将 2017 年 8 月 1 日到 8 月 15 日用作验证集,而将所有更早的数据用作训练集。

新的人、新的船、新的……

你还需要思考,你在生产环境中进行预测的数据,可能在哪些方面与你用于训练模型的数据存在质的差异

在 Kaggle 的分心驾驶员检测竞赛中,独立数据是驾驶员在车内方向盘前的照片,而因变量是一个类别,例如发短信、吃东西或安全地向前看。如果你是一家保险公司,利用这些数据构建模型,请注意,你最感兴趣的是模型对你以前从未见过的驾驶员的表现(因为你可能只有一小部分人的训练数据)。Kaggle 竞赛也是如此:测试数据包含未用于训练集的人员。

同一个人一边开车一边打电话的两张照片。

如果你将上面的一张图片放在训练集中,另一张放在验证集中,你的模型看起来会比对新人的表现更好。另一种看法是,如果你在训练模型时使用了所有这些人,你的模型可能会对这些特定人员的特殊性过拟合,而不是只学习状态(发短信、吃东西等)。

类似的动态也发生在Kaggle 渔业监测竞赛中,该竞赛旨在识别渔船捕获的鱼类物种,以减少对濒危种群的非法捕捞。测试集包含未出现在训练数据中的船只。这意味着你的验证集需要包含不在训练集中的船只。

有时,你的测试数据将如何不同可能并不清楚。例如,对于使用卫星图像的问题,你需要收集更多信息,了解训练集是否只包含某些地理位置,或者是否来自地理上分散的数据。

交叉验证的危险性

sklearn 没有 train_validation_test 分割的原因是,它假定你经常会使用交叉验证,其中训练集的不同子集充当验证集。例如,对于 3 折交叉验证,数据被分成 3 个集合:A、B 和 C。模型首先在 A 和 B 的组合上作为训练集进行训练,并在验证集 C 上进行评估。接下来,模型在 A 和 C 的组合上作为训练集进行训练,并在验证集 B 上进行评估。以此类推,最终将 3 折的模型性能进行平均。

然而,交叉验证的问题在于,由于上述各节所述的所有原因,它很少适用于现实世界的问题。交叉验证仅在你可以随机打乱数据以选择验证集的情况下才有效。

Kaggle的“训练集” = 你的训练集 + 验证集

Kaggle 竞赛的一个优点是,它们迫使你更严格地思考验证集(为了取得好成绩)。对于 Kaggle 的新手来说,这是一个托管机器学习竞赛的平台。Kaggle 通常将数据分成你可以下载的两个集合

  1. 训练集,其中包括独立变量因变量(你要预测的内容)。以厄瓜多尔杂货店预测销售额为例,独立变量包括商店 ID、商品 ID 和日期;因变量是销售数量。以试图确定驾驶员是否正在进行危险行为为例,独立变量可以是驾驶员的照片,因变量是一个类别(例如发短信、吃东西或安全地向前看)。

  2. 测试集,其中只有独立变量。你将对测试集进行预测,然后可以提交给 Kaggle,并获得你的得分。

这是开始使用机器学习所需的基本概念,但要做好,还有一些更复杂的需要理解。你会希望创建自己的训练集和验证集(通过分割 Kaggle 的“训练”数据)。你只需要使用你较小的训练集(Kaggle 训练数据的一个子集)来构建模型,并且可以在提交给 Kaggle 之前在你的验证集(也是 Kaggle 训练数据的一个子集)上评估它。

最重要的原因是 Kaggle 将测试数据分成了两部分:用于公共排行榜和私有排行榜。你在公共排行榜上看到的得分仅是你预测结果的一部分(你不知道是哪一部分!)。你的预测在私有排行榜上的表现直到竞赛结束才会揭晓。这之所以重要,是因为你最终可能会对公共排行榜过拟合,而直到最后在私有排行榜上表现不佳时才会意识到。使用好的验证集可以防止这种情况。你可以通过查看你的模型在验证集上的得分与 Kaggle 测试集上的得分是否相似来检查你的验证集是否足够好。

创建自己的验证集的另一个重要原因是,Kaggle 限制你每天只能提交两次,而你可能希望进行更多的实验。第三,查看你在验证集上具体做错了什么可能会很有启发性,而 Kaggle 不会告诉你测试集的正确答案,甚至不会告诉你哪些数据点错了,只告诉你总得分。

理解这些区别不仅对 Kaggle 有用。在任何预测性机器学习项目中,你都希望你的模型能够在新数据上表现良好。