极度不平衡的类别随机森林

19

我正在解决一个非常不平衡的响应类别的大数据问题,使用随机森林算法。因此,我查阅了文档并找到了以下参数:

strata 

sampsize

这些参数的文档很少(或者我没有找到它们),我真的不知道如何实现它。我正在使用以下代码:

randomForest(x=predictors, 
             y=response, 
             data=train.data, 
             mtry=lista.params[1], 
             ntree=lista.params[2], 
             na.action=na.omit, 
             nodesize=lista.params[3], 
             maxnodes=lista.params[4],
             sampsize=c(250000,2000), 
             do.trace=100, 
             importance=TRUE)

响应是一个具有两个可能值的类,第一个比第二个出现的频率更高(10000:1或更多)。

list.params 是具有不同参数的列表(当然!我知道...)

嗯,问题(再次)是:我如何使用“strata”参数?我是否正确地使用了sampsize?

最后,有时我会收到以下错误:

Error in randomForest.default(x = predictors, y = response, data = train.data,  :
  Still have fewer than two classes in the in-bag sample after 10 attempts.

抱歉,如果我问了这么多(可能是愚蠢的)问题...


1
根据DWin的建议,我将尝试对数据进行描述。数据框的大小为1 M行或稍微多一点(2或3 M行),它有近33列,这些列都是因子,除了两个是数值类型,还有一个额外的响应列,是一个具有两个可能值的因子。希望这可以帮助到您。 - nanounanue
7
在仔细阅读您描述的错误信息后,我仔细查看了CRAN上的源代码,并联系了软件包作者。结果发现代码中确实存在一个小错误,可能导致出现问题并生成该错误。请在接下来的几天内检查CRAN是否有修复版本,并看看是否能够解决问题。 - joran
@joran 哇,谁能知道!让我再用原始代码尝试一遍。谢谢! - nanounanue
抱歉回复这么晚,但现在它已经可行了。正如@joran所说,这是一个bug...再次感谢。 - nanounanue
4个回答

7
你应该尝试使用采样方法来减少不平衡程度,从1:10,000降至1:100或1:10。你还应该减小生成的树的大小。(目前这些只是我从记忆中重复的建议,但我会看看是否可以找到更权威的来源。)
一种减小树大小的方法是设置“节点大小”更大。对于这种程度的不平衡,您可能需要将节点大小设置得非常大,例如5-10,000。这里有一个rhelp线程: https://stat.ethz.ch/pipermail/r-help/2011-September/289288.html 在当前问题的状态下,您有sampsize=c(250000,2000),而我认为像sampsize=c(8000,2000)这样的东西更符合我的建议。我认为您正在创建没有任何被抽样组中的样本。

谢谢DWin,你能给我提供一些代码示例吗?再次感谢您的帮助。 - nanounanue
通常情况下,只有在提供了数据或足够清晰的描述以支持构建与问题相匹配的示例时,我才会尝试编写代码。 - IRTFM
关于您的回答,如果我增加节点大小,它不会影响预测吗?我的意思是,这会降低准确性吗?另一方面,如果我减小树的大小,它如何提高准确性? - nanounanue
我怀疑在这种情况下增加预测的准确性可能更好。类比于逻辑回归,结果组中小的部分通常应该至少是预测变量数的10倍。 - IRTFM
还要考虑此线程中带有costMatrix的答案 https://stats.stackexchange.com/questions/46963/how-to-control-the-cost-of-misclassification-in-random-forests - Ivan
显示剩余2条评论

4
有几种选项。
如果数据很多,请设置一组随机抽样的数据。在其中一个集合上建立模型,然后使用另一个集合来使用 ROC 曲线确定类概率的正确截止点。
您还可以提高少数类别中的数据量。SMOTE 算法可能有所帮助(请参阅下面的参考文献和 DMwR 软件包中的函数)。
您也可以使用其他技术。rpart() 和其他几个函数可以允许不同错误成本,因此您可以更喜欢少数类别。您可以对这种类型的 rpart() 模型进行装袋,以近似随机森林正在执行的操作。
kernlab 软件包中的 ksvm() 也可以使用不平衡成本(但是在执行此操作时,概率估计值不再准确)。许多其他软件包都具有设置先验的参数。您还可以调整此参数,使其更加强调少数类别。
最后想到的一点:基于准确性的最大化模型不会帮助您(您可以一开始就获得 99.99% 的准确性)。caret 可以根据 Kappa 统计量来调整模型,在您的情况下这是一个更好的选择。

4
抱歉,我不知道如何在早期答案上发表评论,因此我将创建一个单独的答案。
我认为问题是由数据集高度不平衡引起的(某个类别的案例太少)。对于RF中的每棵树,算法都会创建引导样本,这是该树的训练集。如果您的数据集中某个类别的示例太少,则引导抽样将仅选择一个类别的示例(主要类别)。因此,树无法在仅有一个类别的示例上生长。看来最多有10次失败的采样尝试。
因此,DWin提议将不平衡度降低到更低的值(1:100或1:10)是最合理的建议。

谢谢DrDom(多一个字母就是个很棒的昵称,顺便说一下,我有点喜欢毁灭博士)。但是在哪里减少不平衡呢?在从数据库获取数据时还是在sampsize属性中?我尝试了第二种方法(我的意思是保留1M行或者其他),平衡了sampsize,但是错误又出现了。但是如果我将1M行减少到25万行,一切都正常了,但是我担心会影响可预测性或者过度拟合...你的意见是什么? - nanounanue
1
@nanounanue,我建议将所有次要类对象添加100倍的主要类对象(可以随机或有监督地选择主要类对象),并开发一个模型。然后重复此步骤多次,以至少使用一次来自主要类的所有对象。因此,您将拥有一组模型。所有这些模型都应用于预测。最终预测是通过多数投票方案进行的-哪个类获得更多投票就获胜。另一种可能性是减少主要类对象的数量。例如,可以根据聚类分析来完成此操作。 - DrDom

1

我非常不赞同从样本中删除观测值的想法。

相反,您可以考虑使用分层抽样来设置每个类别在每次重新采样时的固定百分比。这可以通过Caret软件包完成。这样,您将不会通过减小训练样本的大小来省略观测值。它不会使您的类别过度代表,但确保每个子样本都具有代表性样本。

以下是我找到的一个示例:

len_pos <- nrow(example_dataset[example_dataset$target==1,])
len_neg <- nrow(example_dataset[example_dataset$target==0,])

train_model <- function(training_data, labels, model_type, ...) {
  experiment_control <- trainControl(method="repeatedcv",
                                     number = 10,
                                     repeats = 2,
                                     classProbs = T,
                                     summaryFunction = custom_summary_function)
  train(x = training_data,
        y = labels,
        method = model_type,
        metric = "custom_score",
        trControl = experiment_control,
        verbose = F,
        ...)
}

# strata refers to which feature to do stratified sampling on.
# sampsize refers to the size of the bootstrap samples to be taken from each class. These samples will be taken as input
# for each tree. 

fit_results <- train_model(example_dataset
                           , as.factor(sprintf("c%d", as.numeric(example_dataset$target)))        
                           ,"rf"
                           ,tuneGrid = expand.grid(mtry = c( 3,5,10))
                           ,ntree=500
                           ,strata=as.factor(example_dataset$target)
                           ,sampsize = c('1'=as.integer(len_pos*0.25),'0'=as.integer(len_neg*0.8))
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接