1.11. 集成方法
注意,在本文中
bagging
和
boosting
为了更好的保留原文意图,不进行翻译estimator->估计器
base
estimator->基估计器
集成方法 的目标是把使用给定学习算法构建的多个基估计器的预测结果结合起来,从而获得比单个估计器更好的泛化能力/鲁棒性。
集成方法通常分为两种:
- 平均方法,该方法的原理是构建多个独立的估计器,然后取它们的预测结果的平均。一般来说组合之后的估计器是会比单个估计器要好的,因为它的方差减小了。 示例: Bagging 方法, 随机森林, …
- 相比之下,在 boosting 方法 中,基估计器是依次构建的,并且每一个基估计器都尝试去减少组合估计器的偏差。这种方法主要目的是为了结合多个弱模型,使集成的模型更加强大。 示例: AdaBoost, 梯度提升树, …
1.11.1. Bagging meta-estimator(Bagging 元估计器)
在集成算法中,bagging 方法会在原始训练集的随机子集上构建一类黑盒估计器的多个实例,然后把这多个估计器的预测结果结合起来形成最终的预测结果。 该方法通过在构建模型的过程中引入随机性,来减少基估计器的方差(例如,决策树)。 在多数情况下,bagging 方法提供了一种非常简单的方式来对单一模型进行改进,而无需修改背后的算法。 因为 bagging 方法可以减小过拟合,所以通常最适合在强分类器和复杂模型上使用(例如,完全决策树,fully developed decision trees),相比之下 boosting 方法则在弱模型上表现更好(例如,浅层决策树,shallow decision trees)。
bagging 方法有很多种,区别大多数在于抽取训练子集的方法:
- 如果抽取的数据集是对于样例抽取的子集,我们叫做粘贴 (Pasting) [B1999] 。
- 如果样例抽取是有放回的,我们称为 Bagging [B1996] 。
- 如果抽取的数据集的随机子集是对于特征抽取的随机子集,我们叫做随机子空间 (Random Subspaces) [H1998] 。
- 最后,如果估计器构建在对于样本和特征抽取的子集之上时,我们叫做随机补丁 (Random Patches) [LG2012] 。
在 scikit-learn 中,bagging 方法使用统一的 BaggingClassifier
元估计器(或者 BaggingRegressor
),输入的参数和随机子集抽取策略由用户指定。max_samples
和 max_features
控制着子集的大小(对于样例和特征), bootstrap
和 bootstrap_features
控制着样例和特征的抽取是有放回还是无放回的。 当使用样本子集时,通过设置 oob_score=True
,可以使用袋外(out-of-bag)样本来评估泛化精度。下面的代码片段说明了如何构造一个 KNeighborsClassifier
估计器的 bagging 集成实例,每一个基估计器都建立在 50% 的样本随机子集和 50% 的特征随机子集上。
>>>
代码语言:javascript复制>>> from sklearn.ensemble import BaggingClassifier
>>> from sklearn.neighbors import KNeighborsClassifier
>>> bagging = BaggingClassifier(KNeighborsClassifier(),
... max_samples=0.5, max_features=0.5)
示例:
- Single estimator versus bagging: bias-variance decomposition
参考文献
[B1999] | L. Breiman, “Pasting small votes for classification in large databases and on-line”, Machine Learning, 36(1), 85-103, 1999. |
---|
[B1996] | L. Breiman, “Bagging predictors”, Machine Learning, 24(2), 123-140, 1996. |
---|
[H1998] | T. Ho, “The random subspace method for constructing decision forests”, Pattern Analysis and Machine Intelligence, 20(8), 832-844, 1998. |
---|
[LG2012] | G. Louppe and P. Geurts, “Ensembles on Random Patches”, Machine Learning and Knowledge Discovery in Databases, 346-361, 2012. |
---|
1.11.2. 由随机树组成的森林
sklearn.ensemble
模块包含两个基于 随机决策树 的平均算法: RandomForest 算法和 Extra-Trees 算法。 这两种算法都是专门为树而设计的扰动和组合技术(perturb-and-combine techniques) [B1998] 。 这意味着通过在分类器构造过程中引入随机性来创建一组不同的分类器。集成分类器的预测结果是单个分类器预测结果的平均值。
与其他分类器一样,森林分类器必须拟合(fitted)两个数组: 保存训练样本的数组(或稀疏或稠密的)X,大小为 [n_samples,
n_features]
,和 保存训练样本目标值(类标签)的数组 Y,大小为 [n_samples]
:
>>>
代码语言:javascript复制>>> from sklearn.ensemble import RandomForestClassifier
>>> X = [[0, 0], [1, 1]]
>>> Y = [0, 1]
>>> clf = RandomForestClassifier(n_estimators=10)
>>> clf = clf.fit(X, Y)
同 决策树 一样,随机森林算法(forests of trees)也能够通过扩展来解决 多输出问题 (如果 Y 的大小是 [n_samples, n_outputs])
.
1.11.2.1. 随机森林
在随机森林中(参见 ExtraTreesClassifier
和 ExtraTreesRegressor
类), 集成模型中的每棵树构建时的样本都是由训练集经过有放回抽样得来的(例如,自助采样法-bootstrap sample,这里采用西瓜书中的译法)。 另外,在构建树的过程中进行结点分割时,选择的分割点不再是所有特征中最佳分割点,而是特征的一个随机子集中的最佳分割点。 由于这种随机性,森林的偏差通常会有略微的增大(相对于单个非随机树的偏差),但是由于取了平均,其方差也会减小,通常能够补偿偏差的增加,从而产生一个总体上更好的模型。
与原始文献 [B2001] 不同的是,scikit-learn 的实现是取每个分类器预测概率的平均,而不是让每个分类器对类别进行投票。
1.11.2.2. 极限随机树
在极限随机树中(参见 ExtraTreesClassifier
和 ExtraTreesRegressor
类), 计算分割点方法中的随机性进一步增强。 在随机森林中,使用的特征是候选特征的随机子集;不同于寻找最具有区分度的阈值, 这里的阈值是针对每个候选特征随机生成的,并且选择这些随机生成的阈值中的最佳者作为分割规则。 这种做法通常能够减少一点模型的方差,代价则是略微地增大偏差:
>>>
代码语言:javascript复制>>> from sklearn.model_selection import cross_val_score
>>> from sklearn.datasets import make_blobs
>>> from sklearn.ensemble import RandomForestClassifier
>>> from sklearn.ensemble import ExtraTreesClassifier
>>> from sklearn.tree import DecisionTreeClassifier
>>>
代码语言:javascript复制>>> X, y = make_blobs(n_samples=10000, n_features=10, centers=100,
... random_state=0)
>>>
代码语言:javascript复制>>> clf = DecisionTreeClassifier(max_depth=None, min_samples_split=2,
... random_state=0)
>>> scores = cross_val_score(clf, X, y)
>>> scores.mean()
0.97...
>>>
代码语言:javascript复制>>> clf = RandomForestClassifier(n_estimators=10, max_depth=None,
... min_samples_split=2, random_state=0)
>>> scores = cross_val_score(clf, X, y)
>>> scores.mean()
0.999...
>>>
代码语言:javascript复制>>> clf = ExtraTreesClassifier(n_estimators=10, max_depth=None,
... min_samples_split=2, random_state=0)
>>> scores = cross_val_score(clf, X, y)
>>> scores.mean() > 0.999
True
1.11.2.3. 参数
使用这些方法时要调整的参数主要是 n_estimators
和 max_features
。 前者(n_estimators)是森林里树的数量,通常数量越大,效果越好,但是计算时间也会随之增加。 此外要注意,当树的数量超过一个临界值之后,算法的效果并不会很显著地变好。 后者(max_features)是分割节点时考虑的特征的随机子集的大小。 这个值越低,方差减小得越多,但是偏差的增大也越多。 根据经验,回归问题中使用 max_features = n_features, 分类问题使用 max_features
=
sqrt(n_features
(其中 n_features
是特征的个数)是比较好的默认值。 max_depth
=
None
和
min_samples_split
=
2
结合通常会有不错的效果(即生成完全的树)。 请记住,这些(默认)值通常不是最佳的,同时还可能消耗大量的内存,最佳参数值应由交叉验证获得。 另外,请注意,在随机林中,默认使用自助采样法(bootstrap
=
True
), 然而 extra-trees 的默认策略是使用整个数据集(bootstrap
=
False
)。 当使用自助采样法方法抽样时,泛化精度是可以通过剩余的或者袋外的样本来估算的,设置 oob_score
=
True
即可实现。
提示:
默认参数下模型复杂度是:O(M*N*log(N))
, 其中 M 是树的数目,N
是样本数。 可以通过设置以下参数来降低模型复杂度:min_samples_split
, min_samples_leaf
, max_leaf_nodes`` 和 ``max_depth
.
1.11.2.4. 并行化
最后,这个模块还支持树的并行构建和预测结果的并行计算,这可以通过 n_jobs
参数实现。 如果设置 n_jobs
=
k
,则计算被划分为 k
个作业,并运行在机器的 k
个核上。 如果设置 n_jobs
=
-1
,则使用机器的所有核。 注意由于进程间通信具有一定的开销,这里的提速并不是线性的(即,使用 k
个作业不会快k倍)。 当然,在建立大量的树,或者构建单个树需要相当长的时间(例如,在大数据集上)时,(通过并行化)仍然可以实现显著的加速。
示例:
- Plot the decision surfaces of ensembles of trees on the iris dataset
- Pixel importances with a parallel forest of trees
- Face completion with a multi-output estimators
参考文献
[B2001] | Breiman, “Random Forests”, Machine Learning, 45(1), 5-32, 2001. |
---|
- Breiman, “Random Forests”, Machine Learning, 45(1), 5-32, 2001.
[B1998] | Breiman, “Arcing Classifiers”, Annals of Statistics 1998. |
---|
- Breiman, “Arcing Classifiers”, Annals of Statistics 1998.
- P. Geurts, D. Ernst., and L. Wehenkel, “Extremely randomized trees”, Machine Learning, 63(1), 3-42, 2006.
1.11.2.5. 特征重要性评估
特征对目标变量预测的相对重要性可以通过(树中的决策节点的)特征使用的相对顺序(即深度)来进行评估。 决策树顶部使用的特征对更大一部分输入样本的最终预测决策做出贡献;因此,可以使用接受每个特征对最终预测的贡献的样本比例来评估该 特征的相对重要性 。
通过对多个随机树中的 预期贡献率 (expected activity rates)**取平均**,可以减少这种估计的 方差 ,并将其用于特征选择。
下面的例子展示了一个面部识别任务中每个像素的相对重要性,其中重要性由颜色(的深浅)来表示,使用的模型是ExtraTreesClassifier。
实际上,在拟合模型时这些估计值存储在 feature_importances_
属性中。 这是一个大小为 (n_features,)
的数组,其每个元素值为正,并且总和为 1.0。一个元素的值越高,其对应的特征对预测函数的贡献越大。
示例:
- Pixel importances with a parallel forest of trees
- Feature importances with forests of trees
1.11.2.6. 完全随机树嵌入
RandomTreesEmbedding
实现了一个无监督的数据转换。 通过由完全随机树构成的森林,RandomTreesEmbedding
使用数据最终归属的叶子节点的索引值(编号)对数据进行编码。 该索引以 one-of-K 方式编码,最终形成一个高维的稀疏二进制编码。 这种编码可以被非常高效地计算出来,并且可以作为其他学习任务的基础。 编码的大小和稀疏度可以通过选择树的数量和每棵树的最大深度来影响。对于集成中的每棵树,编码包含一个实体(校对者注:这里真的没搞懂)。 编码的大小(维度)最多为 n_estimators
*
2
**
max_depth
,即森林中的叶子节点的最大数。
由于相邻数据点更可能位于树的同一叶子中,该变换可以作为一种隐式地非参数密度估计。
示例:
- Hashing feature transformation using Totally Random Trees
- Manifold learning on handwritten digits: Locally Linear Embedding, Isomap… 比较了手写体数字的非线性降维技术。
- Feature transformations with ensembles of trees 比较了基于树的有监督和无监督特征变换.
See also
流形学习 方法也可以用于特征空间的非线性表示, 以及降维.
1.11.3. AdaBoost
模型 sklearn.ensemble
包含了流行的提升算法 AdaBoost, 这个算法是由 Freund and Schapire 在 1995 年提出来的 [FS1995].
AdaBoost 既可以用在分类问题也可以用在回归问题中:
- For multi-class classification,
AdaBoostClassifier
implements AdaBoost-SAMME and AdaBoost-SAMME.R [ZZRH2009]. - For regression,
AdaBoostRegressor
implements AdaBoost.R2 [D1997].
1.11.3.1. 使用方法
下面的例子展示了如何拟合一个包含 100 个弱学习器的 AdaBoost 分类器:
>>>
代码语言:javascript复制>>> from sklearn.model_selection import cross_val_score
>>> from sklearn.datasets import load_iris
>>> from sklearn.ensemble import AdaBoostClassifier
>>> iris = load_iris()
>>> clf = AdaBoostClassifier(n_estimators=100)
>>> scores = cross_val_score(clf, iris.data, iris.target)
>>> scores.mean()
0.9...
弱学习器的数量由参数 n_estimators
来控制。 learning_rate
参数用来控制每个弱学习器对 最终的结果的贡献程度(校对者注:其实应该就是控制权重修改的速率,这里不太记得了,不确定)。 弱学习器默认使用决策树。不同的弱学习器可以通过参数 base_estimator``来指定。
获取一个好的预测结果主要需要调整的参数是
``n_estimators
和 base_estimator
的复杂度 (例如:对于弱学习器为决策树的情况,树的深度 max_depth
或叶子节点的最小样本数 min_samples_leaf
等都是控制树的复杂度的参数)
示例:
- Discrete versus Real AdaBoost compares the classification error of a decision stump, decision tree, and a boosted decision stump using AdaBoost-SAMME and AdaBoost-SAMME.R.
- Multi-class AdaBoosted Decision Trees shows the performance of AdaBoost-SAMME and AdaBoost-SAMME.R on a multi-class problem.
- Two-class AdaBoost shows the decision boundary and decision function values for a non-linearly separable two-class problem using AdaBoost-SAMME.
- Decision Tree Regression with AdaBoost demonstrates regression with the AdaBoost.R2 algorithm.
参考
[FS1995] | Y. Freund, and R. Schapire, “A Decision-Theoretic Generalization of On-Line Learning and an Application to Boosting”, 1997. |
---|
[ZZRH2009] | J. Zhu, H. Zou, S. Rosset, T. Hastie. “Multi-class AdaBoost”, 2009. |
---|
[D1997] | Drucker. “Improving Regressors using Boosting Techniques”, 1997. |
---|
- Drucker. “Improving Regressors using Boosting Techniques”, 1997.
[HTF] | T. Hastie, R. Tibshirani and J. Friedman, “Elements of Statistical Learning Ed. 2”, Springer, 2009. |
---|
1.11.4. 梯度树提升(Gradient Tree Boosting)
Gradient Tree Boosting 或梯度提升回归树(GBRT)是对于任意的可微损失函数的提升算法的泛化。 GBRT 是一个准确高效的现有程序, 它既能用于分类问题也可以用于回归问题。梯度树提升模型被应用到各种领域,包括网页搜索排名和生态领域.
GBRT 的优点:
- 对混合型数据的自然处理(异构特征)
- 强大的预测能力
- 在输出空间中对异常点的鲁棒性(通过具有鲁棒性的损失函数实现)
GBRT 的缺点:
- 可扩展性差(校对者注:此处的可扩展性特指在更大规模的数据集/复杂度更高的模型上使用的能力,而非我们通常说的功能的扩展性;GBRT 支持自定义的损失函数,从这个角度看它的扩展性还是很强的!)。由于提升算法的有序性(也就是说下一步的结果依赖于上一步),因此很难做并行.
模块 sklearn.ensemble
通过梯度提升树提供了分类和回归的方法.
1.11.4.1. 分类
GradientBoostingClassifier
既支持二分类又支持多分类问题. 下面的例子展示了如何拟合一个包含 100 个决策树弱学习器的梯度提升分类器:
>>>
代码语言:javascript复制>>> from sklearn.datasets import make_hastie_10_2
>>> from sklearn.ensemble import GradientBoostingClassifier
>>> X, y = make_hastie_10_2(random_state=0)
>>> X_train, X_test = X[:2000], X[2000:]
>>> y_train, y_test = y[:2000], y[2000:]
>>> clf = GradientBoostingClassifier(n_estimators=100, learning_rate=1.0,
... max_depth=1, random_state=0).fit(X_train, y_train)
>>> clf.score(X_test, y_test)
0.913...
弱学习器(例如:回归树)的数量由参数 n_estimators
来控制;每个树的大小可以通过由参数 max_depth
设置树的深度,或者由参数 max_leaf_nodes
设置叶子节点数目来控制。 learning_rate
是一个在 (0,1] 之间的超参数,这个参数通过 shrinkage(缩减步长) 来控制过拟合。
Note
超过两类的分类问题需要在每一次迭代时推导 n_classes
个回归树。因此,所有的需要推导的树数量等 于n_classes
*
n_estimators
。对于拥有大量类别的数据集我们强烈推荐使用 RandomForestClassifier
来代替GradientBoostingClassifier
.
1.11.4.2. 回归
对于回归问题 GradientBoostingRegressor
支持一系列 different loss functions , 这些损失函数可以通过参数 loss
来指定;对于回归问题默认的损失函数是最小二乘损失函数( 'ls'
).
>>>
代码语言:javascript复制>>> import numpy as np
>>> from sklearn.metrics import mean_squared_error
>>> from sklearn.datasets import make_friedman1
>>> from sklearn.ensemble import GradientBoostingRegressor
>>> X, y = make_friedman1(n_samples=1200, random_state=0, noise=1.0)
>>> X_train, X_test = X[:200], X[200:]
>>> y_train, y_test = y[:200], y[200:]
>>> est = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1,
... max_depth=1, random_state=0, loss='ls').fit(X_train, y_train)
>>> mean_squared_error(y_test, est.predict(X_test))
5.00...
下图展示了应用损失函数为最小二乘损失,基学习器个数为 500 的 GradientBoostingRegressor 来处理sklearn.datasets.load_boston
数据集的结果。左图表示每一次迭代的训练误差和测试误差。每一次迭 代的训练误差保存在提升树模型的 train_score_
属性中,每一次迭代的测试误差能够通过 staged_predict
方法获取,该方法返回一个生成器,用来产生每一 个迭代的预测结果。类似下面这样的图表,可以用于决定最优的树的数量,从而进行提前停止。右图表示每个特征的重要性,它 可以通过 feature_importances_
属性来获取.
示例:
- Gradient Boosting regression
- Gradient Boosting Out-of-Bag estimates
1.11.4.3. 拟合额外的弱学习器
GradientBoostingRegressor
和 :class:`GradientBoostingClassifier`都支持设置参数
warm_start=True
,这样设置允许我们在已经拟合的模型上面添加更多的估计器.
>>>
代码语言:javascript复制>>> _ = est.set_params(n_estimators=200, warm_start=True) # set warm_start and new nr of trees
>>> _ = est.fit(X_train, y_train) # fit additional 100 trees to est
>>> mean_squared_error(y_test, est.predict(X_test))
3.84...
1.11.4.4. 控制树的大小
回归树基学习器的大小定义了可以被梯度提升模型捕捉到的变量(即特征)相互作用(即多个特征共同对预测产生影响)的程度。 通常一棵深度为 h
的树能捕获到秩为 h
的相互作用。这里有两种控制单棵回归树大小的方法。
如果你指定 max_depth=h
,那么将会一个深度为 h
的完全二叉树。这棵树将会有(至多) 2**h
个 叶子节点和 2**h
-
1
个切分节点。
另外,你能通过参数 max_leaf_nodes
指定叶子节点的数量来控制树的大小。在这种情况下,树将会 使用最优优先搜索来生成,这种搜索方式是通过每次选取对不纯度提升最大的节点来展开。一棵树的 max_leaf_nodes=k
拥有 k
-
1
个切分节点,因此可以模拟秩最高达到 max_leaf_nodes
-
1
的相互作用(即 max_leaf_nodes
-
1
个特征共同决定预测值)。
我们发现 max_leaf_nodes=k
可以给出与 max_depth=k-1
品质相当的结果,但是其训练速度明显更快,同时 也会多一点训练误差作为代价。参数 max_leaf_nodes
对应于文章 [F2001] 中梯度提升章节中的变量 J
, 同时与 R 语言的 gbm 包的参数 interaction.depth
相关,两者间的关系是 max_leaf_nodes
==
interaction.depth
1
。
1.11.4.5. 数学公式 (Mathematical formulation)
是问题的具体,对于最小二乘回归,通常选择目标值的平均值.
Note
初始化模型也能够通过 init
参数来指定,但被传对象需要实现 fit
和 predict
函数.
梯度提升(Gradient Boosting)尝试通过最速下降法以数字方式解决这个最小化问题.最速下降方向是在当前模型
下评估的 损失函数的负梯度,其中模型
可以计算任何可微损失函数:
其中步长:math:gamma_m 通过如下方式线性搜索获得:
该算法处理分类和回归问题不同之处在于具体损失函数的使用.
1.11.4.5.1. 损失函数 (Loss Functions)
以下是目前支持的损失函数,具体损失函数可以通过参数 loss
指定:
- 回归 (Regression)
- Least squares (
'ls'
): 由于其优越的计算性能,该损失函数成为回归算法中的自然选择。 初始模型通过目标值的均值给出. - Least absolute deviation (
'lad'
): 回归中具有鲁棒性的损失函数,初始模型通过目 标值的中值给出. - Huber (
'huber'
): 回归中另一个具有鲁棒性的损失函数,它是最小二乘和最小绝对偏差两者的结合. 其利用alpha
来控制模型对于异常点的敏感度(详细介绍请参考 [F2001]). - Quantile (
'quantile'
): 分位数回归损失函数.用0
<
alpha
<
1
来指定分位数这个损 失函数可以用来产生预测间隔.(详见 Prediction Intervals for Gradient Boosting Regression ).
- Least squares (
- 分类 (Classification)
- Binomial deviance (
'deviance'
): 对于二分类问题(提供概率估计)即负的二项log似然 损失函数.模型以log的比值比来初始化. - Multinomial deviance (
'deviance'
): 对于多分类问题的负的多项log似然损失函数具有n_classes
个互斥的类.提供概率估计. 初始模型由每个类的先验概率给出.在每一次迭代中n_classes
回归树被构建,这使得 GBRT 在处理多类别数据集时相当低效. - Exponential loss (
'exponential'
): 与AdaBoostClassifier
具有相同的损失 函数.与'deviance'
相比,对具有错误标记的样本的鲁棒性较差,仅用于在二分类问题.
- Binomial deviance (
1.11.4.6. 正则化 (Regularization)
1.11.4.6.1. 收缩率 (Shrinkage)
在拟合一定数量的弱分类器时,参数 learning_rate
和参数 n_estimators
之间有很强的制约关系. 较小的``learning_rate`` 需要大量的弱分类器才能保证训练误差的不变.经验表明数值较小的 learning_rate
将会得到更好的测试误差. [HTF2009] 推荐把 learning_rate
设置为一个较小的常数 (例如: learning_rate
<=
0.1
)同时通过提前停止策略来选择合适的 n_estimators
. 有关 learning_rate
和 n_estimators
更详细的讨论可以参考 [R2007].
1.11.4.6.2. 子采样 (Subsampling)
[F1999] 提出了随机梯度提升,这种方法将梯度提升(gradient boosting)和bootstrap averaging(bagging)相结合.在每次迭代 中,基分类器是通过抽取所有可利用训练集中一小部分的 subsample
训练得到的.子样本采用无放回的方式采 样. subsample
参数的值一般设置为0.5.
下图表明了收缩率和子采样对于模型拟合好坏的影响.我们可以明显看到收缩率在无收缩的情况下拥有更好的表现.而将子采 样和收缩率相结合能进一步的提高模型的准确率.相反,使用子采样而不使用缩减的结果十分糟糕.
另一个减少方差的策略是特征子采样,这种方法类似于 RandomForestClassifier
中的随机分割. 子采样的特征数可以通过参数 max_features
来控制.
Note
采用一个较小的 max_features
值能大大缩减模型的训练时间.
随机梯度提升允许计算测试偏差的袋外估计(Out-of-bag),方法是通过计算那些不在自助采样之内的样本偏差的改进.这个改进保存在属性 oob_improvement_
中. oob_improvement_[i]
如果将 第i步添加到当前预测中,则可以改善OOB样本的损失.袋外估计可以使用在模型选择中,例如决定最优迭代次 数.OOB估计通常都很悲观,因此我们推荐使用交叉验证来代替它,而当交叉验证太耗时时我们就只能使用OOB了.
示例 (Examples):
- Gradient Boosting regularization
- Gradient Boosting Out-of-Bag estimates
- OOB Errors for Random Forests
1.11.4.7. 解释性 (Interpretation)
通过简单地可视化树结构可以很容易地解释单个决策树,然而对于梯度提升模型来说,一般拥有数百棵/种回归树,因此通过目视检查每一棵树 是很难解释的.幸运的是,有很多关于总结和解释梯度提升模型的技术已经被提出.
1.11.4.7.1. 特征重要性 (Feature importance)
通常情况下每个特征对于预测目标的贡献是不同的.在很多情形下大多数特征实际上是无关的.当解释一个模型时,第一 个问题通常是:这些重要的特征是什么?他们如何在预测目标方面做出积极的响应?
单个决策树本质上是通过选择最佳切分点来进行特征选择.这个信息可以用来检测每个特征的重要性.基本思想是:在树 的分割点中使用的特征越频繁,特征越重要。 这个重要的概念可以通过简单地平均每棵树的特征重要性来扩展到 决策树集合.(详见 特征重要性评估 ).
对于一个训练好的梯度提升模型,其特征重要性分数可以通过属性 feature_importances_
查看:
>>>
代码语言:javascript复制>>> from sklearn.datasets import make_hastie_10_2
>>> from sklearn.ensemble import GradientBoostingClassifier
>>> X, y = make_hastie_10_2(random_state=0)
>>> clf = GradientBoostingClassifier(n_estimators=100, learning_rate=1.0,
... max_depth=1, random_state=0).fit(X, y)
>>> clf.feature_importances_
array([ 0.11, 0.1 , 0.11, ...
示例 (Examples):
- Gradient Boosting regression
1.11.4.7.2. 部分依赖 (Partial dependence)
部分依赖图(PDP)展示了目标响应和一系列目标特征的依赖关系,同时边缘化了其他所有特征值(候选特征). 直觉上,我们可以将部分依赖解释为作为目标特征函数 [2] 的预期目标响应 [1] .
由于人类感知能力的限制,目标特征的设置必须小一点(通常是1到2),因此目标特征通常在最重要的特征中选择.
下图展示了加州住房数据集的四个单向和一个双向部分依赖图:
单向 PDPs 告诉我们目标响应和目标特征的相互影响(例如:线性或者非线性).上图中的左上图展示了一个地区中等收入对 中等房价的影响.我们可以清楚的看到两者之间是线性相关的.
具有两个目标特征的 PDPs 显示这两个特征之间的相互影响.例如:上图中两个变量的 PDP 展示了房价中位数与房屋年龄和 每户平均入住人数之间的依赖关系.我们能清楚的看到这两个特征之间的影响:对于每户入住均值而言,当其值大于 2 时, 房价与房屋年龄几乎是相对独立的,而其值小于2的时,房价对房屋年龄的依赖性就会很强.
模型 partial_dependence
提供了一个便捷的函数 plot_partial_dependence
来产生单向或双向部分依 赖图.在下图的例子中我们展示如何创建一个部分依赖的网格图:特征值介于 0
和 1
的两个单向依赖PDPs和一 个在两个特征间的双向 PDPs:
>>>
代码语言:javascript复制>>> from sklearn.datasets import make_hastie_10_2
>>> from sklearn.ensemble import GradientBoostingClassifier
>>> from sklearn.ensemble.partial_dependence import plot_partial_dependence
>>> X, y = make_hastie_10_2(random_state=0)
>>> clf = GradientBoostingClassifier(n_estimators=100, learning_rate=1.0,
... max_depth=1, random_state=0).fit(X, y)
>>> features = [0, 1, (0, 1)]
>>> fig, axs = plot_partial_dependence(clf, X, features)
对于多类别的模型,你需要通过 label
参数设置类别标签来创建 PDPs:
>>>
代码语言:javascript复制>>> from sklearn.datasets import load_iris
>>> iris = load_iris()
>>> mc_clf = GradientBoostingClassifier(n_estimators=10,
... max_depth=1).fit(iris.data, iris.target)
>>> features = [3, 2, (3, 2)]
>>> fig, axs = plot_partial_dependence(mc_clf, X, features, label=0)
如果你需要部分依赖函数的原始值而不是图,你可以调用 partial_dependence
函数:
>>>
代码语言:javascript复制>>> from sklearn.ensemble.partial_dependence import partial_dependence
>>> pdp, axes = partial_dependence(clf, [0], X=X)
>>> pdp
array([[ 2.46643157, 2.46643157, ...
>>> axes
[array([-1.62497054, -1.59201391, ...
该函数允许通过 grid
参数指定应该评估部分依赖函数的的目标特征值或通过 X
参数设置从训练数据中自动创建 grid
的便利模式.如果 X
被给出,函数返回的 axes
为每个目标特征提供轴.
对于 grid
中的每一个’目标’特征值,部分依赖函数需要边缘化一棵树中所有候选特征的可能值的预测. 在决策树中,这个函数可以在不参考训练数据的情况下被高效的评估,对于每一网格点执行加权遍历: 如果切分点包含’目标’特征,遍历其相关的左分支或相关的右分支,否则就遍历两个分支.每一个分支将被通过进入该分支的训练样本的占比加权, 最后,部分依赖通过所有访问的叶节点的权重的平均值给出.组合树(tree ensembles)的整体结果,需要对每棵树的结果再次平均得到.
注解 (Footnotes)
[1] | For classification with loss='deviance' the target response is logit(p). |
---|
[2] | More precisely its the expectation of the target response after accounting for the initial model; partial dependence plots do not include the init model. |
---|
示例 (Examples):
- Partial Dependence Plots
参考 (References)
1.11.5. 投票分类器 (Voting Classifier)
VotingClassifier
(投票分类器)的原理是结合了多个不同的机器学习分类器,并且采用多数表决(majority vote)或者平均预测概率(软投票)的方式来预测分类标签. 这样的分类器可以用于一组同样表现良好的模型,以便平衡它们各自的弱点.
1.11.5.1. 多数类标签 (又称为,多数/硬投票)
在多数投票中,特定样本的预测类别标签是表示单独分类器预测的类别标签中票数占据多数(模式)的类别标签。
例如, 如果给定样本的预测是
- classifier 1 -> class 1
- classifier 2 -> class 1
- classifier 3 -> class 2
类别 1 占据多数,通过 voting='hard'
参数设置投票分类器为多数表决方式,会得到该样本的预测结果是类别 1.
在平局的情况下,投票分类器(VotingClassifier)将根据升序排序顺序选择类标签。 例如,场景如下:
- classifier 1 -> class 2
- classifier 2 -> class 1
这种情况下,class 1 将会被指定为该样本的类标签.
1.11.5.1.1. 用法 (Usage)
以下示例显示如何拟合多数规则分类器:
>>>
代码语言:javascript复制>>> from sklearn import datasets
>>> from sklearn.model_selection import cross_val_score
>>> from sklearn.linear_model import LogisticRegression
>>> from sklearn.naive_bayes import GaussianNB
>>> from sklearn.ensemble import RandomForestClassifier
>>> from sklearn.ensemble import VotingClassifier
>>>
代码语言:javascript复制>>> iris = datasets.load_iris()
>>> X, y = iris.data[:, 1:3], iris.target
>>>
代码语言:javascript复制>>> clf1 = LogisticRegression(random_state=1)
>>> clf2 = RandomForestClassifier(random_state=1)
>>> clf3 = GaussianNB()
>>>
代码语言:javascript复制>>> eclf = VotingClassifier(estimators=[('lr', clf1), ('rf', clf2), ('gnb', clf3)], voting='hard')
>>>
代码语言:javascript复制>>> for clf, label in zip([clf1, clf2, clf3, eclf], ['Logistic Regression', 'Random Forest', 'naive Bayes', 'Ensemble']):
... scores = cross_val_score(clf, X, y, cv=5, scoring='accuracy')
... print("Accuracy: %0.2f ( /- %0.2f) [%s]" % (scores.mean(), scores.std(), label))
Accuracy: 0.90 ( /- 0.05) [Logistic Regression]
Accuracy: 0.93 ( /- 0.05) [Random Forest]
Accuracy: 0.91 ( /- 0.04) [naive Bayes]
Accuracy: 0.95 ( /- 0.05) [Ensemble]
1.11.5.2. 加权平均概率 (软投票)
与大多数投票(硬投票)相比,软投票将类别标签返回为预测概率之和的 argmax.
具体的权重可以通过权重参数 weights
分配给每个分类器.当提供权重参数 weights
时,收集每个分类器的预测分类概率, 乘以分类器权重并取平均值.然后从具有最高平均概率的类别标签导出最终类别标签.
为了用一个简单的例子来说明这一点,假设我们有3个分类器和一个3类分类问题,我们给所有分类器赋予相等的权重:w1 = 1,w2 = 1,w3 = 1.
样本的加权平均概率计算如下:
分类器 | 类别 1 | 类别 2 | 类别 3 |
---|---|---|---|
分类器 1 | w1 * 0.2 | w1 * 0.5 | w1 * 0.3 |
分类器 2 | w2 * 0.6 | w2 * 0.3 | w2 * 0.1 |
分类器 3 | w3 * 0.3 | w3 * 0.4 | w3 * 0.3 |
加权平均的结果 | 0.37 | 0.4 | 0.23 |
这里可以看出,预测的类标签是 2,因为它具有最大的平均概率.
下边的示例程序说明了当软投票分类器(soft VotingClassifier)是基于线性支持向量机(linear SVM)、决策树(Decision Tree)、K近邻(K-nearest )分类器时,决策域可能如何变化:
>>>
代码语言:javascript复制>>> from sklearn import datasets
>>> from sklearn.tree import DecisionTreeClassifier
>>> from sklearn.neighbors import KNeighborsClassifier
>>> from sklearn.svm import SVC
>>> from itertools import product
>>> from sklearn.ensemble import VotingClassifier
>>>
代码语言:javascript复制>>> # Loading some example data
>>> iris = datasets.load_iris()
>>> X = iris.data[:, [0,2]]
>>> y = iris.target
>>>
代码语言:javascript复制>>> # Training classifiers
>>> clf1 = DecisionTreeClassifier(max_depth=4)
>>> clf2 = KNeighborsClassifier(n_neighbors=7)
>>> clf3 = SVC(kernel='rbf', probability=True)
>>> eclf = VotingClassifier(estimators=[('dt', clf1), ('knn', clf2), ('svc', clf3)], voting='soft', weights=[2,1,2])
>>>
代码语言:javascript复制>>> clf1 = clf1.fit(X,y)
>>> clf2 = clf2.fit(X,y)
>>> clf3 = clf3.fit(X,y)
>>> eclf = eclf.fit(X,y)
1.11.5.3. 投票分类器(VotingClassifier )在网格搜索(GridSearch)应用
为了调整每个估计器的超参数,`VotingClassifier` 也可以和 GridSearch 一起使用:
>>>
代码语言:javascript复制>>> from sklearn.model_selection import GridSearchCV
>>> clf1 = LogisticRegression(random_state=1)
>>> clf2 = RandomForestClassifier(random_state=1)
>>> clf3 = GaussianNB()
>>> eclf = VotingClassifier(estimators=[('lr', clf1), ('rf', clf2), ('gnb', clf3)], voting='soft')
>>> params = {'lr__C': [1.0, 100.0], 'rf__n_estimators': [20, 200],}
>>> grid = GridSearchCV(estimator=eclf, param_grid=params, cv=5)
>>> grid = grid.fit(iris.data, iris.target)
1.11.5.3.1. 用法 (Usage)
为了基于预测的类别概率预测类别标签(投票分类器中的 scikit-learn estimators 必须支持 predict_proba
方法):
>>>
代码语言:javascript复制>>> eclf = VotingClassifier(estimators=[('lr', clf1), ('rf', clf2), ('gnb', clf3)], voting='soft')
可选地,也可以为单个分类器提供权重:
>>>
代码语言:javascript复制>>> eclf = VotingClassifier(estimators=[('lr', clf1), ('rf', clf2), ('gnb', clf3)], voting='soft', weights=[2,5,1])
中文文档: http://sklearn.apachecn.org/cn/stable/modules/ensemble.html
英文文档: http://sklearn.apachecn.org/en/stable/modules/ensemble.html
官方文档: http://scikit-learn.org/stable/
GitHub: https://github.com/apachecn/scikit-learn-doc-zh(觉得不错麻烦给个 Star,我们一直在努力)
贡献者: https://github.com/apachecn/scikit-learn-doc-zh#贡献者
关于我们: http://www.apachecn.org/organization/209.html
有兴趣的们也可以和我们一起来维护,持续更新中 。。。
机器学习交流群: 629470233