集成学习和随机森林的关系
集成学习是通过几个模型的组合来解决单一预测问题,它的工作原理是生成多个预测模型,每一个模型都独立的学习并给出预测,最后将这些预测结合成单个预测,因此最后的预测优于任何单分类模型给出的预测。
随机森林是包含多个决策树的分类器,这些决策树之间是没有关联的,最终的分类结果是各个决策树分类结果的投票。
所以随机森林是集成学习的一个子类。为此,先介绍一些常见的集成学习的内容,参考的博文见集成学习原理小结、集成学习之Adaboost算法原理小结和bagging与随机森林算法小结。
关于Bagging思想的说明
随机森林
随机森林是Bagging算法的进化版,它的思想仍然是Bagging思想,但是在某些方面进行了一定的改进。这里,我们想说明几点:
1、为什么要采用随机有放回的采样?
如果不进行随机抽样,每棵树的训练集都一样,那么最终训练出的树分类结果也是完全一样的,这样的话完全没有bagging的必要;
图片上的随机采样通常指的是boostrap采样,即有放回的采样。
2、随机森林中的每棵树的生成过程是怎样的?
Step1:设训练集大小为m,随机森林中决策树的数量为T,对于每一棵决策树, 随 机 \textcolor{red}{随机} 随机有放回地抽取m个训练样本(boostrap采样),作为该书树的训练集;
所以每棵树的训练集都是随机的且是不同的,并且每一个训练集中都包含重复的训练样本。正如Bagging与随机森林算法小结中介绍的一样,在有放回的随机采样中,每个数据点被抽中的概率是
1
m
\frac{1}{m}
m1,不被抽中的概率是
1
−
1
m
1-\frac{1}{m}
1−m1,m次采样都没被抽中的概率是
(
1
−
1
m
)
m
(1-\frac{1}{m})^m
(1−m1)m,当
m
→
∞
m\rightarrow \infty
m→∞时,
(
1
−
1
m
)
m
→
1
e
=
0.368
(1-\frac{1}{m})^m\rightarrow \frac{1}{e}=0.368
(1−m1)m→e1=0.368.
也就是说,在bagging的每轮随机采样中,训练集中大约有36.8%的数据没有被采样集采集中。对于这部分大约36.8%的没有被采样到的数据,我们常常称之为袋外数据(Out Of Bag, 简称OOB)。这些数据没有参与训练集模型的拟合,因此可以用来检测模型的泛化能力。
Step2: 如果每个样本的特征维度为D,指定一个常数d<<D, 随 机 \textcolor{red}{随机} 随机地从D个特征中选取d个特征子集,每次树进行分裂时,从这d个特征中选择最优的;
Step3: 采用常见的决策树的算法,比如ID3、C4.5和CART算法去训练每一个决策树。
3、 随机森林的"随机"性体现在哪里?
随机森林的随机性便是上面标红的地方。两个随机性的引入对随机森林的分类性能至关重要。由于它们的引入,使得随机森林不容易陷入过拟合,并且具有很好得抗噪能力(比如:对缺省值不敏感)。
4、随机森林是Bagging算法的进阶体现在哪里?
- RF通常使用CART决策树作为弱学习器
- 在使用决策树的基础上,RF对决策树的建立做了改进。对于普通的决策树,我们会在节点上所有的D个样本特征中选择一个最优的特征来做决策树的左右子树划分,但是RF通过 随 机 \textcolor{red}{随机} 随机选择节点上的一部分样本特征,这个数字小于D,假设为d, 然后在这些随机选择的d个样本特征中,选择一个最优的特征来做决策树的左右子树划分。这样进一步增强了模型的泛化能力。
5、随机森林分类效果的影响因素有哪些?
随机森林分类效果(错误率)的两个影响因素:
- 森林中任意两棵树的相关性:相关性越大,错误率越大;
- 森林中每棵树的分类能力:每棵树的分类能力越强,整个森林的错误率越低。
减少部分特征选择个数d, 各个决策树之间的相关性会降低,但是每个树的分类能力也相应的降低;如果增大d,两者也会随之增大。因此,要选择一个合适的d对这两种影响因素做出权衡,如何选择最优的d是随机森林要解决的重要的问题,d也是随机森林中唯一的参数。
6、如何找到最优的部分特征数d?
随机森林关键的问题是如何找到最优的d,要解决这个问题可以利用交叉验证的思想。考虑到随机森林算法中,每一棵决策树的训练集总是有可能包含不了0.368的样本数据,在做交叉验证的时候可以直接利用这里没被抽中的样本集进行验证。
7、随机森林在R中的实现
我们将用UCI数据库中的威斯康星州的乳腺癌数据进行上述的方法的R实现,这个数据集可以在UCI数据库中下载,也可以在机器学习数据库中找到,采用下面的R code 可以直接读取数据进到R中。
loc<-"http://archive.ics.uci.edu/ml/machine-learning-databases/"
ds<-"breast-cancer-wisconsin/breast-cancer-wisconsin.data"
url<-paste(loc,ds,sep="")
data<-read.table(url,sep=",",header=F,na.strings="?")
数据集共有689条数据,有16条是存在缺失数据的数据,数据集中的各列分别代表"编号",“肿块厚度”,“肿块大小”,“肿块形状”,“边缘黏附”,“单个表皮细胞大小”,“细胞核大小”,“染色质”,“细胞核常规”,“有丝分裂”,“类别”,其中类别为2代表是良性,类别为4代表是恶性。
### 数据预处理
names(data)<-c("编号","肿块厚度","肿块大小","肿块形状","边缘黏附","单个表皮细胞大小","细胞核大小","染色质","细胞核常规","有丝分裂","类别")
data$类别[data$类别==2]<-"良性"
data$类别[data$类别==4]<-"恶性"
list <- which(rowSums(is.na(data))>0)
miss_data <- data[list,] #只有细胞核大小这一列变量存在缺失
7.1、决策树算法的R实现
在决策树算法中,是没有考虑确实问题的,因为最后选取的分裂决策树的特征可能总不会是缺失变量。
决策树常用的两个R包是rpart和party。关于这两个包的区别,见rpart包与party包的区别
data<-data[-1] #删除第一列元素#
set.seed(1234) #随机抽样设置种子
train<-sample(nrow(data),0.7*nrow(data)) #抽样函数,第一个参数为向量,nrow()返回行数 后面的是抽样参数前
tdata<-data[train,] #训练集
vdata<-data[-train,] #测试集
#### 构建决策树
dtree<-rpart(类别~.,data=tdata,method="class", parms=list(split="information"))#CART算法,split = “gini”
printcp(dtree) # 查看当前的决策树的复杂性参数
print(dtree)
tree<-prune(dtree,cp=dtree$cptable[which.min(dtree$cptable[,"xerror"]),"CP"]) #自动选择xerror最小时候对应的cp值来剪枝
### 画决策树 格式:rpart.plot(tree,type,fallen.leaves=T,branch,…)
opar<-par(no.readonly = T)
par(mfrow=c(1,2))
library(rpart.plot)
rpart.plot(dtree,branch=1,type=2, fallen.leaves=T,cex=0.8, sub="剪枝前")
rpart.plot(tree,branch=1, type=4,fallen.leaves=T,cex=0.8, sub="剪枝后")
par(opar)
#dev.off()
### 测试集检验
predtree<-predict(tree,newdata=vdata,type="class") #利用预测集进行预测
print(predtree)
table(vdata$类别,predtree,dnn=c("真实值","预测值")) #输出混淆矩阵
7.2 随机森林算法
##############################################################
############################## 随机森林 ###############
install.packages("randomForest")
library(randomForest)
#################################################################
### 在随机森林算法中需要确定两个重要的参数:mtry和ntree
### mtry 是每个决策树选择的特征数量
### ntree 是随机森林中决策树的数量
### 一般对mtry的选择是逐一尝试,直到找到比较理想的值,
### ntree的选择可通过图形大致判断模型内误差稳定时的值。
############################################################
loc<-"http://archive.ics.uci.edu/ml/machine-learning-databases/"
ds<-"breast-cancer-wisconsin/breast-cancer-wisconsin.data"
url<-paste(loc,ds,sep="")
data<-read.table(url,sep=",",header=F,na.strings="?")
names(data)<-c("编号","肿块厚度","肿块大小","肿块形状","边缘黏附","单个表皮细胞大小","细胞核大小","染色质","细胞核常规","有丝分裂","类别")
data <- data[-1,]
set.seed(1234) #随机抽样设置种子
train<-sample(nrow(data),0.7*nrow(data)) #抽样函数,第一个参数为向量,nrow()返回行数 后面的是抽样参数前
rf_tdata<-data[train,] #训练集
rf_vdata<-data[-train,] #测试集
#### 寻找最优参数mtry na.roughfix是对缺失数据进行填补
d <- length(names(tdata))
rate=1 #设置模型误判率向量初始值
for(i in 1:(d-1)){
set.seed(123)
mtry_fit <- randomForest(as.factor(rf_tdata$类别)~.,data = rf_tdata,mtry=i,
na.action = na.roughfix,
importance = TRUE)
rate[i]<-mean(mtry_fit$err.rate) #计算基于OOB数据的模型误判率均值
print(mtry_fit)
}
rate #展示所有模型误判率的均值
plot(rate) # mtry 取为2最好
### 寻找最佳参数ntree
set.seed(100)
rf_train<-randomForest(as.factor(as.factor(rf_tdata$类别))~.,rf_tdata,mtry=2,ntree=1000,
na.action = na.roughfix,
importance = TRUE)
plot(rf_train) #绘制模型误差与决策树数量关系图 ntree 取200-400都比较合适
########### 进行随机森林建模
set.seed(101)
rf_train<-randomForest(as.factor(as.factor(rf_tdata$类别))~.,rf_tdata,mtry=2,ntree=300,
na.action = na.roughfix,
importance = TRUE,proximity=TRUE)
### 输出变量重要性测评
importance<- importance(rf_train,type=1)
#计算各个变量在模型中的重要性,1表示使用精度平均较少值作为度量标准
#2表示采用节点不纯度的平均减少值最为度量标准。值越大说明变量的重要性越强;
write.csv(importance,file="/importance.csv",row.names=T,quote=F)
barplot(rf_train$importance[,1],main="输入变量重要性测度指标柱形图")
box()
importance(rf_train,type=1)
varImpPlot(x=rf_train,sort=TRUE,n.var=nrow(rf_train$importance),
main="输入变量重要性测度散点图")
print(rf_train) #展示随机森林模型简要信息
###可视化
hist(treesize(rf_train)) #展示随机森林模型中每棵决策树的节点数
max(treesize(rf_train));min(treesize(rf_train))
MDSplot(rf_train,rf_tdata$类别,palette=rep(1,2),
pch=as.numeric(rf_tdata$类别)) #展示数据集在二维情况下各类别的具体分布情况
### 预测
pred<-predict(rf_train,newdata=rf_vdata)
pred_out_1<-predict(object=rf_train,newdata=rf_vdata,type="prob") #输出测试点是两类的概率
table <- table(pred,rf_vdata$类别)
sum(diag(table))/sum(table) #预测准确率
plot(margin(rf_train,test_data$IS_LIUSHI),main=观测值被判断正确的概率图)
pred <-predict(rf_train,newdata=rf_vdata,type="class") #利用预测集进行预测
print(pred)
table2 <- table(rf_vdata$类别,pred,dnn=c("真实值","预测值")) #输出混淆矩阵
rate2 <- sum(diag(table2))/sum(table2)