随机森林算法-Deep Dive

时间:2022-04-25 15:15:23

0-写在前面

随机森林,指的是利用多棵树对样本进行训练并预测的一种分类器。该分类器最早由Leo Breiman和Adele Cutler提出。简单来说,是一种bagging的思想,采用bootstrap,生成多棵树,CART(Classification And Regression Tree)构成的。对于每棵树,它们使用的训练集是从总的训练集中有放回采样出来的,这意味着,总的训练集中的有些样本可能多次出现在一棵树的训练集中,也可能从未出现在一棵树的训练集中。对于一个有n行的数据集,out of bag 的概率大概是1/e=1/3。n趋向无穷大的时候,(1-1/n)^n~1/e。

选择的是The Elements of Statistical Learning 这本教材进行学习。

原因有两个1.翻了这本书在豆瓣的评论,看到的是评论者对这本书满满的敬仰! 2.女神的博客上有这本书的上课笔记(有中文笔记看,才是重点)

1-随机森林算法特色

因为之前有用过randomForest这个包,所以就先从自己最熟悉的算法开始。

先说说随机森林有哪些特色,我就简单翻译下[1]上面写的

  1. 在与其它现有的算法相比,其预测准确率很好
  2. 在较大的数据集上计算速度依然很快
  3. 不需要降维,算法本身是采取随机降维的
  4. 他能处理有缺失值的数据集。算法内部有补缺失值的函数
  5. 能给出变量的重要性
  6. 能处理imbalanced data set
  7. 能给出观测实例间的相似度矩阵,其实就是proximity啦,继而能做clustering 和 location outlier
  8. 能对unlabeled data 进行无监督的学习,进行clustering
  9. 生成的森林可以保留,应用在新的数据集上

个人感觉最重要的是1-6。7-9我并没找到很有说服力的例子能证明随机森林在这几方面也很强。

2-根据其特色说一说随机森林算法

先说4,随机森林是如何补全缺失值的。主要参考资料[1]、[2]

前提:训练数据集中label(被解释变量)一定要有,不然这条观测实例你为何拿他来学习模型?

randomForest包里,有两种补全缺失值的方法。

方法一(na.roughfix)简单粗暴,对于训练集,同一个class下的数据,如果是分类变量缺失,用众数补上,如果是连续型变量缺失,用中位数补。

方法二(rfImpute)这个方法计算量大,至于比方法一好坏?不好判断。他只能补训练集中的缺失值。是先用na.roughfix补上缺失值,然后构建森林并计算proximity matrix,再回头看缺失值,如果是分类变量,则用没有缺失的观测实例的proximity中的权重进行投票。如果是连续型变量,则用proximity矩阵进行加权平均的方法补缺失值。然后迭代4-5次。这个补缺失值的思想和KNN有些类似。

################ imputation ################
library(kernlab)
library(magrittr)
data(spam)
spam.na <- spam
set.seed(111)
## artificially drop some data values.
for (i in 1:57) spam.na[sample(length(spam[,1]), length(spam[,1])/10), i] <- NA
## calculate missing value ratio for coloumns
missingvalue.ratio <- function(df){
df <- as.data.frame(df)
res <- is.na(df)%>%colSums()/length(df[,1])
return(res)
}
missingvalue.ratio(spam.na)
# na.roughfix impute
spam.roughfix <- na.roughfix(spam.na)
randomForest(type ~ ., data=spam.roughfix)
# in another way
randomForest(type ~ ., data=spam.na, na.action = na.roughfix)
#rfImpute
spam.rfImpute <- rfImpute(type~.,data=spam.na,iter=2,ntree=300)
randomForest(type ~ ., data=spam.rfImpute)

对于7,proximity matrix

这个据说是randomForest中的亮点? [1]中说proximity are one of the most useful tools in random forests,[3]中写到The proximity matrix can be used to identify structure in the data (see Breiman, 2002) or for unsupervised learning with random forests 。

前面的rfImpute就是基于proximity matrix的,proximity matrix其实就是任意两个观测实例间的相似度矩阵。原理是如果两个观测实例落在同一棵树的同一个叶子节点的次数越多,则这两个观测实例的相似度越高。这个矩阵进行了归一化,就是除以ntree,你种了多少颗树。很简单的思想。

library(kernlab)
library(randomForest)
data(spam)
set.seed(1)
rf <- randomForest(type~.
,data=spam
,importance=TRUE
,proximity=FALSE
,ntree=300
,do.trace=20
,na.action=na.omit
,mtry=7
,keep.forest=FALSE)

以上代码中设置proximity=TRUE就可以得到这个矩阵了。对很大的数据集,请慎用,对于一个有n个观测值的数据集,他会生成一个n*n的矩阵,spam是4601行,生成4601*4601的矩阵,就是160m在内存中。

对于5.能给出变量的重要性

这里借鉴[3]中的一段代码,自己改了一下。查看变量重要性,无非就是进行feature selection,降低模型的复杂度的同时获取较好的预测准确率。

par(mfrow = c(2,2))
for(i in 1:4){
plot(sort(rf$importance[,i],decreasing =TRUE)%>%head(20)
,type='h'
,main=paste0(colnames(rf$importance)[i])
,xlab='variable'
,ylab='importance') text(sort(rf$importance[,i],decreasing =TRUE)
,labels=names(sort(rf$importance[,i]))%>%head(20)
,pos=1
,cex=0.9)
}

以上代码能画出变量重要性的图,如下。(spam数据集有57个解释变量,为了能看清图,变量重要性排名只取前20)

随机森林算法-Deep Dive

衡量变量重要性的方法有两种,Decrease GINI 和Decrease Accuracy

Decrease GINI: 引用一篇博客中很好的解释。对于分类问题(将某个样本划分到某一类),也就是离散变量问题,CART使用Gini值作为评判标准。定义为Gini=1-∑(P(i)*P(i)),P(i)为当前节点上数据集中第i类样本的比例。例如:分为2类,当前节点上有100个样本,属于第一类的样本有70个,属于第二类的样本有30个,则Gini=1-0.7×07-0.3×03=0.42,可以看出,类别分布越平均,Gini值越大,类分布越不均匀,Gini值越小。在寻找最佳的分类特征和阈值时,评判标准为:argmax(Gini-GiniLeft-GiniRight),即寻找最佳的特征f和阈值th,使得当前节点的Gini值减去左子节点的Gini和右子节点的Gini值最大。

对于回归问题,相对更加简单,直接使用argmax(Var-VarLeft-VarRight)作为评判标准,即当前节点训练集的方差Var减去减去左子节点的方差VarLeft和右子节点的方差VarRight值最大。

Decrease Accuracy:抄一下女神的文字

随机森林算法-Deep Dive

然后随机改变OOB样本的第j列:保持其他列不变,对第j列进行随机的上下置换,得到误差2。至此,我们可以用误差1-误差2来刻画变量j的重要性。当然这里loss function可以自己定。这里的大致思想就是,如果一个变量j足够重要,那么改变它会极大的增加测试误差;反之,如果改变它测试误差没有增大,则说明该变量不是那么的重要。(典型的实用主义啊!管用才是真,才不管他什么证明不证明呢!自从开始接触机器学习的这些算法,我真的是被他们的各种天真烂漫的想法打败的一塌糊涂,只要直觉上过得去、实际效果看起来比较好就可以了呢,规则真简单)

对于mtry这个值的设定:

default是sqrt(M),作者建议也试一下sqrt(M)/2,2sqrt(M), 甚至是对于有些数据集,取mtry=1效果也很好。

对于 6.能处理imbalanced data set,Balancing prediction error。

这个是非常重要的一个特点。往往我们的数据集,每个class下的观测实例个数,总是不那么均等,比如[4]中提到,在反欺诈识别,疾病识别这种数据集中,数据就非常不平衡。

[4]中提到两种方法处理这种情况,一种是balanced RF,另一种是weighted RF,对于前者采是用分层重抽样的方法,即对minority class采取up-sampling,对于majority class 进行down-smapling。下面举例说明(在Cross Validated 这上面找到的一个很好的例子)R package for Weighted Random Forest? classwt option?

make.data = function(obs=5000,vars=6,noise.factor = .2,smallGroupFraction=.01) {
X = data.frame(replicate(vars,rnorm(obs)))
yValue = with(X,sin(X1*pi)+sin(X2*pi*2)+rnorm(obs)*noise.factor)
yQuantile = quantile(yValue,c(smallGroupFraction,.5))
yClass = apply(sapply(yQuantile,function(x) x<yValue),1,sum)
yClass = factor(yClass)
print(table(yClass)) #five classes, first class has 1% prevalence only
Data=data.frame(X=X,y=yClass)
}
Data = make.data()
# yClass
# 0 1 2
# 50 2450 2500
rf1 = randomForest(y~.,Data,ntree=500, sampsize=5000)
# 下面是rf1的结果,可以看出对于yClass==0的预测错误率100%,但是总体的预测错误率在
# 13.7%左右,如果是做fraud detection,这绝对是不可行的。
OOB estimate of error rate: 13.7%
Confusion matrix:
0 1 2 class.error
0 0 50 0 1.0000000
1 0 2109 341 0.1391837
2 0 294 2206 0.1176000
###########下面用分层抽样改进###########
rf2 = randomForest(y~.,Data,ntree=4000,sampsize=c(50,500,500),strata=Data$y)
rf3 = randomForest(y~.,Data,ntree=4000,sampsize=c(50,100,100),strata=Data$y)
rf4 = randomForest(y~.,Data,ntree=4000,sampsize=c(50,50,50) ,strata=Data$y)
###########结果看下来,rf3,rf4效果最好###########
#作者也提到,让每个class的错误率都均等一些,可能会使总体的预测错误率上升,这需要权衡

randomForest能进行无监督的学习:

这里稍后再写

参考资料

[1]Random Forest Classification/Clustering

[2]randomForest manual

[3]Classification and Regression by randomForest

[4]Using Random Forest to Learn Imbalanced Data

#后续阅读

[1]How to interpret Mean Decrease in Accuracy and Mean Decrease GINI in Random Forest models

[2]http://www.loyhome.com/%E2%89%AA%E7%BB%9F%E8%AE%A1%E5%AD%A6%E4%B9%A0%E7%B2%BE%E8%A6%81the-elements-of-statistical-learning%E2%89%AB%E8%AF%BE%E5%A0%82%E7%AC%94%E8%AE%B0%EF%BC%88%E5%8D%81%E5%85%AD%EF%BC%89/