应用于时间序列的交叉验证需要注意防止泄漏并获得可靠的性能估计。本文将介绍蒙特卡洛交叉验证。这是流行的TimeSeriesSplits方法的替代方法。时间序列交叉验证TimeSeriesSplit通常是时间序列数据交叉验证的首选方法。下面的图1说明了该方法的工作原理。可用的时间序列被分成几个大小相等的折叠。然后使用每个折叠来首先测试模型,然后重新训练它。除了第一折仅用于训练。使用TimeSeriesSplit进行交叉验证的主要好处如下:它保留了观察的顺序。这个问题在时间序列等有序数据集中非常重要。它会产生很多分裂。多次拆分后可以获得更稳健的评估。如果数据集不大,这一点尤其重要。TimeSeriesSplit的主要缺点是训练样本大小在折叠之间不一致。这是什么意思?假设该方法应用于图1中所示的5次折叠。在第一次迭代中,所有可用观察值的20%用于训练。然而,这个数字在上一次迭代中是80%。因此,初始迭代可能不代表完整的时间序列。此问题会影响性能估计。那么如何解决这个问题呢?蒙特卡罗交叉验证蒙特卡罗交叉验证(MonteCarloCV)是一种可用于时间序列的方法。这个想法是在不同的随机起点获取一段时间内的数据。下面是这种方法的直观描述:与TimeSeriesSplit一样,MonteCarloCV也保留观察的时间顺序。它还多次重复估计过程。MonteCarloCV在两个主要方面不同于TimeSeriesSplit:对于训练和验证样本大小,使用TimeSeriesSplit时训练集的大小会增加。在MonteCarloCV中,训练集的大小在每次迭代中都是固定的,这使得训练集的大小无法代表整个数据;对于随机分割,在MonteCarloCV中,验证原点是随机选择的。这个原点标志着训练集的结束和验证的开始。在TimeSeriesSplit的情况下,这一点是确定的。它是根据迭代次数预定义的。MonteCarloCV最初由Picard和Cook使用。可以在参考资料中找到详细信息。经过对MonteCarloCV的详细研究。这包括与其他方法(例如TimeSeriesSplit)的比较。MonteCarloCV可以获得更好的估计,所以我一直在使用它。您可以在参考文献中查看完整的研究。[2].不幸的是,scikit-learn不提供MonteCarloCV的实现。所以,我们决定自己动手实现它:fromtypingimportList,Generatorimportnumpyasnpfromsklearn.model_selection._splitimport_BaseKFoldfromsklearn.utils.validationimportindexable,_num_samplesclassMonteCarloCV(_BaseKFold):def__init__(self,n_splits:int,train_size:float,test_size:float,gap:int=0):"""MonteCarloCross-ValidationHoldoutappliedinmultipletestingperiods测试原点(测试开始的时间步长)根据蒙特卡洛模拟随机选择:paramn_splits:(int)程序中的蒙特卡洛重复次数:paramtrain_size:(float)训练大小,以序列总长度的比率表示:paramtest_size:(float)测试大小,以比率表示系列总长度的:paramgap:(int)要从每个训练集的末尾排除的样本数e测试集。"""self.n_splits=n_splitsself.n_samples=-1self.gap=gapself.train_size=train_sizeself.test_size=test_sizeself.train_n_samples=0self.test_n_samples=0self.mc_origins=[]defsplit(self,X,y=None,groups=None)->Generator:"""生成索引以将数据拆分为训练集和测试集。参数----------X:array-likeofshape(n_samples,n_features)训练数据,其中“n_samples”是样本数,“n_features”是特征数。y:array-likeofshape(n_samples,)总是被忽略,存在是为了兼容性。groups:array-likeofshape(n_samples,)总是被忽略,存在是为了兼容性。Yields------train:ndarray该拆分的训练集索引。test:ndarray测试setindicesforthatsplit."""X,y,groups=indexable(X,y,groups)self.n_samples=_num_samples(X)self.train_n_samples=int(self.n_samples*self.train_size)-1self.test_n_samples=int(self.n_samples*self.test_size)-1#确保我们有足够的样本用于给定的分割参数ifself.n_splits>self.n_samples:raiseValueError(f'Cannothavenumberoffolds={self.n_splits}greater'f'thanthe样本数={self.n_samples}.')ifself.train_n_samples-self.gap<=0:raiseValueError(f'Thegap={self.gap}istoobigfornumberoftrainingsamples'f'={self.train_n_samples}withtestingsamples={self.test_n_samples}andgap={self.gap}.')indices=np.arange(self.n_samples)selection_range=np.arange(self.train_n_samples+1,self.n_samples-self.test_n_samples-1)self.mc_origins=\np.random.choice(a=selection_range,size=self.n_splits,replace=True)fororigininself.mc_origins:ifself.gap>0:train_end=origin-self.gap+1else:train_end=origin-self.gaptrain_start=origin-self.train_n_samples-1test_end=origin+self.test_n_samplesyield(指数[train_start:train_end],指数[origin:test_end],)defget_origins(self)->List[int]:returnself.mc_originsMonteCarloCV接受四个参数:n_splitting:分裂或迭代的次数,这个值趋向于10;training_size:每次迭代时训练集大小与时间序列大小的比值;test_size:类似于training_size,但是用于验证集;差距:分隔训练集和验证集的观察次数。与TimeSeriesSplits一样,此参数的值默认为0(无间隙)。每次迭代的训练和验证大小取决于输入数据。我发现0.6/0.1分区工作得很好。也就是说,在每次迭代中,有60%的数据用于训练。10%的观察结果用于验证。实际使用示例下面是一个配置示例:fromsklearn.datasetsimportmake_regressionfromsrc.mccvimportMonteCarloCVX,y=make_regression(n_samples=120)mccv=MonteCarloCV(n_splits=5,train_size=0.6,test_size=0.1,gap=0)fortrain_index,test_indexinmccv.split(X):print("TRAIN:",train_index,"TEST:",test_index)X_train,X_test=X[train_index],X[test_index]y_train,y_test=y[train_index],y[test_index]这个实现也兼容scikit-learn。以下是合并GridSearchCV的方法:)gsearch.fit(X,y)我希望你发现MonteCarloCV有用!
