1 概述

1.1 集成算法概述

集成学习(ensemble learning)是时下非常流行的机器学习算法,它本身不是一个单独的机器学习算法,而是通 过在数据上构建多个模型集成所有模型的建模结果。基本上所有的机器学习领域都可以看到集成学习的身影,在现实中集成学习也有相当大的作用,它可以用来做市场营销模拟的建模,统计客户来源,保留和流失,也可用来预测疾病的风险和病患者的易感性。在现在的各种算法竞赛中,随机森林,梯度提升树(GBDT),Xgboost等集成算法的身影也随处可见,可见其效果之好,应用之广。

集成算法的目标:集成算法会考虑多个评估器的建模结果,汇总之后得到一个综合的结果,以此来获取比单个模型更好的回归或分类表现。

多个模型集成成为的模型叫做集成评估器(ensemble estimator),组成集成评估器的每个模型都叫做基评估器 (base estimator)。通常来说,有三类集成算法:装袋法(Bagging),提升法(Boosting)和stacking。

装袋法的核心思想是构建多个相互独立的评估器,然后对其预测进行平均或多数表决原则来决定集成评估器的结果。装袋法的代表模型就是随机森林

提升法中,基评估器是相关的,是按顺序一一构建的。其核心思想是结合弱评估器的力量一次次对难以评估的样本进行预测,从而构成一个强评估器。提升法的代表模型有Adaboost和梯度提升树。

1.2 sklearn 中的集成算法

· sklearn 中的集成算法模块 ensemble

类的功能
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.RandomTreesEmbedding 完全随机树的集成
ensemble.VotingClassifier 用于不合适估算器的软投票/多数规则分类器

集成算法中,有一半以上都是树的集成模型,可以想见决策树在集成中必定是有很好的效果。在这堂课中,我们会 以随机森林为例,慢慢为大家揭开集成算法的神秘面纱。

· sklearn的基本建模流程

  1. from sklearn.tree import RandomForestClassifier #导入需要的模块
  2. rfc = RandomForestClassifier() #实例化
  3. rfc = rfc.fit(X_train,y_train) #用训练集数据训练模型
  4. result = rfc.score(X_test,y_test) #导入测试集,从接口中调用需要的信息

2 RandomForestClassifier

随机森林是非常具有代表性的 Bagging 集成算法,它的所有基评估器都是决策树,分类树组成的森林就叫做随机森林分类器,回归树所集成的森林就叫做随机森林回归器。这一节主要讲解 RandomForestClassifier,随机森林分类器。

2.1 重要参数

2.1.1 控制基评估器的参数

参数 含义
criterion 不纯度的衡量指标,有基尼系数和信息熵两种选择
max_depth 树的最大深度,超过最大深度的树枝都会被剪掉
min_samples_leaf 一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf 个训练样本,否则分枝就不会发生
min_samples_split 一个节点必须要包含至少 min_samples_split 个训练样本,这个节点才允许被分枝,否则分枝就不会发生
max_features max_features 限制分枝时考虑的特征个数,超过限制个数的特征都会被舍弃,默认值为总特征个数开平方取整
min_impurity_decrease 限制信息增益的大小,信息增益小于设定数值的分枝不会发生

单个决策树的准确率越高,随机森林的准确率也会越高,因为装袋法是依赖于平均值或 者少数服从多数原则来决定集成的结果的。

2.1.2 n_estimators

这是森林中树木的数量,即基评估器的数量。这个参数对随机森林模型的精确性影响是单调的,n_estimators 越大,模型的效果往往越好。但是相应的,任何模型都有决策边界,n_estimators 达到一定的程度之后,随机森林 的精确性往往不在上升或开始波动,并且,n_estimators 越大,需要的计算量和内存也越大,训练的时间也会越来越长。对于这个参数,我们是渴望在训练难度和模型效果之间取得平衡。

n_estimators 的默认值在现有版本的 sklearn 中是 100。

2.2 建立森林

树模型的优点是简单易懂,可视化之后的树人人都能够看懂,可惜随机森林是无法被可视化的。

2.2.1 导入我们需要的包

  1. %matplotlib inline
  2. from sklearn.tree import DecisionTreeClassifier
  3. from sklearn.ensemble import RandomForestClassifier
  4. from sklearn.datasets import load_wine

2.2.2 导入需要的数据集

  1. wine = load_wine()
  2. wine.data
  3. wine.target

2.2.3 sklearn建模的基本流程

  1. from sklearn.model_selection import train_test_split
  2. Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data,wine.target,test_size=0.3)
  3. clf = DecisionTreeClassifier(random_state=0)
  4. rfc = RandomForestClassifier(random_state=0)
  5. clf = clf.fit(Xtrain,Ytrain)
  6. rfc = rfc.fit(Xtrain,Ytrain)
  7. score_c = clf.score(Xtest,Ytest)
  8. score_r = rfc.score(Xtest,Ytest)
  9. print("Single Tree:{}".format(score_c)
  10. ,"Random Forest:{}".format(score_r)
  11. )

2.2.4 画出随机森林和决策树在十组交叉验证下的效果对比

  1. #交叉验证:是数据集划分为n分,依次取每一份做测试集,每n-1份做训练集,多次训练模型以观测模型稳定性的方法
  2. from sklearn.model_selection import cross_val_score
  3. import matplotlib.pyplot as plt
  4. rfc_1 = []
  5. clf_1 = []
  6. for i in range(10):
  7. rfc = RandomForestClassifier(n_estimators=25)
  8. rfc_s = cross_val_score(rfc,wine.data,wine.target,cv=10).mean()
  9. rfc_1.append(rfc_s)
  10. clf = DecisionTreeClassifier()
  11. clf_s = cross_val_score(clf,wine.data,wine.target,cv=10).mean()
  12. clf_1.append(clf_s)
  13. plt.plot(range(1,11),rfc_l,label = "Random Forest")
  14. plt.plot(range(1,11),clf_l,label = "Decision Tree")
  15. plt.legend()
  16. plt.show()

image.png

可以看到随机森林毫无悬念地碾压了决策树。同时我们也发现了单个决策树的波动轨迹和随机森林基本一致,这再次验证了之前我们的说法,单个决策树的准确率越高,随机森林的准确率也会越高。

2.2.5 n_estimators 的学习曲线

  1. superpa = []
  2. for i in range(200):
  3. rfc = RandomForestClassifier(n_estimators=i+1,n_jobs=-1)
  4. rfc_s = cross_val_score(rfc, wine.data,wine.target,cv=10).mean()
  5. superpa.append(rfc_s)
  6. print(max(superpa),superpa.index(max(superpa)))
  7. plt.figure(figsize=[20,5])
  8. plt.plot(range(1,201),superpa)
  9. plt.show()
  10. #这段代码执行时间可能会非常长,大约需要2分半的时间

image.png

2.3 重要属性和接口

随机森林中有三个非常重要的属性:.estimators_.oobscore以及.featureimportances

.estimators_ 是用来查看随机森林中所有树的列表的。

oobscore 指的是袋外得分。随机森林为了确保林中的每棵树都不尽相同,所以采用了对训练集进行有放回抽样的方式来不断组成新的训练集,在这个过程中,会有一些数据从来没有被随机挑选到,他们就被叫做“袋外数据”。这些袋外数据,没有被模型用来进行训练,sklearn 可以帮助我们用他们来测试模型,测试的结果就由这个属性 oobscore 来导出,本质还是模型的精确度。

而 .featureimportances 和决策树中的 .featureimportances 用法和含义都一致,是返回特征的重要性。

随机森林的接口与决策树完全一致,因此依然有四个常用接口:applyfitpredictscore。除此以外,还需要注意随机森林的 predict_proba 接口,这个接口返回每个测试样本对应的被分到每一类标签的概率,标签有几个分类就返回几个概率。如果是二分类问题,则 predict_proba 返回的数值大于 0.5 的,被分为 1,小于 0.5 的,被分为 0。传统的随机森林是利用袋装法中的规则,平均或少数服从多数来决定集成的结果,而 sklearn 中的随机森林是平均每个样本对应的 predict_proba 返回的概率,得到一个平均概率,从而决定测试样的分类。

  1. rfc = RandomForestClassifier(n_estimators=25)
  2. rfc = rfc.fit(Xtrain, Ytrain)
  3. rfc.score(Xtest,Ytest)
  4. rfc.feature_importances_
  5. rfc.apply(Xtest)
  6. rfc.predict(Xtest)
  7. rfc.predict_proba(Xtest)

3 实例:随机森林在乳腺癌数据上的调参

3.1 导入需要的库

  1. from sklearn.datasets import load_breast_cancer
  2. from sklearn.ensemble import RandomForestClassifier
  3. from sklearn.model_selection import GridSearchCV
  4. from sklearn.model_selection import cross_val_score
  5. import matplotlib.pyplot as plt
  6. import pandas as pd
  7. import numpy as np

3.2 导入数据集,探索数据

  1. data = load_breast_cancer()
  2. data
  3. data.data.shape
  4. data.target

3.3 进行一次简单的建模,看看模型本身在数据集上的效果

  1. rfc = RandomForestClassifier(n_estimators=100,random_state=90)
  2. score_pre = cross_val_score(rfc,data.data,data.target,cv=10).mean()
  3. score_pre

3.4 随机森林调整的第一步:无论如何先来调 n_estimators

  1. score1 = []
  2. for i in range(0,200,10):
  3. rfc = RandomForestClassifier(n_estimators=i+1
  4. ,n_jobs=-1
  5. ,random_state=90
  6. )
  7. score = cross_val_score(rfc,data.data,data.target,cv=10).mean()
  8. score1.append(score)
  9. print(max(score1),(score1.index(max(score1))*10)+1)
  10. plt.figure(figsize=[20,5])
  11. plt.plot(range(1,201,10),score1)
  12. plt.show()

返回值:0.9631265664160402 71
image.png

3.5 在确定好的范围内,进一步细化学习曲线

  1. scorel = []
  2. for i in range(65,75):
  3. rfc = RandomForestClassifier(n_estimators=i
  4. ,n_jobs=-1
  5. ,random_state=90)
  6. score = cross_val_score(rfc,data.data,data.target,cv=10).mean()
  7. scorel.append(score)
  8. print(max(scorel),([*range(35,45)][scorel.index(max(scorel))]))
  9. plt.figure(figsize=[20,5])
  10. plt.plot(range(35,45),scorel)
  11. plt.show()

返回值:0.9666353383458647 43

image.png

3.6 为网格搜索做准备,书写网格搜索的参数

有一些参数是没有参照的,很难说清一个范围,这种情况下我们使用学习曲线,看趋势从曲线跑出的结果中选取一个更小的区间,再跑曲线。

  1. param_grid = {'n_estimators':np.arange(0, 200, 10)}
  2. param_grid = {'max_depth':np.arange(1, 20, 1)}
  3. param_grid = {'max_leaf_nodes':np.arange(25,50,1)}

对于大型数据集,可以尝试从1000来构建,先输入1000,每100个叶子一个区间,再逐渐缩小范围。

有一些参数是可以找到一个范围的,或者说我们知道他们的取值和随着他们的取值,模型的整体准确率会如何变化,这样的参数我们就可以直接跑网格搜索。

  1. param_grid = {'criterion':['gini', 'entropy']}
  2. param_grid = {'min_samples_split':np.arange(2, 2+20, 1)}
  3. param_grid = {'min_samples_leaf':np.arange(1, 1+10, 1)}
  4. param_grid = {'max_features':np.arange(5,30,1)}

3.7 开始按照参数对模型整体准确率的影响程度进行调参,首先调整max_depth

  1. param_grid = {'max_depth':np.arange(1, 20, 1)}
  2. rfc = RandomForestClassifier(n_estimators=39
  3. ,random_state=90
  4. )
  5. GS = GridSearchCV(rfc,param_grid,cv=10)
  6. GS.fit(data.data,data.target)
  7. GS.best_params_
  8. GS.best_score_

返回值:{‘max_depth’: 6} 0.9631265664160402

这里我们发现调了 max_depth 这个参数之后,score 的数值反而下降了。这说明模型现在位于泛化误差最低点的左边。而通常来说,随机森林应该在泛化误差最低点的右边,树模型应该倾向于过拟合,而不是拟合不足。这和数据集本身有关,但也有可能是我们调整的 n_estimators 对于数据集来说太大,因此将模型拉到泛化误差最低点去了。然而,既然我们追求最低泛化误差,那我们就保留这个 n_estimators,除非有其他的因素,可以帮助我们达到更高的准确率。

当模型位于图像左边时,我们需要的是增加模型复杂度(增加方差,减少偏差)的选项,因此 max_depth 应该尽量大,min_samples_leaf 和 min_samples_split 都应该尽量小。这几乎是在说明,除了 max_features,我们没有任何参数可以调整了,因为 max_depth,min_samples_leaf 和 min_samples_split 是剪枝参数,是减小复杂度的参数。 在这里,我们可以预言,我们已经非常接近模型的上限,模型很可能没有办法再进步了。那我们这就来调整一下 max_features,看看模型如何变化。

3.8 调整 max_features

  1. param_grid = {'max_features':np.arange(5,30,1)}
  2. rfc = RandomForestClassifier(n_estimators=39
  3. ,random_state=90
  4. )
  5. GS = GridSearchCV(rfc,param_grid,cv=10)
  6. GS.fit(data.data,data.target)
  7. GS.best_params_
  8. GS.best_score_

返回值:{‘max_features’: 6}
0.968421052631579

3.9 调整 min_samples_leaf

  1. param_grid={'min_samples_leaf':np.arange(1, 1+10, 1)}
  2. rfc = RandomForestClassifier(n_estimators=39
  3. ,random_state=90
  4. )
  5. GS = GridSearchCV(rfc,param_grid,cv=10)
  6. GS.fit(data.data,data.target)
  7. GS.best_params_
  8. GS.best_score_

3.10 调整 min_samples_split

  1. param_grid={'min_samples_split':np.arange(2, 2+20, 1)}
  2. rfc = RandomForestClassifier(n_estimators=39
  3. ,random_state=90
  4. )
  5. GS = GridSearchCV(rfc,param_grid,cv=10)
  6. GS.fit(data.data,data.target)
  7. GS.best_params_
  8. GS.best_score_

3.11 调整 criterion

  1. param_grid = {'criterion':['gini', 'entropy']}
  2. rfc = RandomForestClassifier(n_estimators=39
  3. ,random_state=90
  4. )
  5. GS = GridSearchCV(rfc,param_grid,cv=10)
  6. GS.fit(data.data,data.target)
  7. GS.best_params_
  8. GS.best_score_

3.12 调整完毕,总结出模型的最佳参数

  1. rfc = RandomForestClassifier(n_estimators=39,random_state=90)
  2. score = cross_val_score(rfc,data.data,data.target,cv=10).mean()
  3. score