分类交互项对随机森林机器学习算法的影响

机器算法验证 机器学习 分类 分类数据 相互作用 随机森林
2022-03-14 04:36:40

在此先感谢您的帮助。

我有中等大的数据集(大约 7000 个样本),其中包含许多分类预测变量和单个二元响应。所有的预测变量都是分类的。通过反复试验,我发现使用随机森林模型可以产生最佳的响应预测准确性。

为了提高模型的准确性,我决定包含成对交互项。但是,这样做会导致准确性下降。我最初认为这是因为包含交互项会在我的训练集中添加大量预测变量。具体来说,在添加交互项之前,我有 12 个预测变量,在添加大约 60 个之后。使用虚拟编码,我有大约 60 个预测变量和几百个,恭敬地。

考虑到这一点,我执行了特征选择并选择了 5、10、15 等最佳预测器来训练我的模型。这对我的模型的准确性影响不大,即它不如没有交互项的模型好。请注意,我针对森林中的树木和随机选择中使用的特征数量进行了优化。

在这一点上,人们可能会得出结论,交互项不会向我的模型添加任何内容。但是,我有充分的证据表明它们应该是反应的非常强的预测因子。理想情况下,我可以进行比配对更复杂的交互,但我没有足够的样本来做到这一点。

我现在想知道这种行为是否是我使用的方法的结果,即我使用的是一片树林。假设我现在只使用一棵树。使用分类数据来训练树型模型是否可能固有地对交互进行编码?如果是这样,这可以解释为什么包含交互项会导致我的模型的预测性能下降。

执行特征选择会导致几乎只选择交互预测器。当我使用这些预测器生成随机森林时,我会限制我的“路径”。我会用一个例子来激励这一点。假设我有预测器w,x,y,z并使用特征选择,我选择了交互预测器wxyz. 我不能再生产一棵树x然后在y. 但是,我可以使用原始预测变量生成这样一棵树,而无需交互。

这是我的假设。仅使用原始预测变量生成随机森林允许我通过树的分支行为“编码”交互。仅使用特征选择的结果效果不佳,因为选择的特征主要是交互作用。这限制了生成树的分支行为。最后,包括所有内容都不会表现得那么好,因为这会导致太多的预测变量。我没有足够的样本让模型正确训练。我还应该注意,我研究了使用原始预测变量和少数最佳交互,由特征选择确定。这也没有只使用原始预测器表现得那么好。

这是我的问题。我以正确的方式看待这个吗?更具体地说,仅使用分类预测器生成树是否固有地编码了这些预测器之间的交互?此外,任何关于如何解决这个问题(预测二元响应)的见解或建议都将不胜感激。

谢谢你。

1个回答

我认为您的问题很有趣,我花了一些时间研究随机森林(RF)模型拟合的有效映射曲率。RF 可以根据情况捕获一些交互顺序。x1 * x2 是双向交互,依此类推...您没有写出分类预测变量有多少级别。这很重要。对于连续变量(许多级别),通常只能捕获多个局部双向交互。问题是,RF 模型本身只拆分而不转换数据。因此,RF 受困于局部单变量拆分,这对于吸引交互来说不是最佳的。因此,与深度学习相比,RF 相当浅。在幽灵的另一端是二进制特征。我不知道射频能走多深,所以我运行了一个网格搜索模拟。RF 似乎可以捕获多达 4-8 个二进制特征的交互顺序。我使用 12 个二进制变量和 100 到 15000 个观察值。例如对于四阶交互,预测向量 y 是:

orderOfInteraction = 4
y = factor(apply(X[,1:orderOfInteraction],1,prod))

其中 X 的任何元素是 -1 或 1,并且 X 的前四个变量列的乘积是预测。所有四个变量都是完全互补的。因此,没有主效应、二阶或三阶效应。因此,OOB 预测误差将仅反映 RF 对 N 阶交互作用的吸引力。

使 RF 吸引更高阶交互的因素:大量观察、变量级别少、变量少

RF 吸引高阶的限制因素:与上述相反,有限的样本大小、有限的最大节点和冗余/足够的低阶信息。

最后一个意思是,如果 RF 可以在低阶交互中找到相同的信息,那么可以说,没有必要再深入了。信息甚至可能不是多余的。RF 必须足以做出正确的二进制预测。

随机森林的深度:OOB err.rate vs. 观察 vs. 交互顺序 随机森林的深度:OOB err.rate vs. 观察 vs. 交互顺序

  rm(list=ls())
  library(randomForest)
  library(parallel)
  library(rgl)

  simulate.a.forest = function(std.pars,ite.pars) {
    #Merge standard parameters with iterated parameters
    run.pars = c(std.pars,ite.pars)

    #simulate data of a given order
    X = replicate(run.pars$vars,sample(c(-1,1),run.pars$obs,replace=T))
    y = factor(apply(X[,1:run.pars$intOrder],1,prod))

    #run forest with run.pars, pars with wrong name is ignored
    rfo = do.call(randomForest, run.pars)

    #Fetch OOB error.rate and return
    out = rev(rfo$err.rate[,1])[1] #fetch error rate from object
    names(out) = paste(ite.pars,collapse="-")[1]
    return(out)
  }

  ## Lets try some situations (you can also pass arguments to randomForest here)
  intOrders = c(2,3,4,5,6,12) #hidden signal is a N-way interaction of Nth order
  obss = c(100,500,1000,3500,7000,15000) #available observations

  ## Produce list of all possible combinations of parameters
  ite.pars.matrix = expand.grid(intOrder=intOrders,obs=obss)
  n.runs = dim(ite.pars.matrix)[1]
  ite.pars.list   = lapply(1:n.runs, function(i) ite.pars.matrix[i,])

  i=1 ##for test-purposes
  out = mclapply(1:n.runs, function(i){
    #line below can be run alone without mclapply to check for errors before going multicore
    out = simulate.a.forest(std.pars=alist(x=X,y=y,
                                           ntree=250,
                                           vars=12),
                                           #sampsize=min(run.pars$obs,2000)),
                            ite.pars=ite.pars.list[[i]])
    return(out)
  })

  ##view grid results
  persp3d(x = intOrders,xlab="Nth order interaction",
          y = log(obss,base=10),ylab="10log(observations)",
          z = matrix(unlist(out),nrow=length(intOrders)),zlab="OOB prediction error, binary target",
          col=c("grey","black"),alpha=.2)

  rgl.snapshot(filename = "aweSomePlot.png", fmt = "png", top = TRUE)