随机森林与集成算法

决策树:

使用决策树算法,咱们从树根开始,基于可得到最大信息增益(information gain,IG)的特征来对数据进行划分,咱们将在下一节详细介绍信息增益的概念。
经过迭代处理,在每一个子节点上重复此划分过程,直到叶子节点。这意味着在每个节点处,全部的样本都属于同一类别。
在实际应用中,这可能会致使生成一棵深度很大且拥有众多节点的树,这样容易产生过拟合问题,由此,咱们通常经过对树进行“剪枝”来限定树的最大深度。

最大化信息增益——获知尽量准确的结果
为了在可得到最大信息增益的特征处进行节点划分,须要定义一个可经过树学习算法进行优化的目标函数。
在此,目标函数可以在每次划分时实现对信息增益的最大化。node

信息增益只不过是父节点的不纯度与全部子节点不纯度总和之差——子节点的不纯度越低,信息增益越大。
然而,出于简化和缩小组合搜索空间的考虑,大多数(包括scikit-learn)库中实现的都是二叉决策树。
这意味着每一个父节点被划分为两个子节点:Dleft和Dright。

就目前来讲,二叉决策树中经常使用的三个不纯度衡量标准或划分标准分别是:
基尼系数(Gini index,IG)、熵(entropy,IH),以及误分类率(classification error,IE)。
熵:
若是某一节点中全部的样本都属于同一类别,则其熵为0,当样本以相同的比例分属于不一样的类时,熵的值最大。
对二类别分类来讲,当p(i=1|t)=1或p(i=0|t)=0时,熵为0,若是类别均匀分布,即p(i=1|t)=0.5且p(i=1|t)=0.5时,熵为1。
在决策树中熵的准则就是使得互信息最大化。
基尼系数
能够理解为下降误分类可能性的标准
与熵相似,当全部类别是等比例分布时,基尼系数的值最大
# 在实践中,基尼系数与熵一般会生成很是相似的结果,并不值得花费大量时间使用不纯度标准评估树的好坏,而一般尝试使用不一样的剪枝算法。
误分类率:
这是一个对于剪枝方法颇有用的准则,可是不建议用于决策树的构建过程,由于它对节点中各种别样本数量的变更不敏感。
使用误分类率获得的信息增益都是相同,在使用基尼系数时,与划分A(IGG=0.125)相比,更倾向于使用B(IGG=0.16)的划分,
由于这样子节点处的类别纯度相对更高
熵的纯度系统最高
那么你到底应该使用基尼不纯度仍是信息熵呢?其实,大多数状况下,它们并无什么大的不一样,产生的树都很类似。
基尼不纯度的计算速度略微快一些,因此它是个不错的默认选择。
它们的不一样在于,基尼不纯度倾向于从树枝中分裂出最多见的类别,而信息熵则倾向于生产更平衡的树。算法

 

决策树能够经过将特征空间进行矩形划分的方式来构建复杂的决策边界。然而,必须注意:深度越大的决策树,决策边界也就越复杂,于是很容易产生过拟合现象。
scikit-learn一个吸引人的功能就是,它容许将训练后获得的决策树导出为.dot格式的文件,这使得咱们可使用GraphViz程序进行可视化处理。
经过观察GraphViz建立的图像,能够很好地回溯决策树在训练数据集上对各节点进行划分的过程。
from sklearn.tree import export_graphviz
export_graphviz(
tree_clf,
out_file=image_path("iris_tree.dot"),
feature_names=iris.feature_names[2:],
class_names=iris.target_names,
rounded=True,
filled=True
)
dot -Tpng iris_tree.dot -o iris_tree.png

决策树的众多特性之一就是, 它不须要太多的数据预处理, 尤为是不须要进行特征的缩放或者归一化。

估计分类几率:
决策树还能够估计某个实例属于特定类 k 的几率:首先遍历树来查找此实例的叶节点,而后它返回此节点中类 k 的训练实例的比例。

CART 训练算法:
Scikit-Learn 用分裂回归树(Classification And Regression Tree,简称 CART)算法训练决策树(也叫“增加树”)。
这种算法思想真的很是简单:首先使用单个特征 k 和阈值 (例如,“花瓣长度 ≤2.45cm ”)将训练集分红两个子集。
它如何选择 k 和 呢?它寻找到可以产生最纯粹的子集一对 ,而后经过子集大小加权计算。

当它成功的将训练集分红两部分以后, 它将会继续使用相同的递归式逻辑继续的分割子集,而后是子集的子集。
当达到预约的最大深度以后将会中止分裂(由 max_depth 超参数决定),或者是它找不到能够继续下降不纯度的分裂方法的时候。
几个其余超参数(以后介绍)控制了其余的中止生长条件。

CART 算法是一种贪婪算法:它贪婪地搜索最高级别的最佳分割方式,而后在每一个深度重复该过程。
它不检查分割是否可以在几个级别中的所有分割可能中找到最佳方法。贪婪算法一般会产生一个至关好的解决方法,但它不保证这是全局中的最佳解决方案。

而不幸的是,寻找最优树是一个已知的NP彻底问题:[1]须要的时间是O(exp(m)),因此即便是很小的训练集,也至关棘手。
这就是为何咱们必须接受一个“至关不错”的解。bootstrap

 

计算复杂度:
进行预测须要从根到叶遍历决策树。一般来讲,决策树大体平衡,所以遍历决策树须要经历大约O(log2(m))个节点。(注:log2是以2为底的对数。
等于log2(m)=log(m)/log(2)。)而每一个节点只须要检查一个特征值,因此整体预测复杂度也只是O(log2(m)),与特征数量无关。
如此,即使是处理大型数据集,预测也很快。多线程

可是,训练时在每个节点,算法都须要在全部样本上比较全部特征(若是设置了max_features会少一些)。这致使训练的复杂度为O(n×m log(m))。
对于小型训练集(几千个实例之内),Scikit-Learn能够经过对数据预处理(设置presort=True)来加快训练,
可是对于较大训练集而言,可能会减慢训练的速度。

正则化超参数:
决策树几乎不对训练数据作任何假设(于此相反的是线性回归等模型,这类模型一般会假设数据是符合线性关系的)。
若是不添加约束,树结构模型一般将根据训练数据调整本身,使自身可以很好的拟合数据,而这种状况下大多数会致使模型过拟合。

这一类的模型一般会被称为非参数模型,这不是由于它没有任何参数(一般也有不少),而是由于在训练以前没有肯定参数的具体数量,
因此模型结构能够根据数据的特性自由生长。

与此相反的是,像线性回归这样的参数模型有事先设定好的参数数量,因此自由度是受限的,这就减小了过拟合的风险(可是增长了欠拟合的风险)。

DecisionTreeClassifier 类还有一些其余的参数用于限制树模型的形状:
min_samples_split (节点在被分裂以前必须具备的最小样本数)
min_samples_leaf (叶节点必须具备的最小样本数)
min_weight_fraction_leaf (和 min_samples_leaf 相同,但表示为加权总数的一小部分实例)
max_leaf_nodes (叶节点的最大数量)
max_features (在每一个节点被评估是否分裂的时候,具备的最大特征数量)。

增长 min_* hyperparameters 或者减小 max_* hyperparameters 会使模型正则化。dom

一些其余算法的工做原理是在没有任何约束条件下训练决策树模型,让模型自由生长,而后再对不须要的节点进行剪枝。
当一个节点的所有子节点都是叶节点时,若是它对纯度的提高不具备统计学意义,咱们就认为这个分支是没必要要的。
标准的假设检验,例如卡方检测,一般会被用于评估一个几率值 -- 即改进是否纯粹是偶然性的结果(也叫原假设)。

若是 p 值比给定的阈值更高(一般设定为 5%,也就是 95% 置信度,经过超参数设置),那么节点就被认为是非必要的,它的子节点会被删除。
这种剪枝方式将会一直进行,直到全部的非必要节点都被删光。机器学习

 

回归:
决策树也可以执行回归任务,让咱们使用 Scikit-Learn 的 DecisionTreeRegressor 类构建一个回归树,
让咱们用 max_depth = 2 在具备噪声的二次项数据集上进行训练。
from sklearn.tree import DecisionTreeRegressor
tree_reg = DecisionTreeRegressor(max_depth=2)
tree_reg.fit(X, y)

这棵树看起来很是相似于你以前创建的分类树,它的主要区别在于,它不是预测每一个节点中的样本所属的分类,而是预测一个具体的数值。

CART 算法的工做方式与以前处理分类模型基本同样,不一样之处在于,如今再也不以最小化不纯度的方式分割训练集,而是试图以最小化 MSE 的方式分割训练集。ide

和处理分类任务时同样,决策树在处理回归问题的时候也容易过拟合。

不稳定性:
决策树很喜欢设定正交化的决策边界,(全部边界都是和某一个轴相垂直的),这使得它对训练数据集的旋转很敏感。
解决这个难题的一种方式是使用 PCA 主成分分析(第八章),这样一般能使训练结果变得更好一些。

更加通俗的讲,决策时的主要问题是它对训练数据的微小变化很是敏感。

经过随机森林将弱分类器集成为强分类器:
因为具有好的分类能力、可扩展性、易用性等特色,随机森林(random forest)过去十年间在机器学习应用领域得到了普遍的关注。
直观上,随机森林能够视为多棵决策树的集成。
集成学习的基本理念就是将弱分类器集成为鲁棒性更强的模型,即一个能力更强的分类器,集成后具有更好的泛化偏差,不易产生过拟合现象。

随机森林算法能够归纳为四个简单的步骤:
1)使用bootstrap抽样方法随机选择n个样本用于训练(从训练集中随机可重复地选择n个样本)。
2)使用第1)步选定的样本构造一棵决策树。节点划分规则以下:
(1)不重复地随机选择d个特征;
(2)根据目标函数的要求,如最大化信息增益,使用选定的特征对节点进行划分。
3)重复上述过程1~2000次。
4)汇总每棵决策树的类标进行多数投票(majority vote)。

虽然随机森林没有决策树那样良好的可解释性,但其显著的优点在于没必要担忧超参值的选择。
咱们一般不须要对随机森林进行剪枝,由于相对于单棵决策树来讲,集成模型对噪声的鲁棒性更好。
在实践中,咱们真正须要关心的参数是为构建随机森林所需的决策树数量(步骤3))。
一般状况下,决策树的数量越多,随机森林总体的分类表现就越好,但这同时也相应地增长了计算成本。函数

尽管在实践中不常见,可是随机森林中可优化的其余超参分别是:bootstrap抽样的数量(步骤1))以及在节点划分中使用的特征数量(步骤(2))

经过选择bootstrap抽样中样本数量n,咱们能够控制随机森林的误差与方差权衡的问题。
若是n的值较大,就下降了随机性,由此更可能致使随机森林的过拟合。反之,咱们能够基于模型的性能,经过选择较小的n值来下降过拟合。

包括scikit-learn中RandomForestClassifier在内的大多数对随机森林的实现中,bootstrap抽样的数量通常与原始训练集中样本的数量相同,
由于这样在折中误差与方差方面通常会有一个好的均衡结果。
而对于在每次节点划分中用到的特征数量m,咱们选择一个比训练集中特征总量小的值。
在scikit-learn及其余程序实现中经常使用的默认值通常是开方,其中m是训练集中特征的总量。

forest = RandomForestClassifier(criterion='entropy',n_estimators=10,random_state=1,n_jobs=2)
或:
bag_clf = BaggingClassifier(DecisionTreeClassifier(splitter="random", max_leaf_nodes=16),n_estimators=500, max_samples=1.0, bootstrap=True,
n_jobs=-1)工具

 

集成学习——组合不一样的模型:
构建一组分类器的集合,使得总体分类效果优于其中任意一个单独的分类器。

集成学习:
集成方法(ensemble method)的目标是:将不一样的分类器组合成为一个元分类器,与包含于其中的单个分类器相比,元分类器具备更好的泛化性能。
例如:假设咱们收集到了10位专家的预测结果,集成方法容许咱们以必定策略将这10位专家的预测进行组合,与每位专家单独的预测相比,它具备更好的准确性和鲁棒性。

多数投票(majority voting)原则:
将大多数分类器预测的结果做为最终类标,也就是说,将得票率超过50%的结果做为类标。

严格来讲,多数投票仅用于二类别分类情形。不过,很容易将多数投票原则推广到多类别分类,也称做简单多数票法(plurality voting)。

基于训练集,咱们首先训练m个不一样的成员分类器(C1,…,Cm)。在多数投票原则下,可集成不一样的分类算法,如决策树、支持向量机、逻辑斯谛回归等。
此外,咱们也可使用相同的成员分类算法拟合不一样的训练子集。这种方法典型的例子就是随机森林算法,它组合了不一样的决策树分类器。

想要经过简单的多数投票原则对类标进行预测,咱们要汇总全部分类器Cj的预测类标,并选出得票率最高的类标:

为了说明集成方法的效果为什么好于单个成员分类器,咱们借用下组合学中的概念。

假定每一个分类器都是独立的,且出错率之间是不相关的。基于这些假设,咱们能够将成员分类器集成后的出错几率简单地表示为二项分布的几率密度函数
在知足全部假设的条件下,集成后的出错率(0.034)远远小于单个分类器的出错率(0.25)。

请注意,在此演示示例中,当集成分类中分类器个数n为偶数时,若预测结果为五五分,咱们则将其以错误对待,不过仅有一半的可能会出现这种状况。

为了比较成员分类器在不一样出错率的状况下与集成分类器出错率的差别,咱们用Python实现其几率密度函数:
def ensemble_error(n_classifier,error):
k_start = math.ceil(n_classifier/2.0)
probs = [comb(n_classifier,k)*error**k *(1-error)**(n_classifier-k)
for k in range(k_start,n_classifier+1)]
return sum(probs)
画图可知,当成员分类器出错率低于随机猜想时(ε<0.5),集成分类器的出错率要低于单个分类器。

硬投票分类器只是统计每一个分类器的投票,而后挑选出得票最多的类别。
若是全部的分类器都可以预测类别的几率(例如他们有一个 predict_proba() 方法),那么你就可让 sklearn 以最高的类几率来预测这个类,
平均在全部的分类器上。这种方式叫作软投票。他常常比硬投票表现的更好,由于它给予高自信的投票更大的权重。

你能够经过把 voting="hard" 设置为 voting="soft" 来保证分类器能够预测类别几率。然而这不是 SVC类的分类器默认的选项,
因此你须要把它的 probability hyperparameter 设置为 True (这会使 SVC 使用交叉验证去预测类别几率,其下降了训练速度,
但会添加 predict_proba() 方法)。
from sklearn.ensemble import VotingClassifier
voting_clf = VotingClassifier(estimators=[('lr', log_clf), ('rf', rnd_clf),('svc', svm_clf)],voting='hard')性能

 

实现一个简单的多数投票分类器:
基于多数投票原则,使用Python实现一个简单的集成分类器

集成算法容许咱们使用单独的权重对不一样分类算法进行组合。咱们的目标是构建一个更增强大的元分类器,以在特定的数据集上平衡单个分类器的弱点。

为了使用Python代码实现加权多数投票,可使用NumPy中的argmax和bincount函数:
np.argmax(np.bincount([0,0,1],weights=[0.2,0.2,0.6]))

经过predict_proba方法,scikit-learn中的分类器能够返回样本属于预测类标的几率。
若是集成分类器事先获得良好的修正,那么在多数投票中使用预测类别的几率来替代类标会很是有用。

为实现基于类别预测几率的加权多数投票,咱们能够再次使用NumPy中的numPy.average和np.argmax方法:
ex = np.array([[0.9,0.1],[0.8,0.2],[0.4,0.6]])
p = np.average(ex,axis=0,weights=[0.2,0.2,0.6])
print(np.argmax(p))

实现一个MajorityVotingClassifier,以10折交叉验证做为评估标准,MajorityVotingClassifier的性能与单个成员分类器相比有着质的提升。

评估与调优集成分类器:
在本节,咱们将在测试数据上计算MajorityVoteClassifier类的ROC曲线,以验证其在未知数据上的泛化性能。
请记住,训练数据并未应用于模型选择,使用它们的惟一目的就是对分类系统的泛化性能作出无误差的估计。
for clf,label,clr,ls in zip(all_clf,clf_labels,colors,linestyles):
y_pred = clf.fit(X_train,y_train).predict_proba(X_test)[:,1] # 测出结果
fpr, tpr, thresholds = roc_curve(y_true=y_test,y_score=y_pred)
roc_auc = auc(x=fpr,y=tpr)
plt.plot(fpr, tpr,color=clr,linestyle=ls,label="%s (auc=%0.2f)" %(label,roc_auc))

由于只使用了分类样本中的两个特征,集成分类器的决策区域究竟是什么样子可能会引发咱们的兴趣。
因为逻辑斯谛回归和k-近邻流水线会自动对数据进行预处理,所以没必要事先对训练特征进行标准化。
不过出于可视化考虑,也就是在相同的度量标准上实现决策区域,咱们在此对训练集作了标准化处理。
也如咱们所预期的那样,集成分类器的决策区域看起来像是各成员分类器决策区域的混合。

在学习集成分类的成员分类器调优以前,咱们调用一下get_param方法,以便对如何访问GridSearch对象内的单个参数有个基本的认识:

获得get_params方法的返回值后,咱们如今知道怎样去访问成员分类器的属性了。如"decisiontreeclassifier__max_depth"
出于演示的目的,先经过网格搜索来调整逻辑斯谛回归分类器的正则化系数C以及决策树的深度。

正如咱们所见,当选择的正则化强度较小时(C=100.0),咱们可以获得最佳的交叉验证结果,而决策树的深度彷佛对性能没有任何影响,
这意味着使用单层决策树足以对数据进行划分。
请注意,在模型评估时,不止一次使用测试集并不是一个好的作法,本节不打算讨论超参调优后的集成分类器泛化能力的评估。
将继续学习另一种集成方法:bagging。

咱们在本节实现的多数投票方法有时也称为堆叠(stacking)。
不过,堆叠算法更典型地应用于组合逻辑斯谛回归模型,以各独立分类器的输出做为输入,经过对这些输入结果的继承来预测最终的类标

bagging——经过bootstrap样本构建集成分类器:
# bagging是有放回的随机抽样,pasting是不放回的随机抽样,而boosting是循环训练预测器,每一次都对其前序作出一些改正。
bagging是一种与上一节实现的MajorityVoteClassifier关系紧密的集成学习技术
不过,此算法没有使用相同的训练集拟合集成分类器中的单个成员分类器。对每个分类器都使用相同的训练算法,可是在不一样的训练集上去训练它们。
因为原始训练集使用了boostrap抽样(有放回的随机抽样),这也就是bagging被称为boostrap aggregating的缘由。

在每一轮的bagging循环中,它们都被可放回随机抽样。每一个bootstrap抽样都被用于分类器Cj的训练,这就是一棵典型的未剪枝的决策树:

bagging还与咱们在第3章中曾经介绍过的随机森林紧密相关。实际上,随机森林是bagging的一个特例,它使用了随机的特征子集去拟合单棵决策。

在实战中,分类任务会更加复杂,数据集维度会更高,使用单棵决策树很容易产生过拟合,这时bagging算法就可显示出其优点了。
最后,咱们需注意bagging算法是下降模型方差的一种有效方法。
然而,bagging在下降模型误差方面的做用不大,这也是咱们选择未剪枝决策树等低误差分类器做为集成算法成员分类器的缘由。

聚合函数一般对分类是统计模式(例如硬投票分类器)或者对回归是平均。
一般状况下,集成的结果是有一个类似的误差,可是对比与在原始训练集上的单一分类器来说有更小的方差。

在sklearn中的 Bagging 和 Pasting:
sklearn 为 Bagging 和 Pasting 提供了一个简单的API: BaggingClassifier 类(或者对于回归能够是 BaggingRegressor 。
接下来的代码训练了一个 500 个决策树分类器的集成,每个都是在数据集上有放回采样 100 个训练实例下进行训练
(这是 Bagging 的例子,若是你想尝试Pasting,就设置 bootstrap=False )。
n_jobs 参数告诉 sklearn 用于训练和预测所须要 CPU核的数量。(-1 表明着 sklearn 会使用全部空闲核):
from sklearn.ensemble import BaggingClassifier
rom sklearn.tree import DecisionTreeClassifier
bag_clf = BaggingClassifier(DecisionTreeClassifier(), n_estimators=500,
max_samples=100, bootstrap=True, n_jobs=-1)
bag_clf.fit(X_train, y_train)
y_pred = bag_clf.predict(X_test)

Bootstrap 在每一个预测器被训练的子集中引入了更多的分集,因此 Bagging 结束时的误差比Pasting 更高,但这也意味着预测因子最终变得不相关,
从而减小了集合的方差。整体而言,Bagging 一般会致使更好的模型,这就解释了为何它一般是首选的。

Out-of-Bag 评价:
对于 Bagging 来讲,一些实例可能被一些分类器重复采样,但其余的有可能不会被采样。
BaggingClassifier为默认采样。BaggingClassifier 默认是有放回的采样 m 个实例( bootstrap=True ),其中 m 是训练集的大小,
这意味着平均下来只有63%的训练实例被每一个分类器采样,剩下的37%个没有被采样的训练实例就叫作 Out-of-Bag 实例。
注意对于每个的分类器它们的 37% 不是相同的。

由于在训练中分类器历来没有看到过 oob 实例,因此它能够在这些实例上进行评估,而不须要单独的验证集或交叉验证。
你能够拿出每个分类器的 oob 来评估集成自己。

在 sklearn 中,你能够在训练后须要建立一个 BaggingClassifier 来自动评估时设置 oob_score=True 来自动评估。接下来的代码展现了这个操做。
评估结果经过变量 oob_score_ 来显示:
bag_clf = BaggingClassifier(DecisionTreeClassifier(), n_estimators=500,bootstrap=True, n_jobs=-1, oob_score=True)
bag_clf.fit(X_train, y_train)
bag_clf.oob_score_ # 0.93066666666666664,再与下面测试集上的结果比较一下

根据这个 obb 评估, BaggingClassifier 能够再测试集上达到93.1%的准确率,让咱们修改一下:
from sklearn.metrics import accuracy_score
y_pred = bag_clf.predict(X_test)
accuracy_score(y_test, y_pred) # 0.93600000000000005

咱们在测试集上获得了 93.6% 的准确率,足够接近了!

对于每一个训练实例 oob 决策函数也可经过 oob_decision_function_ 变量来展现。在这种状况下(当基决策器有 predict_proba() 时)
决策函数会对每一个训练实例返回类别几率。例如,oob 评估预测第二个训练实例有 60.6% 的几率属于正类(39.4% 属于负类):
bag_clf.oob_decision_function_
array([[ 0., 1.], [ 0.60588235, 0.39411765],[ 1., 0. ],... [ 1. , 0. ],[ 0., 1.],[ 0.48958333, 0.51041667]])

随机贴片与随机子空间
BaggingClassifier 也支持采样特征。它被两个超参数 max_features 和 bootstrap_features 控制。
他们的工做方式和 max_samples 和 bootstrap 同样,但这是对于特征采样而不是实例采样。所以,每个分类器都会被在随机的输入特征内进行训练。

当你在处理高维度输入下(例如图片)此方法尤为有效。对训练实例和特征的采样被叫作随机贴片。
保留了全部的训练实例(例如 bootstrap=False 和 max_samples=1.0 ),可是对特征采样( bootstrap_features=True 而且/或者
max_features 小于 1.0)叫作随机子空间。
采样特征致使更多的预测多样性,用高误差换低方差。

随机森林:
一般用bagging(有时也多是pasting)方法训练

极端随机树
如前所述,随机森林里单棵树的生长过程当中,每一个节点在分裂时仅考虑到了一个随机子集所包含的特征。
若是咱们对每一个特征使用随机阈值,而不是搜索得出的最佳阈值(如常规决策树),则可能让决策树生长得更加随机。
这种极端随机的决策树组成的森林,被称为极端随机树集成[4](或简称Extra-Trees)。
一样,它也是以更高的误差换取了更低的方差。

极端随机树训练起来比常规随机森林要快不少,由于在每一个节点上找到每一个特征的最佳阈值是决策树生长中最耗时的任务之一。

使用Scikit-Learn的ExtraTreesClassifier能够建立一个极端随机树分类器。它的API与RandomForestClassifier相同。
同理,ExtraTreesRegressor与RandomForestRegressor的API也相同。
一般来讲,很难预先知道一个RandomForestClassifier是否会比一个ExtraTreesClassifier更好或是更差。
惟一的方法是两种都尝试一遍,而后使用交叉验证(还须要使用网格搜索调整超参数)进行比较。

提高法:
提高法(Boosting,最初被称为假设提高)是指能够将几个弱学习器结合成一个强学习器的任意集成方法。
大多数提高法的整体思路是循环训练预测器,每一次都对其前序作出一些改正。

在boosting中,集成分类器包含多个很是简单的成员分类器,这些成员分类器性能仅稍好于随机猜想,常被称做弱学习机。
典型的弱学习机例子就是单层决策树。boosting主要针对难以区分的训练样本,也就是说,弱学习机经过在错误分类样本上的学习来提升集成分类的性能。
与bagging不一样,在boosting的初始化阶段,算法使用无放回抽样从训练样本中随机抽取一个子集。

原始的boosting过程可总结为以下四个步骤:
1)从训练集D中以无放回抽样方式随机抽取一个训练子集d1,用于弱学习机C1的训练。
2)从训练集中以无放回抽样方式随机抽取第2个训练子集d2,并将C1中误分类样本的50%加入到训练集中,训练获得弱学习机C2。
3)从训练集D中抽取C1和C2分类结果不一致的样本生成训练样本集d3,以此训练第3个弱学习机C3。
4)经过多数投票组合三个弱学习机C一、C2和C3。

与bagging模型相比,boosting可同时下降误差和方差。在实践中,boosting算法(如AdaBoost)也存在明显的高方差问题,也就是说,对训练数据有过拟合倾向。

AdaBoost:
在本节对集成方法的介绍中,咱们将重点讨论boosting算法中一个经常使用例子:AdaBoost(Adaptive Boosting的简称)。

AdaBoost与这里讨论的原始boosting过程不一样,它使用整个训练集来训练弱学习机,其中训练样本在每次迭代中都会从新被赋予一个权重,
在上一弱学习机错误的基础上进行学习进而构建一个更增强大的分类器。

新预测器对其前序进行纠正的办法之一,就是更多地关注前序拟合不足的训练实例。
从而使新的预测器不断地愈来愈专一于难缠的问题,这就是AdaBoost使用的技术。

过程:
1.首先须要训练一个基础分类器(好比决策树),用它对训练集进行预测。
2.而后计算该预测器的加权偏差率rj,计算该预测器权重αj,更新实例权重,对错误分类的训练实例增长其相对权重w
# 实例权重w用来衡量决策树更新权重w时,该实例对权重w的更新系数大小,影响每一个实例的贡献(因此只能单个训练?两个for循环)
# 乘以一个 实例权重 w 矩阵? 相似于批量训练的 预测结果最后平均出一个值来更新权重 w,平均的时候先乘以一个实例权重矩阵便可。
预测器的准确率越高,其权重就越高。若是它只是随机猜想,则其权重接近于零。
可是,若是大部分状况下它都是错的(也就是准确率比随机猜想还低),那么它的权重为负。
3.使用这个最新的权重对第二个分类器进行训练,而后再次对训练集进行预测,继续更新权重,并不断循环向前。
4.当到达所需数量的预测器,或获得完美的预测器时,算法中止。
5.预测时简单地计算全部预测器的预测结果,并使用预测器权重αj对它们进行加权。

算法步骤:
1.以等值方式为权重向量w赋值,总和为1
2.在m轮的boosting操做中,对第j轮作以下操做
训练一个加权的弱学习机
预测样本类标
计算权重错误率,正确为1,错误为0
计算相关系数αj
更新权重,计算正确会被下降权重,错误会增长
归一化权重,总为1
3.完成最终的预测。

学习率越低,波动会越小。

AdaBoost这种依序循环的学习技术跟梯度降低有一些殊途同归之处,差异只在于——再也不是调整单个预测器的参数使成本函数最小化,
而是不断在集成中加入预测器,使模型愈来愈好。

一旦所有预测器训练完成,集成总体作出预测时就跟bagging或pasting方法同样了,除非预测器有不一样的权重,由于它们总的准确率是基于加权后的训练集。

序列学习技术的一个重要的缺点就是:它不能被并行化(只能按步骤),由于每一个分类器只能在以前的分类器已经被训练和评价后再进行训练。
所以,它不像Bagging和Pasting同样。

sklearn 一般使用 Adaboost 的多分类版本 SAMME(这就表明了 分段加建模使用多类指数损失函数)。
若是只有两类别,那么 SAMME 是与 Adaboost 相同的。若是分类器能够预测类别几率(例如若是它们有 predict_proba() ),
若是 sklearn 可使用 SAMME 叫作 SAMME.R 的变量(R 表明“REAL”),这种依赖于类别几率的一般比依赖于分类器的更好。
from sklearn.ensemble import AdaBoostClassifier
tree = DecisionTreeClassifier(criterion="entropy",max_depth=1)
ada = AdaBoostClassifier(base_estimator=tree,n_estimators=500,learning_rate=0.1,random_state=0)

能够发现,与单层决策树相比,AdaBoost分类器可以些许提升分类性能,而且与上一节中训练的bagging分类器的准确率接近。
不过请注意:重复使用测试集进行模型选择不是一个好的方法。集成分类器的泛化性能可能会被过分优化。

若是你的AdaBoost集成过分拟合训练集,你能够试试减小估算器数量,或是提升基础估算器的正则化程度。

对集成技术作一个总结:毋庸置疑,与单独分类器相比,集成学习提升了计算复杂度。
但在实践中,咱们需仔细权衡是否愿意为适度提升预测性能而付出更多的计算成本。

 

梯度提高:
另外一个很是著名的提高算法是梯度提高。与 Adaboost 同样,梯度提高也是经过向集成中逐步增长分类器运行的,每个分类器都修正以前的分类结果。
然而,它并不像 Adaboost 那样每一次迭代都更改实例的权重,而是让新的预测器针对前一个预测器的残差进行拟合。

咱们来看一个简单的回归示例,使用决策树做为基础预测器(梯度提高固然也适用于回归任务),这被称为梯度树提高或者是梯度提高回归树(GBRT)。
tree_reg1 = DecisionTreeRegressor(max_depth=2)
tree_reg1.fit(X, y)
如今在第一个分类器的残差上训练第二个分类器:
y2 = y - tree_reg1.predict(X) # 预测结果误差
tree_reg2 = DecisionTreeRegressor(max_depth=2)
tree_reg2.fit(X, y2)
随后在第二个分类器的残差上训练第三个分类器:
y3 = y2 - tree_reg1.predict(X)
tree_reg3 = DecisionTreeRegressor(max_depth=2)
tree_reg3.fit(X, y3)
如今咱们有了一个包含三个回归器的集成。它能够经过集成全部树的预测来在一个新的实例上进行预测。
y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3)) # 求和

可使用 sklean 中的 GradientBoostingRegressor 来训练 GBRT 集成。
与 RandomForestClassifier 类似,它也有超参数去控制决策树的生长(例如 max_depth , min_samples_leaf 等等),
也有超参数去控制集成训练,例如基分类器的数量( n_estimators )。
接下来的代码建立了与以前相同的集成:
from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0)
gbrt.fit(X, y)

超参数learning_rate对每棵树的贡献进行缩放。若是你将其设置为低值,好比0.1,则须要更多的树来拟合训练集,可是预测的泛化效果一般更好。
这是一种被称为收缩的正则化技术。

要找到树的最佳数量,可使用早期中止法(参见第4章)。简单的实现方法就是使用staged_predict()方法:
它在训练的每一个阶段(一棵树时,两棵树时,等等)都对集成的预测返回一个迭代器(用于测量每一个训练阶段的验证偏差,从而找到树的最优数量)。
如下代码训练了一个拥有120棵树的GBRT集成,而后测量每一个训练阶段的验证偏差,从而找到树的最优数量,最后使用最优树数从新训练了一个GBRT集成:
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=120)
gbrt.fit(X_train, y_train)
errors = [mean_squared_error(y_val, y_pred) for y_pred in gbrt.staged_predict(X_test)] # 预测器的数量
bst_n_estimators = np.argmin(errors)
gbrt_best = GradientBoostingRegressor(max_depth=2,n_estimators=bst_n_estimators)

实际上,要实现早期中止法,不必定须要先训练大量的树,而后再回头找最优的数字,还能够真的提早中止训练。
设置warm_start=True,当fit()方法被调用时,Scikit-Learn会保留现有的树,从而容许增量训练。
如下代码会在验证偏差连续5次迭代未改善时,直接中止训练:
gbrt = GradientBoostingRegressor(max_depth=2, warm_start=True)
for n_estimators in range(1, 120):
gbrt.n_estimators = n_estimators # 调整数量,继续fit
gbrt.fit(X_train, y_train) #
y_pred = gbrt.predict(X_val)
val_error = mean_squared_error(y_val, y_pred)
if val_error < min_val_error:
min_val_error = val_error
error_going_up = 0
else:
error_going_up += 1
if error_going_up == 5:
break # early stopping
GradientBoostingRegressor类还能够支持超参数subsample,指定用于训练每棵树的实例的比例。
例如,若是subsample=0.25,则每棵树用25%的随机选择的实例进行训练。如今你能够猜到,这也是用更高的误差换取了更低的方差
# 模型下降了过拟合,可能会欠拟合,方差减少,误差增大(过拟合至少训练集表现好,模式复杂,多个样本集来讲误差较小)
同时在至关大的程度上加速了训练过程。这种技术被称为随机梯度提高。

梯度提高也可使用其余成本函数,经过超参数loss来控制(有关更多详细信息,请参阅Scikit-Learn的文档)。

XGBoost
XGBoost是2014年2月诞生的专一于梯度提高算法的机器学习函数库,此函数库因其优良的学习效果以及高效的训练速度而得到普遍的关注。
XGBoost实现的是一种通用的Tree Boosting算法,此算法的一个表明为梯度提高决策树(Gradient Boosting Decision Tree, GBDT),
又名MART(Multiple Additive Regression Tree)。

GBDT的核心在于后面的树拟合的是前面预测值的残差,这样能够一步步逼近真值。然而,之因此拟合残差能够逼近到真值,是由于使用了平方损失做为损失函数。
若是换成是其余损失函数,使用残差将再也不可以保证逼近真值。

XGBoost的方法是,将损失函数作泰勒展开到第二阶,使用前两阶做为改进的残差。
能够证实,传统GBDT使用的残差是泰勒展开到一阶的结果,所以,GBDT是XGBoost的一个特例。

支持列抽样。

在决策树中,模型复杂度体如今树的深度上。XGBoost使用了一种替代指标,即叶子节点的个数。
此外,与许多其余机器学习模型同样,XGBoost也加入了L2正则项,来平滑各叶子节点的预测值。

速度快的缘由:
1.连续型特征的处理,只枚举若干个分位点,例如将全部样本根据此特征进行排序,而后均分10份,两份之间断开的数值即为分位点,
枚举全部9个分位点后,使用下降损失最多的那个做为分叉点。
2. 利用数据稀疏性
数据稀疏有三个缘由:缺失数据;某些特征自己就含有大量的0;对离散特征作了one-hot处理。
每次分叉时,都指定一条默认分支,若是样本的这个特征为0,就走这个默认分支。
3.数据的预排序和分块存储
分叉的时候为了判断分叉点,须要对每一个特征进行排序。这个步骤是要重复屡次的,
所以XGBoost在训练以前预先对数据进行每一列作了排序,并按列存储到内存中。
4.减小读写相关,提升Cache命中率
5.数据量大时,提升硬盘吞吐率
示例:
import xgboost as xgb
xlf = xgb.XGBClassifier() # 是xgboost的sklearn包,可使用sklearn的utils如Grid Search等,利用函数参数设置模型参数
xlf.fit(X_train, y_train) # 内部调用原始xgboost的xgb.train(),原始利用param列表设置模型参数
preds = xlf.predict(X_test)

LightGBM: 目前已有的 GBDT 工具基本都是基于预排序的方法(pre-sorted)的决策树算法(如 xgboost), 而LightGBM是基于 Histogram 的决策树算法,带深度限制的Leaf-wise的叶子生长策略,直方图作差加速,直接支持类别特征(Categorical Feature), Cache命中率优化,基于直方图的稀疏特征优化 1.Histogram 算法 直方图算法的基本思想是先把连续的浮点特征值离散化成k个整数,同时构造一个宽度为k的直方图。 在遍历数据的时候,根据离散化后的值做为索引在直方图中累积统计量,当遍历一次数据后,直方图累积了须要的统计量, 而后根据直方图的离散值,遍历寻找最优的分割点。 减小了内存,直方图算法不只不须要额外存储预排序的结果,并且能够只保存特征离散化后的值。 在计算上的代价也大幅下降,预排序算法每遍历一个特征值就须要计算一次分裂的增益,而直方图算法只须要计算k次 固然,Histogram 算法并非完美的。因为特征被离散化后,找到的并非很精确的分割点,因此会对结果产生影响。 但在不一样的数据集上的结果代表,离散化的分割点对最终的精度影响并非很大,甚至有时候会更好一点。 2.带深度限制的 Leaf-wise 的叶子生长策略 它抛弃了大多数 GBDT 工具使用的按层生长 (level-wise) 的决策树生长策略,而使用了带有深度限制的按叶子生长 (leaf-wise) 算法。 Level-wise 过一次数据能够同时分裂同一层的叶子,容易进行多线程优化,也好控制模型复杂度,不容易过拟合。 但实际上 Level-wise 是一种低效的算法,由于它不加区分的对待同一层的叶子,带来了不少不必的开销,由于实际上不少叶子的分裂增益较低, 不必进行搜索和分裂。 Leaf-wise 则是一种更为高效的策略,每次从当前全部叶子中,找到分裂增益最大的一个叶子,而后分裂,如此循环。 所以同 Level-wise 相比,在分裂次数相同的状况下,Leaf-wise 能够下降更多的偏差,获得更好的精度。 Leaf-wise 的缺点是可能会长出比较深的决策树,产生过拟合。 所以 LightGBM 在 Leaf-wise 之上增长了一个最大深度的限制,在保证高效率的同时防止过拟合。 3.直方图加速 一个叶子的直方图能够由它的父亲节点的直方图与它兄弟的直方图作差获得。 一般构造直方图,须要遍历该叶子上的全部数据,但直方图作差仅需遍历直方图的k个桶。 利用这个方法,LightGBM 能够在构造一个叶子的直方图后,能够用很是微小的代价获得它兄弟叶子的直方图,在速度上能够提高一倍。 4.直接支持类别特征​ 实际上大多数机器学习工具都没法直接支持类别特征,通常须要把类别特征,转化到多维的0/1 特征,下降了空间和时间的效率。 而类别特征的使用是在实践中很经常使用的。基于这个考虑,LightGBM 优化了对类别特征的支持,能够直接输入类别特征,不须要额外的0/1 展开。 并在决策树算法上增长了类别特征的决策规则。在 Expo 数据集上的实验,相比0/1 展开的方法,训练速度能够加速 8 倍,而且精度一致。 据咱们所知,LightGBM 是第一个直接支持类别特征的 GBDT 工具。 超参: 1.核心参数 - boosting_type,默认gbdt,可选rf,dart,goss gbdt, 传统的梯度提高决策树 rf, Random Forest (随机森林) dart, Dropouts meet Multiple Additive Regression Trees goss, Gradient-based One-Side Sampling (基于梯度的单侧采样) - learning_rate 默认设置成0.1 - n_estimators - num_leaves 调整 num_leaves 的取值时, 应该让其小于 2^(max_depth) - n_jobs - device cpu, gpu 2.控制参数: - max_depth - min_child_samples 默认20,一个叶子上数据的最小数量. 能够用来处理过拟合.对于大数据来讲几百或几千 - min_child_weight,0.001,一个叶子上的最小 hessian 和. 相似于 min_data_in_leaf, 能够用来处理过拟合. - colsample_bytree 1.0,加速训练,处理过拟合 - subsample 1.0,将在不进行重采样的状况下随机选择部分数据 - subsample_freq 0 bagging 的频率, 0 意味着禁用 bagging. k 意味着每 k 次迭代执行bagging - early_stopping_round 若是一个验证集的度量在 early_stopping_round 循环中没有提高, 将中止训练 - reg_alpha L1 正则 - reg_lambda L2 正则 - min_split_gain 0,执行切分的最小增益 - subsample_for_bin:用来构建直方图的数据的数量,在设置更大的数据时, 会提供更好的培训效果, 但会增长数据加载时间。若是数据很是稀疏, 则将其设置为更大的值 3.IO参数 - max_bin 默认255,值越大,准确率越高,可能会过拟合 - save_binary 若是设置为 true LightGBM 则将数据集(包括验证数据)保存到二进制文件中。 能够加快数据加载速度。 - ignore_column ignore_column=0,1,2 - categorical_feature categorical_feature=0,1,2 示例: import lightgbm as lgb lf = lgb.LGBMClassifier() # 相似,封装了sklearn lf.fit()

相关文章
相关标签/搜索