提升(boosting)方法是一族可将弱学习器提升为强学习器的算法。这族算法的工作机制类似:先从初始训练集训练出一个基学习器,再根据基学习器的表现对训练样本分布进行调整,使得先前基学习器做错的训练样本在后续受到更多关注,然后基于调整后的样本分布来训练下一个基学习器;如此重复进行,直至基学习器数目达到事先指定的值T,最终将这T个基学习器进行加权结合。
本文主要介绍提升方法中代表性的算法AdaBoost和提升方法中更具体地实例提升树(boosting tree)。AdaBoost算法是1995年由Freund和Schapire提出的,提升树是2000年由Friedman等人提出的。
AdaBoost是adaptive boosting(自适应boosting)的缩写。
对于分类问题而言,给定一个训练样本集,求比较粗糙的分类规则(弱分类器)要比求精确的分类规则(强分类器)容易得多。提升方法就是从弱学习算法出发,反复学习,得到一系列弱分类器(又称为基本分类器),然后组合这些弱分类器,构成一个强分类器。大多数的提升方法都是改变训练数据的概率分布(训练数据的权值分布),针对不同的训练数据分布调用弱学习算法学习一系列弱分类器。
这样,对提升方法来说,有个两个问题需要回答:一是在每一轮如何改变训练数据的权值或概率分布;二是如何将弱分类器组合成一个强分类器。
关于第一个问题,AdaBoost的做法是,提高那些被前一轮弱分类器错误分类样本的权值,而降低那些被正确分类样本的权值。至于第二个问题,即弱分类器的组合,AdaBoost采取加权多数表决的方法,具体地,加大分类错误率小的弱分类器的权值,使其在表决中起较大的作用,减小分类错误率大的弱分类器的权值,使其在表决中起较小的作用。
假设给定一个二类分类的训练数据集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x i , y i ) , ⋯   , ( x N , y N ) } D=\{(x_1,y_1),(x_2,y_2),\cdots,(x_i,y_i),\cdots,(x_N,y_N)\} D={(x1,y1),(x2,y2),⋯,(xi,yi),⋯,(xN,yN)}其中,每个样本点由实例与标记组成。实例 x i ∈ X ⊆ R n x_i∈X⊆R^n xi∈X⊆Rn,标记 y i ∈ Y = − 1 , + 1 y_i∈Y={-1,+1} yi∈Y=−1,+1, X X X是实例空间, Y Y Y是标记集合。AdaBoost利用以下算法,从训练数据中学习一系列弱分类器或基本分类器,并将这些弱分类器组合成为一个强分类器。
算法1(AdaBoost)
输入:训练数据集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x i , y i ) , ⋯   , ( x N , y N ) } D=\{(x_1,y_1),(x_2,y_2),\cdots,(x_i,y_i),\cdots,(x_N,y_N)\} D={(x1,y1),(x2,y2),⋯,(xi,yi),⋯,(xN,yN)},其中 x i ∈ X ⊆ R n x_i∈X⊆R^n xi∈X⊆Rn, y i ∈ Y = − 1 , + 1 y_i∈Y={-1,+1} yi∈Y=−1,+1;弱分类器算法
输出:最终分类器G(x)
①初始化训练数据的权值分布 D 1 = ( w 11 , ⋯   , w 1 i , ⋯   , w 1 N ) , w 1 i = 1 N , i = 1 , 2 , ⋯   , N D_1=(w_{11},\cdots,w_{1i},\cdots,w_{1N}),\ \ \ \ \ w_{1i}={1\over N},\ \ \ \ \ i=1,2,\cdots,N D1=(w11,⋯,w1i,⋯,w1N), w1i=N1, i=1,2,⋯,N②对 m = 1 , 2 , ⋯   , M m=1,2,\cdots,M m=1,2,⋯,M
(a) 使用具有权值分布 D m D_m Dm的训练数据集学习,得到基本分类器 G m ( x ) : X → { − 1 , + 1 } G_m(x):X\rightarrow\{-1,+1\} Gm(x):X→{−1,+1}(b) 计算 G m ( x ) G_m(x) Gm(x)在训练数据集上的分类误差率KaTeX parse error: Unknown accent ' ̸' at position 16: e_m=P(G_m(x_i)≠̲̲y_i)=\sum_{G_m(…© 计算 G m ( x ) G_m(x) Gm(x)的系数 α m = 1 2 l o g 1 − e m e m ( 1 ) α_m={1\over2}log{{1-e_m}\over{e_m}}\ \ \ \ \ \ \ \ \ \ \ \ \ \ (1) αm=21logem1−em (1)这里的对数是自然对数
(d) 更新训练数据集的权值分布 D m + 1 = ( w m + 1 , 1 , ⋯   , w m + 1 , i , ⋯   , w m + 1 , N ) D_{m+1}=(w_{m+1,1},\cdots,w_{m+1,i},\cdots,w_{m+1,N}) Dm+1=(wm+1,1,⋯,wm+1,i,⋯,wm+1,N) w m + 1 , i = w m i Z m exp ( − α m y i G m ( x i ) ) , i = 1 , 2 , ⋯   , N ( 2 ) w_{m+1,i}={{w_{mi}}\over{Z_m}}\exp(-α_my_iG_m(x_i)),\ \ \ \ \ \ \ \ i=1,2,\cdots,N\ \ \ \ \ \ \ \ \ (2) wm+1,i=Zmwmiexp(−αmyiGm(xi)), i=1,2,⋯,N (2)这里, Z m Z_m Zm是规范化因子 Z m = ∑ i = 1 N w m i exp ( − α m y i G m ( x i ) ) ( 3 ) Z_m=\sum_{i=1}^Nw_{mi}\exp(-α_my_iG_m(x_i))\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (3) Zm=i=1∑Nwmiexp(−αmyiGm(xi)) (3)它使Dm+1成为一个概率分布
③构建基本分类器的线性组合 f ( x ) = ∑ m = 1 M α m G m ( x ) ( 4 ) f(x)=\sum_{m=1}^Mα_mG_m(x)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (4) f(x)=m=1∑MαmGm(x) (4)得到最终分类器(sign函数为若参数大于0返回1,小于0返回-1,等于0返回0) G ( x ) = s i g n ( f ( x ) ) = s i g n ( ∑ m = 1 M α m G m ( x ) ) ( 5 ) G(x)=sign(f(x))=sign(\sum_{m=1}^Mα_mG_m(x))\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (5) G(x)=sign(f(x))=sign(m=1∑MαmGm(x)) (5)
证明:
首先假设训练数据集具有均匀的权值分布,即每个训练样本在基本分类器的学习中作用相同,这一假设保证在 m = 1 m=1 m=1时能够在原始数据上学习基本分类器 G 1 ( x ) G_1(x) G1(x)。
由(1)可知,错误分类的样本,权值越大,分类误差率也越大。前一次因分类错而增大权值的样本,若在这一次仍继续错误,则分类误差率会增大,通过式(2)影响系数,使这时得到的基本分类器在最终分类器中的作用减小。
由(2)可知,当分类误差率 e m ≤ 1 2 e_m≤{1\over2} em≤21时, α m ≥ 0 α_m≥0 αm≥0,并且 α m α_m αm随着 e m e_m em的减小而增大,所以分类误差率越小的基本分类器在最终分类器中的作用越大。
式(3)可写成

由此可知,被基本分类器 G m ( x ) G_m(x) Gm(x)误分类样本的权值得以扩大,而被正确分类样本的权值却得以缩小,这样一来,那些没有得到正确分类的数据,由于其权值的加大而受到后一轮的弱分类器的更大关注。
循环 M M M次,得到 M M M个基本分类器及对应的系数,最后,利用基本分类器的线性组合构建最终分类器。
以下代码来自Peter Harrington《Machine Learing in Action》
代码如下(保存为adaboost.py):
# -- coding: utf-8 --
from numpy import *
def loadSimpData():
datMat = matrix([[ 1. , 2.1],
[ 2. , 1.1],
[ 1.3, 1. ],
[ 1. , 1. ],
[ 2. , 1. ]])
classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
return datMat,classLabels
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
# 该函数接收4个参数,分别为训练集、第dimen个特征、切分点、分类准则
retArray = ones((shape(dataMatrix)[0],1))
if threshIneq == 'lt':
retArray[dataMatrix[:,dimen] <= threshVal] = -1.0# 将特征值小于等于threshVal的元素位置设置-1.0,其他仍为1
else:
retArray[dataMatrix[:,dimen] > threshVal] = -1.0 # 将特征值大于threshVal的元素位置设置-1.0,其他仍为1
return retArray
def buildStump(dataArr,classLabels,D):
# buildStump是一个单层决策树函数,在本例中作为弱分类器
# 该函数接收3个参数,分别为训练集、对应的类别标记、权重向量
dataMatrix = mat(dataArr) # 只包含特征的训练集
labelMat = mat(classLabels).T # 类别标记
m,n = shape(dataMatrix)
numSteps = 10.0
bestStump = {} # 用于存储给定权重D时所得到的最佳单层决策树的相关信息
bestClasEst = mat(zeros((m,1)))
minError = inf
for i in range(n):
# 循环特征数量
rangeMin = dataMatrix[:,i].min() # 获取第i个特征的最小值
rangeMax = dataMatrix[:,i].max(); # 获取第i个特征的最大值
stepSize = (rangeMax-rangeMin)/numSteps # 计算步长
for j in range(-1,int(numSteps)+1):
for inequal in ['lt', 'gt']:
threshVal = (rangeMin + float(j) * stepSize)
predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)
errArr = mat(ones((m,1)))
errArr[predictedVals == labelMat] = 0 # 若predictedVals与类别标记数据一样,则为0,否则为1
weightedError = D.T*errArr # 获取分类误差,权重大的数据分类错误会增大分类误差
if weightedError < minError:
minError = weightedError # 分类误差率
bestClasEst = predictedVals.copy() # 分类后的类别标记
bestStump['dim'] = i # 分类特征为第i个
bestStump['thresh'] = threshVal # 切分点
bestStump['ineq'] = inequal # 分类准则,lt为小于等于是-1,gt为大于等于是-1
return bestStump,minError,bestClasEst # 最后得到分类误差最小的决策树
def adaBoostTrainDS(dataArr,classLabels,numIt=40):
# 基于单层决策树的训练过程,numIt为迭代次数
weakClassArr = [] # 存储各分类器信息
m = shape(dataArr)[0]
D = mat(ones((m,1))/m) # 初始化权重
aggClassEst = mat(zeros((m,1))) # 初始化基本分类器
for i in range(numIt):
bestStump,error,classEst = buildStump(dataArr,classLabels,D) # 获取第i个弱分类器的信息
alpha = float(0.5*log((1.0-error)/max(error,1e-16))) # 根据式(1)计算第i个分类器的系数
bestStump['alpha'] = alpha # 将系数添加到对于的分类器相关信息
weakClassArr.append(bestStump) # 将分类器添加到weakClassArr
expon = multiply(-1*alpha*mat(classLabels).T,classEst) # 计算式子(2)中exp中的数
D = multiply(D,exp(expon)) # 计算式子(3)中wi*exp
D = D/D.sum() # 结合前两个式子,根据算法1(d)更新权重
aggClassEst += alpha*classEst # 根据式(4)构建基本分类器
aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1))) # 根据最终分类器与原先类别信息计算错误率
errorRate = aggErrors.sum()/m
if errorRate == 0.0: break # 若错误率等于0,则退出
return weakClassArr
def adaClassify(datToClass,classifierArr):
# 该函数利用训练出的多个弱分类器进行分类;datToClass为测试数据,classifierArr为训练出的各个弱分类器
dataMatrix = mat(datToClass)
m = shape(dataMatrix)[0]
aggClassEst = mat(zeros((m,1))) # 初始化基本分类器
for i in range(len(classifierArr)):
classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],\
classifierArr[i]['thresh'],\
classifierArr[i]['ineq'])
aggClassEst += classifierArr[i]['alpha']*classEst# 根据式(4)构建基本分类器
return sign(aggClassEst) # 根据式(5)获取最终结果
考虑加法模型 f ( x ) = ∑ m = 1 M β m b ( x ; γ m ) f(x)=\sum_{m=1}^Mβ_mb(x;γ_m) f(x)=m=1∑Mβmb(x;γm)其中, b ( x ; γ m ) b(x;γ_m) b(x;γm)为基函数, γ m γ_m γm为基函数的参数, β m β_m βm为基函数的系数。
在给定训练数据及损失函数 L ( y , f ( x ) ) L(y,f(x)) L(y,f(x))的条件下,学习加法模型 f ( x ) f(x) f(x)成为经验风险极小化即损失函数极小化问题 min β m , γ m ∑ i = 1 N L ( y i , ∑ m = 1 M β m b ( x ; γ m ) ) \min_{β_m,γ_m} \sum_{i=1}^NL(y_i,\sum_{m=1}^Mβ_mb(x;γ_m)) βm,γmmini=1∑NL(yi,m=1∑Mβmb(x;γm)) 通常这是一个复杂的优化问题。
前向分步算法求解这一优化问题的想法是:因为学习的是加法模型,如果能够从前向后,每一步只学习一个基函数及其系数,逐步逼近优化目标函数式(5),那么就可以简化优化的复杂度。
具体地,每步只需优化如下损失函数: min β , γ ∑ i = 1 N L ( y i , β b ( x ; γ ) ) \min_{β,γ} \sum_{i=1}^NL(y_i,βb(x;γ)) β,γmini=1∑NL(yi,βb(x;γ)) 给定训练数据集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x i , y i ) , ⋯   , ( x N , y N ) } D=\{(x_1,y_1),(x_2,y_2),\cdots,(x_i,y_i),\cdots,(x_N,y_N)\} D={(x1,y1),(x2,y2),⋯,(xi,yi),⋯,(xN,yN)}, x i ∈ X ⊆ R n x_i∈X⊆R^n xi∈X⊆Rn, y i ∈ Y = { − 1 , + 1 } y_i∈Y=\{-1,+1\} yi∈Y={−1,+1}。损失函数 L ( y , f ( x ) ) L(y,f(x)) L(y,f(x))和基函数的集合 { b ( x ; γ ) } \{b(x;γ)\} {b(x;γ)},学习加法模型 f ( x ) f(x) f(x)的前向分步算法如下:
算法2(前向分步算法)
输入:训练数据集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x N , y N ) } D=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\} D={(x1,y1),(x2,y2),⋯,(xN,yN)};损失函数 L ( y , f ( x ) ) L(y,f(x)) L(y,f(x));基函数集 { b ( x ; γ ) } \{b(x; γ)\} {b(x;γ)}
输出:加法模型 f ( x ) f(x) f(x)
①初始化 f 0 ( x ) = 0 f_0(x)=0 f0(x)=0
②对 m = 1 , 2 , ⋯   , M m=1,2,\cdots,M m=1,2,⋯,M
(a)极小化损失函数 ( β m , γ m ) = a r g min β , γ ∑ i = 1 N L ( y i , f m − 1 ( x i ) + β b ( x ; γ ) ) (β_m,γ_m)=arg \min_{β,γ} \sum_{i=1}^NL(y_i,f_{m-1}(x_i)+βb(x;γ)) (βm,γm)=argβ,γmini=1∑NL(yi,fm−1(xi)+βb(x;γ))得到参数 β m β_m βm, γ m γ_m γm
(b)更新 f m ( x ) = f m − 1 ( x ) + β m b ( x ; γ m ) f_m(x)=f_{m-1}(x)+β_mb(x;γ_m) fm(x)=fm−1(x)+βmb(x;γm)③得到加法模型 f ( x ) = f M ( x ) = ∑ m = 1 M β m b ( x ; γ m ) f(x)=f_M(x)=\sum_{m=1}^Mβ_mb(x;γ_m) f(x)=fM(x)=m=1∑Mβmb(x;γm) 这样,前向分步算法将同时求解从 m = 1 m=1 m=1到 M M M所有参数 β m β_m βm, γ m γ_m γm的优化问题简化为逐次求解各个 β m β_m βm, γ m γ_m γm的优化问题。
提升树是以分类树或回归树为基本分类器的提升方法。
提升方法实际采用加法模型(即基函数的线性组合)与前向分布算法。以决策树为基函数的提升方法称为提升树。
对分类问题决策树是二叉分类树;对回归问题决策树是二叉回归树。提升树模型可以表示为决策树的加法模型: f M ( x ) = ∑ m = 1 M T ( x ; θ m ) f_M(x)=\sum_{m=1}^MT(x;θ_m) fM(x)=m=1∑MT(x;θm)其中, T ( x ; θ m ) T(x;θ_m) T(x;θm)表示决策树; θ m θ_m θm为决策树的参数; M M M为树的个数。
提升树算法采用前向分步算法,首先确定初始提升树 f 0 ( x ) = 0 f_0(x)=0 f0(x)=0,第 m m m步的模型是 f m ( x ) = f m − 1 ( x ) + T ( x ; θ m ) f_m(x)=f_{m-1}(x)+T(x;θ_m) fm(x)=fm−1(x)+T(x;θm)其中, f m − 1 ( x ) f_{m-1}(x) fm−1(x)为当前模型,通过经验风险极小化确定下一棵决策树的参数 θ m θ_m θm
θ ^ m = a r g min θ m ∑ i = 1 N L ( y i , f m − 1 ( x i ) + T ( x ; θ m ) ) \hat θ_m=arg \min_{θ_m}\sum_{i=1}^NL(y_i,f_{m-1}(x_i)+T(x;θ_m)) θ^m=argθmmini=1∑NL(yi,fm−1(xi)+T(x;θm)) 由于树的线性组合可以很好地拟合训练数据,即使数据中的输入与输出之间的关系很复杂也如此,所以提升树是一个高功能的学习算法。
对于二类分类问题,提升树算法只需将AdaBoost算法1中的基本分类器限制为二类分类树即可,可以说这时的提升树算法是AdaBoost算法的特殊情况,这里不再细述。
已知一个训练集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x N , y N ) } D=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\} D={(x1,y1),(x2,y2),⋯,(xN,yN)}, x i ∈ X ⊆ R n x_i∈X⊆R^n xi∈X⊆Rn, X X X为输入空间, y i ∈ Y ⊆ R y_i∈Y⊆R yi∈Y⊆R, Y Y Y为输出空间。在决策树一文中我们已经讨论了回归树的问题。如果将输入空间 X X X划分为 J J J个互不相交的区域 R 1 , R 2 , ⋯   , R J R_1,R_2,\cdots,R_J R1,R2,⋯,RJ,并且在每个区域上确定输出的常量 c j c_j cj, j = 1 , 2 , ⋯   , J j=1,2,\cdots,J j=1,2,⋯,J,那么树可表示为 T ( x ; θ ) ) = ∑ j = 1 J c j I ( x ∈ R j ) T(x;θ))=\sum_{j=1}^Jc_jI(x∈R_j) T(x;θ))=j=1∑JcjI(x∈Rj)其中,参数 θ = ( R 1 , c 1 ) , ( R 2 , c 2 ) , ⋯   , ( R J , c J ) θ={(R_1,c_1), (R2,c2),\cdots, (R_J,c_J)} θ=(R1,c1),(R2,c2),⋯,(RJ,cJ)表示树的区域划分和各区域上的常数。 J J J是回归树的复杂度即叶结点个数。
回归问题提升树使用以下前向分步算法: f 0 ( x ) = 0 f_0(x)=0 f0(x)=0 f m ( x ) = f m − 1 ( x ) + T ( x ; θ m ) , m = 1 , 2 , ⋯   , M f_m(x)=f_{m-1}(x)+T(x;θ_m),\ \ \ \ \ \ \ \ \ \ m=1,2,\cdots,M fm(x)=fm−1(x)+T(x;θm), m=1,2,⋯,M f M ( x ) = ∑ m = 1 M T ( x ; θ m ) f_M(x)=\sum_{m=1}^MT(x;θ_m) fM(x)=m=1∑MT(x;θm)在前向分步算法的第 m m m步,给定当前模型 f m − 1 ( x ) f_{m-1}(x) fm−1(x),需求解 θ ^ m = a r g min θ m ∑ i = 1 N L ( y i , f m − 1 ( x i ) + T ( x ; θ m ) ) \hat θ_m=arg \min_{θ_m}\sum_{i=1}^NL(y_i,f_{m-1}(x_i)+T(x;θ_m)) θ^m=argθmmini=1∑NL(yi,fm−1(xi)+T(x;θm))得到 θ ^ m \hat θ_m θ^m,即第 m m m棵树的参数。
当采用平方误差损失函数时, L ( y , f ( x ) ) = ( y − f ( x ) ) 2 L(y,f(x))=(y-f(x))^2 L(y,f(x))=(y−f(x))2其损失变为 L ( y , f m − 1 ( x ) + T ( x ; θ m ) ) = [ y − f m − 1 ( x ) − T ( x ; θ m ) ] 2 = [ r − T ( x ; θ m ) ] 2 L(y, f_{m-1}(x)+T(x;θ_m))=[y- f_{m-1}(x)-T(x;θ_m)]^2=[r-T(x;θ_m)]^2 L(y,fm−1(x)+T(x;θm))=[y−fm−1(x)−T(x;θm)]2=[r−T(x;θm)]2这里, r = y − f m − 1 ( x ) ( 6 ) r=y-f_{m-1}(x)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (6) r=y−fm−1(x) (6)是当前模型拟合数据的残差(residual)。所以,对回归问题的提升树算法来说,只需简单地拟合当前模型的残差。
算法3(回归问题的提升树算法)
输入:训练数据集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x N , y N ) } D=\{(x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N)\} D={(x1,y1),(x2,y2),⋯,(xN,yN)}, x i ∈ X ⊆ R n x_i∈X⊆R^n xi∈X⊆Rn, y i ∈ Y ⊆ R y_i∈Y⊆R yi∈Y⊆R
输出:提升树 f M ( x ) f_M(x) fM(x)
①初始化 f 0 ( x ) = 0 f_0(x)=0 f0(x)=0
②对 m = 1 , 2 , ⋯   , M m=1,2,\cdots,M m=1,2,⋯,M
(a) 按式(6)计算残差 r m i = y i − f m − 1 ( x i ) , i = 1 , 2 , ⋯   , N r_{mi}=y_i-f_{m-1}(x_i),\ \ \ \ \ \ \ \ \ \ \ \ i=1,2,\cdots,N rmi=yi−fm−1(xi), i=1,2,⋯,N(b) 拟合残差 r m i r_{mi} rmi学习一个回归树,得到 T ( x ; θ m ) T(x; θ_m) T(x;θm)
© 更新 f m ( x ) = f m − 1 ( x ) + T ( x ; θ m ) f_m(x)=f_{m-1}(x)+T(x;θ_m) fm(x)=fm−1(x)+T(x;θm)
③得到回归问题提升树 f M ( x ) = ∑ m = 1 M T ( x ; θ m ) f_M(x)=\sum_{m=1}^MT(x;θ_m) fM(x)=m=1∑MT(x;θm)
以上全部内容参考书籍如下:
李航《统计学习方法》
周志华《机器学习》