(Bagging、Boosting、随机森林RF、AdaBoost、GBDT)
集成方法 的目标是把多个使用给定学习算法构建的基估计器的预测结果结合起来,从而获得比单个估计器更好的泛化能力/鲁棒性。集成方法 相关的知识内容可以参考
https://blog.csdn.net/luanpeng825485697/article/details/79383492
这里只讲述sklearn中如何使用集成学习。
Bagging 元估计器
# 产生样本数据集from sklearn.model_selection import cross_val_scorefrom sklearn import datasetsiris = datasets.load_iris()X, y = iris.data[:, 1:3], iris.target# ==================Bagging 元估计器=============from sklearn.ensemble import BaggingClassifierfrom sklearn.neighbors import KNeighborsClassifierbagging = BaggingClassifier(KNeighborsClassifier(),max_samples=0.5, max_features=0.5)scores = cross_val_score(bagging, X, y)print('Bagging准确率:',scores.mean())
在 scikit-learn 中,bagging 方法使用统一的 BaggingClassifier 元估计器(或者 BaggingRegressor ),输入的参数和随机子集抽取策略由用户指定。
max_samples 和 max_features 控制着子集的大小(对于样例和特征),
bootstrap 和 bootstrap_features 控制着样例和特征的抽取是有放回还是无放回的。
当使用样本子集时,通过设置 oob_score=True ,可以使用袋外(out-of-bag)样本来评估泛化精度。下面的代码片段说明了如何构造一个 KNeighborsClassifier 估计器的 bagging 集成实例,每一个基估计器都建立在 50% 的样本随机子集和 50% 的特征随机子集上。
由随机树组成的森林
sklearn.ensemble 模块包含两个基于 随机决策树 的平均算法: RandomForest 算法和 Extra-Trees 算法。
集成分类器的预测结果就是单个分类器预测结果的平均值。
# ==================决策树、随机森林、极限森林对比===============# 产生样本数据集from sklearn.model_selection import cross_val_scorefrom sklearn import datasetsiris = datasets.load_iris()X, y = iris.data[:, 1:3], iris.target# 决策树from sklearn.tree import DecisionTreeClassifierclf = DecisionTreeClassifier(max_depth=None, min_samples_split=2,random_state=0)scores = cross_val_score(clf, X, y)print('决策树准确率:',scores.mean())# 随机森林from sklearn.ensemble import RandomForestClassifierclf = RandomForestClassifier(n_estimators=10,max_features=2)clf = clf.fit(X, y)scores = cross_val_score(clf, X, y)print('随机森林准确率:',scores.mean())# 极限随机树from sklearn.ensemble import ExtraTreesClassifierclf = ExtraTreesClassifier(n_estimators=10, max_depth=None,min_samples_split=2, random_state=0)scores = cross_val_score(clf, X, y)print('极限随机树准确率:',scores.mean())print('模型中各属性的重要程度:',clf.feature_importances_)
(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 即可实现。
最后,这个模块还支持树的并行构建和预测结果的并行计算,这可以通过 n_jobs 参数实现。
模型的feature_importances_属性保存了各特征的重要程度。一个元素的值越高,其对应的特征对预测函数的贡献越大。
随机森林调参策略:
1、对Random Forest来说,增加“子模型数”(n_estimators)可以明显降低整体模型的方差,且不会对子模型的偏差和方差有任何影响。模型的准确度会随着“子模型数”的增加而提高。由于减少的是整体模型方差公式的第二项,故准确度的提高有一个上限。
2、在不同的场景下,“分裂条件”(criterion)对模型的准确度的影响也不一样,该参数需要在实际运用时灵活调整。
3、调整“最大叶节点数”(max_leaf_nodes)以及“最大树深度”(max_depth)之一,可以粗粒度地调整树的结构:叶节点越多或者树越深,意味着子模型的偏差越低,方差越高;
4、同时,调整“分裂所需最小样本数”(min_samples_split)、“叶节点最小样本数”(min_samples_leaf)及“叶节点最小权重总值”(min_weight_fraction_leaf),可以更细粒度地调整树的结构:分裂所需样本数越少或者叶节点所需样本越少,也意味着子模型越复杂。一般来说,我们总采用bootstrap对样本进行子采样来降低子模型之间的关联度,从而降低整体模型的方差。
5、适当地减少“分裂时考虑的最大特征数”(max_features),给子模型注入了另外的随机性,同样也达到了降低子模型之间关联度的效果。但是一味地降低该参数也是不行的,因为分裂时可选特征变少,模型的偏差会越来越大。在下图中,我们可以看到这些参数对Random Forest整体模型性能的影响:
AdaBoost
# 产生样本数据集from sklearn.model_selection import cross_val_scorefrom sklearn import datasetsiris = datasets.load_iris()X, y = iris.data[:, 1:3], iris.target# ====================AdaBoost=========================from sklearn.ensemble import AdaBoostClassifierclf = AdaBoostClassifier(n_estimators=100)scores = cross_val_score(clf, X, y)print('AdaBoost准确率:',scores.mean())
弱学习器的数量由参数 n_estimators 来控制。 learning_rate 参数用来控制每个弱学习器对 最终的结果的贡献程度(校对者注:其实应该就是控制每个弱学习器的权重修改速率,这里不太记得了,不确定)。 弱学习器默认使用决策树。不同的弱学习器可以通过参数 base_estimator 来指定。 获取一个好的预测结果主要需要调整的参数是 n_estimators 和 base_estimator 的复杂度 (例如:对于弱学习器为决策树的情况,树的深度 max_depth 或叶子节点的最小样本数 min_samples_leaf 等都是控制树的复杂度的参数)
基于经验,Aarshay提出他的见解:“最大叶节点数”(max_leaf_nodes)和“最大树深度”(max_depth)对整体模型性能的影响大于“分裂所需最小样本数”(min_samples_split)、“叶节点最小样本数”(min_samples_leaf)及“叶节点最小权重总值”(min_weight_fraction_leaf),而“分裂时考虑的最大特征数”(max_features)的影响力最小。
Gradient Tree Boosting(梯度树提升)
Gradient Tree Boosting 或梯度提升回归树(GBRT)是对于任意的可微损失函数的提升算法的泛化。 GBRT 是一个准确高效的现有程序, 它既能用于分类问题也可以用于回归问题。梯度树提升模型被应用到各种领域,包括网页搜索排名和生态领域。
1.划分时考虑的最大特征数max_features: 可以使用很多种类型的值,默认是"None",意味着划分时考虑所有的特征数;如果是"log2"意味着划分时最多考虑
个特征;如果是"sqrt"或者"auto"意味着划分时最多考虑
个特征。如果是整数,代表考虑的特征绝对数。如果是浮点数,代表考虑特征百分比,即考虑(百分比x/N)取整后的特征数。其中N为样本总特征数。一般来说,如果样本特征数不多,比如小于50,我们用默认的"None"就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。
2.决策树最大深度max_depth: 默认可以不输入,如果不输入的话,决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。
3.内部节点再划分所需最小样本数min_samples_split: 这个值限制了子树继续划分的条件,如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分。 默认是2.如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
4.叶子节点最少样本数min_samples_leaf: 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
5.叶子节点最小的样本权重和min_weight_fraction_leaf:这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。 默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
6.最大叶子节点数max_leaf_nodes: 通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。
7.节点划分最小不纯度min_impurity_split: 这个值限制了决策树的增长,如果某节点的不纯度(基于基尼系数,均方差)小于这个阈值,则该节点不再生成子节点。即为叶子节点 。一般不推荐改动默认值1e-7
# 产生样本数据集from sklearn.model_selection import cross_val_scorefrom sklearn import datasetsiris = datasets.load_iris()X, y = iris.data[:, 1:3], iris.target# ====================Gradient Tree Boosting(梯度树提升)=========================# 分类from sklearn.ensemble import GradientBoostingClassifierclf = GradientBoostingClassifier(n_estimators=100, learning_rate=1.0,max_depth=1, random_state=0)scores = cross_val_score(clf, X, y)print('GDBT准确率:',scores.mean())# 回归import numpy as npimport matplotlib.pyplot as pltfrom sklearn.metrics import mean_squared_errorfrom sklearn.datasets import load_bostonfrom sklearn.ensemble import GradientBoostingRegressorfrom sklearn.utils import shufflefrom sklearn.model_selection import train_test_split,cross_val_score,cross_validateboston = load_boston() # 加载波士顿房价回归数据集X1, y1 = shuffle(boston.data, boston.target, random_state=13) # 将数据集随机打乱X_train, X_test, y_train, y_test = train_test_split(X1, y1, test_size=0.1, random_state=0) # 划分训练集和测试集.test_size为测试集所占的比例clf = GradientBoostingRegressor(n_estimators=500, learning_rate=0.01,max_depth=4,min_samples_split=2,loss='ls')clf.fit(X1, y1)print('GDBT回归MSE:',mean_squared_error(y_test, clf.predict(X_test)))# print('每次训练的得分记录:',clf.train_score_)print('各特征的重要程度:',clf.feature_importances_)plt.plot(np.arange(500), clf.train_score_, 'b-') # 绘制随着训练次数增加,训练得分的变化plt.show()
以下是目前支持的损失函数,具体损失函数可以通过参数 loss 指定:
回归 (Regression)
分类 (Classification)
GDBT调参策略:
对Gradient Tree Boosting来说,“子模型数”(n_estimators)和“学习率”(learning_rate)需要联合调整才能尽可能地提高模型的准确度:想象一下,A方案是走4步,每步走3米,B方案是走5步,每步走2米,哪个方案可以更接近10米远的终点?
同理,子模型越复杂,对应整体模型偏差低,方差高,故“最大叶节点数”(max_leaf_nodes)、“最大树深度”(max_depth)等控制子模型结构的参数是与Random Forest一致的。类似“分裂时考虑的最大特征数”(max_features),降低“子采样率”(subsample),也会造成子模型间的关联度降低,整体模型的方差减小,但是当子采样率低到一定程度时,子模型的偏差增大,将引起整体模型的准确度降低。还记得“初始模型”(init)是什么吗?不同的损失函数有不一样的初始模型定义,通常,初始模型是一个更加弱的模型(以“平均”情况来预测),虽说支持自定义,大多数情况下保持默认即可。在下图中,我们可以看到这些参数对Gradient Tree Boosting整体模型性能的影响:
Voting Classifier(投票分类器)
VotingClassifier (投票分类器)的原理是结合了多个不同的机器学习分类器,并且采用多数表决(majority vote)(硬投票) 或者平均预测概率(软投票)的方式来预测分类标签。 这样的分类器可以用于一组同样表现良好的模型,以便平衡它们各自的弱点。
# 产生样本数据集from sklearn.model_selection import cross_val_scorefrom sklearn import datasetsiris = datasets.load_iris()X, y = iris.data[:, 1:3], iris.target# ====================Voting Classifier(投票分类器)=========================from sklearn.linear_model import LogisticRegressionfrom sklearn.naive_bayes import GaussianNBfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.ensemble import VotingClassifierclf1 = LogisticRegression(random_state=1)clf2 = RandomForestClassifier(random_state=1)clf3 = GaussianNB()eclf = VotingClassifier(estimators=[('lr', clf1), ('rf', clf2), ('gnb', clf3)], voting='hard') # 无权重投票eclf = VotingClassifier(estimators=[('lr', clf1), ('rf', clf2), ('gnb', clf3)],voting='soft', weights=[2,1,2]) # 权重投票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("准确率: %0.2f (+/- %0.2f) [%s]" % (scores.mean(), scores.std(), label))# 配合网格搜索from sklearn.model_selection import GridSearchCVparams = {'lr__C': [1.0, 100.0], 'rf__n_estimators': [20, 200],} # 搜索寻找最优的lr模型中的C参数和rf模型中的n_estimatorsgrid = GridSearchCV(estimator=eclf, param_grid=params, cv=5)grid = grid.fit(iris.data, iris.target)print('最优参数:',grid.best_params_)