讀古今文學網 > 機器學習實戰 > 7.4 完整AdaBoost算法的實現 >

7.4 完整AdaBoost算法的實現

在上一節,我們構建了一個基於加權輸入值進行決策的分類器。現在,我們擁有了實現一個完整AdaBoost算法所需要的所有信息。我們將利用7.3節構建的單層決策樹來實現7.2節中給出提綱的算法。

整個實現的偽代碼如下:

對每次迭代:
    利用buildStump函數找到最佳的單層決策樹   
    將最佳單層決策樹加入到單層決策樹數組 
    計算alpha   
    計算新的權重向量D 
    更新累計類別估計值  
    如果錯誤率等於0.0,則退出循環       
  

為了將該函數放入Python中,打開adaboost.py文件並將程序清單7-2的代碼加入其中。

程序清單7-2 基於單層決策樹的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
        weakClassArr.append(bestStump)
        print \"classEst: \",classEst.T
        #❶(以下兩行)為下一次迭代計算*D*
        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\"
        if errorRate == 0.0: break
    return weakClassArr
>>> classifierArray = adaboost.adaBoostTrainDS(datMat,classLabels,9)
D: [[ 0.2 0.2 0.2 0.2 0.2]]
classEst: [[-1. 1. -1. -1. 1.]]
aggClassEst: [[-0.69314718 0.69314718 -0.69314718 -0.69314718 0.69314718]]
total error: 0.2
D: [[ 0.5 0.125 0.125 0.125 0.125]]
classEst: [[ 1. 1. -1. -1. -1.]]
aggClassEst: [[ 0.27980789 1.66610226 -1.66610226 -1.66610226 -0.27980789]]
total error: 0.2
D: [[ 0.28571429 0.07142857 0.07142857 0.07142857 0.5 ]]
classEst: [[ 1. 1. 1. 1. 1.]]
aggClassEst: [[ 1.17568763 2.56198199 -0.77022252 -0.77022252 0.61607184]]
total error: 0.0
  

AdaBoost算法的輸入參數包括數據集、類別標籤以及迭代次數numIt,其中numIt是在整個AdaBoost算法中唯一需要用戶指定的參數。

我們假定迭代次數設為9,如果算法在第三次迭代之後錯誤率為0,那麼就會退出迭代過程,因此,此時就不需要執行所有的9次迭代過程。每次迭代的中間結果都會通過print語句進行輸出。後面,讀者可以把print輸出語句註釋掉,但是現在可以通過中間結果來瞭解AdaBoost算法的內部運行過程。

函數名稱尾部的DS代表的就是單層決策樹(decision stump),它是AdaBoost中最流行的弱分類器,當然並非唯一可用的弱分類器。上述函數確實是建立於單層決策樹之上的,但是我們也可以很容易對此進行修改以引入其他基分類器。實際上,任意分類器都可以作為基分類器,本書前面講到的任何一個算法都行。上述算法會輸出一個單層決策樹的數組,因此首先需要建立一個新的Python表來對其進行存儲。然後,得到數據集中的數據點的數目m,並建立一個列向量D

向量D非常重要,它包含了每個數據點的權重。一開始,這些權重都賦予了相等的值。在後續的迭代中,AdaBoost算法會在增加錯分數據的權重的同時,降低正確分類數據的權重。D是一個概率分佈向量,因此其所有的元素之和為1.0。為了滿足此要求,一開始的所有元素都會被初始化成1/m。同時,程序還會建立另一個列向量aggClassEst,記錄每個數據點的類別估計累計值。

AdaBoost算法的核心在於for循環,該循環運行numIt次或者直到訓練錯誤率為0為止。循環中的第一件事就是利用前面介紹的buildStump函數建立一個單層決策樹。該函數的輸入為權重向量D,返回的則是利用D而得到的具有最小錯誤率的單層決策樹,同時返回的還有最小的錯誤率以及估計的類別向量。

接下來,需要計算的則是alpha值。該值會告訴總分類器本次單層決策樹輸出結果的權重。其中的語句max(error, 1e-16)用於確保在沒有錯誤時不會發生除零溢出。而後,alpha值加入到bestStump字典中,該字典又添加到列表中。該詞典包括了分類所需要的所有信息。

接下來的三行❶則用於計算下一次迭代中的新權重向量D。在訓練錯誤率為0時,就要提前結束 for循環。此時程序是通過aggClassEst變量保持一個運行時的類別估計值來實現的❷。該值只是一個浮點數,為了得到二值分類結果還需要調用sign函數。如果總錯誤率為0,則由break語句中止for循環。

接下來我們觀察一下中間的運行結果。還記得嗎,數據的類別標籤為[1.0, 1.0, -1.0, -1.0, 1.0]。在第一輪迭代中,D中的所有值都相等。於是,只有第一個數據點被錯分了。因此在第二輪迭代中,D向量給第一個數據點0.5的權重。這就可以通過變量aggClassEst的符號來瞭解總的類別。第二次迭代之後,我們就會發現第一個數據點已經正確分類了,但此時最後一個數據點卻是錯分了。D向量中的最後一個元素變成0.5,而D向量中的其他值都變得非常小。最後,第三次迭代之後aggClassEst所有值的符號和真實類別標籤都完全吻合,那麼訓練錯誤率為0,程序就此退出。

為了觀察classifierArray的值,鍵入:

>>> classifierArray
[{\'dim\': 0, \'ineq\': \'lt\', \'thresh\': 1.3, \'alpha\': 0.69314718055994529},
     {\'dim\': 1, \'ineq\': \'lt\', \'thresh\': 1.0, \'alpha\': 0.9729550745276565},
     {\'dim\': 0,\'ineq\': \'lt\', \'thresh\': 0.90000000000000002, \'alpha\':0.89587973461402726}]
  

該數組包含三部詞典,其中包含了分類所需要的所有信息。此時,一個分類器已經構建成功,而且只要我們願意,隨時都可以將訓練錯誤率降到0。那麼測試錯誤率會如何呢?為了觀察測試錯誤率,我們需要編寫分類的一些代碼。下一節我們將討論分類。