使用R中XGBoost进行时间序列预测时出现常数预测值问题
问题
我尝试在R中使用梯度提升(Gradient Boosting)的XGBoost模型进行时间序列预测,遇到了如下问题:
- 92条观测数据划分为75条训练集、17条测试集,测试集预测结果尚可
- 但预测未来12个季度时,模型返回恒定的预测值(水平线)
数据量较小(92条观测、10个自变量),但不清楚常数预测值的成因,以下是我的代码:
数据预处理与划分
# Load data a = diff(log(UnempRate$LRUN64TTBEQ156S)) b = diff(log(GDP$CPMNACSCAB1GQBE)) c = diff(log(URateUS$UNRATE)) d = diff(log(AirT$PCU481481)) e = diff(log(DGoods$PITGCD02BEQ661N)) f = diff(log(GCredit$QBEGAMUSDA)) g = diff(log(HWage$LCWRMN01BEQ661N)) h = diff(log(Sales$SLRTTO02BEQ661S)) i = diff(log(ActRate$LRAC64TTBEQ156S)) j = diff(log(Imports$Imports)) k = diff(log(Exports$XTEXVA01BEM667S)) dset2 = data.frame(a,b,c, d,e,f,g,h,i,j,k) # Split data train = head(dset2,75) test = dset2[76:92, ]
训练XGBoost模型并测试
# XGBoost grid_tune = expand.grid(nrounds = 1000, max_depth = 8, eta = 0.1, gamma = 0.00, colsample_bytree = 1, min_child_weight = 1, subsample = 1) train_control = trainControl(method = "cv", number = 3, verboseIter = TRUE, allowParallel = TRUE) xgb_tune = caret::train(x = train[, -1], y = train[,1], trControl = train_control, tuneGrid = grid_tune, method = "xgbTree", verbose = TRUE) # 测试集预测 xgb_pred = predict(xgb_tune, test) ts.plot(test$a, xlab = "Quarters 76 to 92", ylab = "Diff(log()) value", main = "XGB predictions on testing data", col = "#009999") lines(xgb_pred, col = "red")
未来12季度预测(出现常数结果)
# 添加空行准备预测 empty_df <- data.frame(matrix(nrow = 12, ncol = 11)) colnames(empty_df) <- c("a","b","c","d","e","f","g","h","i","j","k") dset3 = rbind(dset2, empty_df) train2 = head(dset3, 92) test2 = dset3[93: 104, ] # 重新训练模型 grid_tune = expand.grid(nrounds = 1000, max_depth = 8, eta = 0.1, gamma = 0.00, colsample_bytree = 1, min_child_weight = 1, subsample = 1) train_control = trainControl(method = "cv", number = 3, verboseIter = TRUE, allowParallel = TRUE) xgb_tune = caret::train(x = train2[, -1], y = train2[,1], trControl = train_control, tuneGrid = grid_tune, method = "xgbTree", verbose = TRUE) # 预测结果为常数 xgb_pred = predict(xgb_tune, test2) # 输出:[1] 0.0709851 0.0709851 ...(共12个相同值)
原因分析与解决方案
核心原因:未来预测的自变量全为缺失值
你用来预测未来的test2数据中,10个自变量(b到k)全是NA。XGBoost遇到所有输入特征都缺失的样本时,会默认输出模型训练时目标变量的全局基准值(通常是均值),因此所有预测结果都是同一个常数。
另外,你的时间序列预测逻辑存在误区:未来的自变量(b到k)本身也是未知的,不能直接留空等待模型预测,必须先对这些自变量做预测,或者改用不需要同期自变量的时间序列建模方式。
具体解决步骤
1. 先预测未来的自变量值
针对每个自变量(b到k),单独用时间序列模型(如ARIMA、ETS)预测未来12个季度的值,填充到test2中:
library(forecast) # 示例:用ARIMA预测自变量b的未来值(季度数据频率设为4) b_ts <- ts(dset2$b, frequency = 4) b_fit <- auto.arima(b_ts) b_forecast <- forecast(b_fit, h=12)$mean # 对c-k重复上述操作,得到各自的预测值 # c_forecast <- ... # d_forecast <- ... # 填充test2的自变量 test2$b <- b_forecast test2$c <- c_forecast # ... 依次填充d到k # 再进行预测 xgb_pred <- predict(xgb_tune, test2)
2. 重构时间序列特征(更合理的建模方式)
对于时间序列预测,更合理的方式是用历史特征预测未来,而非依赖同期未知的自变量。可以构造滞后特征、滚动统计特征:
library(dplyr) # 构造滞后特征:a的滞后1-4期(对应季度数据的历史值)、b的滞后1期等 dset2 <- dset2 %>% mutate( a_lag1 = lag(a, 1), a_lag2 = lag(a, 2), a_lag3 = lag(a, 3), a_lag4 = lag(a, 4), b_lag1 = lag(b, 1), # 可添加滚动统计特征:比如过去4期a的均值 a_roll_mean4 = zoo::rollmean(a, k=4, fill=NA, align="right") ) %>% na.omit() # 去掉因滞后产生的NA行 # 重新划分训练集和测试集 train <- head(dset2, nrow(dset2)-17) test <- tail(dset2, 17) # 训练模型:用历史特征预测当前的a xgb_tune <- caret::train( x = train[, !colnames(train) %in% "a"], y = train$a, trControl = train_control, tuneGrid = grid_tune, method = "xgbTree", verbose = TRUE )
这种方式下,预测未来时可以用递归预测:先用第92期的特征预测第93期的a,再把第93期的a作为滞后特征加入,预测第94期,以此类推。
3. 调整模型参数避免过拟合
你的数据量仅92条,nrounds=1000极易导致过拟合,尝试减小模型复杂度:
grid_tune <- expand.grid( nrounds = 200, max_depth = 3, # 减小树深度,降低复杂度 eta = 0.1, gamma = 0.1, # 加入正则化,减少不必要的分裂 colsample_bytree = 0.8, min_child_weight = 2, subsample = 0.8 )
内容的提问来源于stack exchange,提问作者Skurt
相关产品推荐
相关产品推荐

