adaboost原理和實現


上兩篇說了決策樹到集成學習的大概,這節我們通過adaboost來具體了解一下集成學習的簡單做法。

集成學習有bagging和boosting兩種不同的思路,bagging的代表是隨機森林,boosting比較基礎的adaboost,高級一點有GBDT,在這里我也說下我理解的這兩個做法的核心區別:

隨機森林的bagging是采用有放回抽樣得到n個訓練集,每個訓練集都會有重復的樣本,每個訓練集數據都一樣,然后對每個訓練集生成一個決策樹,這樣生成的每個決策樹都是利用了整個樣本集的一部分,也就說每棵決策樹只是學習了大部分,然后決策的時候綜合每棵決策樹的評分,最終得出一個總的評分

boosting的adaboost每次訓練的時候用的是同一個數據集,但是前一棵決策樹分錯的樣本在后面的權重會升高,相當於說,后面的決策樹利用了前面決策樹學習的結果,不斷的優化這個結果,也就是說,后面的決策樹恰恰擅長的是前面決策樹不擅長(分錯)的樣本,相當於說,boosting的每棵決策樹擅長的“領域”不一樣,並且在擅長的領域都很厲害

而對比bagging,bagging中的每一棵樹都是同樣普通的決策樹,相當於每棵決策樹擅長的“領域”區分不大,也沒有很擅長。

下面我們看下adaboost是如何工作的吧,這次我們使用更簡單的例子。

這是二維平面的上的兩個點,紅色是正樣本,綠色是負樣本,如果使用強分類器,例如深度大於1的決策樹,很簡單就可以區分開了,或者使用邏輯回歸,計算一下,就可以輕松得到一條直線把這兩個類別的點區分開了,我們這里主要是先學習adaboost是如何把弱分類器組裝成強大的強分類器以及boosting的學習效果

類別和之前不一樣,這里的正負樣本的類似是1,-1,原因是預測結果的分界線不一樣,決策樹是沒有對類別做任何的操作,adaboost設計到多棵樹的權重相加,使用0作為正負樣本的分界線會更好

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

 

選擇弱分類器:深度為1的決策樹無疑是很弱的分類器,深度為1的決策樹我們一般稱為決策樹墩,決策樹墩在二維平面上是一條平行坐標軸的直線

對於我們的數據集,決策樹墩是沒有辦法正確划分的,除非能學習出直線之間的組合關系

決策樹墩做什么事情:決策樹墩做的事情是根據特征的特征值給出判斷錯誤的列表(個數)

def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):#just classify the data
    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']: #遍歷當前特征當前步伐的所有方向
                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

 

下面是adaboost的核心內容:如何提高划分錯的樣本的權重

首先用當前划分的錯誤計算得到一個α值(每個決策樹墩的權重),然后對每個樣本根據區分對錯改變其權重值,最后最改變權重后的樣本的權重做歸一化

def adaBoostTrainDS(dataArr,classLabels,numIt=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)))#計算α值
        bestStump['alpha'] = alpha  
        #print "classEst: ",classEst.T
        expon = multiply(-1*alpha*mat(classLabels).T,classEst) #計算新的權重
        D = multiply(D,exp(expon))                              #權重歸一化
        D = D/D.sum()
        #每個樣本到當前的評分
        aggClassEst += alpha*classEst
        #得到分類錯誤的列表(分對為零,分錯為1,計算內積,得到分錯的個數)
        aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1)))
        errorRate = aggErrors.sum()/m
        print "total error: ",errorRate
        bestStump['error_rate']=errorRate
        weakClassArr.append(bestStump)                  #記錄每一棵最優的決策樹墩
        if errorRate == 0.0: break
    return weakClassArr,aggClassEst

 

下面我們看每一棵決策樹墩的分類的效果吧:樣本類別如下:

[1.0, 1.0, -1.0, -1.0, 1.0]

每個樣本的權重如下

[ 0.2  0.2  0.2  0.2  0.2]

第一棵決策樹,X1小於等於1.3為負樣本,這棵樹的權重為0.69,錯誤率0.2(分錯了一個),每個樣本被預測的結果如下:

[-0.69314718  0.69314718 -0.69314718 -0.69314718  0.69314718]

第一次權重調整:可以發現第一個樣本(被分錯的)權重升高了

[ 0.5    0.125  0.125  0.125  0.125]

兩棵決策樹:第二棵決策樹X2小於等於1.0為負樣本,權重0.97,錯誤率還是0.2,樣本預測總評分如下

[ 0.27980789  1.66610226 -1.66610226 -1.66610226 -0.27980789]

第二次調整權重,可以看到最后一次被分錯的權重最高,前一次被分錯的稍小,一直被分對的概率最小

[ 0.28571429  0.07142857  0.07142857  0.07142857  0.5       ]

看下三棵決策樹墩的效果:X1小於等於0.9的為負樣本,權重0.9,錯誤率0,所有樣本評分如下:可以發現雖然每個正樣本的評分都大於0,但是有些評分已經高達2.5了,不過我們也發現,最后的評分跟一直分對或者分錯沒什么關系

[ 1.17568763  2.56198199 -0.77022252 -0.77022252  0.61607184]

在這個簡單的數據中,錯誤率可以降到0,但是不是所有的數據集都可以達到錯誤率為0的,比如我們前面的100個點的實驗

真正的原因在於數據集在特征維度是否是超平面可分的,如果可以,adaboost理論上可以把錯誤率降到0

希望到這里大家可以理解adaboost,理解集成學習。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM