首页 文章

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

提问于
浏览
1

我正在构建一个随机森林中的一些工作数据(这意味着我无法共享该数据,有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

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

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

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

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

3 回答

  • 0

    这是一个棘手的问题! ;-)
    该错误来自 trainControl 中误用 index 选项 .

    根据帮助页面, index 应该是:

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

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

    您可以使用 createFolds(train_indices, k=5, returnTrain = T) 而不是 createFolds(train_indices, k=5) 来表示 .
    另请注意,inaialy,afaik, caret 正在创建相对于您要预测的类 balancer 的折叠 . 所以代码理想情况应该更像: createFolds(my_data[train_indices, "Class"], k=5, returnTrain = T) ,特别是如果类不 balancer ...

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

    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))
    

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

    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
    

    更正了代码,只需 returnTrain = T (这里你真的使用80%的数据进行培训......)

    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
    

    reprex package(v0.2.0)于2018-03-09创建 .

  • 2

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

    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
    

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

    for (fold in my_folds) {
      train_data <- Sonar[fold,]
      test_data <- Sonar[-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",
                                              seeds = 512)) #use the same seeds as above
    
      p <- predict(model, test_data)
      e <- ifelse(p == test_data$Class, T, F)
      print(sum(e) / nrow(test_data))
    }
    #output
    [1] 0.8536585
    [1] 0.8095238
    [1] 0.8536585
    [1] 0.7317073
    [1] 0.8372093
    

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

  • 1

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

相关问题