Adaboost

Adaboost算法概述

Adaboost算法核心思想:“三个臭皮匠赛过一诸葛亮”。我们平常构建的分类模型可以说是弱分类器,若将这些弱分类器组合起来可以成为一个强分类器。大多数的提升方法是该表训练数据的概率分布(训练数据的权值分布),针对不同的训练数据分布调用弱学习算法学习。

 

如何改变训练数据的权值分布:第一轮初始化均匀分布样本权值,往后每一轮,提高那些被前一轮弱分期错误分类样本的权值,而降低那些被正确分类样本的权值。这样错误分类的样本权值加大会受到后一轮弱分类器的更大关注,于是分类问题被一系列的弱分类器分而治之

 

如何将弱分类器组合成强分类器:Adaboost采取多数表决的方法。即加大分类误差率小的弱分类器的权值,减少分类误差率大的弱分类器的权值。

 

adaBoost的运行过程

训练数据的每一个样本,并赋予其一个权重,这些权值构成权重向量D,维度等于数据集样本个数。开始时,这些权重都是相等的,首先在训练数据集上训练出一个弱分类器并计算该分类器的错误率,然后在同一数据集上再次训练弱分类器,但是在第二次训练时,将会根据分类器的错误率,对数据集中样本的各个权重进行调整,分类正确的样本的权重降低,而分类错的样本权重则上升,但这些权重的总和保持不变为1.

  并且,最终的分类器会基于这些训练的弱分类器的分类错误率,分配不同的决定系数alpha,错误率低的分类器获得更高的决定系数,从而在对数据进行预测时起关键作用。alpha的计算根据错误率得来:

  alpha=0.5*ln(1-ε/max(ε,1e-16))

其中,ε=为正确分类的样本数目/样本总数,max(ε,1e-16)是为了防止错误率为而造成分母为0的情况发生

  计算出alpha之后,就可以对权重向量进行更新了,使得分类错误的样本获得更高的权重,而分类正确的样本获得更低的权重。D的计算公式如下:

  如果某个样本被正确分类,那么权重更新为:
  D(m+1,i)=D(m,i)*exp(-alpha)/sum(D)

  如果某个样本被错误分类,那么权重更新为:
  D(m+1,i)=D(m,i)*exp(alpha)/sum(D)

其中,m为迭代的次数,即训练的第m个分类器,i为权重向量的第i个分量,i<=数据集样本数量

  当我们更新完各个样本的权重之后,就可以进行下一次的迭代训练。adaBoost算法会不断重复训练和调整权重,直至达到迭代次数,或者训练错误率为0。

 基于单层决策树构建弱分类器

  单层决策树是一种简单的决策树,也称为决策树桩。单层决策树可以看做是由一个根节点直接连接两个叶结点的简单决策树,比如x>v或x<v,就可以看做是一个简单决策树。

  为了更好的演示adaBoost的训练过程,我们首先建立一个简单的数据集,并将其转为我们想要的数据格式,代码如下:

复制代码
#获取数据集
def loadSimpData():
    dataMat=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 dataMat,classLabels
复制代码

  接下来,我们就要通过上述数据集来寻找最佳的单层决策树,最佳单层决策树是具有最低分类错误率的单层决策树,伪代码如下:

复制代码
#构建单层分类器
#单层分类器是基于最小加权分类错误率的树桩
#伪代码
#将最小错误率minError设为+∞
#对数据集中的每个特征(第一层特征):
    #对每个步长(第二层特征):
        #对每个不等号(第三层特征):
            #建立一颗单层决策树并利用加权数据集对它进行测试
            #如果错误率低于minError,则将当前单层决策树设为最佳单层决策树
#返回最佳单层决策树
复制代码

  接下来看单层决策树的生成函数代码:

复制代码
#单层决策树的阈值过滤函数
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
    #对数据集每一列的各个特征进行阈值过滤
    retArray=ones((shape(dataMatrix)[0],1))
    #阈值的模式,将小于某一阈值的特征归类为-1
    if threshIneq=='lt':
        retArray[dataMatrix[:,dimen]<=threshVal]=-1.0
    #将大于某一阈值的特征归类为-1
    else:
        retArray[dataMatrix[:,dimen]>threshVal]=-1.0

        
def buildStump(dataArr,classLabels,D):
#将数据集和标签列表转为矩阵形式
    dataMatrix=mat(dataArr);labelMat=mat(classLabels).T
    m,n=shape(dataMatrix)
    #步长或区间总数 最优决策树信息 最优单层决策树预测结果
    numSteps=10.0;bestStump={};bestClasEst=mat(zeros((m,1)))
    #最小错误率初始化为+∞
    minError=inf
    #遍历每一列的特征值
    for i in range(n):
        #找出列中特征值的最小值和最大值
        rangeMin=dataMatrix[:,i].min();rangeMax=dataMatrix[:,i].max()
        #求取步长大小或者说区间间隔
        stepSize=(rangeMax-rangeMin)/numSteps
        #遍历各个步长区间
        for j in range(-1,int(numSteps)+1):
            #两种阈值过滤模式
            for inequal in ['lt','gt']:
            #阈值计算公式:最小值+j(-1<=j<=numSteps+1)*步长
            threshVal=(rangeMin+float(j)*stepSize)
            #选定阈值后,调用阈值过滤函数分类预测
            predictedVals=\
                stumpClassify(dataMatrix,i,threshVal,'inequal')
            #初始化错误向量
            errArr=mat(ones((m,1)))
            #将错误向量中分类正确项置0
            errArr[predictedVals==labelMat]=0
            #计算"加权"的错误率
            weigthedError=D.T*errArr
            #打印相关信息,可省略
            #print("split: dim %d, thresh %.2f,thresh inequal:\
            #    %s, the weighted error is %.3f",
            #    %(i,threshVal,inequal,weigthedError))
            #如果当前错误率小于当前最小错误率,将当前错误率作为最小错误率
            #存储相关信息
            if weigthedError<minError:
                minError=weigthedError
                bestClasEst=predictedVals.copy()
                bestStump['dim']=i
                bestStump['thresh']='threshVal'
                bestStump['ineq']=inequal
    #返回最佳单层决策树相关信息的字典,最小错误率,决策树预测输出结果
    return bestStump,minError,bestClasEst
复制代码

  需要说明的是,上面的代码包含两个函数,第一个函数是分类器的阈值过滤函数,即设定某一阈值,凡是超过该阈值的结果被归为一类,小于阈值的结果都被分为另外一类,这里的两类依然同SVM一样,采用+1和-1作为类别。

 完整AdaBoost算法实现

  上面已经构建好了基于加权输入值进行决策的单层分类器,那么就已经有了实现一个完整AdaBoost算法所需要的所有信息了。下面先看一下整个AdaBoost的伪代码实现:

复制代码
#完整AdaBoost算法实现
#算法实现伪代码
#对每次迭代:
    #利用buildStump()函数找到最佳的单层决策树
    #将最佳单层决策树加入到单层决策树数组
    #计算alpha
    #计算新的权重向量D
    #更新累计类别估计值
    #如果错误率为等于0.0,退出循环
复制代码

  再来看看具体的实现代码吧:

复制代码
#adaBoost算法
#@dataArr:数据矩阵
#@classLabels:标签向量
#@numIt:迭代次数    
def adaBoostTrainDS(dataArr,classLabels,numIt=40):
    #弱分类器相关信息列表
    weakClassArr=[]
    #获取数据集行数
    m=shape(dataArr)[0]
    #初始化权重向量的每一项值相等
    D=mat(ones((m,1))/m)
    #累计估计值向量
    aggClassEst=mat((m,1))
    #循环迭代次数
    for i in range(numIt):
        #根据当前数据集,标签及权重建立最佳单层决策树
        bestStump,error,classEst=buildStump(dataArr,classLabels,D)
        #打印权重向量
        print("D:",D.T)
        #求单层决策树的系数alpha
        alpha=float(0.5*log((1.0-error)/(max(error,1e-16))))
        #存储决策树的系数alpha到字典
        bestStump['alpha']=alpha
        #将该决策树存入列表
        weakClassArr.append(bestStump)
        #打印决策树的预测结果
        print("classEst:",classEst.T)
        #预测正确为exp(-alpha),预测错误为exp(alpha)
        #即增大分类错误样本的权重,减少分类正确的数据点权重
        expon=multiply(-1*alpha*mat(classLabels).T,classEst)
        #更新权值向量
        D=multiply(D,exp(expon))
        D=D/D.sum()
        #累加当前单层决策树的加权预测值
        aggClassEst+=alpha*classEst
        print("aggClassEst",aggClassEst.T)
        #求出分类错的样本个数
        aggErrors=multiply(sign(aggClassEst)!=\
                    mat(classLabels).T,ones((m,1)))
        #计算错误率
        errorRate=aggErrors.sum()/m
        print("total error:",errorRate,"\n")
        #错误率为0.0退出循环
        if errorRate==0.0:break
    #返回弱分类器的组合列表
    return weakClassArr
复制代码

测试算法

  那么有了训练好的分类器,是不是要测试一下呢,毕竟训练错误率针对的是已知的数据,我们需要在分类器未知的数据上进行测试,看看分类效果。上面的训练代码会帮我们保存每个弱分类器的重要信息,比如分类器系数,分类器的最优特征,特征阈值等。有了这些重要的信息,我们拿到之后,就可以对测试数据进行预测分类了

复制代码
#测试adaBoost,adaBoost分类函数
#@datToClass:测试数据点
#@classifierArr:构建好的最终分类器
def adaClassify(datToClass,classifierArr):
    #构建数据向量或矩阵
    dataMatrix=mat(datToClass)
    #获取矩阵行数
    m=shape(dataMatrix)[0]
    #初始化最终分类器
    aggClassEst=mat(zeros((m,1)))
    #遍历分类器列表中的每一个弱分类器
    for i in range(len(classifierArr)):
        #每一个弱分类器对测试数据进行预测分类
        classEst=stumpClassify(dataMat,classifierArr[i]['dim'],\
                                classifierArr[i]['thresh'],
                                classifierArr[i]['ineq'])
        #对各个分类器的预测结果进行加权累加
        aggClassEst+=classifierArr[i]['alpha']*classEst
        print('aggClassEst',aggClassEst)
    #通过sign函数根据结果大于或小于0预测出+1或-1
    return sign(aggClassEst)
复制代码

 

posted @ 2018-11-09 08:43  Python少年  阅读(319)  评论(0编辑  收藏  举报