从随机森林中获取知识


127

随机森林被认为是黑匣子,但是最近我在想可以从随机森林中获得什么知识?

最明显的是变量的重要性,在最简单的变体中,只需计算变量的出现次数即可完成。
我正在考虑的第二件事是交互。我认为,如果树的数量足够大,则可以测试变量对的出现次数(类似于卡方独立性)。第三件事是变量的非线性。我的第一个想法只是看可变Vs得分的图表,但我不确定这是否有意义。

添加23.01.2012
动机

我想利用这些知识来改进logit模型。我认为(或至少希望如此)可以找到被忽略的相互作用和非线性。


1
使用R您可以产生Dotchart如通过随机森林测量的变量重要性。
George Dontas 2012年

1
有关如何为随机梯度树增强计算变量重要性度量的相关主题
Antoine

我意识到这可能为时已晚,但是如果您只想改善logit模型,为什么不使用套索后逻辑回归呢?选择后,您只需使用所选系数重新拟合模型即可,而不会受到损失/收缩。您将不得不稍微调整调整过程,但这是一个更直接的选项,可以完全满足您的需求。
ilprincipe

Answers:


122

随机森林几乎不是黑匣子。它们基于决策树,决策树很容易解释:

#Setup a binary classification problem
require(randomForest)
data(iris)
set.seed(1)
dat <- iris
dat$Species <- factor(ifelse(dat$Species=='virginica','virginica','other'))
trainrows <- runif(nrow(dat)) > 0.3
train <- dat[trainrows,]
test <- dat[!trainrows,]

#Build a decision tree
require(rpart)
model.rpart <- rpart(Species~., train)

这产生了一个简单的决策树:

> model.rpart
n= 111 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

1) root 111 35 other (0.68468468 0.31531532)  
  2) Petal.Length< 4.95 77  3 other (0.96103896 0.03896104) *
  3) Petal.Length>=4.95 34  2 virginica (0.05882353 0.94117647) *

如果Petal.Length <4.95,则此树将观察分类为“其他”。如果大于4.95,则将观察结果归类为“ virginica”。随机森林是许多这样的树的简单集合,其中每棵都是在数据的随机子集上训练的。然后,每棵树对每个观察结果的最终分类进行“投票”。

model.rf <- randomForest(Species~., train, ntree=25, proximity=TRUE, importance=TRUE, nodesize=5)
> getTree(model.rf, k=1, labelVar=TRUE)
  left daughter right daughter    split var split point status prediction
1             2              3  Petal.Width        1.70      1       <NA>
2             4              5 Petal.Length        4.95      1       <NA>
3             6              7 Petal.Length        4.95      1       <NA>
4             0              0         <NA>        0.00     -1      other
5             0              0         <NA>        0.00     -1  virginica
6             0              0         <NA>        0.00     -1      other
7             0              0         <NA>        0.00     -1  virginica

您甚至可以从射频中提取单个树,然后查看它们的结构。格式与rpart模型略有不同,但是您可以根据需要检查每棵树,并查看其如何对数据建模。

此外,没有一个模型是真正的黑匣子,因为您可以检查数据集中每个变量的预测响应与实际响应。无论您要构建哪种模型,这都是一个好主意:

library(ggplot2)
pSpecies <- predict(model.rf,test,'vote')[,2]
plotData <- lapply(names(test[,1:4]), function(x){
  out <- data.frame(
    var = x,
    type = c(rep('Actual',nrow(test)),rep('Predicted',nrow(test))),
    value = c(test[,x],test[,x]),
    species = c(as.numeric(test$Species)-1,pSpecies)
    )
  out$value <- out$value-min(out$value) #Normalize to [0,1]
  out$value <- out$value/max(out$value)
  out
})
plotData <- do.call(rbind,plotData)
qplot(value, species, data=plotData, facets = type ~ var, geom='smooth', span = 0.5)

情节

我已经将变量(花瓣和花瓣的长度和宽度)标准化为0-1范围。响应也为0-1,其中0为其他,1为virginica。如您所见,即使在测试集上,随机森林也是一个很好的模型。

此外,随机森林将计算各种变量重要性的量度,这可能会非常有用:

> importance(model.rf, type=1)
             MeanDecreaseAccuracy
Sepal.Length           0.28567162
Sepal.Width           -0.08584199
Petal.Length           0.64705819
Petal.Width            0.58176828

该表表示删除每个变量将降低模型准确性的程度。最后,您可以从随机森林模型中绘制出许多其他图,以查看黑匣子中发生的情况:

plot(model.rf)
plot(margin(model.rf)) 
MDSplot(model.rf, iris$Species, k=5)
plot(outlier(model.rf), type="h", col=c("red", "green", "blue")[as.numeric(dat$Species)])

您可以查看每个功能的帮助文件,以更好地了解它们的显示内容。


6
感谢您的回答,这里有很多有用的信息,但这并不是我一直在寻找的信息。也许我需要更好地阐明这个问题背后的动机。我想使用随机森林来改进logit模型,通过改进我的意思是添加一些交互或使用非线性变换。
Tomek Tarczynski 2012年

@TomekTarczynski,这是一个有趣的问题,类似于我现在正在处理的问题。我假设“ logit模型”是指逻辑回归或类似的东西?我正在使用套索逻辑回归(来自glmnet R包)从具有所有变量对之间相互作用的模型中选择预测变量。我还没有添加任何非线性术语-但原则上也应该可行。我猜唯一的问题是确定尝试使用哪些非线性项(多项式项,指数变换等)。而且,我不会进行任何高级交互,但这也很容易。
Anne Z. 2012年

2
@Tomek,您从这个答案中得到什么?如果您在R中使用randomForest软件包,则Zach描述的图将非常有用。具体来说,您可以使用varImpPlot在logit模型中进行功能选择,并使用partialPlot估计转换类型,以尝试在logit模型中使用连续预测变量。我建议将后一个图用于确定预测变量和响应之间存在非线性关系的位置,然后允许您显式进行该转换或对该变量使用样条线。
B_Miner 2012年

2
@b_miner-只是一个猜测,但这听起来像tomek在询问如何查找变量之间的非线性相互作用,因为逻辑回归已经捕获了线性关系。
rm999 2012年

@ rm999如何在logit模型中定义非线性相互作用?在转换变量之间创建了交互项?
B_Miner 2012年

52

不久前,我不得不为公司中的一些化学家证明RF模型适合。我花了很多时间尝试不同的可视化技术。在此过程中,我不小心还提出了一些新技术,这些技术专门用于随机森林可视化,并放入R包(forestFloor)中。

经典方法是受以下部分支持的部分依赖图:Rminer(基于数据的敏感性分析被重新发明为部分依赖)或randomForest包中的partialPlot。我发现部分依赖包iceBOX是发现交互的一种优雅方式。尚未使用edarf软件包,但似乎有一些专门用于RF的精细可视化。该ggRandomForest包中还含有大量组有用的可视化。

当前,forestFloor支持randomForest对象(正在支持其他RF实现)。还可以为梯度增强树计算特征贡献,因为训练后的这些树与随机森林树没有太大区别。因此forestFloor将来可以支持XGBoost。偏相关图完全是模型不变的。

所有程序包的共同点在于可视化模型从特征空间到目标空间的几何映射结构。正弦曲线y = sin(x)将是从x到y的映射,并且可以2D形式绘制。直接绘制RF映射通常会需要太多尺寸。相反,可以映射,切片或分解整个映射结构,以便将整个映射结构简化为2D边际图序列。如果您的RF模型仅捕获了主要效果,而变量之间没有相互作用,那么经典的可视化方法就可以了。然后您可以简化模型结构,例如y=F(X)f1(x1)+f2(x2)+...+fd(xd。然后,每个变量的每个部分函数都可以像正弦曲线一样可视化。如果您的RF模型已经捕获了相当大的交互作用,那么问题就更大了。结构的3D切片可以可视化两个要素和输出之间的交互。问题是要知道要可视化的功能组合(iceBOX确实解决了此问题)。同样,很难说出是否还没有考虑其他潜在的交互作用。

本文中,我使用了非常早期的forestFloor版本来解释一个非常小的RF模型已捕获的实际生化关系。并且在本文中,我们彻底描述了特征贡献的可视化,随机森林的林底可视化

我已经从forestFloor包中粘贴了模拟示例,在该示例中,我展示了如何发现模拟的隐藏函数 noiseÿ=X1个2+s一世ñX2π+2X3X4+

#1 - Regression example:
set.seed(1234)
library(forestFloor)
library(randomForest)

#simulate data y = x1^2+sin(x2*pi)+x3*x4 + noise
obs = 5000 #how many observations/samples
vars = 6   #how many variables/features
#create 6 normal distr. uncorr. variables
X = data.frame(replicate(vars,rnorm(obs)))
#create target by hidden function
Y = with(X, X1^2 + sin(X2*pi) + 2 * X3 * X4 + 0.5 * rnorm(obs)) 

#grow a forest
rfo = randomForest(
  X, #features, data.frame or matrix. Recommended to name columns.
  Y, #targets, vector of integers or floats
  keep.inbag = TRUE,  # mandatory,
  importance = TRUE,  # recommended, else ordering by giniImpurity (unstable)
  sampsize = 1500 ,   # optional, reduce tree sizes to compute faster
  ntree = if(interactive()) 500 else 50 #speedup CRAN testing
)

#compute forestFloor object, often only 5-10% time of growing forest
ff = forestFloor(
  rf.fit = rfo,       # mandatory
  X = X,              # mandatory
  calc_np = FALSE,    # TRUE or FALSE both works, makes no difference
  binary_reg = FALSE  # takes no effect here when rfo$type="regression"
)


#plot partial functions of most important variables first
plot(ff,                       # forestFloor object
     plot_seq = 1:6,           # optional sequence of features to plot
     orderByImportance=TRUE    # if TRUE index sequence by importance, else by X column  
)

在此处输入图片说明

#Non interacting features are well displayed, whereas X3 and X4 are not
#by applying color gradient, interactions reveal themself 
#also a k-nearest neighbor fit is applied to evaluate goodness-of-fit
Col=fcol(ff,3,orderByImportance=FALSE) #create color gradient see help(fcol)
plot(ff,col=Col,plot_GOF=TRUE) 

在此处输入图片说明

#feature contributions of X3 and X4 are well explained in the context of X3 and X4
# as GOF R^2>.8


show3d(ff,3:4,col=Col,plot_GOF=TRUE,orderByImportance=FALSE)

在此处输入图片说明 在此处输入图片说明

最后,由J.Friedman描述的A.Liaw编码的部分依赖图的代码。主要效果很好。

par(mfrow=c(2,3))
for(i in 1:6) partialPlot(rfo,X,x.var=names(X)[i])

在此处输入图片说明


我之前假设,也可以为增强树计算特征贡献。我写了一个测试算法,这是可能的。不幸的是,增强树木的特征贡献没有表现出与袋装树木相同的有益特性。一个功能的效果往往分散在所有功能的贡献上,因此可视化变得很混乱。
索伦·哈弗隆德·威灵

哎呀!我在测试算法中发现了一个错误,该错误使所有问题消失了。可以将forestFloor更新为在梯度增强树上正常工作。
索伦·哈弗隆德·威灵

3
ForestFloor是否已更新为接受gbm对象?
Misha

到目前为止,我已经做了一个反向实现,将randomForest实现包装到一个功能齐全的梯度提升机中,并定义了一些计算特征贡献的方法。我认为实施实际上是合理有效的。您可以在此处找到代码:github.com/sorhawell/forestFloor/blob/master/inst/examples/…–
Soren Havelund Welling,

要获得完整的端口很困难:)此实现只用了几行就完成了。
索伦·哈弗隆德·威灵

24

为了补充这些出色的响应,我会提到使用梯度增强树(例如R中GBM包)。在R中,我更喜欢随机森林,因为与需要插补的randomForest相比,允许缺失值。变量重要性和局部图可用(如randomForest中一样)可帮助您在logit模型中进行特征选择和非线性变换探索。此外,变量交互使用Friedman的H统计量(interact.gbm)进行处理,参考号为J.H. Friedman and B.E. Popescu (2005). “Predictive Learning via Rule Ensembles.” Section 8.1。索尔福德系统公司(Salford Systems)可提供称为TreeNet的商业版本,该视频演示了他们对可变交互估计视频的看法。


2
我同意,GBM是随机森林的合理下一步。
Zach 2012年

@B_miner:太好了!我不知道如何,但是我忽略了GBM。使用GBM似乎很容易检测相互作用和非线性。
Tomek Tarczynski 2012年

15

答案forestFloor很晚,但我遇到了一个最新的R包(2015),它可以帮助您以自动化方式完成此“取消黑匣子”任务。看起来很有前途!

library(forestFloor)
library(randomForest)
#simulate data
obs=1000
vars = 18
X = data.frame(replicate(vars,rnorm(obs)))
Y = with(X, X1^2 + sin(X2*pi) + 2 * X3 * X4 + 1 * rnorm(obs))
#grow a forest, remeber to include inbag
rfo=randomForest(X,Y,keep.inbag = TRUE,sampsize=250,ntree=50)
#compute topology
ff = forestFloor(rfo,X)
#ggPlotForestFloor(ff,1:9)
plot(ff,1:9,col=fcol(ff))

产生以下图:

在此处输入图片说明

如果您正在寻找交互,它还提供了三维可视化。


4
我删除了对ggplot2的支持,而不是最后一行尝试,例如: plot(ff,1:9,col = fcol(ff,1:4))
Soren Havelund Welling 15/12/22

9

正如Zach所提到的,理解模型的一种方法是随着预测变量的变化绘制响应。您可以使用plotmo R软件包轻松地对“任何”模型进行此操作。例如

library(randomForest)
data <- iris
data$Species <- factor(ifelse(data$Species=='virginica','virginica','other'))
mod <- randomForest(Species~Sepal.Length+Sepal.Width, data=data)
library(plotmo)
plotmo(mod, type="prob")

这使

情节

这将更改一个变量,同时将其他变量保持在中间值。对于交互图,它将更改两个变量。(请注意,2016年11月添加:plotmo现在还支持部分依赖图。)

上面的示例仅使用两个变量。通过一次查看一个或两个变量,可以零碎地显示更复杂的模型。由于“其他”变量保持在中间值,因此仅显示了一部分数据,但仍然有用。在plotmo包小插图中有一些示例。其他示例在使用rpart.plot软件包绘制rpart树的第10章中。


4

我自己对这些类型的问题非常感兴趣。我确实认为我们可以从随机森林中获得很多信息。

关于交互,似乎Breiman和Cultier已经尝试着研究它,特别是对于分类RF。

据我所知,还没有在randomForest R包中实现。可能是因为它可能不那么简单,并且因为“可变交互”的含义非常取决于您的问题。

关于非线性,我不确定您要寻找的是什么,回归林可用于非线性多重回归问题,而无需先验使用哪种类型的非线性函数。


3

在游戏的后期,但是在这方面有一些新的发展,例如LIMESHAP。还有一个值得检查的软件包是DALEX(特别是如果使用R但无论如何都包含漂亮的备忘单等),尽管目前似乎并不涵盖交互。这些都是与模型无关的,因此适用于随机森林,GBM,神经网络等。


(+1)不错的资源!
mkt

2

最近开发的因果森林方法是对随机森林的略微修改,可提供有关数据的更多信息。请参阅此处GRF R包装和激励性文件。这个想法是使用随机森林基线方法来发现因果关系的异质性。

较早的文章(在此)提供了一种简单的因果森林的详细方法。本文的第9页给出了逐步种植因果树的步骤,然后可以按照通常的方法将其扩展为森林。摘自Athey and Wager 2017的第9页

公式4:

等式4

式5: 式5


1
更新了指向较早论文的链接和该论文的屏幕截图,以显示因果树过程。
gannawag's

1

有关我的问题的答案晚期这里我们可以做随机森林100%可解释通过固定种子?):

ž1个ž2

  1. ž1个d1个ž1个d2ž1个d3ž1个dž1个
  2. Ť1个ž1个ž2Ť2ž1个ž2Ť3ž1个ž2Ťž1个ž2
  3. ĴĴ=1个2X一世F^ĴX一世一世ñĴ
    F^X一世=>1个Ĵ=1个F^ĴX一世
  4. F^X一世ž1个ž2X一世
  5. X一世
    X1个F^X1个 -固定的>感谢 ž1个ž2
    X2F^X2 ->这是固定的,由于 ž1个ž2
    X3→>F^X3 -由于 ž1个ž2
    X4>→F^X4 -由于 ž1个>ž2
  6. X1个X2

这也适用于所有基于树聚合的集成方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.