Agi*_*ean 7 statistics r machine-learning prediction r-caret
我发现了一个奇特的效果,即测试集的RMSE低于sample带有caret包装功能的训练集的RMSE 。
我的代码对训练和测试集进行了常见的划分:
set.seed(seed)
training.index <- createDataPartition(dataset[[target_label]], p = 0.8, list = FALSE)
training.set <- dataset[training.index, ]
testing.set <- dataset[-training.index, ]
Run Code Online (Sandbox Code Playgroud)
例如,0.651这可以提供比训练集RMSE高的测试集RMSE- 0.575如预期的那样。
遵循许多资料来源的建议,例如here,应该对数据进行混洗,因此在进行上述拆分之前我会这样做:
# shuffle data - short version:
set.seed(17)
dataset <- data %>% nrow %>% sample %>% data[.,]
Run Code Online (Sandbox Code Playgroud)
经过这种洗牌之后,测试集RMSE会低于0.528训练集RMSE 0.575!这个发现在包括在内的许多算法中是一致的lm, glm, knn, kknn, rf, gbm, svmLinear, svmRadial。
据我所知,sample()的默认值为,replace = FALSE因此不会有任何数据泄漏到测试集中。尽管createDataPartition执行分层,但在分类(准确性和kappa)中也会出现相同的观察结果,因此应处理任何数据不平衡情况。
我不使用任何非常规配置,仅使用普通的交叉验证:
training.configuration <- trainControl(
method = "repeatedcv", number = 10
, repeats = CV.REPEATS
, savePredictions = "final",
# , returnResamp = "all"
)
Run Code Online (Sandbox Code Playgroud)
我在这里想念什么?
-
我检查了数据分布,发现了所描述效果的潜在提示。
训练集分布:
. Freq prop
1 1 124 13.581599
2 2 581 63.636364
3 3 194 21.248631
4 4 14 1.533406
Run Code Online (Sandbox Code Playgroud)
测试集分布而无洗牌:
. Freq prop
1 1 42 18.502203
2 2 134 59.030837
3 3 45 19.823789
4 4 6 2.643172
Run Code Online (Sandbox Code Playgroud)
使用洗牌测试集分布:
. Freq prop
1 1 37 16.299559
2 2 139 61.233480
3 3 45 19.823789
4 4 6 2.643172
Run Code Online (Sandbox Code Playgroud)
如果我们查看模式(最常出现的值),则其在带有随机播放的测试61.2%集中的比例63.6%要比没有随机播放的更接近训练集的比例59.0%。
我不知道如何用基本理论从统计学上解释这一点-有人可以吗?
我的直觉是,混洗使测试集分布(由隐式执行createDataPartition())的分层变得更加“分层”,即我的意思是“更接近训练集分布”。这可能会导致数据泄漏到相反方向的影响-进入测试集。
library(caret)
library(tidyverse)
library(magrittr)
library(mlbench)
data(BostonHousing)
seed <- 171
# shuffled <- TRUE
shuffled <- FALSE
if (shuffled) {
dataset <- BostonHousing %>% nrow %>% sample %>% BostonHousing[., ]
} else {
dataset <- BostonHousing %>% as_tibble()
}
target_label <- "medv"
features_labels <- dataset %>% select_if(is.numeric) %>%
select(-target_label) %>% names %T>% print
# define ml algorithms to train
algorithm_list <- c(
"lm"
, "glmnet"
, "knn"
, "gbm"
, "rf"
)
# repeated cv
training_configuration <- trainControl(
method = "repeatedcv", number = 10
, repeats = 10
, savePredictions = "final",
# , returnResamp = "all"
)
# preprocess by standardization within each k-fold
preprocess_configuration = c("center", "scale")
# select variables
dataset %<>% select(target_label, features_labels) %>% na.omit
# dataset subsetting for tibble: [[
set.seed(seed)
training.index <- createDataPartition(dataset[[target_label]], p = 0.8, list = FALSE)
training.set <- dataset[training.index, ]
testing.set <- testing.set <- dataset[-training.index, ]
########################################
# 3.2: Select the target & features
########################################
target <- training.set[[target_label]]
features <- training.set %>% select(features_labels) %>% as.data.frame
########################################
# 3.3: Train the models
########################################
models.list <- list()
models.list <- algorithm_list %>%
map(function(algorithm_label) {
model <- train(
x = features,
y = target,
method = algorithm_label,
preProcess = preprocess_configuration,
trControl = training_configuration
)
return(model)
}
) %>%
setNames(algorithm_list)
Run Code Online (Sandbox Code Playgroud)
observed <- testing.set[[target_label]]
models.list %>%
predict(testing.set) %>%
map_df(function(predicted) {
sqrt(mean((observed - predicted)^2))
}) %>%
t %>% as_tibble(rownames = "model") %>%
rename(RMSE.testing = V1) %>%
arrange(RMSE.testing) %>%
as.data.frame
Run Code Online (Sandbox Code Playgroud)
同时在给出的代码shuffled = FALSE和代码shuffled = TRUE上运行以下代码testing.set:
model RMSE.testing RMSE.testing.shuffled
1 gbm 3.436164 2.355525
2 glmnet 4.516441 3.785895
3 knn 3.175147 3.340218
4 lm 4.501077 3.843405
5 rf 3.366466 2.092024
Run Code Online (Sandbox Code Playgroud)
效果可重现!
小智 6
您获得不同的测试RMSE的原因是因为您拥有不同的测试集。您正在重新整理数据,然后training.index每次都使用相同的数据,因此没有理由相信每次测试集都会相同。
在原始比较中,您需要将改组后的测试数据的RMSE与改组后的训练数据(而不是原始训练数据)的RMSE进行比较。
编辑:改组也是不必要的,因为createDataPartition它有自己的采样方案。如果您想要不同的测试/培训分组,则可以只更改种子