PCA在随机森林之前用于降维

8

我正在处理大约4500个变量的二元分类随机森林。其中许多变量高度相关,有些只是原始变量的分位数。我不确定是否明智应用PCA进行降维。这样做能提高模型性能吗?

我希望知道哪些变量对我的模型更具有显著性,但如果使用PCA,我只能知道哪些主成分更重要。

提前感谢您的帮助。


对于有兴趣的读者来说,这也可能很有用,因为它提供了不同的视角:https://stats.stackexchange.com/questions/258938/pca-before-random-forest-regression-provide-better-predictive-scores-for-my-data/258942 - Anonymous
2个回答

8
我的经验是,在随机森林(RF)之前使用主成分分析(PCA)并没有什么明显的优势。主成分回归(PCR)是一种方法,其中PCA用于在普通最小二乘线性回归(OLS)之前对训练特征进行正则化,对于稀疏数据集非常必要。由于RF本身已经在不假设线性的情况下执行良好/公平的正则化,因此这并不一定是一个优势。尽管如此,两周前我自己编写了一个PCA-RF包装器给R。代码包括一个模拟数据集,其中有100个特征,只包含5个真实的线性成分。在这种情况下,使用PCA预过滤确实是一个小优势。该代码是无缝实现的,因此每个RF参数都可以简单地传递到RF中。加载向量保存在model_fit中,以在预测期间使用。
我希望能够知道哪些变量对我的模型更重要,但如果使用PCA,我只能告诉哪些主成分更重要。简单的方法是不使用PCA运行并获取变量重要性,并期望在PCA-RF中找到类似的结果。
麻烦的方法是,在新的包装方案中包装PCA-RF,并加入自己的变量重要性代码。这可能需要50-100行左右的代码。
PCA-RF的源代码建议:
#wrap PCA around randomForest, forward any other arguments to randomForest
#define as new S3 model class
train_PCA_RF = function(x,y,ncomp=5,...) {
  f.args=as.list(match.call()[-1])
  pca_obj = princomp(x)
  rf_obj = do.call(randomForest,c(alist(x=pca_obj$scores[,1:ncomp]),f.args[-1]))
  out=mget(ls())
  class(out) = "PCA_RF"
  return(out)    
}

#print method
print.PCA_RF = function(object) print(object$rf_obj)

#predict method
predict.PCA_RF = function(object,Xtest=NULL,...) {
  print("predicting PCA_RF")
  f.args=as.list(match.call()[-1])
  if(is.null(f.args$Xtest)) stop("cannot predict without newdata parameter")
  sXtest = predict(object$pca_obj,Xtest) #scale Xtest as Xtrain was scaled before
  return(do.call(predict,c(alist(object = object$rf_obj, #class(x)="randomForest" invokes method predict.randomForest
                                 newdata = sXtest),      #newdata input, see help(predict.randomForest)
                                 f.args[-1:-2])))  #any other parameters are passed to predict.randomForest

}

#testTrain predict #
make.component.data = function(
  inter.component.variance = .9,
  n.real.components = 5,
  nVar.per.component = 20,
  nObs=600,
  noise.factor=.2,
  hidden.function = function(x) apply(x,1,mean),
  plot_PCA =T
){
  Sigma=matrix(inter.component.variance,
               ncol=nVar.per.component,
               nrow=nVar.per.component)
  diag(Sigma)  = 1
  x = do.call(cbind,replicate(n = n.real.components,
                              expr = {mvrnorm(n=nObs,
                                              mu=rep(0,nVar.per.component),
                                              Sigma=Sigma)},
                              simplify = FALSE)
            )
  if(plot_PCA) plot(prcomp(x,center=T,.scale=T))
  y = hidden.function(x)
  ynoised = y + rnorm(nObs,sd=sd(y)) * noise.factor
  out = list(x=x,y=ynoised)
  pars = ls()[!ls() %in% c("x","y","Sigma")]
  attr(out,"pars") = mget(pars) #attach all pars as attributes
  return(out)
}

一个代码示例:

运行代码示例:

#start script------------------------------
#source above from separate script
#test
library(MASS)
library(randomForest)

Data = make.component.data(nObs=600)#plots PC variance
train = list(x=Data$x[  1:300,],y=Data$y[1:300])
test = list(x=Data$x[301:600,],y=Data$y[301:600])

rf = randomForest (train$x, train$y,ntree =50) #regular RF
rf2 = train_PCA_RF(train$x, train$y,ntree= 50,ncomp=12)

rf
rf2


pred_rf = predict(rf  ,test$x)
pred_rf2 = predict(rf2,test$x)

cat("rf, R^2:",cor(test$y,pred_rf  )^2,"PCA_RF, R^2", cor(test$y,pred_rf2)^2)

cor(test$y,predict(rf ,test$x))^2
cor(test$y,predict(rf2,test$x))^2

pairs(list(trueY = test$y,
           native_rf = pred_rf,
           PCA_RF = pred_rf2)
)

以上代码无法运行。
is.data.frame(y) 函数出错:对象 'pred_rf' 未找到。
您是否忘记包含 pred_rf 和 pred_rf2 对象的来源?
- Hammao
通过将cat()函数调用向下移动几行,直到变量pred_rf和pred_rf2被赋值后,修复了错误。 - Soren Havelund Welling
谢谢。您介意看一下这个问题吗?https://dev59.com/IWoMtIcB2Jgan1znpfc0 - Hammao

2
你可以在这里看一下,以获得更好的想法。链接说针对较小的数据集使用PCA!! 当我的一些同事在处理基因组时,他们使用随机森林来达到相同的目的。他们有约30000个变量和大量的RAM。
另一个我发现的问题是,随机森林消耗大量内存,而你有4500个变量。因此,你可以将PCA应用于单独的树。

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