caret包train()与RMSE()计算泊松模型交叉验证RMSE差异疑问
差异原因
- 预测值尺度不匹配:泊松回归属于广义线性模型,
glm()默认调用predict()返回的是对数连接函数转换后的线性预测量(对数尺度),而caret的train()函数计算RMSE时,会自动将广义线性模型的预测值转换为原始响应尺度。你手动计算时没有添加type = "response"参数,相当于用对数尺度的预测值和原始尺度的观测值计算RMSE,数值自然差异极大。而普通线性回归没有连接函数转换,predict()默认返回的就是原始响应尺度的结果,所以不会出现差异。 - 折划分逻辑不符合标准k折规则:你使用
sample(1:10, nrow(exd), replace = TRUE)生成折标签,属于有放回抽样,会导致部分样本不会出现在测试集、部分样本多次出现在测试集,和caret的repeatedcv采用的无放回划分(每轮重复中所有样本恰好属于1个测试集)逻辑不一致,也会带来结果偏差。
手动交叉验证方法的修正方案
原手动实现思路可用,修改两处问题即可正常使用:
- 计算测试集预测值时添加
type = "response"参数,确保预测值和观测值尺度一致 - 替换折标签生成逻辑,使用无放回抽样生成标准k折划分
修正后参考代码如下:
library(caret) library(datasets) exd <- warpbreaks ##k-fold cross validation Repeats <- 100 cv_repeat_num <- Repeats / 10 # 固定随机种子方便复现 set.seed(123) the_control <- trainControl(method = "repeatedcv", number = 10, repeats = cv_repeat_num) cv_ex <- train(breaks~wool+tension, data = exd, method = "glm",family= "poisson", trControl = the_control) m_ex <- glm(data = exd, breaks~wool+tension, family = "poisson") results <- numeric(10 * cv_repeat_num) set.seed(123) for(j in 0:(cv_repeat_num - 1)){ # 改为无放回划分,每轮重复每个样本恰好属于1个折 cv_group <- sample(rep(1:10, length.out = nrow(exd))) for(i in 1:10){ train_data <- exd[cv_group != i, ] test_data <- exd[cv_group == i, ] m_ex <- update(m_ex, data = train_data) results[j * 10 + i] <- RMSE( # 添加type参数获取原始响应尺度预测值 predict(m_ex, newdata = test_data, type = "response"), test_data$breaks) } } #RMSE from manual cross validation mean(results) #RMSE from train function mean(cv_ex$resample$RMSE)
修正后两者的RMSE结果会基本一致。
内容的提问来源于stack exchange,提问作者EllyJ
相关产品推荐
相关产品推荐

