Bagging和Boosting的概念
集成算法通常有两种方式,分别是套袋法(bagging)和提升法(boosting)。随机森林属于集成学习(Ensemble Learning)中的bagging算法。
Bagging(套袋法)
- 从原始样本集中使用Bootstraping方法(自助法,是一种有放回的抽样方法)随机抽取n个训练样本,共进行k轮抽取,得到k个训练集。(k个训练集之间相互独立,元素可以有重复)
- 对于k个训练集,我们训练k个模型(这k个模型可以根据具体问题而定,比如决策树,knn等)
- 对于分类问题:由投票表决产生分类结果;对于回归问题:由k个模型预测结果的均值作为最后预测结果。(所有模型的重要性相同)
Boosting(提升法)
- 对于训练集中的每个样本建立权值wi,表示对每个样本的关注度。当某个样本被误分类的概率很高时,需要加大对该样本的权值。
- 进行迭代的过程中,每一步迭代都是一个弱分类器。我们需要用某种策略将其组合,作为最终模型。(例如AdaBoost给每个弱分类器一个权值,将其线性组合最为最终分类器。误差越小的弱分类器,权值越大)
Bagging,Boosting的主要区别
样本选择上:
- Bagging采用的是Bootstrap随机有放回抽样;
- Boosting每一轮的训练集是不变的,改变的只是每一个样本的权重。
样本权重:
- Bagging使用的是均匀取样,每个样本权重相等;
- Boosting根据错误率调整样本权重,错误率越大的样本权重越大。
预测函数:
- Bagging所有的预测函数的权重相等;
- Boosting中误差越小的预测函数其权重越大。
并行计算:
- Bagging各个预测函数可以并行生成;
- Boosting各个预测函数必须按顺序迭代生成。
将决策树与这些算法框架进行结合所得到的新的算法:
- Bagging + 决策树 = 随机森林
- AdaBoost + 决策树 = 自适应提升树
- Gradient Boosting + 决策树 = GBDT(梯度提升树)
函数构造
AdaBoost 分类:
from sklearn.ensemble import AdaBoostClassifier
AdaBoostClassifier(base_estimator=None, n_estimators=50, learning_rate=1.0, algorithm=’SAMME.R’, random_state=None)
参数含义:
- base_estimator:代表的是基分类器(弱分类器)。在 AdaBoost 的分类器和回归器中都有这个参数,在 AdaBoost 中默认使用的是决策树,一般不需要修改这个参数,当然也可以指定具体的分类器。基分类器需要支持带样本权重的学习以及具备classes_和n_classes_属性。
- n_estimators:算法的最大迭代次数,也是分类器的个数,每一次迭代都会引入一个新的弱分类器来增加原有的分类器的组合能力。默认是 50。当完美地拟合训练数据了,提升算法会提前停止,这时的基本分类器数量小于给定的值。
- learning_rate:代表学习率,取值在 0-1 之间,默认是 1。如果学习率较小,就需要比较多的迭代次数才能收敛,也就是说学习率和迭代次数是有相关性的。当调整 learning_rate 的时候,往往也需要调整 n_estimators 这个参数。
学习率就是下面公式中的v:F_m(x)=F_m-1(x)+v*alpha_m*G_m(x)
其中G_m(x)就是第m个基本分类器。aplha_m是第m个基本分类器的系数。
- algorithm:代表要采用哪种 boosting 算法,一共有两种选择:SAMME 和 SAMME.R。默认是 SAMME.R。这两者之间的区别在于对弱分类权重的计算方式不同。标准的AdaBoost算法只适用于二分类问题,SAMME适用于AdaBoost多分类
- algorithm='SAMME',采用SAMME离散提升算法。
- algorith='SAMME.R',采用SAMME.R真正的提升算法。如果选择SAMME.R算法,基本分类器必须必须支持类别概率的计算。
- SAMME.R算法通常比SAMME收敛得更快,通过较少的提升迭代实现较低的测试误差。
- random_state:代表随机数种子的设置,默认是 None。随机种子是用来控制随机模式的,当随机种子取了一个值,也就确定了一种随机规则,其他人取这个值可以得到同样的结果。如果不设置随机种子,每次得到的随机数也就不同。
- 如果为整数,则它指定了随机数生成器的种子。
- 如果为RandomState实例,则指定了随机数生成器。
- 如果为None,则使用默认的随机数生成器
属性:
- estimators_:list of classifiers.分类器列表。所有训练过的基本分类器。
- classes_ : array of shape = [n_classes]。类别标签。
- n_classes_ : int。类别数量。
- estimator_weights_ : array of floats。数组,存放每个基本分类器的权重。
- estimator_errors_ : array of floats。数组,存放每个基本分类器的分类误差。
- feature_importances_ : array of shape = [n_features]。每个特征的重要性。
方法:
- fit():模型训练。
- predict():模型预测。
- predict_log_proba():预测的每个样本属于各个类别的概率对数值。
- predict_proba():预测的每个样本属于各个类别的概率值。
- staged_predict():预测每一轮迭代后输入样本的预测值。
- staged_predict_proba():预测每一轮迭代后输入样本属于各个类别的概率值。
AdaBoost 回归:
from sklearn.ensemble import AdaBoostRegressor
AdaBoostRegressor(base_estimator=None, n_estimators=50, learning_rate=1.0, loss=‘linear’, random_state=None)
- loss 代表损失函数的设置,一共有 3 种选择,分别为 linear、square 和 exponential,它们的含义分别是线性损失函数、平方损失函数和指数损失函数。默认是线性。一般采用线性就可以得到不错的效果。在每一轮提升迭代之后,都需要通过损失函数更新权重。
方法:
- fit():模型训练。
- predict():模型预测。
- staged_predict():预测每一轮迭代后输入样本的预测值
实战
sklearn 中自带的波士顿房价数据集
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.datasets import load_boston
from sklearn.ensemble import AdaBoostRegressor
# 加载数据
data=load_boston()
# 分割数据
train_x, test_x, train_y, test_y = train_test_split(data.data, data.target, test_size=0.25, random_state=33)
# 使用AdaBoost回归模型
regressor=AdaBoostRegressor()
regressor.fit(train_x,train_y)
pred_y = regressor.predict(test_x)
mse = mean_squared_error(test_y, pred_y)
print("房价预测结果 ", pred_y)
print("均方误差 = ",round(mse,2))
运行结果:
房价预测结果 [20.2 10.4137931 14.63820225 17.80322581 24.58931298 21.25076923
27.52222222 17.8372093 31.79642857 20.86428571 27.87431694 31.09142857
12.81666667 24.13131313 12.81666667 24.58931298 17.80322581 17.66333333
27.83 24.58931298 17.66333333 20.90823529 20.10555556 20.90823529
28.20877193 20.10555556 21.16882129 24.58931298 13.27619048 31.09142857
17.08095238 26.19217391 9.975 21.03404255 26.74583333 31.09142857
25.83960396 11.859375 13.38235294 24.58931298 14.97931034 14.46699029
30.12777778 17.66333333 26.19217391 20.10206186 17.70540541 18.45909091
26.19217391 20.10555556 17.66333333 33.31025641 14.97931034 17.70540541
24.64421053 20.90823529 25.83960396 17.08095238 24.58931298 21.43571429
19.31617647 16.33733333 46.04888889 21.25076923 17.08095238 25.83960396
24.64421053 11.81470588 17.80322581 27.63636364 23.59731183 17.94444444
17.66333333 27.7253886 20.21465517 46.04888889 14.97931034 9.975
17.08095238 24.13131313 21.03404255 13.4 11.859375 26.19214286
21.25076923 21.03404255 47.11395349 16.33733333 43.21111111 31.65730337
30.12777778 20.10555556 17.8372093 18.40833333 14.97931034 33.31025641
24.58931298 22.88813559 18.27179487 17.80322581 14.63820225 21.16882129
26.91538462 24.64421053 13.05 14.97931034 9.975 26.19217391
12.81666667 26.19214286 49.46511628 13.27619048 17.70540541 25.83960396
31.09142857 24.13131313 21.25076923 21.03404255 26.91538462 21.03404255
21.16882129 17.8372093 12.81666667 21.03404255 21.03404255 17.08095238
45.16666667]
均方误差 = 18.05
使用不同的回归分析模型分析这个数据集,比如使用决策树回归和 KNN 回归。
# 使用决策树回归模型
dec_regressor=DecisionTreeRegressor()
dec_regressor.fit(train_x,train_y)
pred_y = dec_regressor.predict(test_x)
mse = mean_squared_error(test_y, pred_y)
print("决策树均方误差 = ",round(mse,2))
# 使用KNN回归模型
knn_regressor=KNeighborsRegressor()
knn_regressor.fit(train_x,train_y)
pred_y = knn_regressor.predict(test_x)
mse = mean_squared_error(test_y, pred_y)
print("KNN均方误差 = ",round(mse,2))
运行结果:
决策树均方误差 = 23.84
KNN均方误差 = 27.87
相比之下,AdaBoost 的均方误差更小,也就是结果更优。虽然 AdaBoost 使用了弱分类器,但是通过 50 个甚至更多的弱分类器组合起来而形成的强分类器,在很多情况下结果都优于其他算法。因此 AdaBoost 也是常用的分类和回归算法之一。
AdaBoost 与决策树模型的比较
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.metrics import zero_one_loss
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
# 使用make_hastie_10_2 函数生成二分类数据
X,y=datasets.make_hastie_10_2(n_samples=12000,random_state=1)
# 从12000个数据中取前2000行作为测试集,其余作为训练集
train_x, train_y = X[2000:],y[2000:]
test_x, test_y = X[:2000],y[:2000]
# 弱分类器
dt_stump = DecisionTreeClassifier(max_depth=1,min_samples_leaf=1)
dt_stump.fit(train_x, train_y)
dt_stump_err = 1.0-dt_stump.score(test_x, test_y)
# 决策树分类器
dt = DecisionTreeClassifier()
dt.fit(train_x, train_y)
dt_err = 1.0-dt.score(test_x, test_y)
# 随机森林
fore = RandomForestClassifier(n_estimators=10, random_state=0)
fore.fit(train_x, train_y)
dt_fore_err = 1.0 - fore.score(test_x, test_y)
# AdaBoost分类器
# 设置AdaBoost迭代次数
n_estimators=200
ada = AdaBoostClassifier(base_estimator=dt_stump,n_estimators=n_estimators)
ada.fit(train_x, train_y)
# 三个分类器的错误率可视化
fig = plt.figure()
# 设置plt正确显示中文
plt.rcParams['font.sans-serif'] = ['SimHei']
ax = fig.add_subplot(111)
ax.plot([1,n_estimators],[dt_stump_err]*2, 'k-', label=u'决策树弱分类器 错误率')
ax.plot([1,n_estimators],[dt_err]*2,'k--', label=u'决策树模型 错误率')
ax.plot([1,n_estimators],[dt_fore_err]*2,'k:', label=u'随机森林模型 错误率')
ada_err = np.zeros((n_estimators,))
# 遍历每次迭代的结果 i为迭代次数, pred_y为预测结果
for i,pred_y in enumerate(ada.staged_predict(test_x)):
# 统计错误率
ada_err[i]=zero_one_loss(pred_y, test_y)
# 绘制每次迭代的AdaBoost错误率
ax.plot(np.arange(n_estimators)+1, ada_err, label='AdaBoost Test 错误率', color='orange')
ax.set_xlabel('迭代次数')
ax.set_ylabel('错误率')
leg=ax.legend(loc='upper right',fancybox=True)
plt.show()
运行结果:
从图中能看出来,弱分类器的错误率最高,只比随机分类结果略好,准确率稍微大于 50%。决策树模型的错误率明显要低很多,随机森林的错误率更低一点,而 AdaBoost 模型在迭代次数超过 25 次之后,错误率有了明显下降,经过 125 次迭代之后错误率的变化形势趋于平缓。