caret中treebag模型预测与preProcess的关联及单树预测异常问题
问题根源与解决方案
这个问题的核心原因是你在提取单树做预测时,忽略了caret中preProcess的作用时机——模型训练用的是预处理后的数据,但你给单树传入的是原始数据,导致预测异常。让我一步步拆解并解决这个问题:
1. 为什么加preProcess后单树预测变成常数?
当你在train()中指定preProcess = c("center", "scale")时,caret会执行以下步骤:
- 首先对整个训练数据集拟合预处理规则(计算每个特征的均值、标准差);
- 将这个规则应用到训练数据上,得到标准化后的特征矩阵;
- 用标准化后的数据训练treebag模型,每棵袋装树都是基于bootstrap抽样的标准化数据训练的。
但你在提取单树做预测时,直接调用了predict(btree, newdata = x)——这里的x是原始未标准化的数据,和树训练时的特征分布完全不一致。树在标准化后的特征空间里学习,现在收到的原始尺度特征会让所有样本都落到树的同一个叶子节点,所以预测值都是常数。
而模型整体的predict(model, x)是正常的:caret会自动先对x应用拟合好的预处理规则,再传入模型预测,符合训练时的特征分布。
对于未缩放的模型,训练和预测用的都是原始数据,所以单树预测的平均值和整体模型预测完全一致,这符合bagging的基本逻辑(袋装模型的预测就是单树预测的平均)。
2. 修正后的代码
我们需要在单树预测前,先对输入数据应用和训练时一致的预处理规则。修改你的predict_from_treebag函数如下:
predict_from_treebag <- function(model) { # 提取模型的预处理规则(如果存在) preproc <- model$preProcess # 对输入数据应用预处理 x_processed <- if (!is.null(preproc)) { predict(preproc, newdata = x) } else { x } # 提取25棵袋装树 bagged_trees <- map(.x = model$finalModel$mtrees, .f = pluck, "btree") # 用预处理后的数据做单树预测 pred_trees <- map(bagged_trees, .f = predict, newdata = x_processed) names(pred_trees) <- paste0("tree_", seq_along(pred_trees)) # 聚合预测结果 pred_trees <- as.data.frame(pred_trees) %>% add_column(date = economics$date, .before = 1) %>% gather(tree, value, matches("^tree")) %>% group_by(date) %>% mutate(mean_pred_from_trees = mean(value)) %>% ungroup() # 添加模型整体预测 pred_trees$bagging_model_prediction = predict(model, x) pred_trees <- pred_trees %>% gather(model, pred_value, 4:5) # 绘图 p <- ggplot(data = pred_trees, aes(date)) + geom_line(aes(y = value, group = tree), alpha = .2) + geom_line(aes(y = pred_value, col = model)) + theme_minimal() + theme( panel.grid.major = element_blank(), panel.grid.minor = element_blank(), legend.position = "bottom" ) p }
3. 验证结果
运行修改后的函数后,无论是缩放还是未缩放的模型,你都会看到:
- 单树预测的平均值(
mean_pred_from_trees)和整体模型预测(bagging_model_prediction)完全重合; - 缩放模型的单树预测不再是常数,而是呈现出合理的波动,符合bagging模型的预期。
内容的提问来源于stack exchange,提问作者markus
相关产品推荐
相关产品推荐

