随机森林被认为是黑匣子,但是最近我在想可以从随机森林中获得什么知识?
最明显的是变量的重要性,在最简单的变体中,只需计算变量的出现次数即可完成。
我正在考虑的第二件事是交互。我认为,如果树的数量足够大,则可以测试变量对的出现次数(类似于卡方独立性)。第三件事是变量的非线性。我的第一个想法只是看可变Vs得分的图表,但我不确定这是否有意义。
添加23.01.2012
动机
我想利用这些知识来改进logit模型。我认为(或至少希望如此)可以找到被忽略的相互作用和非线性。
随机森林被认为是黑匣子,但是最近我在想可以从随机森林中获得什么知识?
最明显的是变量的重要性,在最简单的变体中,只需计算变量的出现次数即可完成。
我正在考虑的第二件事是交互。我认为,如果树的数量足够大,则可以测试变量对的出现次数(类似于卡方独立性)。第三件事是变量的非线性。我的第一个想法只是看可变Vs得分的图表,但我不确定这是否有意义。
添加23.01.2012
动机
我想利用这些知识来改进logit模型。我认为(或至少希望如此)可以找到被忽略的相互作用和非线性。
Answers:
随机森林几乎不是黑匣子。它们基于决策树,决策树很容易解释:
#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)])
您可以查看每个功能的帮助文件,以更好地了解它们的显示内容。
不久前,我不得不为公司中的一些化学家证明RF模型适合。我花了很多时间尝试不同的可视化技术。在此过程中,我不小心还提出了一些新技术,这些技术专门用于随机森林可视化,并放入R包(forestFloor)中。
经典方法是受以下部分支持的部分依赖图:Rminer(基于数据的敏感性分析被重新发明为部分依赖)或randomForest包中的partialPlot。我发现部分依赖包iceBOX是发现交互的一种优雅方式。尚未使用edarf软件包,但似乎有一些专门用于RF的精细可视化。该ggRandomForest包中还含有大量组有用的可视化。
当前,forestFloor支持randomForest对象(正在支持其他RF实现)。还可以为梯度增强树计算特征贡献,因为训练后的这些树与随机森林树没有太大区别。因此forestFloor将来可以支持XGBoost。偏相关图完全是模型不变的。
所有程序包的共同点在于可视化模型从特征空间到目标空间的几何映射结构。正弦曲线y = sin(x)将是从x到y的映射,并且可以2D形式绘制。直接绘制RF映射通常会需要太多尺寸。相反,可以映射,切片或分解整个映射结构,以便将整个映射结构简化为2D边际图序列。如果您的RF模型仅捕获了主要效果,而变量之间没有相互作用,那么经典的可视化方法就可以了。然后您可以简化模型结构,例如。然后,每个变量的每个部分函数都可以像正弦曲线一样可视化。如果您的RF模型已经捕获了相当大的交互作用,那么问题就更大了。结构的3D切片可以可视化两个要素和输出之间的交互。问题是要知道要可视化的功能组合(iceBOX确实解决了此问题)。同样,很难说出是否还没有考虑其他潜在的交互作用。
在本文中,我使用了非常早期的forestFloor版本来解释一个非常小的RF模型已捕获的实际生化关系。并且在本文中,我们彻底描述了特征贡献的可视化,随机森林的林底可视化。
我已经从forestFloor包中粘贴了模拟示例,在该示例中,我展示了如何发现模拟的隐藏函数 noise
#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])
为了补充这些出色的响应,我会提到使用梯度增强树(例如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的商业版本,该视频演示了他们对可变交互估计视频的看法。
答案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))
产生以下图:
如果您正在寻找交互,它还提供了三维可视化。
正如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章中。
我自己对这些类型的问题非常感兴趣。我确实认为我们可以从随机森林中获得很多信息。
关于交互,似乎Breiman和Cultier已经尝试着研究它,特别是对于分类RF。
据我所知,还没有在randomForest R包中实现。可能是因为它可能不那么简单,并且因为“可变交互”的含义非常取决于您的问题。
关于非线性,我不确定您要寻找的是什么,回归林可用于非线性多重回归问题,而无需先验使用哪种类型的非线性函数。