前言
本篇博客是 Andrew NG 《Machine Learning Yearning》 的「第二章:配置开发集和训练集」翻译。随着机器学习正朝着更大的数据集方向发展,关于配置开发/测试集的准则也在发生变化,本章内容将指导你如何在团队中调整机器学习策略,以及如何设置开发集和测试集,以适应现代化的机器学习项目。
👉官网传送门
👉GitHub 项目传送门,欢迎 Star
5. 你的开发集和测试集
让我们回到一开始那个猫咪图片识别的例子来。你投入运营了一个手机 App,用户可以上传很多不同类型的图片到你的 App 来。此时,你希望在 App 上实现一个自动识别猫咪图片的功能。
你的团队通过从不同的网站抓取到很多猫图(正例)和非猫图(反例)组成了一个很大的数据集,然后按照 70%/30% 的法则将数据集分为了训练集和测试集两部分。通过使用这些数据,团队实现了一个在训练集和测试集上均表现良好的猫咪检测器。
但是,当你将这个检测器部署到手机 App 上的时候,发现它的性能变得相当糟糕!
这……发生了什么?
你发现用户上传的图片数据和团队从网站上抓取下来作为训练集的图片数据存在差异:用户上传的图片普遍使用手机拍摄,这些图片的分辨率比较低,清晰度差甚至曝光不足,然而,你的训练集/测试集上的图片则是来自网页抓取的图片。因此,你的分类器算法并不能很好的泛化到 App 的实际场景中——识别来自手机拍摄的图片。
在大数据时代来临之前,使用 70%/30% 的比例将数据集随机分割成训练集和测试集是传统机器学习的通用法则。这种做法是可行的,但是,当越来越多的应用部署在与训练集(来自网页抓取的图片)有着不同分布的使用场景(用户手机拍摄的图片)中的时候,这种分割做法就比较糟糕了。
我们通常定义:
- 训练集(Training Set):对算法或者模型进行训练所使用的数据集;
- 开发集(Dev(development) Set):用于调整参数,选择特征和做出其他算法相关决定的数据集,又称作「交叉验证集」(Hold-Out Cross Validation Set);
- 测试集(Test set):只用来评估算法性能而不会对使用何种算法或者参数做出决策的数据集。
一旦定义好了开发集和测试集之后,你的团队将会尝试很多新的想法,例如,设置不同的学习算法参数来看一下哪种效果最佳。总之,开发集和测试集的使用可以让你的团队对算法调优进行快速迭代。
换句话说,设置开发集和测试集的目的是指导你的团队对机器学习系统做出最正确的优化。
因此,你应该遵从下列原则:
- 选择能够映射出你在未来将要获得的数据,且表现出良好效果的开发集和测试集。
也就是说,你的测试集不应该仅仅包含现阶段可用数据的 30%,特别是当你期望得到的数据(用户的手机拍摄的图片)和你的训练集的数据(网站抓取的图片)来自不同分布的时候。
如果你还没有上线你的 App,那么你可能还没有任何用户,因此无法获取符合未来数据分布的图片,但是您仍然可以尝试去模拟这种分布。比如,请求你的朋友们使用手机拍些猫咪的图片给你。一旦应用上线,就可以使用用户上传的数据去更新你的开发集和测试集了。
如果真的没有任何办法去获取那些符合未来数据分布的图片的话,也许你可以使用上边提到的网站抓取的图片来优化算法,但是应该清醒地意识到这种方式训练出来的系统的泛化(Generalize)性能肯定是不好的。
你需要有一定的判断力来决定投入多少预算去提高开发集和测试集的质量。切记不要假定你的训练集和测试集有着相同的分布,尝试挑选那些能够很好的体现出你最终想要的结果的数据作为测试样本,而不仅仅是挑那些和训练集有着相同分布的数据。
6. 开发集和测试集应当服从同一分布
根据你的 App 的受众地区,将猫咪数据来源分为四个区域:(i)美国、(ii)中国,(iii)印度和(iv)其他。我们提出了一个服从这样分布(Distribution)的开发集和测试集:将美国和印度地区的数据归入开发集中,将来自中国和其他地区的数据归入测试集中。简单说就是,我们随机将两个地区的数据分配给开发集,另外两个分配给测试集。这种分法对吗?
一旦定义好开发集和测试集,你的团队将会专注于提高开发集上的表现。所以,开发集应该要反映出你最想要改进的任务——在四个区域都表现良好,而不是其中的两个。
开发集和测试集服从不同分布导致的第二个问题是:你的团队可能会构建出来在开发集上表现良好的系统,但发现它在测试集中却表现很差,我曾在大量的挫折和错误的努力中得到过这种结果。现在要避免这种悲剧发生在你身上。
还是以例子来说明,假设你的团队构建的系统在开发集上表现良好但是在测试集却很糟糕。如果此时你的开发集和测试集服从同一个分布,那么你将非常清楚的知道哪里出了问题——数据过拟合了。一个有效的办法就是获取更多的开发集数据。
但是,如果开发集和测试集服从不同的分布,那么你的决策就不那么明朗了,以下几个方面都可能是出错的地方:
- 你过拟合(Overfit)了开发集;
- 测试集的数据比开发集数据要更加复杂,你的算法已经达到了预期的效果并且已经无法进一步改善了;
- 测试集的数据并不比开发集数据复杂,只是因为服从不同分布,所以开发集上良好的性能表现并不能泛化到训练集中。在这种情况下,你在开发集上所做的努力就全部白费了。
开发机器学习应用已经足够困难,不匹配的开发集和测试集则引入了额外的不确定性——改进开发集的分布是否能提高测试集表现?这会使得更加难以确定哪些优化措施是有效的哪些是徒劳的,从而难以指定优化措施的优先级顺序。
如果你处理第三方的基准测试,它们的创建者可能指定了来自不同分布的开发集和训练集。与服从同一分布的开发集和训练集相比,对于基准测试的性能表现,运气将会比技巧产生更大的影响。当然,构建能够在一种分布中表现良好而且能泛化到其他分布的学习算法是很重要的研究方向。但是,如果你的目标是构建出能在特定的机器学习应用中取得进展的话,我建议你尝试选择服从同一分布的开发集和测试集,这会使您的团队更有效率。
7. 开发集/测试集多大合适?
开发集应该足够大,大到能够检测出你在尝试的不同算法之间的差异。比如,如果分类器 A 的准确率有 90%,分类器 B 的准确率有 90.1%,那么只有100个样本的开发集是无法检测到这 0.1% 的差异的。与我所见过的机器学习问题相比,100 样本大小的开发集着实很小。样本规模在 1000-10000 之间的开发集很常见,通过 10000 样本大小的开发集,你才有更大的可能性检测出这 0.1% 的提高1。
对于那些成熟的,重要的应用来说,比如那些广告、搜索引擎和产品推荐等,我也看到过为了提升那 0.01% 而不懈努力的团队,这个努力将直接影响公司的利润。在这种情况下,为了能够检测到更加小算法性能改进,开发集的大小可能远大于 10000。
那么测试集的大小多少合适呢?他也应该足够大,大到能够为系统的整体性能提供很高的可信度。一种流行的启发性方法就是将数据集的 30% 用于训练集,这适用于一个具有中等大小规模(100-10000个样本)的数据集。但是在大数据时代,我们所面临的可能是有着数十亿样本的机器学习问题,分配给开发/测试集的数据比例一直在降低,但是开发/测试集的数据量却是不断递增的。总之,在给开发/测试集分配数据时,没有必要分配超出评估算法性能所需的数据量。
8. 为团队进行算法优化建立单一数字评估指标
分类准确率是单一数字评估指标(Single-Number Evaluation Metric)的其中一种:你在开发集(测试集)上运行你的分类器,然后得到一个样本分类准确率的一个数字,通过这个指标,如果分类器 A 的准确率是 97%,而分类器 B 的准确率是 90%,则我们判定分类器 A 更好。
相比之下,查准率(Precision)和查全率(Recall)2就不属于单一数字评估指标:它给出了两个数字用来评估分类器。使用混合数字评估指标(Multiple-Number Evaluation Metrics)使得比较算法的差异变得更加困难。假设你的算法表现如下表:
分类器 | 查准率 | 查全率 |
---|---|---|
A | 95% | 90% |
B | 98% | 85% |
上边的举例中,任意一个分类器都没有明显表现的比另一个好,所以这类混合数字评估指标无法立刻引导你判断出哪一个分类器更好。
在开发过程中,你的团队会进行大量关于算法架构、模型参数、特征选择方面的尝试。使用单一数字评估指标,比如准确率,可以允许你依照该指标下的性能表现对所有模型进行排序,从而快速的决断出哪一个模型效果更好。
如果你希望综合考虑查准率、查全率的性能度量,我建议使用一种标准的方法将它们组合成一个单一数字性能指标。例如,可以将查准率和查全率的均值作为评价指标。或者你可以计算「F1 度量」(F1 score),这是一种基于查准率和查全率的调和平均,比单纯的求两者的均值效果要好3。
分类器 | 查准率 | 查全率 | F1 度量 |
---|---|---|---|
A | 95% | 90% | 92.4% |
B | 98% | 85% | 91.0% |
当你要在大量分类器中做出选择的时候,单一数字评估指标能帮助你更快的做出决策。它为算法表现提供了一个明确的优先级顺序,从而给出一个清晰的优化方向。
作为最后一个例子,假设你已经得到了你的猫分类器在四个主要市场(美国、印度、中国和其他)的分类准确率。这相当于给出了四个指标,通过将这四个地区的分类准确率进行平均或者加权平均,最终将得到一个单一数字指标。取均值或者加权平均是将多个指标合并为一个的最常用的办法之一。
9. 优化和满足指标
这是组合多个评估指标的另一个方法。
假定你同时关心算法的准确率和运行时间,你需要从下列三个分类器中选一个最好的。
分类器 | 准确率 | 运行时间 |
---|---|---|
A | 90% | 80ms |
B | 92% | 895ms |
C | 95% | 1500ms |
此时,将准确率和运行时间放入单一公式计算得到单一指标的做法似乎并不合适,比如:
下面,是你可以尝试的操作:首先,设定一个「可接受」的运行时间,假定说只要是运行时间在 100ms 以内的算法都是可以接受的。然后,从满足时间要求的分类器里,最大化准确率。在这里,「运行时间」就是「满足指标」——你的分类器只需要在这个指标上表现的足够好就行了(运行时间低于100ms),而「准确率」就是「优化指标」。
当你正在权衡 N 个不同的标准的时候,比如,一个模型的二进制文件的大小(这对于一个移动 App 来说很重要,因为用户并不想下载很大的 App)、运行时间和准确率。可以考虑将其中的 N-1 个标准设置为「满足指标」,也就是说,只需要这些条件满足一定的值就行了。然后将剩下那个设置为「优化指标」。还是上头的例子,可以将模型的二进制文件大小和运行时间都设置为「满足指标」,并尝试在这些约束条件下不断优化准确率(优化指标)。
作为本节最后一个例子,假设你在搭建一个硬件设备,该设备使用麦克风来监听用户说出的某个特定的唤醒词(Wakeword),从而唤醒系统。就好比亚马逊的「Echo」听到「Alexa」、苹果Siri听到「hey Siri」、百度应用听到「你好百度」都会唤醒系统一样。你不仅需要关心假正例( False Positive Rate)——系统在用户没有人说出唤醒词的时候被唤醒,而且还要关心假反例(False Negative Rate)——当用户说出唤醒词系统却没有被唤醒。那么,对于该系统的性能而言,一个合理的优化目标就是最大限度的减少假反例的比例(优化指标),同时满足每隔 24 小时最多出现一个假正例(满足指标)即可。
10. 使用开发集和评估指标加速迭代
对于一个新的问题很难预先知哪种解决办法最有效。即使是那些经验丰富的机器学习专家,在发现有效的解决办法之前也大量尝试各种想法,在构建一个机器学习系统的时候,我通常:
- 从一些关于如何构建系统的 Idea 开始;
- 在代码中实现这些想法;
- 通过实验观察这个 Idea 的效果(通常来说最开始的想法都是无效的!),基于这些知识,回过头来产生更多的 Idea,并继续迭代。
这是一个迭代的过程。这个循环的过程越快,你的进展就会越快。这就是开发/测试集和单一评估指标非常重要的原因:每次你尝试一个想法时,通过在开发集上衡量该想法的性能,让你快速地判断出你自己是否朝着正确的方向前进。
相反,假设你没有特定的开发集和评估指标。那么每次当你的团队构建出新的猫分类器时,你都不得不将它整合进你的 App 里,并好好地玩上个几小时来感受这个新的分类器的性能是否有所提升,这种方法非常慢。另外,如果你的团队将分类器的性能从 95.0% 提升到 95.1%,通过使用 App 的方式并不能让你感受到这 0.1% 的变化。而每次这 0.1% 的改进,累积起来就会帮助你的系统取得很大的进步。拥有一个开发集和评估标准让你能够快速的发现那些成功地带来了小的(大的)改进的想法,从而让你可以快速的决定哪些办法是需要继续提炼的,哪些是可以舍弃的。
11. 何时更改开发/训练集和评估指标
每当开始一个新项目的时候,我都会试图快速地去选择开发集和训练集,因为这可以给团队一个明确定义好的目标。
我通常会要求我的团队在不到一周的时间内创建好一个初始的开发/训练集(很少情况下会长于这个时间),最好是能够提出一个不太完美的方案并快速行动起来,而不是在过度思考。但是「一周」的这个时间限制并不适用于成熟的应用,比如,反垃圾邮件就是一个成熟的深度学习应用。我曾见过那些负责成熟系统的团队们,甚至会花费数个月的时间来去获取更好的开发/训练集。
如果你后来意识到了你的初始开发/训练集或者评估指标没有达到要求,那么一定要尽快地修改它们。例如,如果你的开发集+评估指标认为分类器 A 好于分类器 B,但是你的团队却认为分类器 B 其实更适合你的产品,那么这可能表示你该修改你的开发/测试集或者评估指标了。
下列是可能导致分类器 A 的评级偏高的三个主要原因:
- 开发/测试集不服从实际数据的真实分布。
假设你的开发/训练集里大部分都是成年的猫。这时,你检查你的猫分类 App 发现用户比预期上传了更多小猫咪的图片。这个情况下,开发/测试集的分布就不能很好的代表真实数据的分布了,此时应该做的事就是更新你的开发/训练集使其更具代表性。
- 你过拟合了开发集。
重复地使用开发集评估想法的过程会导致你的算法慢慢过拟合开发集。当你完成开发后,你会在测试集上评估你的系统,如果发现算法在你的开发集的表现明显优于测试集,这就意味已经过拟合了开发集了,这种情况下,需要获取一个新的开发集。
如果你需要跟踪团队的进度,你也可以定期使用测试集对系统进行评估,比如一周一次或者一个月一次。但不要使用测试集来做出关于改进算法任何决定,包括是否回滚到上一周的系统。一旦你这样做了,你的系统将会开始对测试集过拟合,同时再也不能依靠它来给出系统性能的完全无偏估计(Completely Unbiased Estimate)(你可能需要在发表论文或者根据某个评估指标做出重要的商业决策的时候使用这个完全无偏估计)。
- 该评估指标衡量的不是项目需要优化的内容。
以你的猫分类 App 为例,你的评估指标是分类准确度,这个评估指标目前认为分类器 A 好于分类器 B.但是当你尝试应用这两种算法的时候,发现分类器 A 偶尔会将色情图片识别为猫,那么即使分类器 A 的准确度更高,无法完全将色情图片过滤所引发的不良印象意味着它的表现也是无法被接受的。你应该怎么做?
在这个例子中,该评估指标未能准确判断出对于产品而言算法 B 比算法 A 更好这一事实。所以,你不能再相信该指标能够选出最优算法,是时候改变评估指标了。比如,你可以更改评估指标来加大对色情图片的惩罚权重。我强烈建议你选择一个新的评估指标,以此来为团队定义一个新的目标,而不是在一个没有可信度的指标下持续太长时间,最后不得不手工选择分类器。
在项目中更改开发/测试集或者评估指标是很常见的。使用初始开发/测试集和评估指标能够帮助你快速迭代。如果你发现开发/测试集或者评估指标不再为你的团队指明正确的方向,也不是什么大不了的事情!只需要修改它们,并确保你的团队了解了新的方向。
12. 小结:设置开发和测试集
- 从能反映你将来希望获得的数据并想做得更好的分布中选取开发集和测试集。这可能不同于你的训练集;
- 尽可能选择服从同一分布的开发集和测试集;
- 为你的团队选择一个单一数字评估指标来进行优化。如果你关心多个目标,考虑将他们合并到一个公式中(例如多个误差指标的均值)或者定义好满足指标和优化指标;
- 机器学习是一个高速迭代的过程:在发现满意的方法之前可能要尝试很多的Idea;
- 拥有开发/训练集和单一数字评估指标能够帮助你快速评估算法,从而加快迭代速度;
- 当开始构建一个全新的应用的时候,尝试快速在一周内建立开发/训练集和评估指标。当然,在成熟的应用上花费更长的时间建立这些指标是没问题的;
- 当你拥有大量数据的时候,传统的 70%/30% 的训练集/测试集分割比例不再适用,开发集和测试集对总数据集的占比可以远远少于 30%;
- 你的开发集应该足够大,大到能够检测到你的算法准确率上的有意义的变化,当然也没有必要太大。你的测试集应该大到能够为你系统性能评估提供一个高可信度;
- 如果你的开发集和评估指标不能再正确地指导你的团队,请迅速修改它们:(i)如果你过拟合了开发集,请增大开发集数据量、(ii)如果开发集/测试集的分布不服从真实数据的分布,请获取新的开发/测试集、(iii)如果你的评估指标不再衡量对你而言最重要的内容,请更新评估指标。
-
理论上,我们还可以在开发集上测试算法的改变能否产生统计意义上的显著差异。事实上,大多数团队不会为此而操心(除非他们正在准备发表学术论文),而且我通常没有发现对测量中期进展有用的统计显着性检验。 ↩
-
对于猫分类器这个案例。查准率的定义是:分类器将开发(测试)集标记为猫的样本中,有多少真的是猫,$\frac{预测为正类的正类数量}{预测为正类的数量} * 100%$;查全率就是在开发(测试)集中,分类器正确识别出的真是猫的图片数量占所有的真是猫的图片数量的百分比,$\frac{预测为正类的正类数量}{正类数量} * 100%$。通常需要在高查准率与高查全率之间进行权衡。 ↩
-
如果你想更多的了解关于「F1 度量」的信息,请参阅:https://en.wikipedia.org/wiki/F1_score,其计算公式:$F1 = \frac{2}{\frac{1}{P}+\frac{1}{R} } = \frac{2PR}{P+R}$ ↩
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。欢迎转载,并请注明来自:黄钢的博客 ,同时保持文章内容的完整和以上声明信息!