装袋(Bagging)
全称Bootstrap Aggregation的集成算法。每个基学习器都会对训练集进行有放回抽样得到子训练集,比较著名的采样法为 0.632 自助法。每个基学习器基于不同子训练集进行训练,并综合所有基学习器的预测值得到最终的预测结果。Bagging 常用的综合方法是投票法,票数最多的类别为预测类别。
Boosting
Boosting 训练过程为阶梯状,基模型的训练是有顺序的,每个基模型都会在前一个基模型学习的基础上进行学习,最终综合所有基模型的预测值产生最终的预测结果,用的比较多的综合方式为加权法。
Stacking
Stacking 是先用全部数据训练好基模型,然后每个基模型都对每个训练样本进行的预测,其预测值将作为训练样本的特征值,最终会得到新的训练样本,然后基于新的训练样本进行训练得到模型,然后得到最终预测结果。
那么,为什么集成学习会好于单个学习器呢?原因可能有三:
训练样本可能无法选择出最好的单个学习器,由于没法选择出最好的学习器,所以干脆结合起来一起用;
假设能找到最好的学习器,但由于算法运算的限制无法找到最优解,只能找到次优解,采用集成学习可以弥补算法的不足;
可能算法无法得到最优解,而集成学习能够得到近似解。比如说最优解是一条对角线,而单个决策树得到的结果只能是平行于坐标轴的,但是集成学习可以去拟合这条对角线。
不是所有集成学习框架中的基模型都是弱模型。Bagging 和 Stacking 中的基模型为强模型(偏差低,方差高),而Boosting 中的基模型为弱模型(偏差高,方差低)。
Bagging 的偏差与方差
对于 Bagging 来说,每个基模型的权重等于 1/m 且期望近似相等,故我们可以得到:
通过上式我们可以看到:
-
整体模型的期望等于基模型的期望,这也就意味着整体模型的偏差和基模型的偏差近似。 -
整体模型的方差小于等于基模型的方差,当且仅当相关性为 1 时取等号,随着基模型数量增多,整体模型的方差减少,从而防止过拟合的能力增强,模型的准确度得到提高。但是,模型的准确度一定会无限逼近于 1 吗?并不一定,当基模型数增加到一定程度时,方差公式第一项的改变对整体方差的作用很小,防止过拟合的能力达到极限,这便是准确度的极限了。
在此我们知道了为什么 Bagging 中的基模型一定要为强模型,如果 Bagging 使用弱模型则会导致整体模型的偏差提高,而准确度降低。Random Forest 是经典的基于 Bagging 框架的模型,并在此基础上通过引入特征采样和样本采样来降低基模型间的相关性,在公式中显著降低方差公式中的第二项,略微升高第一项,从而使得整体降低模型整体方差。
Boosting 的偏差与方差
对于 Boosting 来说,由于基模型共用同一套训练集,所以基模型间具有强相关性,故模型间的相关系数近似等于 1,针对 Boosting 化简公式为:
通过观察整体方差的表达式我们容易发现:
- 整体模型的方差等于基模型的方差,如果基模型不是弱模型,其方差相对较大,这将导致整体模型的方差很大,即无法达到防止过拟合的效果。因此,Boosting 框架中的基模型必须为弱模型。
- 此外 Boosting 框架中采用基于贪心策略的前向加法,整体模型的期望由基模型的期望累加而成,所以随着基模型数的增多,整体模型的期望值增加,整体模型的准确度提高。
基于 Boosting 框架的 Gradient Boosting Decision Tree 模型中基模型也为树模型,同 Random Forrest,我们也可以对特征进行随机抽样来使基模型间的相关性降低,从而达到减少方差的效果。
2.4 小结
- 我们可以使用模型的偏差和方差来近似描述模型的准确度;
- 对于 Bagging 来说,整体模型的偏差与基模型近似,而随着模型的增加可以降低整体模型的方差,故其基模型需要为强模型;
- 对于 Boosting 来说,整体模型的方差近似等于基模型的方差,而整体模型的偏差由基模型累加而成,故基模型需要为弱模型。
️那么这里有一个小小的疑问,Bagging 和 Boosting 到底用的是什么模型呢?
随机森林的思想
随机森林就是通过集成学习的思想将多棵树集成的一种算法,它的基本单元是决策树,而它的本质属于集成学习方法。随机森林的名称中有两个关键词,一个是“随机”,一个就是“森林”。“森林”我们很好理解,一棵叫做树,那么成百上千棵就可以叫做森林了,这也是随机森林的主要思想--集成思想的体现。然而,bagging的代价是不用单棵决策树来做预测,具体哪个变量起到重要作用变得未知,所以bagging改进了预测准确率但损失了解释性。
“森林”容易理解,就是由很多“树”组成,那么“随机”体现在什么方面呢?
(1)训练集随机的选取:如果训练集大小为N,对于每棵树而言,随机且有放回地从训练集中的抽取N个训练样本(这种采样方式称为bootstrap sample方法),作为该树的训练集;这样保证了每颗树的训练集都不同,从而构建的树也不同
(2)特征的随机选取:从M个特征中选取m个特征,这样可以避免某个特征与分类结果具有强相关性,如果所有特征都选取,那么所有的树都会很相似,那样就不够“随机”了
另外还有一点,随机森林法构建树的时候不需要做额外的剪枝操作。个人理解:因为前两个“随机”操作,以及多颗树的建立,已经避免了过拟合现象,所以这种情况下,我们只需要让每棵树在它自己的领域内做到最好就可以了。
随机森林算法的预测性能与两个因素有关:
(1)森林中任意两棵树的相关性,相关性越强,则总体性能越容易差
(2)森林中每棵树的预测性能,每棵树越好,则总体性能越好
其实可以理解为要求“好而不同”。然而特征数m的选择越大,则相关性与个体性能都比较好,特征数m选择越小,则相关性与个体性能都更小,所以m的选择影响着随机森林的预测性能。
过程如下:
- 构建多个数据集
在包括N个样本的数据集中,采用有放回的抽样方式选择N个样本,构成中间数据集,然后在这个中间数据集的所有特征中随机选择几个特征,作为最终的数据集。以上述方式构建多个数据集;一般回归问题选用全部特征,分类问题选择全部特征个数的平方根个特征
- 为每个数据集建立完全分裂的决策树
利用CART为每个数据集建立一个完全分裂、没有经过剪枝的决策树,最终得到多棵CART决策树;
- 预测新数据
根据得到的每一个决策树的结果来计算新数据的预测值。
- 回归问题:采用多棵树的平均值。
- 分类问题:采用投票计数的方法,票数大的获胜,相同的随机选择。可以把树的棵树设置为奇数避免这一问题。
随机森林的推广
由于RF在实际应用中的良好特性,基于RF,有很多变种算法,应用也很广泛,不光可以用于分类回归,还可以用于特征转换,异常点检测等。下面对于这些RF家族的算法中有代表性的做一个总结。
4.1 extra trees
extra trees是RF的一个变种, 原理几乎和RF一模一样,有区别有:
对于每个决策树的训练集,RF采用的是随机采样bootstrap来选择采样集作为每个决策树的训练集,而extra trees一般不采用随机采样,即每个决策树采用原始训练集。
在选定了划分特征后,RF的决策树会基于基尼系数,均方差之类的原则,选择一个最优的特征值划分点,这和传统的决策树相同。但是extra trees比较的激进,他会随机的选择一个特征值来划分决策树。
从第二点可以看出,由于随机选择了特征值的划分点位,而不是最优点位,这样会导致生成的决策树的规模一般会大于RF所生成的决策树。 也就是说,模型的方差相对于RF进一步减少,但是偏倚相对于RF进一步增大。在某些时候,extra trees的泛化能力比RF更好。
4.2 Totally Random Trees Embedding
Totally Random Trees Embedding(以下简称 TRTE)是一种非监督学习的数据转化方法。它将低维的数据集映射到高维,从而让映射到高维的数据更好的运用于分类回归模型。我们知道,在支持向量机中运用了核方法来将低维的数据集映射到高维,此处TRTE提供了另外一种方法。
TRTE在数据转化的过程也使用了类似于RF的方法,建立T个决策树来拟合数据。当决策树建立完毕以后,数据集里的每个数据在T个决策树中叶子节点的位置也定下来了。比如我们有3颗决策树,每个决策树有5个叶子节点,某个数据特征x划分到第一个决策树的第2个叶子节点,第二个决策树的第3个叶子节点,第三个决策树的第5个叶子节点。则x映射后的特征编码为(0,1,0,0,0, 0,0,1,0,0, 0,0,0,0,1), 有15维的高维特征。这里特征维度之间加上空格是为了强调三颗决策树各自的子编码。
映射到高维特征后,可以继续使用监督学习的各种分类回归算法了。
4.3 Isolation Forest
Isolation Forest(以下简称IForest)是一种异常点检测的方法。它也使用了类似于RF的方法来检测异常点。
对于在T个决策树的样本集,IForest也会对训练集进行随机采样,但是采样个数不需要和RF一样,对于RF,需要采样到采样集样本个数等于训练集个数。但是IForest不需要采样这么多,一般来说,采样个数要远远小于训练集个数?为什么呢?因为我们的目的是异常点检测,只需要部分的样本我们一般就可以将异常点区别出来了。
对于每一个决策树的建立, IForest采用随机选择一个划分特征,对划分特征随机选择一个划分阈值。这点也和RF不同。
另外,IForest一般会选择一个比较小的最大决策树深度max_depth,原因同样本采集,用少量的异常点检测一般不需要这么大规模的决策树。
对于异常点的判断,则是将测试样本点x拟合到T颗决策树。计算在每颗决策树上该样本的叶子节点的深度ht(x),从而可以计算出平均高度h(x)。此时我们用下面的公式计算样本点x的异常概率:
其中,m为样本个数。c(m)的表达式为:
s(x,m)的取值范围是[0,1],取值越接近于1,则是异常点的概率也越大。
随机森林的优缺点
RF的主要优点有:
训练可以高度并行化,对于大数据时代的大样本训练速度有优势。
由于可以随机选择决策树节点划分特征,这样在样本特征维度很高的时候,仍然能高效的训练模型。
在训练后,可以给出各个特征对于输出的重要性
由于采用了随机采样,训练出的模型的方差小,泛化能力强。
相对于Boosting系列的Adaboost和GBDT, RF实现比较简单。
对部分特征缺失不敏感。
缺点:
在某些噪音比较大的样本集上,RF模型容易陷入过拟合。
取值划分比较多的特征容易对RF的决策产生更大的影响,从而影响拟合的模型的效果。
随机森林的应用场景
数据维度相对低(几十维),同时对准确性有较高要求时。
因为不需要很多参数调整就可以达到不错的效果,基本上不知道用什么方法的时候都可以先试一下随机森林。
sklearn
sklearn.ensemble.RandomForestClassifier(n_estimators=100, criterion='gini', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features='auto', max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, bootstrap=True, oob_score=False, n_jobs=None, random_state=None, verbose=0, warm_start=False, class_weight=None)
- n_estimators:基础分类器数量
- criterion:划分衡量指标
- max_depth:决策树最大深度
- min_samples_split:决策树叶结点继续分裂最小样本数量
- min_samples_leaf:决策树叶结点最小样本数量
- min_weight_fraction_leaf:决策树叶结点最小加权样本数量
- max_features:搜索划分时考虑的特征数量
- max_leaf_nodes:决策树最大叶结点数量
- min_impurity_decrease:决策树叶结点最小衡量指标提升
- bootstrap:是否进行有放回取样
- oob_score:是否通过未参加训练的样本估计模型效果
- n_jobs:控制并行
- random_state:随机种子
- verbose:控制输出
- warm_start:是否使用之前的输出
- class_weight:类别权重
- RandomForestRegressor类似
名称 | 解释 | 默认值 | 经验值 |
---|---|---|---|
n_estimators | 森林中决策树的个数 | 100 | |
criterion | 度量分裂标准 | (可选值)mse,mae | |
oobscore | 采用袋外样本来评估模型的好坏 | False | 推荐True 反应模型拟合后的泛化能力 |
RF决策树参数(和GBDT基本是相同的)
名称 | 解释 | 默认值 | 经验值 |
---|---|---|---|
max_features | RF划分时考虑的最大特征数 | auto(defult): 划分时最多考虑个特征 : 划分时最多考虑个特征 int(具体值),float(占总数的百分比) | |
max_depth | 决策树最大深度 | 可以不输入 | 10 ~ 100 |
min_samples_split | 内部节点再划分所需最小样本数 如果某节点的样本数少于该值,则不会继续再尝试选择最优特征来划分 | 2 | |
min_samples_leaf | 叶子节点最少样本数 | 1 | 如果样本量不大,可以不管这个值 |
n_jobs | 设定fit和predict阶段并列执行得任务个数 |
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
## 导入数据集
dataset = pd.read_csv('../datasets/Social_Network_Ads.csv')
X = dataset.iloc[:, [2, 3]].values
y = dataset.iloc[:, 4].values
## 将数据集拆分成训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, random_state = 0)
## 特征缩放
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
### 调试训练集的随机森林
from sklearn.ensemble import RandomForestClassifier
classifier = RandomForestClassifier(n_estimators = 10, criterion = 'entropy', random_state = 0)
classifier.fit(X_train, y_train)
## 预测测试集结果
```python
y_pred = classifier.predict(X_test)
## 生成混淆矩阵,也称作误差矩阵
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred)
from matplotlib.colors import ListedColormap
X_set, y_set = X_train, y_train
X1, X2 = np.meshgrid(np.arange(start = X_set[:, 0].min() - 1, stop = X_set[:, 0].max() + 1, step = 0.01),
np.arange(start = X_set[:, 1].min() - 1, stop = X_set[:, 1].max() + 1, step = 0.01))
plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(), X2.ravel()]).T).reshape(X1.shape),
alpha = 0.75, cmap = ListedColormap(('red', 'green')))
plt.xlim(X1.min(), X1.max())
plt.ylim(X2.min(), X2.max())
for i, j in enumerate(np.unique(y_set)):
plt.scatter(X_set[y_set == j, 0], X_set[y_set == j, 1],
c = ListedColormap(('red', 'green'))(i), label = j)
plt.title('Random Forest Classification (Training set)')
plt.xlabel('Age')
plt.ylabel('Estimated Salary')
plt.legend()
plt.show()
from matplotlib.colors import ListedColormap
X_set, y_set = X_test, y_test
X1, X2 = np.meshgrid(np.arange(start = X_set[:, 0].min() - 1, stop = X_set[:, 0].max() + 1, step = 0.01),
np.arange(start = X_set[:, 1].min() - 1, stop = X_set[:, 1].max() + 1, step = 0.01))
plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(), X2.ravel()]).T).reshape(X1.shape),
alpha = 0.75, cmap = ListedColormap(('red', 'green')))
plt.xlim(X1.min(), X1.max())
plt.ylim(X2.min(), X2.max())
for i, j in enumerate(np.unique(y_set)):
plt.scatter(X_set[y_set == j, 0], X_set[y_set == j, 1],
c = ListedColormap(('red', 'green'))(i), label = j)
plt.title('Random Forest Classification (Test set)')
plt.xlabel('Age')
plt.ylabel('Estimated Salary')
plt.legend()
plt.show()