---
title: "随机森林"
output: 
  html_document:
    self_contained: FALSE
    number_sections: TRUE
    toc: TRUE
    toc_float: TRUE
    code_folding: show
    fig_width: 7
    fig_height: 4.5
    theme: united
    highlight: tango
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
```

# 随机森林简介
<font size=4 face="微软雅黑">

***
&emsp;&emsp;随机森林是一种比较新的机器学习模型，其与上面所讲的k近邻、朴素贝叶斯，决策树等这些算法最大的不同之处就是它是一种基于决策树的集成学习方法，其中每一颗树都依赖于一个随机向量，森林中所有的向量都是独立分布的。

&emsp;&emsp;随机森林的实质就是对数据集的列变量和行观测进行随机化，生成多个分类数，最终将分类树结果进行汇总，把汇总后的结果作为最终的预测结果。


# 随机森林原理及优缺点

***
&emsp;&emsp;我们在决策树这一节已经介绍了决策树的原理，随机森林就是由多个**未剪枝**的CART分类决策树组成，其不仅可以做回归，还可以做分类。其做回归时返回的结果是单棵树输出结果的简单平均，做分类时的返回结果是单棵树输出结果的众数。

## 构建流程
&emsp;&emsp;随机森林的构建流程并不复杂，其主要流程如下：

* 应用Bootstrap重采样方法有放回地随机抽取n个新的自助样本集
* 随机从所有变量中抽取m个变量作为子数据集的变量
* 对k个含有m个变量的子数据集分别构建CART树，其他子数据集作为测试集
* 把需要预测的数据代入生成的多个CART树，根据每棵树的投票结果取票数最高的一个类别

## 性能
* 每棵CART树生长越茂盛，组成的随机森林的性能越好
* 每棵树之间独立性越高，租成的随机森林的性能越好


## 优缺点

***
| 优点 | 缺点 |
|:-----|:-----|
|可以适用于大多数问题，可处理噪声和缺失值；分类和连续性特征数据|训练和预测比较慢|
|两个随机性的引入，使模型不容易过拟合|在分类问题中，若目标数据集偏小并变量类别过多，则表现较差|
|处理高维度的数据时，可以不用做很详细的征选择|
|在创建随机森林的时候，对generlization error使用的是无偏估计|
|可以返回变量的重要性|


# 案例实战

&emsp;&emsp;本案例的主要内容就是使用随机森林对R中自带的iris数据集进行分类。

&emsp;&emsp;在R中常用randomforst程序包的实现随机森林算法，该包主要涉及到3个重要的函数，每个参数的详细信息详见下方：

**1)randomForest()函数用于构建随机森林模型**

randomForest(formula, data=NULL, ..., subset, na.action=na.fail)

randomForest(x, y=NULL,  xtest=NULL, ytest=NULL, ntree=500,<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;mtry=if (!is.null(y) && !is.factor(y))<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;max(floor(ncol(x)/3), 1) else floor(sqrt(ncol(x))),<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;replace=TRUE, classwt=NULL, cutoff, strata,<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;sampsize = if (replace) nrow(x) else ceiling(.632*nrow(x)),<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;nodesize = if (!is.null(y) && !is.factor(y)) 5 else 1,<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;maxnodes = NULL,<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;importance=FALSE, localImp=FALSE, nPerm=1,<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;proximity, oob.prox=proximity,<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;norm.votes=TRUE, do.trace=FALSE,<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;keep.forest=!is.null(y) && is.null(xtest), corr.bias=FALSE,<br>
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;keep.inbag=FALSE, ...)

* **formula：**指定模型的公式形式，类似于y~x1+x2+x3...;
* **data：**指定分析的数据集；
* **subset：**以向量的形式确定样本数据集；
* **na.action：**指定数据集中缺失值的处理方法，默认为na.fail，即不允许出现缺失值，也可以指定为na.omit，即删除缺失样本；
* **x：**指定模型的解释变量，可以是矩阵，也可以是数据框；
* **y：**指定模型的因变量，可以是离散的因子，也可以是连续的数值，分别对应于随机森林的分类模型和预测模型。这里需要说明的是，如果不指定y值，则随机森林将是一个无监督的模型；
 **xtest**和**ytest**用于预测的测试集；
* **ntree：**指定随机森林所包含的决策树数目，默认为500；
* **mtry：**指定节点中用于二叉树的变量个数，默认情况下数据集变量个数的二次方根(分类模型)或三分之一(预测模型)。一般是需要进行人为的逐次挑选，确定最佳的m值；
* **replace：**指定Bootstrap随机抽样的方式，默认为有放回的抽样；
* **classwt：**指定分类水平的权重，对于回归模型，该参数无效；
* **strata：**为因子向量，用于分层抽样；
* **sampsize：**用于指定样本容量，一般与参数strata联合使用，指定分层抽样中层的样本量；
* **nodesize：**指定决策树节点的最小个数，默认情况下，判别模型为1，回归模型为5；
* **maxnodes：**指定决策树节点的最大个数；
* **importance：**逻辑参数，是否计算各个变量在模型中的重要性，默认不计算，该参数主要结合importance()函数使用；
* **proximity：**逻辑参数，是否计算模型的临近矩阵，主要结合MDSplot()函数使用；
* **oob.prox：**是否基于OOB数据计算临近矩阵；
* **norm.votes：**显示投票格式，默认以百分比的形式展示投票结果，也可以采用绝对数的形式；
* **do.trace：**是否输出更详细的随机森林模型运行过程，默认不输出；
* **keep.forest：**是否保留模型的输出对象，对于给定xtest值后，默认将不保留算法的运算结果。

**2)importance()函数用于计算模型变量的重要性**

importance(x, type=NULL, class=NULL, scale=TRUE, ...)

* **x：**为randomForest对象；
* **type”**可以是1，也可以是2，用于判别计算变量重要性的方法，1表示使用精度平均较少值作为度量标准；2表示采用节点不纯度的平均减少值最为度量标准。值越大说明变量的重要性越强；
* **scale：**默认对变量的重要性值进行标准化。


**3)MDSplot()绘制建立模型过程中产生的临近矩阵经过标准化后的坐标图，就是可以将高位图缩放到任意小的维度下来观看模型各个类别在不同维度下的分布情况**

MDSplot(rf, fac, k=2, palette=NULL, pch=20, ...)

* **rf：**为randomForest对象，需要说明的是，在构建随机森林模型时必须指定计算临近矩阵，即设置proximity参数为TRUE；
* **fac：**指定随机森林模型中所使用到的因子向量(因变量)；
* **palette：**指定所绘图形中各个类别的颜色；
* **pch：**指定所绘图形中各个类别形状；


**4)rfImpute()函数可为存在缺失值的数据集进行插补(随机森林法)，得到最优的样本拟合值**

rfImpute(x, y, iter=5, ntree=300, ...)

rfImpute(x, data, ..., subset)

* **x：**为存在缺失值的数据集；
* **y：**为因变量，不可以存在缺失情况；
* **iter：**指定插值过程中迭代次数；
* **ntree：**指定每次迭代生成的随机森林中决策树数量；
* **subset：**以向量的形式指定样本集。


**5)treesize()函数用于计算随机森林中每棵树的节点个数**

treesize(x, terminal=TRUE)

* **x：**为randomForest对象；
* **terminal：**指定计算节点数目的方式，默认只计算每棵树的根节点，设置为FALSE时将计算所有节点(根节点+叶节点)。

***
## 获取并探索数据

```{r}

mydata <- iris
str(mydata)
summary(mydata)

```

&emsp;&emsp;从上述简单的数据探索中发现，数据一共五列。其中四列是数值型，一列是因子型，并且因子型变量的因子水平一致，每个水平的观测均是50个。

## 分层抽样
&emsp;&emsp;分层抽样，又称分组抽样。是一种比SRS更精准的随机抽样法，所用的方法是跟据我们的研究性质，依照相关的条件把母群体中的个体分成不同的层别或组别(strata)，再分别从每一层别或组别中的个体随机抽出一定的个体来组成样本。

&emsp;&emsp;在caret程序包中提供了一种非常方便的分层抽样函数**createDataPartition()**，下面是该函数的参数解释：

createDataPartition(y, times = 1, p = 0.5, list = TRUE, groups = min(5, length(y)))

* **y：**指定数据集中的输出变量
* **times：**指定创建的样本个数，默认简单随机抽取一组样本
* **p：**指定数据集中用于训练集的比例
* **list：**是否已列表或矩阵的形式存储随机抽取的索引号，默认为TRUE
* **groups：**如果输出变量为数值型数据，则默认按分位数分组进行取样

```{r}

# install.packages("caret")
library(caret)

set.seed(45)
idx <- createDataPartition(y = mydata$Species, p = 0.7, list = F)

trainset <- mydata[idx, ]
testset <- mydata[-idx, ]

prop.table(table(mydata$Species)) # 计算原数据集中Species的占比
prop.table(table(trainset$Species)) # 计算训练集中Species的占比
prop.table(table(testset$Species)) # 计算测试集中Species的占比

```

&emsp;&emsp;通过分层抽样，我们把数据拆分为了训练集和测试集，并计算其对应的因变量的比例，发现每个数据集中各个类别的比例均是0.33，说明达到了分层抽样的目的。

## 训练模型

&emsp;&emsp;我们使用上面提到的**randomForest()**函数对训练集训练模型，设置随机变量的个数为3，树的个数为30，并且计算其变量重要性。
```{r}

# install.packages("randomForest")
library(randomForest)

set.seed(45)
rf <- randomForest(Species ~., data = trainset, mtry = 3, ntree = 30, importance = T)

```

## 展示变量重要性
```{r}

imp <- importance(x = rf) # 计算变量重要性
imp

varImpPlot(rf) # 展示变量重要性

```

&emsp;&emsp;上面使用MeanDecreaseAccuracy和MeanDecreaseGini两种指标衡量了各个变量的重要性，发现最重要的是Patal.Width，其次是Patal.Length这两个变量，从一定程度上也说明了鸢尾花的分类与花瓣有比较大的关系。


## 预测且评估
```{r}

pred <- predict(rf, testset[-5]) # 对测试集进行预测
table(pred, testset$Species)

```

&emsp;&emsp;从混淆矩阵中可以看出，仅有一个样本被预测错误(一个virginica的鸢尾花被错误的分类为了versicolor)，改模型的分类效果还是非常好的。


# 参考文献

***
[1] 卡巴科弗. R语言实战[M]. 人民邮电出版社, 2013.<br>
[2] 机器学习——随机森林算法及原理. https://blog.csdn.net/nieson2012/article/details/51279332v. 2016.<br>
[3] 教你使用caret包(一)--数据预处理. https://mp.weixin.qq.com/s/EXq9B-imnX6D27okYvPGkw. 2016<br>
[4] 基于R语言的随机森林算法运用. https://mp.weixin.qq.com/s?__biz=MzIxNjA2ODUzNg==&mid=400806663&idx=1&sn=51b4b7a44a33606bd9262e208f91c2df&mpshare=1&scene=23&srcid=0621JANvo04Zy7uaMQt5kxbg#rd. 2015<br>
[6] 机器学习十大算法---8. 随机森林算法. https://blog.csdn.net/julialove102123/article/details/78405284. 2017

</font>

