一、算法简介

    Adaboost算法是一种集成算法,所谓集成算法就是将多个弱的分类器组合在一起变成一个强的分类器。弱分类器通常是指分类效果比随机分类稍微好一点的分类器。就像我们在做一个重要决定的时候,通常会请教多个人的意见而不是一个人的意见,我们会综合考虑多个方面最终才会下决定。假如此时远处走来一个人,你要判断他是否是你的朋友。这时候你会从他的穿着,身高,走路姿势等多个特征来进行判断,如果仅仅是看其中一个特征都是很难做出正确判断的。这就是adaboost算法的主要思想了,下面我们来看一看计算机是怎样实现这个过程的。

二、算法原理

    实现adaboost算法的基分类器有很多中,这里我们采用单层决策树作为基分类器。因为只有一层,这个决策树可以看成是一个树桩。首先我们对所有样本赋予1/n的权重,然后构建一个单层分类器,使得该分类器分类效果最好。假如下一次我们还继续在原来的数据集上进行分类的话,那么我们选出的分类器一定和之前是一样的。所以这次我们需要对数据进行一下处理,我们改变每个样本的权重,让上一次分类错误的样本我们让它的权重高一点,这样再次训练的时候,那些被分类错误的样本被分类正确的概率就高一些。第t次迭代的权重向量Dt的计算方式如下:

如果分类正确:     

 

 

如果分类错误:

a为分类器的权重:其中为分类器的错误率。迭代n次之后,我们就能得到n个分类器h(x)。接下来我们给每一个分类器赋予一个权重,然后将所有分类器组合在一起,这样我们就形成了一个最终的分类器H(x).这样分类出来的效果是不是类似于将数据集所在的(超)平面分成了许多个小区域呢。在理论上可以证明这样做是合理的,下面我们通过一个简单例子对adaboost算法过程进行说明。

    首先我们可以举一个例子,这是一个一维特征的数据。假如按照上述分类之后得出的结果如下,共有三个分类器,分别为以7,13,19作为阈值。箭头所指向的方向表示的是被分类为+1的区域,数字代表分类器的权重。以把7作为阈值的那个分类器为例,当分类的样本x小于7的时候,它就被分类成+1而大于7的时候它就会被分类成-1.这样就有了三个分类器h1h2h3,接下来我们把这三个分类器进行整合。方法如下:当x小于7的时候,h1(x)=0.5h2(x)=-0.3h3(x)=0.4,因此H(x)=sign(h1(x)+h2(x)+h3(x))=sign(0.6)=1.所以小于7的点就被分类成+1,同理可以计算其他区域的分类结果。如果数据在更高维度上,计算方式也类似。到这里,对于adaboost算法的工作过程应该能够理解清楚了。

 

   接下来本来打算再从数学上说明为什么这样做,但是因为网上已经有大量的相关知识,而且公式编辑的过程太过复杂,因此这一步就省掉了。如果希望深入了解其中的数学推导过程,我建议参考一下周志华《机器学习》p172,其中解释地非常详细,也很容易明白。

三、adaboost算法的应用

  为了检验adaboost算法的效果,我们需要将该算法应用到实际数据上。这里我找了一个利用声呐判断物体的数据集,其中包含208个样本,每个样本都有60个特征,分别表示从60个不同的角度测出的回声强度。由于数据集中在一起,因此我们首先向数据进行随机打乱,然后选择其中的160个作为训练集,另外48个作为测试集。这里选了迭代次数为1000次,多次修改迭代次数之后,测试的错误为16%左右。这个结果对该数据集来说已经非常不错了,据网上所说该数据集的准确率在其他算法上一般为为60-70%左右。

Roc曲线如下:

四、程序python代码

 

from numpy import *


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


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']: #对于阈值我们并不知道是设定大于或者小于阈值为-1哪种效果好,因此需要对两种情况讨论,取最好的方法
                threshVal = (rangeMin + float(j) * stepSize)#阈值
                predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)
                errArr = mat(ones((m,1)))
                errArr[predictedVals == labelMat] = 0
                weightedError = D.T*errArr  #
                #print("split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f" % (i, threshVal, inequal, weightedError))
                if weightedError < minError:#判断错误率是否下降,若下降则更新分类字典。直到找到最好的哪个
                    minError = weightedError
                    bestClasEst = predictedVals.copy()
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal
    return bestStump,minError,bestClasEst

def adaBoostTrainDS(dataArr,classLabels,numIt=40):#迭代次数默认为40
    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)#构件简单树
        #print "D:",D.T
        alpha = float(0.5*log((1.0-error)/max(error,1e-16)))#计算alpgha,同时控制其下溢为0
        bestStump['alpha'] = alpha  
        weakClassArr.append(bestStump)                  #把树存储到分类器中
        #一下用于计算样本权重值
        expon = multiply(-1*alpha*mat(classLabels).T,classEst) #exponent for D calc, getting messy
        D = multiply(D,exp(expon))                              #Calc New D for next iteration
        D = D/D.sum()
        #计算所有分类器的错误数
        aggClassEst += alpha*classEst
        aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1)))
        errorRate = aggErrors.sum()/m
        if errorRate == 0.0: break
    return weakClassArr,aggClassEst

def adaClassify(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'])#此处在书中版本可能出现问题,需要在[i]前面加[0]
        aggClassEst += classifierArr[i]['alpha']*classEst
        #print(aggClassEst)
    return sign(aggClassEst)

def plotROC(predStrengths, classLabels):
    import matplotlib.pyplot as plt
    cur = (1.0,1.0) #cursor
    ySum = 0.0 #variable to calculate AUC
    numPosClas = sum(array(classLabels)==1.0)
    yStep = 1/float(numPosClas); xStep = 1/float(len(classLabels)-numPosClas)
    sortedIndicies = predStrengths.argsort()#get sorted index, it's reverse
    fig = plt.figure()
    fig.clf()
    ax = plt.subplot(111)
    #loop through all the values, drawing a line segment at each point
    for index in sortedIndicies.tolist()[0]:
        if classLabels[index] == 1.0:
            delX = 0; delY = yStep;
        else:
            delX = xStep; delY = 0;
            ySum += cur[1]
        #draw line from cur to (cur[0]-delX,cur[1]-delY)
        ax.plot([cur[0],cur[0]-delX],[cur[1],cur[1]-delY], c='b')
        cur = (cur[0]-delX,cur[1]-delY)
    ax.plot([0,1],[0,1],'b--')
    plt.xlabel('False positive rate'); plt.ylabel('True positive rate')
    plt.title('ROC curve for AdaBoost horse colic detection system')
    ax.axis([0,1,0,1])
    plt.show()
    print("the Area Under the Curve is: ",ySum*xStep)

 

 

 

 

from adaboost import*
def loadDataSet1(fileName):
    dataMat=[];labelMat=[];data=[]
    fr=open(fileName)
    for line in fr.readlines():
        line=line.replace(',','\t')#去除逗号
        line=line.replace('M','-1')#对标签进行替换
        line=line.replace('R','1')
        lineArr=line.strip('\n').split('\t')
        data.append([float(lineArr[i]) for i in range(len(lineArr))])
    random.shuffle(data)  #随机打乱列表
    for i in range(len(data)):
        dataMat.append(data[i][0:len(data[1])-1])
        labelMat.append(data[i][-1])
    dataMat=matrix(dataMat)*100#将所有元素都放大100倍
    labelMat=labelMat
    return dataMat,labelMat
def test(testArr,testLabel,classifyArr):
    errors=0
    for i in range(len(testArr)):
        predictVal=adaClassify(testArr[i],classifyArr)
        if predictVal!=testLabel[i]:
            errors+=1
    rate=errors/len(testArr)
    return rate

def getTrainTestData(dataMat,labelMat):
    '''
    trainData=dataMat[0:100];trainLabel=labelMat[0:100]#前160个数据
    testData=dataMat[100:len(dataMat)];testLabel=labelMat[100:len(dataMat)]#后48个数据
    '''
    trainData=dataMat[0:160];trainLabel=labelMat[0:160]#前208个数据
    testData=dataMat[160:len(dataMat)];testLabel=labelMat[160:len(dataMat)]#后48个数据
    return trainData,trainLabel,testData,testLabel

dataArr,labelArr=loadDataSet1('Sonar.txt')
trainData,trainLabel,testData,testLabel=getTrainTestData(dataArr,labelArr)
classifyArr,aggClassEst=adaBoostTrainDS(trainData,trainLabel,numIt=100)
#l=adaClassify(testData[1],classifyArr)
rate=test(testData,testLabel,classifyArr)
print(rate)
plotROC(aggClassEst.T,labelArr)#绘制roc曲线

 

 参考文献:

[1]Adaboost入门教程——最通俗易懂的原理介绍(图文实例)

https://blog.csdn.net/px_528/article/details/72963977

[2] 机器学习实战 peter Harrington    

[3] 周志华《机器学习》     

 

posted on 2018-06-05 13:33  d_760  阅读(305)  评论(0编辑  收藏  举报