1. 概述
1.1 集成算法概述
集成学习(ensemble learning)是时下非常流行的机器学习算法, 它本身不是一个单独的机器学习算法, 而是通过在数据上构建多个模型, 集成所有模型的建模结果. 基本上所有的机器学习领域都可以看到集成学习的身影, 在现实中集成学习也有相当大的作用, 它可以用来做市场营销模拟的建模, 统计客户来源, 保留和流失, 也可用来预测疾病的风险和病患者的易感性. 在现在的各种算法竞赛中, 随机森林, 梯度提升树(GBDT), Xgboost等集成算法的身影也随处可见, 可见其效果之好, 应用之广.
集成算法的目标:
集成算法会考虑多个评估器的建模结果, 汇总之后得到一个综合的结果, 以此来获取比单个模型更好的回归或分类效果.
多个模型集成成为的模型叫做集成评估器(ensemble estimator), 组成集成评估器的每个模型都叫做基评估器(base estimator). 通常来讲, 有三类集成算法: Bagging, Boosting, Stacking.
Bagging的核心思想是构建多个相互独立的评估器, 然后对其预测进行平均或多数表决原则来决定集成评估器的结果. 其代表模型就是随机森林.
Boosting中, 基评估器是相关的, 是按顺序一一构建的. 其核心思想是结合弱评估器的力量一次次对难以评估的样本进行预测, 从而构成一个强评估器. 代表模型有Adaboost和梯度提升树.
Stacking算法聚合多个分类或回归模型, 分阶段训练模型.
1.2 sklearn中的集成算法
类 | 功能 |
---|---|
ensemble.AdaBoostClassifier | AdaBoost分类 |
ensemble.AdaBoostRegressor | Adaboost回归 |
ensemble.BaggingClassifier | 装袋分类器 |
ensemble.BaggingRegressor | 装袋回归器 |
ensemble.ExtraTreesClassifier | Extra-trees分类(超树,极端随机树) |
ensemble.ExtraTreesRegressor | Extra-trees回归 |
ensemble.GradientBoostingClassifier | 梯度提升分类 |
ensemble.GradientBoostingRegressor | 梯度提升回归 |
ensemble.IsolationForest | 隔离森林 |
ensemble.RandomForestClassifier | 随机森林分类 |
ensemble.RandomForestRegressor | 随机森林回归 |
ensemble.RandomTreesEmbedding | 完全随机树的集成 |
ensemble.VotingClassifier | 用于不合适估算器的软投票/多数规则分类器 |
2. RandomForestClassifier
class sklearn.ensemble.RandomForestClassifier (n_estimators=’10’, 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)
2.1 重要参数
criterion, max_depth, min_samples_leaf, max_features等系列参数同决策树一致, 这里不再做解释.
有兴趣的朋友可以看这篇文章: 利用sklearn对红酒数据集分类
2.1.1 n_estimators
这是森林中树木的数量, 即基评估器的数量. 这个参数对随机森林模型的精确性影响是单调的, n_estimators越大, 模型的效果往往越好. 但是相应的, 任何模型都有决策边界, n_estimators达到一定的程度之后, 随机森林的精确性往往不在上升或开始波动. 并且n_estimators越大,需要的计算量和内存也越大, 训练的时间也会越来越长. 对于这个参数, 我们是渴望在训练难度和模型效果之间取得平衡.
下面我们来构建一片森林, 然后与树来做个对比:
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
wine = load_wine()
Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data, wine.target, test_size=0.3)
clf = DecisionTreeClassifier(random_state=1)
rfc = RandomForestClassifier(random_state=1)
clf = clf.fit(Xtrain, Ytrain)
rfc = rfc.fit(Xtrain, Ytrain)
score1 = clf.score(Xtest, Ytest)
score2 = rfc.score(Xtest, Ytest)
print("Single Tree:{}".format(score1))
print("Random Forest:{}".format(score2))
结果:
Single Tree:0.9074074074074074
Random Forest:0.9444444444444444
可以看出随机森林的效果比单个决策树要好. (可惜的是随机森林整体无法被可视化)
另外, 我们也可以通过交叉验证法来看看对比, 添加以下代码, 再运行一次:
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
score3 = cross_val_score(rfc, wine.data, wine.target, cv=10)
score4 = cross_val_score(clf, wine.data, wine.target, cv=10)
plt.plot(range(1, 11), score3, label="Random Forest")
plt.plot(range(1, 11), score4, label="Single Tree")
plt.legend()
plt.show()
2.1.3 random_state
随机森林的本质是一种装袋集成算法(bagging), 装袋集成算法是对基评估器的预测结果进行平均或用多数表决原则来决定集成评估器的结果. 假如我们建立了25棵树, 对任何一个样本而言, 平均或多数表决原则下, 当且仅当有13棵以上的树判断错误的时候,随机森林才会判断错误. 单独一棵决策树对红酒数据集的分类准确率在0.85上下浮动, 假设一棵树判断错误的可能性为0.2(ε), 那13棵树以上都判断错误的可能性是:
$$ \mathbf{e} = \sum_{i=13}^{25} C_{25}^i \varepsilon^i (1-\varepsilon)^{25-i} =0.000369 $$
可见, 判断错误的几率非常小, 这让随机森林在红酒数据集上的表现远远好于单棵决策树.
那现在就有一个问题了: 我们说袋装法服从多数表决原则或对基分类器结果求平均 . 这即是说, 我们默认森林中的每棵树应该是不同的, 并且会返回不同的结果. 设想一下, 如果随机森林里所有的树的判断结果都一致(全判断对 或全判断错), 那随机森林无论应用何种集成原则来求结果, 都应该无法比单棵决策树取得更好的效果才对.但我 们使用了一样的类DecisionTreeClassifier, 一样的参数, 一样的训练集和测试集, 为什么随机森林里的众多树会有不同的判断结果?
问到这个问题, 很多小伙伴可能就会想到了: sklearn中的分类树DecisionTreeClassifier自带随机性, 所以随机森林中的树天生就都是不一样的. 决策树从最重要的特征中随机选择出一个特征来进行分枝, 因此每次生成的决策树都不一样,这个功能由参数random_state控制.
随机森林中其实也有random_state, 用法和分类树中相似, 只不过在分类树中, 一个random_state只控制生成一棵树, 而随机森林中的random_state控制的是生成森林的模式, 而非让一个森林中只有一棵树.
但这种做法的局限性是很强的, 当我们需要成千上万棵树的时候, 数据不一定能够提供成千上万的特征来让我们构筑尽量多尽量不同的树. 因此除了random_state, 我们还需要其他的随机性.
2.1.4 bootstrap & oob_score
要让基分类器尽量都不一样, 一种很容易理解的方法是使用不同的训练集来进行训练, 而袋装法正是通过有放回的随机抽样技术来形成不同的训练数据, bootstrap就是用来控制抽样技术的参数.
在一个含有n个样本的原始训练集中, 我们进行随机采样, 每次采样一个样本, 并在抽取下一个样本之前将该样本 放回原始训练集, 也就是说下次采样时这个样本依然可能被采集到, 这样采集n次, 最终得到一个和原始训练集一样大, n个样本组成的自助集. 由于是随机采样, 这样每次的自助集和原始数据集不同, 和其他的采样集也是不同的. 这样我们就可以自由创造取之不尽用之不竭, 并且互不相同的自助集, 用这些自助集来训练我们的基分类器, 我们的基分类器自然也就各不相同了.
bootstrap参数默认True, 代表采用这种有放回的随机抽样技术. 通常, 这个参数不会被我们设置为False.
然而有放回抽样也会有自己的问题. 由于是有放回, 一些样本可能在同一个自助集中出现多次, 而其他一些却可能被忽略, 一般来说, 自助集大约平均会包含63%的原始数据. 因为每一个样本被抽到某个自助集中的概率为:
$$1-(1-\frac {1}{n})^n $$
假设一个样本被抽到一次概率是\(\frac 1n\), 那么它没被抽到的概率为\(1-\frac 1n\).
n次没被抽到的概率为\((1-\frac 1n)^n\), 那么这n次中, 它被抽到的概率为\(1-(1-\frac {1}{n})^n\)
当n足够大时, 这个概率收敛于\(1-\frac {1}{e}\), 约等于0.632. 因此, 会有约37%的训练数据被浪费掉,没有参与建模, 这些数据被称为袋外数据(out of bag data, 简写为oob).除了我们最开始就划分好的测试集之外, 这些数据也可以被用来作为集成算法的测试集. 也就是说, 在使用随机森林时,我们可以不划分测试集和训练集,只需要用袋外 数据来测试我们的模型即可. 当然, 这也不是绝对的, 当n和n_estimators都不够大的时候, 很可能就没有数据掉落在袋外, 自然也就无法使用oob数据来测试模型了.
如果希望用袋外数据来测试, 则需要在实例化时就将oob_score这个参数调整为True, 训练完毕之后, 我们可以用 随机森林的另一个重要属性:oob_score_来查看我们的在袋外数据上测试的结果.
原作者: 菜菜