集成学习:聚合一组预测器的预测结果,比最好的单个预测器要好。
随机森林:训练一组决策树分类器,每一棵树都基于训练集不同的随机子集进行训练,将所有预测树的结果中最多的类别作为预测结果。
硬投票分类器:聚合每个分类器的预测,将得票最多的结果作为预测类别。
集成学习的效果好于单个弱分类器的原因:大数定理。
假设创建了一个包含1000个分类器的集成,每个分类器都只有51%的几率是正确的(弱分类器),如果你以大多数投票的类别作为预测结果,你可以期待的准确率是75%。
其前提是:所有的分类器都是完全独立的,彼此的错误毫不相关。
当预测器尽可能互相独立时,集成方法的效果最优。获得多种分类器的方法之一是使用不同的算法训练分类器,这会增加他们犯不同类型的错误的机会,从而提升集成的准确率。
训练一个投票分类器,由三种不同的分类器组成:
#聚合一组预测器的预测结果,得到的最终结果好于单个预测器。
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
log_clf=LogisticRegression()
rnd_clf=RandomForestClassifier()
svm_clf=SVC()
voting_clf=VotingClassifier(
estimators=[('lr',log_clf),('rf',rnd_clf),('svc',svm_clf)],
voting='hard'
)
from sklearn.datasets import make_moons
x,y=make_moons(n_samples=1000,noise=0.3)
voting_clf.fit(x,y)
数据集可视化:
import matplotlib.pyplot as plt
plt.plot(x[:,0],x[:,1],'b.')
from sklearn.metrics import accuracy_score
x_test,y_test=make_moons(n_samples=1000)
for clf in (log_clf,rnd_clf,svm_clf,voting_clf):
clf.fit(x,y)
y_pred=clf.predict(x_test)
print(clf.__class__.__name__,accuracy_score(y_test,y_pred))
输出为:
LogisticRegression 0.867
RandomForestClassifier 0.988
SVC 0.978
VotingClassifier 0.974
软投票法:如果所有分类器都可以估算出类别的概率,那么可以将概率在所有单个分类器上平均,然后将平均概率最高的类别作为预测结果。
前面提到,获得不同种类的分类器的方法之一是使用不同的训练算法,另一种方法是每个预测器使用的算法相同,但是在不同的训练集随机子集上进行训练。
采样时如果将样本放回,则是bagging(bootstrap aggregating的缩写),采样时不放回叫作pasting.
与单个弱分类器相比,集成的偏差和方差都更低。
以下代码训练一个包含500个决策树分类器的集成,每次随机从数据集中采样100个训练实例进行训练,然后放回(bagging示例,如果想用pasting,只需要设置bootstrap=False.)
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
bag_clf=BaggingClassifier(
DecisionTreeClassifier(),n_estimators=500,
max_samples=100,bootstrap=True,n_jobs=-1
)
bag_clf.fit(x,y)
y_pred=bag_clf.predict(x_test)
print(clf.__class__.__name__,accuracy_score(y_test,y_pred))
输出:
VotingClassifier 0.971
注:如果基础分类器可以估算类别概率,则BaggingClassifier自动执行的是软投票法。
由于bagging算法采用有放回的抽样方式(自助采样法),假设训练集有m个样本,每次抽取一个后放回,直到抽到m个样本,那么样本始终没有被抽到的概率为 ( 1 − 1 m ) m (1-\frac{1}{m})^m (1−m1)m,取极限得:37%
这意味着对于每一个分类器大约有36.8%的样本没有用于训练,这样的样本称为包外(oob)实例,可以使用这些实例进行评估,而不单独的验证集或交叉验证。在Scikit-learn中只需要设置参数oob_score=True即可使用这种方法估计。
#包外评估
bag_clf=BaggingClassifier(
DecisionTreeClassifier(),n_estimators=500,
bootstrap=True,n_jobs=-1,oob_score=True
)
bag_clf.fit(x,y)
bag_clf.oob_score_
输出:
0.908
这个集成的包外评估结果为90.8%,计算其在测试集上的表现:
from sklearn.metrics import accuracy_score
y_pred=bag_clf.predict(x_test)
accuracy_score(y_test,y_pred)
输出:
0.8666666666666667
抽样对象不是实例,而是特征。每个预测器使用输入特征的随机子集进行训练,保留所有实例。
对训练实例和特征都进行抽样。
随机森林是决策树的集成。可以使用RandomForestClassifier或者BaggingClassifier类生成随机森林。
训练一个拥有500棵树的随机森林分类器,每棵树限制为最多16个叶节点。
from sklearn.ensemble import RandomForestClassifier
rnd_clf=RandomForestClassifier(n_estimators=500,max_leaf_nodes=16,n_jobs=-1)
rnd_clf.fit(x,y)
y_pred_rf=rnd_clf.predict(x_test)
accuracy_score(y_test,y_pred)
输出:
0.8666666666666667
重要的特征更可能出现在靠近根节点的位置,而不重要的特征通常出现在靠近叶节点的位置,甚至根本不出现。因此,可以通过计算一个特征在森林中所有书上的平均深度,来估算其重要程度。
rnd_clf.feature_importances_ #查看特征重要性
输出:
array([0.43920445, 0.56079555])
循环训练预测器,每一次都对其前序做出一些改正。
新预测期对其前序进行纠正的办法之一,就是更多地关注前序拟合不足的训练实例,从而使新的预测器不断地越来越关注于难缠的问题。
首先训练一个基础分类器(比如决策树),用它对训练集进行预测,然后对错误分类的训练实例增加其相对权重,接着,使用这个最新的权重对第二个分类器进行训练,然后再次对训练集进行预测,继续更新权重,不断循环向前,并且把所有的基础分类器组合在一起就是AdaBoost集成。
梯度下降旨在逐渐调整单个预测期的参数使得成本函数最小化,AdaBoost集成在于不断往集成中加入预测器,使模型越来越好。
缺点:无法并行计算,扩展性不如bagging和pasting。
#adaboost分类器
from sklearn.ensemble import AdaBoostClassifier
ada_clf=AdaBoostClassifier(
DecisionTreeClassifier(max_depth=1),n_estimators=200,
algorithm='SAMME.R',learning_rate=0.5
)
ada_clf.fit(x,y)
y_pred=ada_clf.predict(x_test)
accuracy_score(y_test,y_pred)
输出:
0.86
和AdaBoost类似,也是逐步在集成中添加预测器,每一个预测器都对其前序做出改正。不同之处在于,它不是在每个迭代中调整实例权重,而是让新的预测器针对前一个预测器的残差进行拟合。
#梯度提升
from sklearn.ensemble import GradientBoostingClassifier
gbrt=GradientBoostingClassifier(max_depth=2,n_estimators=3,learning_rate=1.0)
gbrt.fit(x,y)
y_pred=gbrt.predict(x_test)
accuracy_score(y_test,y_pred)
输出:
0.86
早期停止法。
#寻找最优基础预测期个数:早期停止法
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
x_train,x_val,y_train,y_val=train_test_split(x,y)
gbrt=GradientBoostingClassifier(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_val)]
bst_n_estimators=np.argmin(errors)
gbrt_best=GradientBoostingClassifier(max_depth=2,n_estimators=bst_n_estimators)
gbrt_best.fit(x_train,y_train)
y_pred=gbrt.predict(x_test)
accuracy_score(y_test,y_pred)
输出:
0.88
早期停止法的其他实现方法:连续5次迭代验证误差未改善时,直接停止训练。
#早期停止法的其他实现:验证误差连续5次迭代未改善时,直接停止训练
gbrt=GradientBoostingClassifier(max_depth=2,warm_start=True)
min_val_error=float('inf')
error_going_up=0
for n_estimators in range(1,120):
gbrt.n_estimators=n_estimators
gbrt.fit(x_train,y_train)
y_pred=gbrt.predict(x_val)
val_error=mean_squared_error(y_val,y_pred)
if val_error
输出:
0.8667
与其使用一些简单的函数(硬投票、软投票)来聚合集成中所有预测器的预测,为什么不训练一个模型来执行这一步的聚合呢?堆叠法就是对基础预测器的预测结果进行机器学习获得最后结果。
sklearn暂不支持堆叠法。