Boosting-导论
本章介绍的Boosting是与Bagging截然不同的思想,Boosting方法是使用同一组数据集进行反复学习,得到一系列简单模型,然后组合这些模型构成一个预测性能十分强大的机器学习模型。显然,Boosting思想提高最终的预测效果是通过不断减少偏差的形式,与Bagging有着本质的不同。在Boosting这一大类方法中,笔者主要介绍两类常用的Boosting方式:Adaptive Boosting 和 Gradient Boosting 以及它们的变体Xgboost、LightGBM以及Catboost。
基本概念
- 弱学习:识别错误率小于1/2(即准确率仅比随机猜测略高的学习算法)
- 强学习:识别准确率很高并能在多项式时间内完成的学习算法
强可学习和弱可学习是等价的,也就是说一个概念是强可学习的充分必要条件是这个概念是弱可学习的。这样一来,问题便是:在学习中,如果已经发现了弱可学习算法,能否将他提升至强可学习算法。因为,弱可学习算法比强可学习算法容易得多。提升方法就是从弱学习算法出发,反复学习,得到一系列弱分类器(又称为基本分类器),然后通过一定的形式去组合这些弱分类器构成一个强分类器。大多数的Boosting方法都是通过改变训练数据集的概率分布(训练数据不同样本的权值),针对不同概率分布的数据调用弱分类算法学习一系列的弱分类器。
对于Boosting方法来说,有两个问题需要给出答案:第一个是每一轮学习应该如何改变数据的概率分布,第二个是如何将各个弱分类器组合起来。
Boosting过程
- 这一阶段中,首先将形成不同的基分类器。通过在测试集中训练,得到一个结果。选择出对的和错的——这就是一种类似错题本的模式,产生新的训练集。C2在新的训练集上训练。
- 在这一阶段,C2学习后(针对2分类),重新在D中进行测试。
对比C1、C2在测试集上的结果,找到二者产生分歧的样本,组成新的训练集。之后在新的训练集上继续学习,产生C3。
Boosting相对于Bagging是一种串联的模式。后面的分类器重点学习之前分类器分类错误的结果 - 最后针对3个结果中,C1和C2判断正确,则取C1,否则取C3
Boosting的优势
-
Bagging aims at reducing variance, not bias.
-
In Boosting, classifiers are generated sequentially.
-
Focuses on most informative data points.
-
Training samples are weighted.
-
Outputs are combined via weighted voting.
-
Can create arbitrarily strong classifiers.
-
The base learners can be arbitrarily weak.
-
As long as they are better than random guess!
Adaboost算法
Adaboost基本流程
- 针对这个流程需要注意的要点
- 每个训练器的权重是经过计算得来的 α t = 1 2 ln ( 1 − ϵ t ϵ t ) \alpha_{t}=\frac{1}{2} \ln \left(\frac{1-\epsilon_{t}}{\epsilon_{t}}\right) αt=21ln(ϵt1−ϵt)
- 权重系数与判断正确与否有关
{
exp
(
−
α
t
)
if
h
t
(
x
i
)
=
y
i
exp
(
α
t
)
if
h
t
(
x
i
)
≠
y
i
\left\{\begin{array}{ll}\exp \left(-\alpha_{t}\right) & \text { if } h_{t}\left(\boldsymbol{x}_{i}\right)=y_{i} \\ \exp \left(\alpha_{t}\right) & \text { if } h_{t}\left(\boldsymbol{x}_{i}\right) \neq y_{i}\end{array}\right.
{exp(−αt)exp(αt) if ht(xi)=yi if ht(xi)=yi
判断正确即为负值,表示减少对这个样本的学习,一直正确,权重进一步降低
判断错误即为正值,表示增加对这个样本的学习,一直错误,权重进一步升高 - 利用 Z t Z_t Zt,可以证明误差上限是越来越小的; α t \alpha_{t} αt是一个解析解,可以推导得来,无需手动调节
相关实例
- 这部分在《统计学习方法》中也有(P140)
我们使用一组简单的数据来手动计算Adaboost算法的过程:(例子来源:http://www.csie.edu.tw)
训练数据如下表,假设基本分类器的形式是一个分割
x
<
v
x<v
x<v或
x
>
v
x>v
x>v表示,阈值v由该基本分类器在训练数据集上分类错误率
e
m
e_m
em最低确定。
序号
1
2
3
4
5
6
7
8
9
10
x
0
1
2
3
4
5
6
7
8
9
y
1
1
1
−
1
−
1
−
1
1
1
1
−
1
\begin{array}{ccccccccccc} \hline \text { 序号 } & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 \\ \hline x & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \\ y & 1 & 1 & 1 & -1 & -1 & -1 & 1 & 1 & 1 & -1 \\ \hline \end{array}
序号 xy10121132143−154−165−1761871981109−1
解:
初始化样本权值分布
D
1
=
(
w
11
,
w
12
,
⋯
,
w
110
)
w
1
i
=
0.1
,
i
=
1
,
2
,
⋯
,
10
\begin{aligned} D_{1} &=\left(w_{11}, w_{12}, \cdots, w_{110}\right) \\ w_{1 i} &=0.1, \quad i=1,2, \cdots, 10 \end{aligned}
D1w1i=(w11,w12,⋯,w110)=0.1,i=1,2,⋯,10
对m=1:
- 在权值分布
D
1
D_1
D1的训练数据集上,遍历每个结点并计算分类误差率
e
m
e_m
em,阈值取v=2.5时分类误差率最低,那么基本分类器为:
G 1 ( x ) = { 1 , x < 2.5 − 1 , x > 2.5 G_{1}(x)=\left\{\begin{array}{ll} 1, & x<2.5 \\ -1, & x>2.5 \end{array}\right. G1(x)={1,−1,x<2.5x>2.5 - G 1 ( x ) G_1(x) G1(x)在训练数据集上的误差率为 e 1 = P ( G 1 ( x i ) ≠ y i ) = 0.3 e_{1}=P\left(G_{1}\left(x_{i}\right) \neq y_{i}\right)=0.3 e1=P(G1(xi)=yi)=0.3。
- 计算 G 1 ( x ) G_1(x) G1(x)的系数: α 1 = 1 2 log 1 − e 1 e 1 = 0.4236 \alpha_{1}=\frac{1}{2} \log \frac{1-e_{1}}{e_{1}}=0.4236 α1=21loge11−e1=0.4236
- 更新训练数据的权值分布:
D 2 = ( w 21 , ⋯ , w 2 i , ⋯ , w 210 ) w 2 i = w 1 i Z 1 exp ( − α 1 y i G 1 ( x i ) ) , i = 1 , 2 , ⋯ , 10 D 2 = ( 0.07143 , 0.07143 , 0.07143 , 0.07143 , 0.07143 , 0.07143 , 0.16667 , 0.16667 , 0.16667 , 0.07143 ) f 1 ( x ) = 0.4236 G 1 ( x ) \begin{aligned} D_{2}=&\left(w_{21}, \cdots, w_{2 i}, \cdots, w_{210}\right) \\ w_{2 i}=& \frac{w_{1 i}}{Z_{1}} \exp \left(-\alpha_{1} y_{i} G_{1}\left(x_{i}\right)\right), \quad i=1,2, \cdots, 10 \\ D_{2}=&(0.07143,0.07143,0.07143,0.07143,0.07143,0.07143,\\ &0.16667,0.16667,0.16667,0.07143) \\ f_{1}(x) &=0.4236 G_{1}(x) \end{aligned} D2=w2i=D2=f1(x)(w21,⋯,w2i,⋯,w210)Z1w1iexp(−α1yiG1(xi)),i=1,2,⋯,10(0.07143,0.07143,0.07143,0.07143,0.07143,0.07143,0.16667,0.16667,0.16667,0.07143)=0.4236G1(x)
对于m=2:
- 在权值分布
D
2
D_2
D2的训练数据集上,遍历每个结点并计算分类误差率
e
m
e_m
em,阈值取v=8.5时分类误差率最低,那么基本分类器为:
G 2 ( x ) = { 1 , x < 8.5 − 1 , x > 8.5 G_{2}(x)=\left\{\begin{array}{ll} 1, & x<8.5 \\ -1, & x>8.5 \end{array}\right. G2(x)={1,−1,x<8.5x>8.5 - G 2 ( x ) G_2(x) G2(x)在训练数据集上的误差率为 e 2 = 0.2143 e_2 = 0.2143 e2=0.2143
- 计算 G 2 ( x ) G_2(x) G2(x)的系数: α 2 = 0.6496 \alpha_2 = 0.6496 α2=0.6496
- 更新训练数据的权值分布:
D 3 = ( 0.0455 , 0.0455 , 0.0455 , 0.1667 , 0.1667 , 0.1667 0.1060 , 0.1060 , 0.1060 , 0.0455 ) f 2 ( x ) = 0.4236 G 1 ( x ) + 0.6496 G 2 ( x ) \begin{aligned} D_{3}=&(0.0455,0.0455,0.0455,0.1667,0.1667,0.1667\\ &0.1060,0.1060,0.1060,0.0455) \\ f_{2}(x) &=0.4236 G_{1}(x)+0.6496 G_{2}(x) \end{aligned} D3=f2(x)(0.0455,0.0455,0.0455,0.1667,0.1667,0.16670.1060,0.1060,0.1060,0.0455)=0.4236G1(x)+0.6496G2(x)
对m=3:
- 在权值分布
D
3
D_3
D3的训练数据集上,遍历每个结点并计算分类误差率
e
m
e_m
em,阈值取v=5.5时分类误差率最低,那么基本分类器为:
G 3 ( x ) = { 1 , x > 5.5 − 1 , x < 5.5 G_{3}(x)=\left\{\begin{array}{ll} 1, & x>5.5 \\ -1, & x<5.5 \end{array}\right. G3(x)={1,−1,x>5.5x<5.5 - G 3 ( x ) G_3(x) G3(x)在训练数据集上的误差率为 e 3 = 0.1820 e_3 = 0.1820 e3=0.1820
- 计算 G 3 ( x ) G_3(x) G3(x)的系数: α 3 = 0.7514 \alpha_3 = 0.7514 α3=0.7514
- 更新训练数据的权值分布:
D 4 = ( 0.125 , 0.125 , 0.125 , 0.102 , 0.102 , 0.102 , 0.065 , 0.065 , 0.065 , 0.125 ) D_{4}=(0.125,0.125,0.125,0.102,0.102,0.102,0.065,0.065,0.065,0.125) D4=(0.125,0.125,0.125,0.102,0.102,0.102,0.065,0.065,0.065,0.125)
于是得到:
f
3
(
x
)
=
0.4236
G
1
(
x
)
+
0.6496
G
2
(
x
)
+
0.7514
G
3
(
x
)
f_{3}(x)=0.4236 G_{1}(x)+0.6496 G_{2}(x)+0.7514 G_{3}(x)
f3(x)=0.4236G1(x)+0.6496G2(x)+0.7514G3(x),分类器
sign
[
f
3
(
x
)
]
\operatorname{sign}\left[f_{3}(x)\right]
sign[f3(x)]在训练数据集上的误分类点的个数为0。
于是得到最终分类器为:
G
(
x
)
=
sign
[
f
3
(
x
)
]
=
sign
[
0.4236
G
1
(
x
)
+
0.6496
G
2
(
x
)
+
0.7514
G
3
(
x
)
]
G(x)=\operatorname{sign}\left[f_{3}(x)\right]=\operatorname{sign}\left[0.4236 G_{1}(x)+0.6496 G_{2}(x)+0.7514 G_{3}(x)\right]
G(x)=sign[f3(x)]=sign[0.4236G1(x)+0.6496G2(x)+0.7514G3(x)]
- 针对结果的分析:
因为我们Boosting是一个串联的学习器,我们现在产生了3个学习器,M1,M2,M3。最后判段逻辑是,当M1=M2时,取M1的结果;当M1≠M2时,取M3。针对序号1、2、3、10的结果,M1、M2是判断正确的所以取M1.又比如序号4、5、6、7、8、9,M1与M2结果刚好相反,因此取M3。M3的分类结果是正确的。因此,当M=3时,就没有误分点了
利用sklearn对Adaboost算法进行建模
# 引入数据科学相关工具包:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use("ggplot")
%matplotlib inline
import seaborn as sns
from sklearn.preprocessing import LabelEncoder
from sklearn import datasets
wine = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data",header=None)
wine.columns = ['Class label', 'Alcohol', 'Malic acid', 'Ash', 'Alcalinity of ash','Magnesium', 'Total phenols','Flavanoids', 'Nonflavanoid phenols',
'Proanthocyanins','Color intensity', 'Hue','OD280/OD315 of diluted wines','Proline']
# 数据查看:
print("Class labels",np.unique(wine["Class label"]))
wine.head()
Class labels [1 2 3]
Class label | Alcohol | Malic acid | Ash | Alcalinity of ash | Magnesium | Total phenols | Flavanoids | Nonflavanoid phenols | Proanthocyanins | Color intensity | Hue | OD280/OD315 of diluted wines | Proline | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 14.23 | 1.71 | 2.43 | 15.6 | 127 | 2.80 | 3.06 | 0.28 | 2.29 | 5.64 | 1.04 | 3.92 | 1065 |
1 | 1 | 13.20 | 1.78 | 2.14 | 11.2 | 100 | 2.65 | 2.76 | 0.26 | 1.28 | 4.38 | 1.05 | 3.40 | 1050 |
2 | 1 | 13.16 | 2.36 | 2.67 | 18.6 | 101 | 2.80 | 3.24 | 0.30 | 2.81 | 5.68 | 1.03 | 3.17 | 1185 |
3 | 1 | 14.37 | 1.95 | 2.50 | 16.8 | 113 | 3.85 | 3.49 | 0.24 | 2.18 | 7.80 | 0.86 | 3.45 | 1480 |
4 | 1 | 13.24 | 2.59 | 2.87 | 21.0 | 118 | 2.80 | 2.69 | 0.39 | 1.82 | 4.32 | 1.04 | 2.93 | 735 |
数据预处理
# 仅仅考虑2,3类葡萄酒,去除1类
wine = wine[wine['Class label'] != 1]
y = wine['Class label'].values
X = wine[['Alcohol','OD280/OD315 of diluted wines']].values
分类标签变为二进制编码
le=LabelEncoder()
y=le.fit_transform(y)
划分训练集与测试集
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.2,random_state=1,stratify=y)# stratify参数代表了按照y的类别等比例抽样
使用单一决策树建模
from sklearn.tree import DecisionTreeClassifier
tree=DecisionTreeClassifier(criterion='entropy'
,random_state=30
,max_depth=1)
from sklearn.metrics import accuracy_score
tree=tree.fit(X_train,y_train)
y_train_pred=tree.predict(X_train)
y_test_pred=tree.predict(X_test)
tree_train=accuracy_score(y_train,y_train_pred)
tree_test=accuracy_score(y_test,y_test_pred)
print('Decision tree train/test accuracies %.3f/%.3f' % (tree_train,tree_test))
Decision tree train/test accuracies 0.916/0.875
使用sklearn实现Adaboost(基分类器为决策树)
- 相关参数详解Adaboost参数详解-中文
'''
AdaBoostClassifier相关参数:
base_estimator:基本分类器
object, default = None
建立增强集成的基础估计器。需要支持示例权重,以及适当的classes_和n_classes_属性。如果没有,那么基础估计器是DecisionTreeClassifier(max_depth=1)
n_estimators:终止迭代的次数
int, default = 50
终止推进的估计器的最大数目。如果完全拟合,学习过程就会提前停止。
learning_rate:学习率
float, default = 1
学习率通过learning_rate缩小每个分类器的贡献程度。learning_rate和n_estimators之间存在权衡关系。
algorithm:训练的相关算法
{'SAMME', 'SAMME.R'}, default = 'SAMME.R'
若为"SAMME.R"则使用real bossting算法。base_estimator必须支持类概率的计算。若为SAMME,则使用discrete boosting算法。SAMME.R算法的收敛速度通常比SAMME快,通过更少的增强迭代获得更低的测试误差。
random_state:随机种子
'''
from sklearn.ensemble import AdaBoostClassifier
ada=AdaBoostClassifier(base_estimator=tree
,n_estimators=300
,learning_rate=0.1
,random_state=30)
ada.fit(X_train,y_train)
y_train_pred=ada.predict(X_train)
y_test_pred=ada.predict(X_test)
ada_train=accuracy_score(y_train,y_train_pred)
ada_test=accuracy_score(y_test,y_test_pred)
print('Adaboost train/test accuracies %.3f/%.3f' % (ada_train,ada_test))
Adaboost train/test accuracies 1.000/0.917
结果分析:单层决策树似乎对训练数据欠拟合,而Adaboost模型正确地预测了训练数据的所有分类标签,而且与单层决策树相比,Adaboost的测试性能也略有提高。
图示Adaboost在训练集和测试集上的性能差异
x_min = X_train[:, 0].min() - 1
x_max = X_train[:, 0].max() + 1
y_min = X_train[:, 1].min() - 1
y_max = X_train[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),np.arange(y_min, y_max, 0.1))
f, axarr = plt.subplots(nrows=1, ncols=2,sharex='col',sharey='row',figsize=(12, 6))
for idx, clf, tt in zip([0, 1],[tree, ada],['Decision tree', 'Adaboost']):
clf.fit(X_train, y_train)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
axarr[idx].contourf(xx, yy, Z, alpha=0.3)
axarr[idx].scatter(X_train[y_train==0, 0],X_train[y_train==0, 1],c='blue', marker='^')
axarr[idx].scatter(X_train[y_train==1, 0],X_train[y_train==1, 1],c='red', marker='o')
axarr[idx].set_title(tt)
axarr[0].set_ylabel('Alcohol', fontsize=12)
plt.tight_layout()
plt.text(0, -0.2,s='OD280/OD315 of diluted wines',ha='center',va='center',fontsize=12,transform=axarr[1].transAxes)
plt.show()
注解笔记:
- 1.np.c_与np.r_
np.r_()是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等。
np.c_()是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。
从图中可以看出,单层决策树的决策边界为直线,而Adaboost的决策边界为曲线,Adaboost的分类性能直观上有所提升
adaboost是提高了模型复杂度从而降低偏差。因此在训练集和测试集之间的性能存在较大的差距
总结
这是集成学习中很重要的章节,开始对Boost相关集成算法进行学习。通过结合清华大学的数据库挖掘课程进行同步相关算法的学习,能够有效的加深对这段时间较难算法的学习。比如,在本次进行的算法实例中,通过对Boosting的补充学习,明白了最后的结果含义。如果自己看书或许不能明白!这一路径需要保持。同时,开始对这一系列课程进行学习,加油,继续!