引言
时间序列具有时间依赖性,即相邻数据点之间存在时间先后顺序,因此不适用于随机划分训练集和测试集的交叉验证方法。举个例子,我们通常基于训练集来构造模型,使用测试集验证预测结果。如果随机抽取一定比例的时间数据分别作为训练集和测试集,可能会出现测试数据早于训练数据的情况,这是异常的,因为我们无法用未来数据去“预测”历史数据。
时间序列交叉验证方法
对于时间序列数据,常用的交叉验证方法是滚动窗口交叉验证(rolling window cross-validation)和递增窗口交叉验证(expanding window cross-validation)。下面我将分别介绍这两种方法的原理和在R语言中的实现。
滚动窗口交叉验证
滚动窗口交叉验证是指使用固定大小的窗口在时间序列上进行交叉验证。具体步骤如下:
- 根据原有样本量确定窗口大小和滑动步长;
- 将窗户中的数据按先后顺序划分为训练集(例如,前 70%)和测试集(例如:后 30%);
- 根据滑动步长移动窗口,重复进行模型训练和测试,直到覆盖整个时间序列。
滚动窗口交叉验证示意图
下面是在 R 语言中实现滚动窗口交叉验证的示例代码:
# 将数据转换为时间序列对象 ts_X <- ts(X, start = 1, frequency = 1) ts_Y <- ts(Y, start = 1, frequency = 1) # 设置时间窗口大小和滑动步长 window_size <- 14 step <- 1 # 初始化交叉验证输出列表 cv_r <- list() RMSE <- list() MAE <- list() # 开始交叉验证 for (i in seq(window_size, length(ts_X), by = step)) { # 定义训练集,窗口中的第 1 ~ 10个数据 train_X <- window(ts_X, end = i - 4) train_Y <- window(ts_Y, end = i - 4) # 定义测试集,窗口中的第 11 ~ 14个数据 test_X <- window(ts_X, start = i-3, end = i) test_Y <- window(ts_Y, start = i-3, end = i) # 构造目标模型:举例线性回归 model <- lm(train_Y ~ train_X) # 预测 prediction <- predict(model, newdata = data.frame(train_X = test_X)) # 选取一些指标来评价模型 cv_r[[i]] <- summary(model)$r.squared RMSE[[i]] <- sqrt(mean((na.omit(prediction-test_Y))^ 2)) MAE[[i]] <- mean(abs(na.omit(prediction-test_Y))) } # 将误差值组合成向量 cv_r_values <- unlist(cv_r) cv_RMSE_values <- unlist(RMSE) cv_MAE_values <- unlist(MAE)
可以利用箱线图来可视化交叉验证的结果:
cv_r_summary <- data.frame( metric = rep(c("R-squared"), each = length(cv_r_values)), value = c(cv_r_values) ) ggplot(cv_r_summary[-which.min(cv_r_summary$value),], aes(x = metric, y = value)) + geom_boxplot(col="#00BA38") + geom_point(aes(x = metric, y = value), size = 1,col="#00BA38") + xlab("") + ylab("") + theme_bw() + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(), panel.background = element_blank(), axis.line = element_line(colour = "black"))
递增窗口交叉验证
递增窗口交叉验证使用逐步增加的训练窗口来预测未来数据。其基本原理如下:
- 选取初始训练窗口和训练集,完成第一次迭代;
- 扩展训练窗口,测试窗口的长度保持不变,进行模型训练和测试;
- 重复第二次迭代,直到覆盖整个时间序列。
递增窗口交叉验证示意图
下面是在 R 语言中实现滚动窗口交叉验证的示例代码:
## 递增窗口交叉验证 # 设置初始训练窗口大小和扩展步长 train_window_size <- 11 test_window_size <- 5 expand <- 1 # 初始化交叉验证输出列表 cv_r <- list() RMSE <- list() MAE <- list() # 开始交叉验证 for (i in seq(train_window_size, (length(ts_X)-test_window_size), by = expand)) { # 定义训练集 train_X <- ts_X[1:i] train_Y <- ts_Y[1:i] # 定义测试集 test_X <- ts_X[(i+1):(i+test_window_size)] test_Y <- ts_Y[(i+1):(i+test_window_size)] # 构造目标模型:举例线性回归 model <- lm(train_Y ~ train_X) # 预测 prediction <- predict(model, newdata = data.frame(train_X = test_X)) # 选取一些指标来评价模型 cv_r[[i]] <- summary(model)$r.squared RMSE[[i]] <- sqrt(mean((na.omit(prediction-test_Y))^ 2)) MAE[[i]] <- mean(abs(na.omit(prediction-test_Y))) } # 将误差值组合成向量 cv_r_values <- unlist(cv_r) cv_RMSE_values <- unlist(RMSE) cv_MAE_values <- unlist(MAE)