Adaboost 详解

Adaboost 详解

本文会详细介绍 Adaboost (Adaptive Boosting) 这一 ensemble 模型(属于 boosting)。

1. 算法简介

boosting

这属于集成(ensemble)算法中的提升(boosting)方法,是迭代算法。我们每一轮迭代寻找一个合适的 weak learner 集成到模型中(本质上是梯度下降),通过 T 轮的迭代来集成出一个强分类器,这是一个 boosting 提升过程。

adaptive

这涉及实现提升的细节。所有的样本都有自己的 weight,初始化时一致。training 时每一轮迭代,我们要更新所有样本的 weight,模型正确判别的样本 weight 减小,而错误判别的样本 weight 增加。这就是 Adaboost 的核心了,这非常像一个我们依据错误经验持续学习的过程。

steepest decent with approximate functional gradient

本算法数学上可以解释为类似在函数空间上做最速梯度下降。每次迭代中,选择的 weak classifier 函数其实是当前负梯度方向,而对应的权值则是在此方向使 loss 减少最多的步长(greedy~)。这里使用的 loss function 是 exponential function,而 GBDT(gradient boost decision tree)推广到了其他 error function,也可以说 AdaBoost 是一种 GBDT。

2. 训练流程详解

实际上不同 Adaboost 版本的细节策略略有不同,这里讲解的是Viola-Jones 的 Real-Time Face Detection 论文(AdaBoost 的一个著名的成功应用)中使用的版本。

2.1 initialization

假设我们有 N 个样本 D=[(x1,y1),(x2,y2),...,(xn,yn)] ,其中 x 为特征向量, y 为 0/1 分类的 label
我们要迭代训练 T 轮,样本集 weight 向量 ut=1,2...T 初始化为 ut=1=[1N,1N,...,1N] ,即 N 个样本的重要性一开始是一致的,且和为 1。
(这是正例负例等比例的情况,不等比例时,可以假设正例 M 个而负例 L 个则初始化为 ut=1=[12M,12M,...,12L,12L] ,即保证正负例各自总权重皆为 12

2.2 training iteration

(对于第 t 轮迭代)

  • 选取 weak classifier 并计算 error:
    根据当前样本权重 ut 获取一个当前最好的 weak classifier ht(x) 函数(训练一个 decision tree 或者 decision stump),函数输出 0/1 结果,其相关 error 为 ϵt=1NNi=1uti|ht(xi)yi| 。这里应该有 0ϵt<0.5 ,因为我们要求 weak classifier 至少优于乱猜。

  • 更新训练集样本权重:
    针对 ϵt ,我们设置一个核心变量 scaling factor st=1ϵtϵt 1<st< ,并更新样本集 weight :

    ut+1i={utiuti/stht(xi)yi, incorrect caseht(xi)=yi, correct case

    除了上述公式,之后还会对 ut+1i 重新 normalize ut+1i=ut+1iNj=1ut+1j ,保证和为 1。
    可以注意到这里的 adaptive 的机制:本轮迭代 ht(x) 错误分类的样本的 weight 会增大(scale up incorrect),正确分类样本的 weight 相应减小(scale down correct)

  • 确认此 weak classifer 权重:
    会根据本轮 ht(x) 的表现给予它一个权重 αt=ln st 0<αt< );当 ϵt=0 时, αt= 即对于完美的 classifier 我们可以给到无穷大的权重;而当 ϵt=0.5 时, αt=0 即对于乱猜的 classifier 直接不予集成。可见 error 越小的分类器权重越大。

2.3 aggregation

历经 T 轮训练后,将 T 个 ht(x) 线性集成为 strong classifier。实际上集成的参数在迭代过程中已经决定了,这又称为 linearly on the fly with theoretical guarantee,涉及的理论验证之后会详述。

C(xi)={10Ttαtht(xi)12Ttαtotherwise

ht(xi) 是 weak classifier 的 0/1 投票, αtht(xi) 则是加权投票;当所有 weak classifier 对样本的加权投票结果大于整体权值的 12 时,strong classifier 判定样本为 positive,否则为 negative。

3. Weak Classifier

这里把训练流程中的 weak classifier 单独拿出来详细说明。

3.1 Compare to Random Forest

Random Forest 也是 ensemble 集成算法,属于 bagging 类别,通过 bootstrap 来 uniformly aggregate 了一组等权重的 decision tree,然后通过投票给出整体结果。这是使用 classification(输出 0/1 结果) 或者 regression decision tree 都可以。

这个也是 weak classifier 集成为 strong classifier 的过程,但是集成思想和 Adaboost 不一致(bagging vs. boosting)。Adaboost 也可以使用 classification decision tree 作为 weak classifier,但更常用的是更 weaker 的 decision stump。这里还可以想象一下,我们如果给 Adaboost 一个 fully grown 的 decision tree,那么可能会有 ϵ=0s= ,训练就崩坏了~ 所以即使用 decision tree 也要做约束出弱的树,而不是像 Random Forest 那样 fully grown。

3.2 Decision Stump

这个是一个弱弱的 weak classifier,类似只有 1 层的树,只剩一个树桩了。具体公式如下:

hf,p,θ(x)={10if p(f(x)θ)>0otherwise

x 为样本, f(x) 为某一 feature 维度上的取值, θ 为 threshold, p 用于控制 direction。总的来说,decision stump 即选择一个特定 feature 维度,使用简单 threshold 和 direction 来进行决策。无论多么复杂的数据集,只需要3个参数就完全定义了这个弱弱的 decision stump,我们对它的期待只是比随机好一点点, ϵ<12 就好。

3.3 Adaboost 中使用 Decision Stump

Adaboost 常用 decision stump 作 weak classifier,即训练流程中寻找的 h(x) 就是 decision stump。在样本集 N-samples 和 特征空间 M-dimensions 中结合当时样本 weight 学习到最优 h(x) 其实是一个搜索过程,也可以看做是 feature selection;目标是最小化加权 error rate:(VJ 版本的 uti 保证和为 1,所以下式不需要再除以 Ni=1uti

minhϵt=i=1Nuti|ht(xi)yi|

具体应用时还有 2 个性能上的考量:

  • 时间复杂度
    为了优化训练时间,在迭代之前要对M个维度依次排序并 cache 结果,复杂度 O(MNlogN) 。之后每次迭代只需要 O(MN) 就可以找到当前轮 t 最优 ht(x)
  • 空间复杂度
    cache 住的结果如果存在硬盘的话会极大降低搜索速度,我们希望全部放在内存,这里空间复杂度为 O(MN) 。对于 VJ 原论文中 N=1万、M=16万,考虑 int 存储 feature value 则 cost=4×16×108B=6.4GB 。考虑到扩充特征集、扩充数据集、存储结构效能等问题,其实内存要求是很严峻的。

4. 理论支持

(本节参考了 Hsuan-Tien Lin 机器学习技法课程)
Adaboost 凭什么能完成 weak classifiers to strong classifier?凭什么可以收敛?可以越来越好?我们可以从数学层面(steepest decent with approximate functional gradient)找到充分解释,本节详细阐述。

4.1 公式及符号重新定义

之前“2. 训练流程详解”使用的是 VJ 论文中原版公式。这里为了更清晰解释数学理论,会改动一些公式定义:

  • 定义数据 label y 与 classifier h(x) 输出为 -1/+1 而非之前的 0/1,这可以简化很多公式
    • yiht(xi)=1 即表明判定正确
    • 弱分类的 error rate 写作: ϵt=Ni=1uti [yiht(xi)]Ni=1uti ,这里是标准的 rate 了
    • strong classifier 判别公式写作: C(xi)=sign(Ttαtht(xi)) (使用符号函数 sign 直接处理正负向加权投票之和,非常简洁),依然有 αt=ln st
    • 这里将上式中弱分类器加权投票和记作 vote score=Ttαtht(xi) ,后续数学推导使用
  • 改变 scaling factor s 公式为 st=1ϵtϵt ,比之前版本多了一个 square root,取值范围不变,这是为了方便后续数学推导

  • 样本权重 u 公式更改为下述,这个改动也不大,也是为了方便后续数学推导(原公式只考虑 scale up,然后 normalize;现在加入 scale down 而去掉了麻烦的 normalize):

    ut+1i={utistuti/stht(xi)yi, incorrect caseht(xi)=yi, correct case

    然后这里最妙的一步是直接把上式简化为: ut+1i=utisyiht(xi)t

4.2 loss function of AdaBoost

根据 αt=lnst 可以有 st=exp{α}

进而可以对样本权值公式进一步变换:

ut+1i=utisyiht(xi)t=uti(exp{αt})yiht(xi)=utiexp{yiαtht(xi)}

我们假设所有样本权值训练前初始化为 u0i=1N ,则可以得到经过 T 轮训练后的样本权值:
uT+1i=u0it=1Texp(yiαtht(xi))=1Nexp(yit=1Tαtht(xi))

可以看到任意样本 xi 的最终权重与所有 weak classifier 整体 vote score=Tt=1αtht(xi)) 有关,即:
uT+1iexp{yi(vote score on xi)}

上式右侧实际上就是 exponential error:

  • 整体 strong classifer 可以写作 C(xi)=sign(vote score on xi) ,所以 yi(vote score on xi) 的正负表示判定正确或错误
  • yi(vote score on xi) 越大,表示判定越正确,对应的负指数值就越小
  • yi(vote score on xi) 越小,表示判定越错误,对应的负指数值就越大

这实际上就是 Adaboost 的目标就是 exponential error 越来越小(最优化问题),也可以说是让所有样本的权值越来越小;最终 error/loss function 为所有样本的 exponential error 之和:

EADA=i=1NuT+1i=1Ni=1Nexp(yit=1Tαtht(xi))

4.3 loss function 内在意涵

上述的 exponential error 实际上可以找到直观解释。先变化 strong classifier 判别公式如下:

C(xi)=sign(tTαtht(xi))=sign(tTwtϕt(xi))

我们可以把 α 看作线性模型中的 weight,而一系列的 h(x) 看做是 SVM(support vector machine) 中一系列函数映射 ϕ(x) ,SVM 正是利用映射原始数据到其他空间进行线性分割的。结合 SVM 优化目标:
margin=yi(wtransϕ(xi)+b)||w||

比对上式,其实 AdaBoost 包含带符号的非正规化 margin:

signed and unnormalized margin=yiTt=1αtht(xi)

AdaBoost 的目标是让这个 margin 尽量为正数且最大化,也就是让其负指数尽量小于1且最小化,也就是我们之前得到的 error/loss function。
另外,因为使用了 exponential,分类错误的 penalty 呈指数增长 (1,) ;而一旦分类正确,则 penalty 只在 (0,1) 范围。这里有个问题是 AdaBoost 对噪声或脏数据敏感。

4.4 Gradient Descent

标准套路

Gradient Descent 标准套路是我要从当前位置(模型状态)走一小步,让 loss 最快的下降(减少):

min||v||=1E(wt+ηv)E(wt)+ηvE(wt)

  • wt 表示第 t 轮迭代时,当前的 weight vector
  • v 表示走的方向(向量,有 length 约束)
  • η 表示走的步长(标量,一般是一个较小的正数)

目标其实是找到一个让 loss 函数下降最快的方向 v ,上式的约等于号是一阶泰勒展开,再加上其他数学工具可以证明最优的方向是负梯度方向 v=E(wt)

AdaBoost 的 Gradient Boost 套路

AdaBoost 的套路很类似,核心是其通过新加入函数来完成梯度下降,这个函数对应最速下降的方向,而函数的权重对应了下降的步长。AdaBoost 在第 t 轮迭代时,优化目标如下:

minhtEADA=1Ni=1Nexp{yi(τ=1t1ατhτ(xi)+ηht(xi))}

我们在原始 loss function 中新加入一项 ηht(xi) ,就是说在第 t 轮的时候,我们想找一个新的函数 ht(xi) 给出 loss 最速下降的方向(相当于标准套路中的向量 v ), η 则同样是走的步长,整体上就是想优化 loss 而已。以下先对上式进行化简:
minhtEADA=i=1N1Nexp{yi(τ=1t1ατhτ(xi))}exp{yiηht(xi)}=i=1Nutiexp{yiηht(xi)}

然后我们把上式看作: Ni=1utiexp{0+γ}, γ=yiηht(xi) ,即把 γ 作为自变量(这样原函数导数还是原函数~),然后作一阶泰勒展开(或者说对原式作麦克劳林展开):
minhtEADAi=1Nutiexp{0}+i=1Nutiexp{0}(yiηht(xi))=i=1Nutii=1Nutiyiηht(xi)=i=1Nuti+ηi=1Nuti(yiht(xi))

这里注意,在当前第 t 轮,上式左侧的 Ni=1uti 是一个确认的常数,不能再优化了。上式右侧的步长 η 我们先认为它是一个固定值(反正肯定是一个非负实数);这里我们的优化目标变为:
发现一个方向 h(x) ,使得 i=1Nuti(yiht(xi)) 最小

这里特别重要的一点是, ht(x) 确定的是方向,它的 scale 理论上是没有意义,这种情况下我们一般会约束其 scale(例如标准 GBDT 中就会)。但是!在 AdaBoost 中,我们的 ht(x) 本身的输出只是 -1/1 状态,本身就没有 scale 的概念,所以不需要额外约束!

对上述目标进一步变化:

i=1Nuti(yiht(xi))=i=1Nuti [yi=ht(xi)]+i=1Nuti [yiht(xi)]=i=1Nuti+i=1N0 [yi=ht(xi)]+2i=1Nuti [yiht(xi)]=i=1Nuti+2(i=1Nuti)ϵt

经过变换后,上式除了 ϵt 都是 constant,我们要优化的目标只有最右侧的 ϵt=Ni=1uti [yiht(xi)]Ni=1uti

Recap 一下,我们想找到一个方向 ht(x) 使 ϵt 最小。这本身就是 AdaBoost 定义的 weak classifier ht(x) 的选取标准,一般是通过在特征空间上搜索选取 Decision Stump 实现,这一过程近似地获取到最速下降方向。

4.5 Steepest Descent

我们在上一小节中已经确认了让 AdaBoost loss function EADA 最速下降的方向是某一 ht(x) ,我们接下来需要考虑在这个方向要走多大的一步( η )?

对于 Gradient Descent 的标准套路, η 会作为一个训练的超参数(学习率),可以尝试定义不同的值,然后通过 validation 选择合适的学习率;有时还会加入 annealing 退火机制或其他机制来动态调节学习率。但是在 AdaBoost 中,我们每走一步的代价是巨大的:

  • 不能直接计算出负梯度方向,而是通过一个子过程寻找一个近似最优函数方向,耗费时间
  • 需要在最终模型上多集成一个函数,这直接影响最终模型使用性能

所以,我们希望每走一步,都尽可能走最大的一步,尽量减少走的步数。上一小节,我们固定 η ,通过 minhEADA^ 确定了 ht(x) 方向(approximate functional gradient);现在,我们使用这个 ht(x) ,通过 minηEADA^ 来走出减少 loss 的最大的一步(greedily faster),这个套路被称为 Steepest Descent。以下进行最优 η 的推导(注意这里使用了泰勒展开之前的准确 loss 公式):

minηEADA^=i=1Nutiexp{yiηht(xi)}=i=1Nutiexp{η}[yi=ht(xi)]+i=1Nutiexp{η}[yiht(xi)]=(i=1Nuti)((1ϵt)exp{η}+ϵtexp{η})

由于上式只有一个未知数 η ,所以直接求导 EADA^η=0 来解出最优值:
EADA^η=(i=1Nuti)((1ϵt)exp{η}+ϵtexp{η})=0(1ϵt)exp{η}+ϵtexp{η}=0ϵtexp{η}exp{η}=(1ϵt)exp{η}exp{η}=1ϵtϵtη=ln1ϵtϵt

上式即标准的 AdaBoost 计算 weak classifier 相关权重的公式 αt=lnst=ln1ϵtϵt

现在可以完整地说:AdaBoost 每一次迭代,会选取一个函数 ht(x) 近似 loss 最速下降方向,并计算 αt 作为当前最优下降步长,来集成这个新的 weak classifer;本质上是做梯度下降,这是 AdaBoost 的理论基础。

你可能感兴趣的:(机器学习)