如何对时间序列数据应用 Stacking 交叉验证?

数据挖掘 时间序列 交叉验证 集成学习
2021-10-10 11:52:38

通常堆叠算法使用 K 折交叉验证技术来预测用于 2 级预测的 oof 验证。

对于时间序列数据(比如股票走势预测),不能使用 K 折交叉验证,而时间序列验证(在 sklearn lib 上建议的一种)适合评估模型性能。在这种情况下,第一个折叠不应进行预测,最后一个折叠不应进行任何训练。我们如何对时间序列数据使用堆叠算法交叉验证技术?

2个回答

TL;博士

时间序列算法假设数据点是有序的传统的 K-Fold不能用于时间序列,因为它没有考虑数据点出现的顺序。验证时间序列算法的一种方法是使用基于时间的拆分

K-Fold 与基于时间的拆分

下面的两张图显示了 K-Fold 和基于时间的拆分之间的差异。从它们中,可以观察到以下特征。


K-Fold 总是所有数据点

时基拆分使用所有数据点的一小部分


K-Fold 让测试集是任何数据点

时基拆分只允许测试集比训练集具有更高的索引


K-Fold 将使用第一个数据点进行测试最后一个数据点进行训练

Time Base Splitting永远不会使用第一个数据点进行测试也永远不会使用最后一个数据点进行训练

在此处输入图像描述 时间序列分裂图

Scikit-learn 实现

Scikit-learn 有一个名为TimeSeriesSplit的算法实现

查看他们的文档,您会发现以下示例:

from sklearn.model_selection import TimeSeriesSplit
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([1, 2, 3, 4, 5, 6])
tscv = TimeSeriesSplit(n_splits=5)

for train_index, test_index in tscv.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]

>> TRAIN: [0] TEST: [1]
>> TRAIN: [0 1] TEST: [2]
>> TRAIN: [0 1 2] TEST: [3]
>> TRAIN: [0 1 2 3] TEST: [4]
>> TRAIN: [0 1 2 3 4] TEST: [5]

sklearn 的标准TimeSeriesSplit无法使用,StackingRegressor因为在后台StackingRegressor使用cross_val_predict这将导致如下错误:

cross_val_predict 仅适用于分区

要使用时间序列和 sklearn 模型进行堆叠,您只需编写这几行代码......

from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import TimeSeriesSplit

# generate dummy data
X = np.random.uniform(0,1, (1000,10))
y = np.random.uniform(0,1, (1000,))

# initialize two models to be stacked
rf = RandomForestRegressor()
gb = GradientBoostingRegressor()

# generate cross-val-prediction with rf and gb using TimeSeriesSplit
cross_val_predict = np.row_stack([
    np.column_stack([
        rf.fit(X[id_train], y[id_train]).predict(X[id_test]),
        gb.fit(X[id_train], y[id_train]).predict(X[id_test]),
        y[id_test]  # we add in the last position the corresponding fold labels
    ])
    for id_train,id_test in TimeSeriesSplit(n_splits=3).split(X)
])  # (test_size*n_splits, n_models_to_stack+1)

# final fit rf and gb with all the available data
rf.fit(X,y)
gb.fit(X,y)

# fit a linear stacking on cross_val_predict
stacking = LinearRegression()
stacking.fit(cross_val_predict[:,:-1], cross_val_predict[:,-1])

# how generate predictions on new unseen data
X_new = np.random.uniform(0,1, (30,10))
pred = stacking.predict(
    np.column_stack([
        rf.predict(X_new),
        gb.predict(X_new)
    ])
)