使用插入符号列函数的R随机森林交叉验证不会产生与手工完成时相同的精度

问题描述 投票:2回答:3

我正在构建一个随机森林中的一些工作数据(这意味着我无法共享该数据,有15k观测值),使用插入符号列功能进行交叉验证,模型的准确性非常低:0.9%。

这是我使用的代码:

set.seed(512)
n <- nrow(my_data)

train_indices <- sample(1:n)
my_folds <- createFolds(train_indices, k=5)

model <- train(ICNumber ~ ., tuneGrid = data.frame(mtry = c(32), min.node.size = 1, splitrule = "gini"),
                 data = my_data, method = "ranger",
                 trControl = trainControl(verboseIter = TRUE, savePredictions = T, index=my_folds))

print(model$resample)

- 编辑 正如吉尔斯所注意到的那样,折叠指数构造错误,并且在20%的观测值上进行了训练,但即使我通过添加returnTrain = T来解决这个问题,我仍然接近零准确度 - 编辑

model $ resample产生这个:

Accuracy ___ Kappa_____ Resample  
0.026823683_ 0.0260175246_ Fold1  
0.002615234_ 0.0019433907_ Fold2  
0.002301118_ 0.0017644472_ Fold3  
0.001637733_ 0.0007026352_ Fold4  
0.010187315_ 0.0094986595_ Fold5  

现在,如果我像这样手工进行交叉验证:

set.seed(512)
n <- nrow(my_data)

train_indices <- sample(1:n)
my_folds <- createFolds(train_indices, k=5)

for (fold in my_folds) {
  train_data <- my_data[-fold,]
  test_data <- my_data[fold,]

  model <- train(ICNumber ~ ., tuneGrid = data.frame(mtry = c(32), min.node.size = 1, splitrule = "gini"),
                 data = train_data, method = "ranger",
                 trControl = trainControl(method = "none"))

  p <- predict(model, test_data)
  e <- ifelse(p == test_data$ICNumber, T, F)
  print(sum(e) / nrow(test_data))
}

我得到以下准确度:

[1] 0.743871  
[1] 0.7566957  
[1] 0.7380645  
[1] 0.7390181  
[1] 0.7311168

我期待获得相同的准确度值,我在火车上做错了什么?或者手动预测代码是错误的?

- 编辑 此外,此代码适用于大豆数据,我可以重现以下Gilles的结果 - 编辑

--Edit2 以下是我的数据的一些细节: 15493 obs。 17个变量: ICNUmber是一个包含1531个不同值的字符串,这些是类 其他16个变量是33个级别的因子 --Edit2

--Edit3 我的最后一个实验是放弃所有类别的观察结果发生不到10次,仍有12k观察到396个类别。对于此数据集,手动和自动交叉验证精度匹配... --Edit3

r r-caret
3个回答
3
投票

这是一个棘手的问题! ;-) 该错误来自于indextrainControl选项的误用。

根据帮助页面,qazxsw poi应该是:

包含每个重采样迭代元素的列表。每个列表元素是一个整数向量,对应于在该迭代中用于训练的行。

在您的代码中,您提供了与应从训练数据集中删除的行相对应的整数,而不是提供与应该使用的行相对应的整数...

您可以使用index而不是createFolds(train_indices, k=5, returnTrain = T)来实现这一目标。 另请注意,inaialy,afaik,createFolds(train_indices, k=5)正在创建相对于您要预测的类平衡的折叠。所以代码应该更像是:caret,特别是如果类不平衡的话......

以下是Soybean数据集的可重现示例

createFolds(my_data[train_indices, "Class"], k=5, returnTrain = T)

您的代码(训练数据比预期的要小得多,您只使用20%的数据,因此精度较低)。 由于训练数据集中某些类的缺失(由于类不平衡和训练集减少),您也会收到一些警告。

library(caret)
#> Le chargement a nécessité le package : lattice
#> Le chargement a nécessité le package : ggplot2
data(Soybean, package = "mlbench")
my_data <- droplevels(na.omit(Soybean))

更正了代码,只需使用set.seed(512) n <- nrow(my_data) train_indices <- sample(1:n) my_folds <- createFolds(train_indices, k=5) model <- train(Class ~ ., tuneGrid = data.frame(mtry = c(32), min.node.size = 1, splitrule = "gini"), data = my_data, method = "ranger", trControl = trainControl(verboseIter = F, savePredictions = T, index=my_folds)) #> Warning: Dropped unused factor level(s) in dependent variable: rhizoctonia- #> root-rot. #> Warning: Dropped unused factor level(s) in dependent variable: downy- #> mildew. print(model$resample) #> Accuracy Kappa Resample #> 1 0.7951002 0.7700909 Fold1 #> 2 0.5846868 0.5400131 Fold2 #> 3 0.8440980 0.8251373 Fold3 #> 4 0.8822222 0.8679453 Fold4 #> 5 0.8444444 0.8263563 Fold5 (这里你真的使用80%的数据进行培训......)

returnTrain = T

要与你的循环进行比较。仍然存在一些小的差异,所以可能还有一些我不理解的东西。

set.seed(512)
n <- nrow(my_data)

train_indices <- sample(1:n)
my_folds <- createFolds(train_indices, k=5, returnTrain = T)

model <- train(Class ~ ., tuneGrid = data.frame(mtry = c(32), min.node.size = 1, splitrule = "gini"),
               data = my_data, method = "ranger",
               trControl = trainControl(verboseIter = F, savePredictions = T, 
                                        index=my_folds))

print(model$resample)
#>    Accuracy     Kappa Resample
#> 1 0.9380531 0.9293371    Fold1
#> 2 0.8750000 0.8583687    Fold2
#> 3 0.9115044 0.9009814    Fold3
#> 4 0.8660714 0.8505205    Fold4
#> 5 0.9107143 0.9003825    Fold5

set.seed(512) n <- nrow(my_data) train_indices <- sample(1:n) my_folds <- createFolds(train_indices, k=5) for (fold in my_folds) { train_data <- my_data[-fold,] test_data <- my_data[fold,] model <- train(Class ~ ., tuneGrid = data.frame(mtry = c(32), min.node.size = 1, splitrule = "gini"), data = train_data, method = "ranger", trControl = trainControl(method = "none")) p <- predict(model, test_data) e <- ifelse(p == test_data$Class, T, F) print(sum(e) / nrow(test_data)) } #> [1] 0.9380531 #> [1] 0.875 #> [1] 0.9115044 #> [1] 0.875 #> [1] 0.9196429 创建于2018-03-09(v0.2.0)。


2
投票

为了扩展吉尔斯的优秀答案。除了指定用于测试和训练的索引的错误,要获得一个完全可重现的算法模型,其中包含一些随机过程,如随机forrest,你应该在reprex package中指定seeds参数。此参数的长度应等于重新采样的数量+ 1(对于最终模型):

trainControl

现在让我们手动重新采样:

library(caret)
library(mlbench)
data(Sonar)

data(Sonar)

set.seed(512)
n <- nrow(Sonar)
train_indices <- sample(1:n)
my_folds <- createFolds(train_indices, k = 5, returnTrain = T)

model <- train(Class ~ .,
               tuneGrid = data.frame(mtry = c(32),
                                     min.node.size = 1,
                                     splitrule = "gini"),
               data = Sonar,
               method = "ranger",
               trControl = trainControl(verboseIter = F,
                                        savePredictions = T, 
                                        index = my_folds,
                                        seeds = rep(512, 6))) #this is the important part

 model$resample
#output
   Accuracy     Kappa Resample
1 0.8536585 0.6955446    Fold1
2 0.8095238 0.6190476    Fold2
3 0.8536585 0.6992665    Fold3
4 0.7317073 0.4786127    Fold4
5 0.8372093 0.6681367    Fold5

@semicolo如果您可以在Sonar数据集上重现此示例,但不能使用您自己的数据,则问题出在数据集中,任何进一步的见解都需要调查相关数据。


0
投票

看起来火车功能将类列转换为一个因子,在我的数据集中有很多(约20%)具有少于4个观察值的类。当手动分割集合时,在分割之后构造因子,并且对于每个因子值,至少有一个观察值。但是在自动交叉验证期间,因子是在完整数据集上构建的,并且当完成拆分时,该因子的某些值没有任何观察值。这似乎在某种程度上搞砸了准确性。这可能需要一个新的不同的问题,感谢吉尔斯和他们的帮助。

© www.soinside.com 2019 - 2024. All rights reserved.