集成学习系列第三篇(Boosting相关方法),传送前两篇:
文章目录
一、Boosting
Boosting算法中,个体学习器之间存在强依赖关系。在boosting系列算法中, 典型的算法有 Adaboost、GBDT。
工作机制如下:
- 先从初始训练集中学习一个基学习器;
- 根据弱学习器的学习错误率对训练样本分布进行调整,使得先前基学习器做错的训练样本在后续收到更多关注;
- 基于调整权重后的训练样本分布来训练下一个弱学习器;
- 如此反复,直到弱学习器数目达到 T,最终将这 T 个弱学习器进行加权结合。
关键问题:
- 如何计算学习误差率e?
- 如何得到弱学习器权重系数α?
- 如何更新样本权重D?
- 使用何种结合策略?
二、Adaboost原理
核心思想:针对同一个训练集训练不同的分类器(弱分类器),然后把这些弱分类器集合起来,构成一个更强的最终分类器(强分类器)。
其算法本身是通过改变数据分布来实现的,它根据每次训练集之中每个样本的分类是否正确,以及上次的总体分类的准确率,来确定每个样本的权值。将修改过权值的新数据集送给下层分类器进行训练,最后将每次训练得到的分类器最后融合起来,作为最后的决策分类器。
优点
- Adaboost作为分类器时,分类精度很高
- 在Adaboost的框架下,可以使用各种回归分类模型来构建弱学习器,非常灵活。
- 作为简单的二元分类器时,构造简单,结果可理解。
- 不容易发生过拟合
缺点
- 对异常样本敏感,异常样本在迭代中可能会获得较高的权重,影响最终的强学习器的预测准确性。
1、基本思路
假设我们的训练集样本是
训练集的在第 个弱学习器的输出权重为
(1)Adaboost分类
分类问题的误差率
由于多元分类是二元分类的推广,这里假设我们是二元分类问题,输出为,则第 个弱分类器 在训练集上的加权误差率为
弱学习器权重系数
对于二元分类问题,第 个弱分类器 的权重系数为
==》如果分类误差率 越大,则对应的弱分类器权重系数 越小。也就是说,误差率小的弱分类器权重系数越大。
==》公式的由来在损失函数优化时介绍。
更新样本权重D
假设第 个弱分类器的样本集权重系数为 ,则对应的第 个弱分类器的样本集权重系数为
其中, 是规范因子,
==》从 计算公式可以看出,如果第 个样本分类错误,则 ,导致样本的权重在第 个弱分类器中增大,如果分类正确,则权重在第 个弱分类器中减少。
==》公式的由来在损失函数优化时介绍。
集合策略
Adaboost分类采用的是加权表决法,最终的强分类器为
(2)Adaboost回归
由于Adaboost的回归问题有很多变种,这里我们以Adaboost R2算法为准。
回归问题的误差率
对于第 个弱学习器,计算他在训练集上的最大误差
计算每个样本的相对误差
注意:这里是误差为线性时的情况;若使用平方误差,则;若使用指数误差,则
最终得到第 个弱分类器的错误率:
弱学习器权重系数
第 个弱分类器 的权重系数为
更新样本权重D
第 个弱学习器的样本集权重系数为
其中, 是规范因子,
结合策略
采用的是对加权的弱学习器取权重中位数对应的弱学习器作为强学习器的方法,最终的强回归器为
其中,是所有 , 的中位数值对应序号 对应的弱学习器。
2、AdaBoost分类问题的损失函数优化
Adaboost 是模型为加法模型,学习算法为前向分步学习算法,损失函数为指数函数的分类问题。
-
加法模型:最终的强分类器是若干个弱分类器加权平均而得到的。
-
前向分步学习算法:通过一轮轮的弱学习器学习,利用前一个弱学习器的结果来更新后一个弱学习器的训练集权重。第 轮的强学习器为
而第 轮的强学习器为
则可以得到 -
损失函数为指数函数,即定义损失函数为
利用前向分步学习算法的关系可以得到损失函数为
令 ,它的值不依赖于 ,因此与最小化无关,仅仅依赖于,随着每一轮迭代而改变。
将这个式子带入损失函数,损失函数转化为
- 损失函数求解过程
- 求 。对任意 ,使上式最小的 由下式得到:
- 将 带入损失函数,并对 求导,使其等于0,则可以得到
其中, 为分类错误率。
-
样本权重的更新。
利用 和 ,即可得:
3、AdaBoost二元分类问题算法流程
输入:样本集 ,输出为 ,弱分类器算法,弱分类器迭代次数 。
输出:最终的强分类器
(1) 初始化样本集权重为
(2) 对于 :
a) 使用具有权重 的样本集来训练数据,得到弱分类器
b) 计算 的分类错误率
c) 计算弱分类的系数
d) 更新样本集的权重分布
其中, 是规范因子,
(3) 构建最终分类器为:
对于Adaboost多元分类算法,其实原理和二元分类类似,最主要区别在弱分类器的系数上。比如 Adaboost SAMME 算法,它的弱分类器的系数:
其中 为类别数。从上式可以看出,如果是二元分类,,则上式和我们的二元分类算法中的弱分类器的系数一致。
4、Adaboost回归问题的算法流程
为Adaboost R2回归算法过程。
输入:样本集 ,弱分类器算法,弱分类器迭代次数 。
输出:最终的强分类器
(1) 初始化样本集权重为
(2) 对于 :
a) 使用具有权重 的样本集来训练数据,得到弱分类器
b) 计算训练集上的最大误差
c) 计算每个样本的相对误差
如果是线性误差,则
如果是平方误差,则
如果是指数误差,则
d) 计算回归误差率
e) 计算弱学习器的系数
f) 更新样本集的权重分布为
其中, 是规范因子,
(3) 构建最终强学习器为:
其中,是所有 , 的中位数值对应序号 对应的弱学习器。
5、Adaboost算法的正则化
为了防止Adaboost过拟合,我们通常也会加入正则化项,这个正则化项我们通常称为步长(learning rate)。定义为 ,对于前面的弱学习器的迭代
如果我们加上了正则化项,则有
其中,。对于同样的训练集学习效果,较小的 意味着我们需要更多的弱学习器的迭代次数。通常我们用步长和迭代最大次数一起来决定算法的拟合效果。
三、Adaboost实现
1、Adaboost类库简介
scikit-learn中Adaboost类库:
- AdaBoostClassifier——分类
- 两种实现方法:SAMME和SAMME.R
- AdaBoostRegressor——回归
+实现方法: Adaboost.R2(详见2.1.2)
Adaboost调参
- Adaboost的框架参数
- 弱分类器的参数
2、AdaBoost框架参数
AdaBoostClassifier和AdaBoostRegressor的大部分框架参数相同,下面我们一起讨论这些参数,两个类如果有不同点我们会指出。
(1) base_estimator:弱分类学习器或者弱回归学习器。
- 一般是CART决策树或者神经网络MLP,默认是决策树(
DecisionTreeClassifierorDecisionTreeRegressor)。 - 注意:分类中参数
algorithm选择的是SAMME.R,则弱分类学习器还需要支持概率预测(支持predict_proba方法)
(2) n_estimators:弱学习器的最大迭代次数,或者说最大的弱学习器的个数。
- 值太小,易欠拟合,值太大,易过拟合,一般选择一个适中的数值。默认是50。
- 在实际调参的过程中,我们常常将n_estimators和参数learning_rate一起考虑。
(3) learning_rate:每个弱学习器的权重缩减系数
- 为了防止Adaboost过拟合,我们通常也会加入正则化项,这个正则化项我们通常称为
步长(learning rate)。 - 定义为 ,对于前面的弱学习器的迭代 加上了正则化项,则有 , 的取值范围为 。
- 对于同样的训练集学习效果,较小的 意味着我们需要更多的弱学习器的迭代次数。通常我们用步长和迭代最大次数一起来决定算法的拟合效果,所以
n_estimators和learning_rate要一起调参。
(4) algorithm(只有AdaBoostClassifier有该参数)
- 实现分类Adaboost的两种算法
SAMME和SAMME.R。 -
两者主要区别:基学习器权重的度量。
SAMME使用对样本集分类效果作为弱学习器权重 (原理中即为SAMME),而SAMME.R使用对样本集分类的预测概率大小来作为弱学习器权重。 - 默认为
SAMME.R,它迭代一般比SAMME快。但若用SAMME.R, 则基学习器参数base_estimator必须使用支持概率预测的分类器。
(5) loss(只有AdaBoostRegressor有该参数)
-
Adaboost.R2算法需要用到——有线性linear, 平方square和指数exponential三种选择, 默认是线性。
3、弱分类器参数
本部分关注AdaBoostClassifier和AdaBoostRegressor弱学习器参数,仅讨论默认的决策树弱学习器的参数。即CART分类树DecisionTreeClassifier和CART回归树DecisionTreeRegressor的关键参数。
(1) max_features:划分时考虑的最大特征数
- 可以使用很多种类型的值:
- 默认是
None,表示划分时考虑所有的特征数; - 如果是
log2意味着划分时最多考虑 个特征; - 如果是
sqrt或者auto意味着划分时最多考虑 个特征。 - 如果是
整数,代表考虑的特征绝对数。 - 如果是
浮点数,代表考虑特征百分比,即考虑 取整后的特征数。其中N为样本总特征数。
- 默认是
- 一般来说,如果样本特征数不多,比如小于50,默认
None即可;如果特征数非常多,根据情况调整,以控制决策树的生成时间。
(2) max_depth: 决策树最大深度。
- 默认可以不输入,则决策树在建立子树的时候不会限制子树的深度。
- 一般来说,数据少或者特征少时可以不管这个值。
- 如果模型样本量多,特征也多的情况下,常限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。
(3) min_samples_split: 内部节点再划分所需最小样本数。
- 该值限制了子树继续划分的条件,如果某节点的样本数少于
min_samples_split,则不会继续再尝试选择最优特征来进行划分。 - 默认是 ,如果样本量不大,不需要管这个值。若样本量数量级非常大,则增大该值。
(4) min_samples_leaf: 叶子节点最少样本数。
- 该值限制了叶子节点最少的样本数,若某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。
- 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
(5) min_weight_fraction_leaf:叶子节点最小的样本权重和。
- 该值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。 默认是0,就是不考虑权重问题。
- 一般来说,若较多样本有缺失值,或分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
(6) max_leaf_nodes: 最大叶子节点数。
- 通过限制最大叶子节点数,可以防止过拟合,默认是
None,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。 - 如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。
4、实战
# -*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_gaussian_quantiles
"""
1. 生成一些随机数据,用于做二元分类
"""
# 生成2维正态分布,生成的数据按分位数分为两类,200个样本,2个样本特征,协方差系数为2
X1, y1 = make_gaussian_quantiles(cov=2.,
n_samples=200, n_features=2,
n_classes=2, random_state=2019)
# 生成2维正态分布,生成的数据按分位数分为两类,300个样本,2个样本特征均值都为3,协方差系数为2
X2, y2 = make_gaussian_quantiles(mean=(3, 3), cov=1.5,
n_samples=300, n_features=2,
n_classes=2, random_state=2019)
# 将两组数据合成一组数据
X = np.concatenate((X1, X2))
y = np.concatenate((y1, - y2 + 1))
"""
2. 基于决策树的Adaboost来做分类拟合
"""
# 这里我们选择了SAMME算法,最多200个弱分类器,步长0.8
bdt = AdaBoostClassifier(DecisionTreeClassifier(max_depth=1),
algorithm="SAMME",
n_estimators=200)
bdt.fit(X, y)
"""
3. 拟合结果可视化
"""
plot_colors = "br"
plot_step = 0.02
class_names = "AB"
plt.figure(figsize=(10, 5))
# Plot the decision boundaries
plt.subplot(121)
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, plot_step),
np.arange(y_min, y_max, plot_step))
Z = bdt.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
cs = plt.contourf(xx, yy, Z, cmap=plt.cm.Paired)
plt.axis("tight")
# Plot the training points
for i, n, c in zip(range(2), class_names, plot_colors):
idx = np.where(y == i)
plt.scatter(X[idx, 0], X[idx, 1],
c=c, cmap=plt.cm.Paired,
s=20, edgecolor='k',
label="Class %s" % n)
plt.xlim(x_min, x_max)
plt.ylim(y_min, y_max)
plt.legend(loc='upper right')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Decision Boundary')
# Plot the two-class decision scores
twoclass_output = bdt.decision_function(X)
plot_range = (twoclass_output.min(), twoclass_output.max())
plt.subplot(122)
for i, n, c in zip(range(2), class_names, plot_colors):
plt.hist(twoclass_output[y == i],
bins=10,
range=plot_range,
facecolor=c,
label='Class %s' % n,
alpha=.5,
edgecolor='k')
x1, x2, y1, y2 = plt.axis()
plt.axis((x1, x2, y1, y2 * 1.2))
plt.legend(loc='upper right')
plt.ylabel('Samples')
plt.xlabel('Score')
plt.title('Decision Scores')
plt.tight_layout()
plt.subplots_adjust(wspace=0.35)
plt.show()
在下一篇中将介绍GDBT相关内容,敬请期待~