最近周围的小伙伴们都在玩kaggle上进行的IEEE-CIS,作为经常“地震”的时序题,关于如何选取验证集的讨论自然也不少。究竟如何选择一个靠谱的线下验证集?关于这个问题,我也思考过很多,现在将一些见解与大家分享,抛砖引玉。
首先,我们要知道选择验证集的意义是什么。在做表格题时,经常参加比赛的人可能会非常惯性地来套5CV,大力出奇迹,一发LGB,毁天又灭地。如果是时间序列类的,可能会找相似的一天或一段时间作为验证,其他时间段用来训练等等。很多时候这都是基于直觉或者习惯,并没有什么硬性的道理。但私以为事情并不是那么简单。
如果用考试来做比方的话,训练集就像我们用来学习的有答案的习题集。验证集像是一张模拟考卷,而测试集就是最终的大考。所以一般一个有效的验证集往往意味着这题已经成功了一半,而一个错误的验证集意味着南辕北辙。
对于大多数问题,我们通常会有个默认的假设,那就是训练数据与测试数据是满足独立同分布的(iid)。也就是说训练数据和测试数据是差不多的两张卷子,在数据量够大的情况下,我们可以随机拿一部分习题集用来学习,另一部分作为模拟卷用来试试自己的实力,由此演变而来的就是最常见的带shuffle的train_test_split和K-Fold(以及Stratified K-Fold)。
但并非所有的问题都可以这么简单粗暴,在选取验证集的时候,我们至少需要考虑3个问题:
(1)我们假设数据分布的相同是基于什么?
(2)我们希望模型通过训练学到什么信息?
(3)有什么是不能在训练中泄露给模型的(leakage)?
基于此3点,我们主要用3种不同的线下验证集的创建方法,分别是:
(1)带shuffle的K-Fold/Stratified K-Fold
(2)顺序切分(如时间序列在某个时间点切一刀)
(3)Group K-Fold
接下来分别说说个人对此的理解。
(1)带shuffle的K-Fold/Stratified K-Fold
假设某个学生巨多的年级我们抽了一批学生(测试集),想要了解他们的某科成绩。我们的假设是所有学生的成绩都服从一个正态分布,这些学生都是这同一个分布中抽样出来的。那么我们在剩下的学生中要分割训练集和测试集的话就很简单。我们会认为:
1) 所有的数据都来源于同一个分布;
2) 等量(足够大)但不同的抽样对模型能学到的信息没有太大影响;
3) 基本不存在信息泄露问题。
在这种情况下,我们会采用带shuffle的K-Fold,因为既然所有的数据都来源于同一个分布,那么学习我们采样的数据中的信息就能够估计到整个分布的信息。因此,我们用交叉验证(Cross Validation)就可以保证同分布的问题,而shuffle则代表我们抽样的随机性并不会很大程度地影响模型的学习,这种验证是非常有效的。在正负样本极端不平衡的情况下,我们需要使用Stratified K-Fold来保证正样本被模型学习到,从而避免某一折全是负样本的情况。
所以,我们再随机抽一批学生(最好与测试集等量)做验证集就行。
(2)顺序切分
假设我们有一群学生过去几周上课打瞌睡的数据,我们想要预测下周什么情况下他们会打瞌睡。我们的假设是学生每周的行为是稳定的,重复的(按周期重复)。但是在一周之内,他们每天的行为可能并不相同,比如周一会更困,而周五会更兴奋等等。我们会认为:
1)每个周期之间的分布是相同的(周与周),但每个周期内部的分布是不同的(天与天);
2)模型要学习一个完整周期内的信息(周一到周日);
3)跨周期的信息不应该泄露(本周与下周)。
在这种情况下,基于以上三点,我们一般会采用按时间切分,因为只有按时间切分能满足以上三个要求。
如果用带shuffle的K-Fold会怎么样呢?首先,我们不能保证模型能学习到一个完整周期内的所有信息。此外,即便数据量达到可以保证这一点,shuffle的存在会使得下一个周期的信息混入上一个周期,反之亦然,这就导致了泄露。模型在线下验证集上的得分会很高,但线上会差一截。因为这个“好成绩”是由于信息泄露导致的,而不是模型真的学习到了什么。
所以,我们可以选择保留最后一周作为验证集。
(3) Group K-Fold
假设有五个班级的学生,班级之间的水平参差不齐,给你一班、二班、三班、五班的一些历史信息,要你预测四班的学生下次考试的成绩。我们的假设是虽然五个班级的分布并不相同,但学霸考高分的模式总是相同的。
如果我们使用带shuffle的K-Fold会怎么样呢?假如一班平均实力特别强,而三班特别弱,我们会发现班级这个特征占的分量会很重,然而测试集的四班模型从来都没见过,这明显会导致过拟合,从而线下虚高,线上崩盘。这里就需要使用Group K-Fold了。
Group K-Fold比较难解释,也相对较新,早期的sklearn里似乎并没有这个功能。它让你可以指定某个或某些特征,并且保证这些特征的值不跨越每个Fold,也就是实现特征值的隔离。
举个例子,如果你指定班级来进行Group,那么所有一班相关的内容都出现在某个Fold中,而你在其他Fold中找不到任何关于一班的内容。也就是说,你把一班的信息隔离在了那个Fold里。
那么我们什么时候会需要Group K-Fold呢?当我们认为:
1)所有数据本身未必同分布(一班三班有差异),但其背后某种潜在的模式是一致的;
2)模型要学习对的是某种跨Group的内容或者模式(不局限于某个班级的);
3)不同Group之间的信息不应该泄露(一班与三班)
使用Group K-Fold将掩蔽用来进行Group的特征(班级),因为这个特征是无法被泛化的。而模型将学习的是跨越该特征的信息(挖掘学霸),这样的信息才是有价值的,可以被泛化的。
总而言之,线下验证集的选取并不是一件很随便的事情。它取决于数据本身的构成以及我们对数据所传达的信息的假设。所以,要保证线下验证集靠谱的话,我们还是要——多做EDA啊!