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 为特征向量,
我们要迭代训练 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=1N∑Ni=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+1i∑Nj=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 越小的分类器权重越大。- 时间复杂度
为了优化训练时间,在迭代之前要对M个维度依次排序并 cache 结果,复杂度 O(M⋅N∗logN)。之后每次迭代只需要 O(M⋅N) 就可以找到当前轮 t 最优ht(x) 。 - 空间复杂度
cache 住的结果如果存在硬盘的话会极大降低搜索速度,我们希望全部放在内存,这里空间复杂度为 O(M⋅N)。对于 VJ 原论文中 N=1万、M=16万,考虑 int 存储 feature value 则 cost=4×16×108B=6.4GB。考虑到扩充特征集、扩充数据集、存储结构效能等问题,其实内存要求是很严峻的。 - 定义数据 label y 与 classifier h(x) 输出为 -1/+1 而非之前的 0/1,这可以简化很多公式
- yiht(xi)=1 即表明判定正确
- 弱分类的 error rate 写作:ϵt=∑Ni=1uti [yi≠ht(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=utis−yiht(xi)t- 整体 strong classifer 可以写作 C(xi)=sign(vote score on xi),所以 yi(vote score on xi) 的正负表示判定正确或错误
- yi(vote score on xi) 越大,表示判定越正确,对应的负指数值就越小
- yi(vote score on xi) 越小,表示判定越错误,对应的负指数值就越大
- wt 表示第 t 轮迭代时,当前的 weight vector
- v 表示走的方向(向量,有 length 约束)
η 表示走的步长(标量,一般是一个较小的正数)- 不能直接计算出负梯度方向,而是通过一个子过程寻找一个近似最优函数方向,耗费时间
- 需要在最终模型上多集成一个函数,这直接影响最终模型使用性能
2.3 aggregation
历经 T 轮训练后,将 T 个 ht(x) 线性集成为 strong classifier。实际上集成的参数在迭代过程中已经决定了,这又称为 linearly on the fly with theoretical guarantee,涉及的理论验证之后会详述。
C(xi)={10∑Ttαtht(xi)≥12∑Ttα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,那么可能会有 ϵ=0,s=∞,训练就崩坏了~ 所以即使用 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 个性能上的考量:
4. 理论支持
(本节参考了 Hsuan-Tien Lin 机器学习技法课程)
Adaboost 凭什么能完成 weak classifiers to strong classifier?凭什么可以收敛?可以越来越好?我们可以从数学层面(steepest decent with approximate functional gradient)找到充分解释,本节详细阐述。4.1 公式及符号重新定义
之前“2. 训练流程详解”使用的是 VJ 论文中原版公式。这里为了更清晰解释数学理论,会改动一些公式定义:
4.2 loss function of AdaBoost
根据 αt=lnst 可以有 st=exp{α},
进而可以对样本权值公式进一步变换:
ut+1i=utis−yiht(xi)t=uti(exp{αt})−yiht(xi)=utiexp{−yiαtht(xi)}
我们假设所有样本权值训练前初始化为 u0i=1N,则可以得到经过 T 轮训练后的样本权值:
uT+1i=u0i⋅∏t=1Texp(−yiαtht(xi))=1N⋅exp(−yi∑t=1Tαtht(xi))
可以看到任意样本 xi 的最终权重与所有 weak classifier 整体 vote score=∑Tt=1αtht(xi)) 有关,即:
uT+1i∝exp{−yi(vote score on xi)}
上式右侧实际上就是 exponential error:这实际上就是 Adaboost 的目标就是 exponential error 越来越小(最优化问题),也可以说是让所有样本的权值越来越小;最终 error/loss function 为所有样本的 exponential error 之和:
EADA=∑i=1NuT+1i=1N⋅∑i=1Nexp(−yi∑t=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=yi∑Tt=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)+ηv∇E(wt)目标其实是找到一个让 loss 函数下降最快的方向 v,上式的约等于号是一阶泰勒展开,再加上其他数学工具可以证明最优的方向是负梯度方向
v=−∇E(wt) 。AdaBoost 的 Gradient Boost 套路
AdaBoost 的套路很类似,核心是其通过新加入函数来完成梯度下降,这个函数对应最速下降的方向,而函数的权重对应了下降的步长。AdaBoost 在第 t 轮迭代时,优化目标如下:
minhtEADA=1N⋅∑i=1Nexp{−yi(∑τ=1t−1ατhτ(xi)+ηht(xi))}
我们在原始 loss function 中新加入一项 ηht(xi),就是说在第 t 轮的时候,我们想找一个新的函数 ht(xi) 给出 loss 最速下降的方向(相当于标准套路中的向量 v),η 则同样是走的步长,整体上就是想优化 loss 而已。以下先对上式进行化简:
minhtEADA=∑i=1N1Nexp{−yi(∑τ=1t−1ατhτ(xi))}exp{−yiηht(xi)}=∑i=1Nutiexp{−yiηht(xi)}
然后我们把上式看作:∑Ni=1utiexp{0+γ}, γ=−yiηht(xi),即把 γ 作为自变量(这样原函数导数还是原函数~),然后作一阶泰勒展开(或者说对原式作麦克劳林展开):
minhtEADA≈∑i=1Nutiexp{0}+∑i=1Nutiexp{0}(−yiηht(xi))=∑i=1Nuti−∑i=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=1N−uti [yi=ht(xi)]+∑i=1Nuti [yi≠ht(xi)]=−∑i=1Nuti+∑i=1N0 [yi=ht(xi)]+2∑i=1Nuti [yi≠ht(xi)]=−∑i=1Nuti+2(∑i=1Nuti)⋅ϵt
经过变换后,上式除了 ϵt 都是 constant,我们要优化的目标只有最右侧的 ϵt=∑Ni=1uti [yi≠ht(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{η}[yi≠ht(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 的理论基础。