提升和加法树及AdaBoost算法总结

提升方法概述

一个弱分类器的误差率只比随机猜测好一些,提升的目的就是连续对反复修改的数据应用弱分类算法,由此产生一个弱分类器序列 Gm(x) , m=1,2,3...M ,然后通过一个加权的多数表决来合并全部预测,以产生最终预测
G(x)=sign(Mm=1αmGm(x))
这里, αm 由提升方法来计算,它们的作用是对序列中较精确的分类器给予较高的影响。
在每个提升步,数据修改就是对每一训练观测 (xi,yi) 实施加权 wi ,开始,所有权都置成 wi=1/N ,以便第一步能够简单地用通常的方式在数据上训练分类。对每一个相继的迭代 m=2,3...M ,观测的权值被分别修改,并且分类算法被再次应用于加权观测。在第 m 步,那些被前一步导出的分类器 Gm1(x) 误分类的观测权值提高,而被正确分类的观测权值降低。这样,随着迭代的进行,那些很难正确分类的观测受到了不断增长的影响。因此,每个后继分类器被强制关注被前面的分类器误分类的训练观测。

AdaBoost算法

算法流程如下:
1.初始化观测权值 wi=1/N,i=1,2...N
2.对于 m=1 M
(a)使用权值 wi ,用分类器 Gmx 拟合数据
(b)计算 Gmx 在训练数据集上的分类误差率
em=P(Gm(xi)!=yi)=Ni=1wmiI(Gm(xi)!=yi)
(c)计算 Gmx 的系数
αm=12log1emem
(d)更新训练数据的权值分布
Dm+1=(wm+1,1,...wm+1,N)
wm+1,i=wmiZmexp(αmyiGm(x)),i=1,2...N
这里,Z_m是规范化因子
Zm=Ni=1wmiexp(αmyiGm(x))
它使得 Dm+1 成为一个概率分布
3.构建基本分类器的线性组合
f(x)=Mm=1αmGm(x)
得到最终分类器
G(x)=sign(f(x))

αm=12log1emem 可以看出,分类误差率越小的基本分类器在最终分类器中的作用越大
在每一次迭代过程中,训练数据的权值更改

wm+1,j={wmiZmexp(αm),wmiZmexp(αm),Gm(xi)=yiGm(xi)!=yi

也就是说被基本分类器误分类的样本权值扩大,正确分类的样本权值缩小

指数损失函数和AdaBoost

AdBoost算法等价于使用以指数函数为损失函数的加法模型,AdaBoost算法是前向分步加法算法的特例。

提升树模型

提升方法实际采用加法模型与前向分步算法,以决策树为基函数的提升方法成为提升树。

二类分类问题的提升树算法

对于二类分类问题,提升树算法只需将AdaBoost算法中的基本分类器限制为二类分类树即可。以一个结点的决策树为例,即决策树桩为例,
决策树桩由三部分组成,特征,特征值,不等号的方向,代码见最后

回归问题的提升树算法

采用平方损失的回归树算法流程如下:
1.初始化 f0(x)=0
2.对 m=123...M
(a)计算残差 rmi=yifm1(xi),i=1,2...N
(b)拟合残差 rmi 学习一个回归树,得到 T(x;Θ)
(c)更新 fm(x)=fm1(x)+T(x;Θ)
3.得到回归问题提升树
fM(x)=Mm=1T(x;Θ)

数值优化

上面限定了损失函数并限定了加法模型为树之和,考虑任意损失函数L(x)和任意加法模型f(x)
损失为
L(f)=Ni=1L(yi,f(xi)) (A)
对上式进行极小化可以看做是一种数值优化
f^=argminfL(f) (B)
其中,“参数” fIRN 是逼近函数 f(xi) 在N个数据点上的函数值:
f={f(x1),f(x2)...f(xN)}
数值优化的过程函数求解(B)式作为分量向量的和
fM=Mm=0hm,hmIRN
其中 f0=h0 是初始猜测,每个后继的 fm 都是根据当前的参数向量 fm1 得到的,而 fm1 是前面导出的向量之和,数值优化方法在计算每个增量向量 hm 上是不同的。

最速下降法

最速下降选择的是 hm=ρmgm ,其中 ρm 是一个标量,而 gmIRN 是在 f=fm1 处计算的 L(f) 的梯度。梯度 gim 的分量是
gim=[L(yi,f(xi)f(xi)]f(xi)=fm1(xi)
步长 ρm 是下式的解
ρm=argminρ L(fm1ρgm)
接着,当前解被更新为
fm=fm1ρgm
并在下一次迭代过程中重复该过程。最速下降法可以看做是一个非常贪心的策略,因为 gm IRN 中的一个局部方向,在此方向上, L(f) f=fm1 上下降最快。

梯度提升

逐步前向提升也是一种非常贪心的策略。在每一步,给定当前的模型 fm1 和它的拟合 fm1(xi) ,解树是下式的解
Θ^m=argminΘmNi=1 L(yi,fm1+T(xi;Θm)) (C)
这样,树预测 T(xi;Θm) 类似于负梯度的分量。两者主要的不同在于树分量不是独立的。他们被限制为一个决策树的预测,而负梯度是无约束的最大下降方向。

如果极小化训练数据上的损失是唯一目的,则最速下降将是最可取的策略。对于任意可微的损失函数,梯度的计算是平凡的,而直接求解式(C)是困难的。然而,梯度仅定义在训练数据点 xi 上,而最终的目标是将 fM(x) 拓广到不在训练集中出现的新数据。
解决这个问题的可能方法是在第m次迭代时引进一棵树 T(xi;Θm) ,使得它的预测 tm 与负梯度尽可能接近。使用平方误差来度量接近程度,即
Θ^=argminΘNi=1(gimT(xi;Θ))2
即我们通过最小平方误差将树T拟合到负梯度值。这里就可以用上面说到的损失函数为平方误差的树回归算法了,负梯度称作广义或伪残差r。

adaBoost算法代码

from numpy import *


# adaBoost算法
# 决策树桩仅分裂一次,也就是说只选择一次特征
# 决策树桩由三部分组成,特征,特征值,不等号的方向


# 读入数据
def loadData():
    dataMat = matrix([[1. , 2.1],
                     [2. , 1.1],
                     [1.3, 1. ],
                     [1. , 1. ],
                     [2. , 1. ]])
    classLabels = matrix([1.0, 1.0,-1.0, -1.0, 1.0]).T
    return dataMat, classLabels


# 根据当前树桩对数据进行分类
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
    retArray = ones((shape(dataMatrix)[0], 1))
    if threshIneq == 'lt':
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
    else:
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0
    return retArray


# 构造弱分类器,即决策树桩
# D为数据权重向量
# numStep是步数,据此可算出步长, bestClassEst是最优情况下的分类情况
def buildStump(dataMatrix, classLabels, D):
    m, n = shape(dataMatrix)
    numStep = 10.0
    bestStump = {}
    minError = inf
    bestClasEst = mat(ones((m, 1)))
    for i in range(n):
        rangeMin = dataMatrix[:, i].min()
        rangeMax = dataMatrix[:, i].max()
        stepSize = (rangeMax-rangeMin)/numStep
        for j in range(-1, int(numStep)+1):
            threshVal = rangeMin + j*stepSize
            for inequal in ['lt', 'rt']:
                predictVals = stumpClassify(dataMatrix, i, threshVal, inequal)
                errArr = mat(ones((m, 1)))
                errArr[predictVals == classLabels] = 0
                weightedError = D.T*errArr
                if weightedError < minError:
                    minError = weightedError
                    bestClassEst = predictVals
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal
    return bestStump, minError, bestClassEst


# adaBoost训练过程
def adaBoostTrain(dataMatrix, classLabels, numIter=40):
    weakClassArr = []
    m = shape(dataMatrix)[0]
    D = mat(ones((m, 1))/m)
    aggClassEst = mat(zeros((m, 1)))
    for i in range(numIter):
        bestStump, error, classEst = buildStump(dataMatrix, classLabels, D)
        # 防止除以零溢出
        alpha = float(0.5*log((1-error)/max(error, 1e-16)))
        bestStump['alpha'] = alpha
        weakClassArr.append(bestStump)
        expon = multiply(-1*alpha*classLabels, classEst)
        D = multiply(D, exp(expon))
        D /= D.sum()
        aggClassEst += alpha*classEst
        # 比较两个矩阵的值得出预测正误情况
        aggError = sign(aggClassEst) != classLabels
        errorRate = aggError.sum()/m
        if errorRate == 0.0:
            break
    return weakClassArr

def adaClassify(dataMatrix, classifierArr):
    m = shape(dataMatrix)[0]
    aggClassEst = mat(zeros((m, 1)))
    for i in range(len(classifierArr)):
        stump = classifierArr[i]
        classEst = stumpClassify(dataMatrix, stump['dim'], stump['thresh'], stump['ineq'])
        aggClassEst += stump['alpha']*classEst
    return sign(aggClassEst)

dataMat, classLabels = loadData()
classifiers = adaBoostTrain(dataMat, classLabels, 9)
labels = adaClassify(mat([0, 0]), classifiers)
print(labels)





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