集成学习—Adaboost(理解与应用)

在上一篇集成学习—Adaboost(论文研读)中已经将Adaboost的原始论文精读了一遍,这篇博客主要是对Adaboost算法(主要是二分类的Adaboost)进行更深入的理解和推导,以及尝试下关于它的基于Python的代码的应用。

模型回顾

Adaboost算法,全称为the adaptive boosting algorithm,它是boosting算法中的代表。对于boosting来说,由于它是一个串行生成的集成学习方法,主要有两方面需要考虑:一是在每一轮中如何改变训练数据的权值或者概率分布;二是如何将弱学习器组合成一个强学习器。Adaboost的做法是:提高那些被前一轮弱学习器错误学习的样本的权重,对于那些弱学习器学习的不够好的数据,下一轮的弱学习器会重点学习;弱学习器的组合方式,Adaboost采用的是加权多数表决的方法,即学习错误率小的弱分类器权重较大,在表决中起较大作用,相反错误率大的权重小,作用也相对较小。Adaboost的巧妙之处就在于它将这些想法自然且有效地实现在一种算法中。

在原始的论文中,Freund和Schapire将二分类 { 0 , 1 } \{0,1\} 问题的Adaboost算法的伪代码描述为:
在这里插入图片描述

简单来说,算法的步骤如下:首先需要对数据样本的权重向量赋初值,一般是设为均匀分布,即 w i 1 = 1 / N , i = 1 , , N w_i^1=1/N, i=1,\dots,N ,然后在 t t 次迭代中计算分布 p t p^t ,对第 t t 个弱学习器赋予这个分布,得到弱学习器的输出假设 h t h_t ,然后再计算这个假设的(相对于当前分布的)加权错误率 ε t \varepsilon_t ,令 β t = ε t / ( 1 ε t ) \beta_t=\varepsilon_t/(1-\varepsilon_t) ,再对下一轮的新权重进行更新,更新规则为 w i t + 1 = w i t β t 1 h t ( x i ) y i w_i^{t+1}=w_i^t\beta_t^{1-|h_t(x_i)-y_i|} ,这样经过 T T 次迭代,得到 T T 个弱学习器,最后将他们的结果进行加权,输出最后算法的最终假设 h f ( x ) h_f(x) ,最后 h f ( x ) = 1 h_f(x)=1 的条件为 t = 1 T ( l o g 1 / β t ) h t ( x ) t = 1 T ( l o g 1 / β t ) 1 / 2 {\sum_{t=1}^T(log1/\beta_t)h_t(x)\over \sum_{t=1}^T(log1/\beta_t)}\geqslant1/2 即所有弱假设的加权和若大于0.5则判定最后输出的标签为1,否则为0。

总的来看,这个算法的迭代过程主要更新的是数据分布的权重 w w 以及每个弱分类器的权重 l o g ( 1 / β ) log(1/\beta) 。具体来说,在弱学习器给出弱假设的结果后,若计算出来的错误率 ε t \varepsilon_t 较大,则算法第4步中计算出来的 β t \beta_t 就较大,同时它的精度 1 h t ( x i ) y i 1-|h_t(x_i)-y_i| 就小,因而权重更新中的系数 β t 1 h t ( x i ) y i \beta_t^{1-|h_t(x_i)-y_i|} 就趋于1(较大,因为弱学习器是PAC学习的,所以关于它的误差假设是 ε t 1 / 2 γ \varepsilon_t\leqslant1/2-\gamma ,其中 γ > 0 \gamma>0 ,因此这样定义的 β [ 0 , 1 ] \beta\in[0,1] ),导致下一步更新后的权重较大,从而上一步分类错误的那些数据将在下一个弱学习器中重点学习,由此一点点改善数据的分类错误率;此外,根据每次迭代得到的 β t \beta_t 值,得到每个弱分类器在最后假设中的权重,错误率较大的弱分类器它的 β t \beta_t 就较大,因此 l o g ( 1 / β t ) log(1/\beta_t) 较小,意味着它在最后的决策中占得比重较小,最后的结果由 T T 个弱分类器加权投票得到,更多的是依靠分类正确率高的那些弱分类器的信息。

加性模型

Adaboost算法有多种推导方式,周志华老师的《机器学习》上说比较容易理解的是基于“加性模型”,即基学习器的线性组合来最小化指数损失函数的方法,这个方法是由Friedman等人在2000年正式发表,而且这个理解方法是现在大多数学习Adaboost算法接触到的,因此这里也对针对这篇文章进行了简单的理解。

在Additive logistic regression: a statistical view of boosting这篇文章中,作者主要是从统计学的角度出发来解释Adaboost算法,说明了Adaboost实际上是一个加性逻辑回归模型,它用牛顿方法来最优化一个特别的指数损失函数,文章最后还提出了提升决策树(boosting decision tree),但这里不做讨论。

Friedman总结的Adaboost算法如下(实际上与上图的是一样的,除了权重 w w 的更新的系数表示上有些微差别,Freund的权重不需要做归一化处理,但是这里需要):在这里插入图片描述

理解与推导过程:

Adaboost算法是模型为加性模型、损失函数为指数函数、学习算法为前向分步算法(forward stage-wise algorithm)时的二分类学习方法(摘自李航老师的《统计学习方法》)。由上图知道Adaboost最终的分类器为 M M 个弱学习器 f m ( x ) f_m(x) 的加权投票 F ( x ) = m = 1 M c m f m ( x ) F(x)=\sum_{m=1}^Mc_mf_m(x) ,它的目标是最小化指数损失函数 E ( e y F ( x ) ) E(e^{-yF(x)})

因此,论文中首先给出了一个有用的引理:

引理1 E ( e y F ( x ) ) E(e^{-yF(x)}) F ( x ) = 1 2 l o g P ( y = 1 x ) P ( y = 1 x ) (1) F(x)={1\over2}log{P(y=1|x)\over P(y=-1|x)}\tag{1} 处达到最小。因此, P ( y = 1 x ) = e F ( x ) e F ( x ) + e F ( x ) ,    P ( y = 1 x ) = e F ( x ) e F ( x ) + e F ( x ) P(y=1|x)={e^{F(x)}\over e^{-F(x)}+e^{F(x)}},\ \ P(y=-1|x)={e^{-F(x)}\over e^{-F(x)}+e^{F(x)}} 证明:因为在 y y x x 的联合分布上求期望,可以充分得到在 x x 上的条件期望最小值,所以对 E ( e y F ( x ) x ) = P ( y = 1 x ) e F ( x ) + P ( y = 1 x ) e F ( x ) E(e^{-yF(x)}|x)=P(y=1|x)e^{-F(x)}+P(y=-1|x)e^{F(x)} E ( e y F ( x ) x ) F ( x ) = P ( y = 1 x ) e F ( x ) + P ( y = 1 x ) e F ( x ) {\partial E(e^{-yF(x)}|x)\over \partial F(x)}=-P(y=1|x)e^{-F(x)}+P(y=-1|x)e^{F(x)} 为0即可得到结论。

一般来说逻辑回归模型不像式(1)中有一个 1 2 1\over 2 的因子,但是通过上下同乘 e F ( x ) e^{F(x)} 可以得到通常意义下的逻辑回归模型 p ( x ) = e 2 F ( x ) 1 + e 2 F ( x ) p(x)={e^{2F(x)}\over1+e^{2F(x)}} 因此说这两个模型是等价的。这也是为什么作者提出来Adaboost实际上可以解释为加性的逻辑回归模型。然后再给出了解释这个算法过程的命题(命题的证明就是算法的主要推导过程)。

命题1 离散的Adaboost算法产生了自适应的牛顿迭代更新来最小化 E ( e y F ( x ) ) E(e^{-yF(x)}) ,这是对一个加性逻辑回归模型的分步贡献。

命题1的证明如下
J ( F ) = E [ e y F ( x ) ] J(F)=E[e^{-yF(x)}] ,假设我们已经有了一个当前的分类器 F ( x ) F(x) ,想要得到一个改进的分类器 F ( x ) + c f ( x ) F(x)+cf(x) ,也就是在当前分类器中继续添加一个弱学习器,希望新的弱学习器在上一步更新后的样本分布中能最小化损失函数 J ( F + c f ) J(F+cf) ,也就是使前向分步算法(stage-wise)得到的 c c f f 使 F + c f F+cf 在训练集上的指数损失最小。现在的任务就是证明使得损失函数 J ( F + c f ) J(F+cf) 达到最小的 c ^ \hat c f ^ \hat f ,就是Adaboost算法所得到的 c c f f ,证明可以分为两步:

首先,求 f ^ \hat f 。对于固定的 c c x x ,我们在 f ( x ) = 0 f(x)=0 处将 J ( F + c f ) J(F+cf) 泰勒展开到二阶: J ( F + c f ) = E [ e y ( F ( x ) + c f ( x ) ) ] = E [ e y F ( x ) e c y f ( x ) ] E [ e y F ( x ) ( 1 c y f ( x ) + c 2 y 2 f 2 ( x ) 2 ) ] = E [ e y F ( x ) ( 1 c y f ( x ) + c 2 f 2 ( x ) 2 ) ]    ( 因为 y { + 1 , 1 } ) \begin{aligned} J(F+cf)&=E[e^{-y(F(x)+cf(x))}]\\ &=E[e^{-yF(x)}e^{-cyf(x)}]\\ &\approx E[e^{-yF(x)}(1-cyf(x)+{c^2y^2f^2(x)\over2})]\\ &= E[e^{-yF(x)}(1-cyf(x)+{c^2f^2(x)\over2})]\ \ (\text{因为}y\in\{+1,-1\}) \end{aligned} 关于 f ( x ) { + 1 , 1 } f(x)\in\{+1,-1\} 逐点最小化,得到最优的弱学习器为: f ^ ( x ) = arg min f E w [ 1 c y f ( x ) + c 2 f 2 ( x ) 2 x ] = arg min f E w [ ( y c f ( x ) ) 2 x ] = arg min f E w [ ( y f ( x ) ) 2 x ]    ( 因为 f ( x ) { + 1 , 1 } ) \begin{aligned} \hat{f}(x)&=\arg\min_f E_w[1-cyf(x)+{c^2f^2(x)\over2}|x]\\ &=\arg\min_f E_w[(y-cf(x))^2|x]\\ &=\arg\min_f E_w[(y-f(x))^2|x]\ \ (\text{因为}f(x)\in\{+1,-1\}) \end{aligned} 这里的 w ( y x ) = e x p ( y F ( x ) ) / E [ e x p ( y F ( x ) ) ] w(y|x)=exp(-yF(x))/E[exp(-yF(x))] ,因此这里得到了下一步最佳的弱分类器 f ^ \hat{f}

其次,求 c ^ \hat c 。给定 f ^ { + 1 , 1 } \hat{f}\in\{+1,-1\} ,我们就可以直接最小化 J ( F + c f ^ ) = E [ e y F ( x ) e c y f ( x ) ] J(F+c\hat{f})=E[e^{-yF(x)}e^{-cyf(x)}] 来求得系数 c c ,因为 e y F ( x ) e^{-yF(x)} 部分与 c c f ^ \hat{f} 都无关,因此最小化时可以不用考虑,相当于常数,因此: c ^ = arg min c E w [ e c y f ^ ( x ) ] \begin{aligned} \hat{c}&=\arg\min_c E_w[e^{-cy\hat{f}(x)}] \end{aligned} 利用引理1的方法可以得到 E ( e c y f ^ ( x ) x ) = P ( y = f ^ x ) e c + P ( y f ^ x ) e c E(e^{-cy\hat{f}(x)}|x)=P(y=\hat{f}|x)e^{-c}+P(y\neq\hat{f}|x)e^{c} E ( e c y f ^ ( x ) x ) c = P ( y = f ^ x ) e c + P ( y f ^ x ) e c {\partial E(e^{-cy\hat{f}(x)}|x)\over \partial c}=-P(y=\hat{f}|x)e^{-c}+P(y\neq\hat{f}|x)e^{c} 等于0,即可得 c ^ = 1 2 l o g P ( y = f ^ x ) P ( y f ^ x ) = 1 2 l o g 1 e e \hat{c}={1\over2}log{P(y=\hat{f}|x)\over P(y\neq\hat{f}|x)}={1\over2}log{1-e\over e} 其中 e = P ( y f ^ x ) = E w [ 1 ( y f ^ ) ] e=P(y\neq\hat{f}|x)=E_w[1_{(y\neq\hat{f})}] 表示错误率,这也是算法图中(b)的那一步的更新规则,因此就得到了最后输出结果 F ( x ) F(x) 的更新规则: F ( x ) F ( x ) + 1 2 l o g 1 e e f ^ ( x ) F(x)\leftarrow F(x)+{1\over2}log{1-e\over e}\hat{f}(x)

最后,对于权重的更新,由 J ( F + c f ) J(F+cf) 的定义就可以得到更新的规则: w ( y x ) w ( y x ) e c ^ y f ^ ( x ) w(y|x)\leftarrow w(y|x)\cdot e^{-\hat{c}y\hat{f}(x)} 再加上归一化即可。由于 y f ^ ( x ) = 2 × 1 ( y f ^ ( x ) ) 1 y\hat f(x)=2\times1_{(y\neq \hat f(x))}-1 ,因此上面的更新规则等价于: w ( y x ) w ( y x ) e x p ( l o g ( 1 e e ) 1 ( y f ^ ( x ) ) ) w(y|x)\leftarrow w(y|x)\cdot exp\bigg(log\big({1-e\over e}\big)1_{(y\neq \hat f(x))}\bigg) 与给出的Adaboost算法图中的一样。

至此,证明了二分类的Adaboost的过程。

Adaboost算法优缺点:

  1. 运行很快;
  2. 十分灵活,它提供的是一个框架,它的弱学习器可以搭配各种学习算法,例如决策树、SVM等;
  3. 多用途,可以用来做二分类、多分类、回归等,也适用于文本数据、连续数据、离散数据等;
  4. 不需要知道弱学习器的任何先验知识;
  5. 易用,需要调节的参数很少,只有一个迭代次数;
  6. 有效性,训练的错误率随着迭代次数的增加呈指数级下降,因此很快就能达到很好的效果;
  7. 大部分时候能抵抗过拟合。

但是主要的缺点也很明显:

  1. Adaboost的性能取决于数据和使用的弱学习器,如果弱学习器太强可能会导致算法过拟合,太弱可能会欠拟合;
  2. 对异常样本敏感,异常样本在迭代中可能会获得较高的权重,弱学习器在这些样本的关注较多,影响最终的预测准确性。

Python与应用

  • 搭配Adaboost的基学习器一般都选择决策树,此时这样的算法Adaboost-Decision tree,总结起来就是:Adaboost + 根据权重sampling + 剪枝的decision tree(具体可以参考机器学习技法视频课)。使用Adaboost的方法计算每次的数据权重,然后根据这个权重采样出部分的样本给决策树进行学习,这个决策树一般会限制它的高度,让每个弱学习器不要学得太好,最后综合投票这些弱学习器的结果。

  • Python:sklearn.ensemble模块中包含了AdaBoost, 包括用于分类的AdaBoostClassifier以及用于回归的AdaBoostRegressor。由于这里使用的数据集是连续回归问题,因此这里只讨论AdaBoostRegressor,它的算法主要是用Drucker, Harris. “Improving Regressors Using Boosting Techniques.” Fourteenth International Conference on Machine Learning 1997. 的这篇论文中的AdaBoost.R2算法,基本上和Freund提出的AdaBoost.R类似,不过弱学习器默认选择的是CART决策树,使用MSE作为划分准则,具体可以参考之前的博客描述的CART决策树的内容。

  • 数据来源及说明:天池新人赛工业蒸汽量预测,经脱敏后的锅炉传感器采集的数据(采集频率是分钟级别),根据锅炉的工况,预测产生的蒸汽量。数据分成训练数据(train.txt)和测试数据(test.txt),其中字段”V0”-“V37”,这38个字段是作为特征变量,”target”作为目标变量。选手利用训练数据训练出模型,预测测试数据的目标变量,排名结果依据预测结果的MSE(mean square error)。

AdaBoostRegressor某些参数说明,具体可参考文档说明

  • base_estimator:基学习器,可选(默认为回归决策树,max_depth=3,分类的话默认为决策树桩)
  • n_estimators:终止boosting的最大估计数,也就是基学习器的个数。如果完全匹配,则提前停止学习。可选(默认为50)
  • learning_rate:学习率,学习率缩小了每个回归器的贡献,在学习率和学习器的个数之间有一个平衡, 可选(默认为1.)
  • loss:在每次boosting迭代后更新权重时使用的损失函数。可选线性、平方、指数损失函数,即{‘linear’, ‘square’, ‘exponential’},可选(默认为线性损失)
  • X:训练集样本,数组类型的稀疏矩阵,shape = [n_samples, n_features]
  • y:目标变量(实数),数组类型,shape = [n_samples]
  • sample_weight:初始的样本权重,数组类型,shape = [n_samples],可选,默认为 1 / n s a m p l e s 1 / n_{samples}

代码


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split #划分数据集
from sklearn.metrics import mean_squared_error #MSE,与下面的定义的是一样的

#训练集和测试集导入
training_data = pd.read_csv('zhengqi_train.txt', sep='\t')
test_data = pd.read_csv('zhengqi_test.txt', sep='\t')
training_x = training_data.iloc[:,0:38] #用索引取前38列得到训练数据
training_target = training_data['target'] #最后一列为label
#选出30%为验证集(validation data set)
x_train, x_val, y_train, y_val = train_test_split(training_x, training_target, test_size=0.3, random_state=0)

#mean square error
def MSE(predict_x, y):
    m = len(predict_x)
    return np.sum((predict_x-y)**2)/(m) 

'''============================ Adaboost =============================='''
from sklearn.ensemble import AdaBoostRegressor

#随着参数变化的验证集MSE
def n_estimators(n):
    rg = AdaBoostRegressor(n_estimators = n, loss = 'square')
    rg.fit(x_train, y_train) 
    return MSE(rg.predict(x_val), y_val)

#画图选择参数
a = np.linspace(10, 310, num=31)
b = np.zeros((31,))
for i in range(31):
    b[i,] = n_estimators(int(a[i,]))
plt.scatter(a, b)
plt.plot(a, b)
plt.scatter(a[np.argmin(b),], min(b), c='r') #最优点标红


#用较优的参数来训练模型
best_n = int(a[np.argmin(b),]) #返回最优的参数值
rg = AdaBoostRegressor(n_estimators=best_n, loss='square')
rg.fit(x_train, y_train) 

MSE(rg.predict(x_val), y_val) #验证集MSE为0.15772145020073947


'''保存测试集的预测结果'''
predict_x = pd.DataFrame(rg.predict(test_data))
predict_x.to_csv('/Users/Desktop/Adaboost_results1.txt', header=0, index=0) #不保留列名和索引

根据验证集的MSE变化,选择最优的参数为80
在这里插入图片描述

AdaBoost分类

稍微尝试了下AdaBoostClassifier,这里给出了一个比较直观的例子,由于可视化的原因,这里的数据源采用的是吴恩达|机器学习作业7.0.k-means聚类的数据源,但是因为k-means是无监督学习方法,Adaboost是监督学习方法,这个数据是无标签的数据,因此这里先用k-means聚类的结果当做是这个数据源的标签,然后再改变Adaboost中的弱学习器的个数来观察它的效果。具体代码如下:

import numpy as np
import matplotlib.pyplot as plt
import scipy.io as scio
from sklearn.ensemble import AdaBoostClassifier
from sklearn.cluster import MiniBatchKMeans #Mini Batch K-Means聚类


data = scio.loadmat('C:/Users/dell/Desktop/ex7data2.mat')
x = data['X']


'''可视化数据集'''
plt.scatter(x[:,0], x[:,1], marker='o', c='w', edgecolors='k')


'''聚类得到数据标签'''
K = 3 #设置聚类数量
kmeans = MiniBatchKMeans(K) 
kmeans.fit(x) 

#画出标签之后的数据
def plot_idx(x, idx):
    color = ['r', 'g', 'b'] #颜色序列
    for i in range(3):
        plt.scatter(x[idx==i][:,0], x[idx==i][:,1], marker='o', c='w', edgecolors=color[i-1])

plot_idx(x, kmeans.predict(x))      
  

#得到数据标签
y = kmeans.predict(x)


'''Adaboost分类'''
def plot_process(n):
    clf = AdaBoostClassifier(n_estimators=n)
    clf.fit(x, y)
    plot_idx(x, clf.predict(x))


plot_process(4) #改变这里的弱学习器个数,观察分类效果随学习器个数的变化

首先数据集是这样的:
在这里插入图片描述
然后用k-means聚类得到数据标签:
在这里插入图片描述

最后改变Adaboost中弱学习器的个数,从1到4个,观察算法的学习效果:
在这里插入图片描述在这里插入图片描述
很明显当只有一个弱学习器的时候显然效果不是很好,两个的时候显著提升,从弱学习器个数增加到3个开始,这个算法基本上已经把分类错误的样本全都纠正过来了,但由于这个数据集十分简单,所以学习器的个数不需要很多,在面对复杂的数据集的时候Adaboost表现还是值得期待的。

参考资料:

《机器学习》周志华
《统计学习方法》李航
Friedman J, Hastie T, Tibshirani R. Additive logistic regression: a statistical view of boosting (With discussion and a rejoinder by the authors)[J]. Annals of Statistics, 2000, 28(2):337-374.
Drucker, Harris. “Improving Regressors Using Boosting Techniques.” Fourteenth International Conference on Machine Learning 1997.
机器学习技法视频课

发布了32 篇原创文章 · 获赞 33 · 访问量 6624

猜你喜欢

转载自blog.csdn.net/weixin_44750583/article/details/100728574
今日推荐