AdaBoost算法的原理與實現

前面講到了增強學習的基本原理以及兩種方法Boosting,現在就介紹我們的主角AdaBoost。

集成學習的兩個關注點

  • 在每一輪如何改變訓練數據的權值或者概率分佈
  • 如何將弱分類器組合成一個強分類器

AdaBoost思想

對於第一個問題:每一個訓練樣本都被賦予一個權重,表明它被某個分類器選入訓練集的概率。然後提高被前一輪若分類器錯誤分類的樣本的權值,降低那些被錯誤分類的樣本的權值。這樣一來沒有被正確分類的數據由於其權值增大,而在後面的分類中受到更大的關注,這也是AdaBoost的自適應的體現。
而關於分類器的組合策略,AdaBoost採用的是加權多數表決法。具體做法是加大分類誤差率小的分類器的權值,減小分類誤差器大的的權值。
AdaBoost算法可以分爲下面三步:

  1. 給每一個訓練樣本賦值一個初始化權值,假設有N個數據集,那麼將權值設置成1/N.

  2. 然後利用弱分類器hi 訓練數據,然後計算出每一個弱分類器的錯誤率,選擇錯誤率最低的弱分類器,然後將錯誤分類的樣本權值增大,正確分類的樣本的權值減小。然後計算出分類器的權值a ,如此迭代下去。

  3. 最後將訓練得到的弱分類器組合成一個強分類器,具體做法是加大分類誤差率小的分類器的權值,減小分類誤差器大的的權值。

AdaBoost算法過程

假設給定一個二分類數據訓練集

T=(x1,y1),(x2,y2)...(xN,yN)

其中yi{+1,1} ,AdaBoost算法利用下面一些列弱算法或者基本若分類,並將這些弱分類器組合成一個強分類器。
有一些列弱分類器Gi(x) ,最後要求輸出強分類器G(x)
具體步驟如下:
(1)初始化訓練數據權值分佈
D1=(w11,...,w1i...,wiN),w1i=1N,i=1,2,...N

(2)對m=1,2,3...M
(a)使用具有權值分佈Dm 的訓練數據集學習,得到基本分類器
Gm(x){+1,1}

(b)計算Gm(x) 在訓練集上面的分類誤差
em=P(Gm(xi)yi)=i=1NwmiI(Gm(xi)yi)

i=1Nwmi=1

由上述式子可知,Gm(x) 在訓練集上的錯誤率em 其實就是被Gm(x) 劃分錯誤的樣本的權值之和

(c)計算分類器Gm(x) 的係數

am=12log1emem

(d)更新訓練後的權值分佈
Dm+1=(wm+1,1....wm+1,i...wm+1,N)

wm+1,i=wm+1,iZmeamyiGm(xi),i=1,2....N

其中,Zm 是規範化因子,是爲了保證Ni=1wmi=1
Zm=2em1em

(3構建基本分類器的線性組合
f(x)=m=1MamGm(x)

最終得到的分類器爲
G(x)=sign(f(x))=sign(m=1MamGm(x))

關於權值更新具體的推導補充:
1. 當樣本分類錯誤時候有,yiGm(i)=1

Dm+1,i=Dm,iZmeam=Dm2em(1em)em1em=Dm,i2(1em)

2. 當樣本分對時有,yiGm(i)=1
Dm+1,i=Dm,iZmeam=Dm2em(1em)1emem=Dm,i2em

下面是AdaBoost算法的示意圖,左邊數數據集,中間是分類器,而右邊的數字表示的是每一個分類器的權值a
這裏寫圖片描述

推薦大家看一下AdaBoost算法原理分析與實例這篇博客,裏面作者舉了一個例子,可以幫助我們更好的理解AdaBoost的過程。

下面我們結合代碼分析一下。

基於單層決策樹構建弱分類器

from numpy import *
import numpy as np
import matplotlib.pyplot as plt

def loadDataSet():
    datMat = np.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

def showDataSet(datMat,classLabels):
    xcord0 = [];ycord0 = []
    xcord1 = [];ycord1 = []
    colors = []
    makers = []
    for i in range(len(classLabels)):
        if classLabels[i]==1.0:
            xcord0.append(datMat[i,0])
            ycord0.append(datMat[i,1])
        else:
            xcord1.append(datMat[i,0])
            ycord1.append(datMat[i,1])
    fig=plt.figure()
    ax=fig.add_subplot(111)
    ax.scatter(xcord0,ycord0,marker='o',s=90,c='blue')
    ax.scatter(xcord1,ycord1,marker='o',s=90,c='red')
    plt.title('DataSet')
    plt.show()

這裏寫圖片描述

如圖所示,我們找不出一條平行於座標軸的直線將兩種顏色的點劃分開,這就是單層決策樹難以解決的一個著名的問題,但是我們可以通過使用多顆單層決策樹,構建一個能夠對該數據正確分類的分類器。
接下來我們構建最優的單層決策樹:
算法的僞代碼如下

將最小錯誤率設置爲無窮大
對數據集中每一個特徵(第一層循環)
    對每一個步長(第二層循環):
        對每一個不等號:
            建立一顆單層決策樹並且用加權數據集對它進行測試
            如果錯誤率低於minError,則將當中單層決策樹設爲最佳決策樹
返回最佳單層決策樹

僞代碼

'''
單層決策樹分類函數
函數參數:
    dataMatrix - 數據矩陣
    dimen -  第dimen列,也就是第幾個特徵
    threshVal - 閾值
    threshIneq - 標誌
返回值:
    retArray - 分類結果
'''
def stumpClassify(datMatrix,dimen,threshVal,threshIneq):
    retArray = np.ones((np.shape(datMatrix)[0],1))
    if threshIneq =='lt':#初始化retArray爲1
        retArray[datMatrix[:,dimen]<= threshVal] = -1.0#如果小於閾值,則賦值爲-1
    else:
        retArray[datMatrix[:,dimen]>threshVal] = -1.0#如果大於閾值,則賦值爲-1
    return retArray

'''
找到數據集上最佳的單層決策樹
函數參數:
    dataArr - 數據矩陣
    classLabels - 數據標籤
    D - 樣本權重
返回值:
    bestStump - 最佳單層決策樹
    minError - 最小誤差
    besetClasEst - 最佳的分類器    
'''

def buildStump(dataArr,classLabels,D):
    dataMatrix = np.mat(dataArr)
    labelMat = np.mat(classLabels).T
    m,n=np.shape(dataMatrix)
    numSteps = 10.0
    bestStump = {}
    bestClaEst = np.mat(np.zeros((m,1)))
    minError = np.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)#計算閾值
                predictVals = stumpClassify(dataMatrix,i,threshVal,inequal)#計算分類的結果
                errArr = np.mat(np.ones((m,1)))#初始化誤差矩陣
                #判斷計算值和實際是否有誤差,
                #如果沒有誤差可以則設置爲0,若有誤差則設置爲1
                errArr[predictVals==labelMat]=0#
                weightedError = D.T*errArr#計算誤差
                #print('split: dim %d,thresh % .2f,thresh inequal: %s,the weighted error is %.3f' %(i,threshVal,inequal,weightedError))
                if weightedError<minError:#找到最小的誤差分類
                    minError = weightedError
                    bestClaEst=predictVals.copy()
                    bestStump['dim']=i
                    bestStump['thresh']=threshVal
                    bestStump['ineq']=inequal
    return bestStump,minError,bestClaEst

這裏一個單層決策樹就構建完成,通過AdaBoost構建一個完整的決策樹
實現的思想如下:

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

基於單層決策樹的AdaBoost訓練過程

'''
使用AdaBoost算法提升弱分類器性能
函數參數:
    dataArr - 數據矩陣
    classLabels - 數據標籤
    numIt - 最大迭代次數
返回值:
    weakClassArr - 訓練好的樣本
    aggClassEst - 類別估計累計值
'''
def adaBoostTrainDS(dataArr,classLabels,numIt=40):
    weakClassArr = []
    m=np.shape(dataArr)[0]
    D=np.mat(np.ones((m,1))/m)#初始化權值
    aggClassEst = np.mat(np.zeros((m,1)))
    for i in range(numIt):
        bestStump,error,classEst = buildStump(dataArr,classLabels,D)#構建單層決策樹
        alpha = float(0.5*np.log((1.0-error)/max(error,1e-16)))#計算弱分類器的權值alpha應爲分母權值不能爲0
        bestStump['alpha'] = alpha#單層決策樹裏面保存權值
        weakClassArr.append(bestStump)#儲存單層決策樹
        #print("ClassEst: ",classEst.T)
        expon = np.multiply(-1*alpha*np.mat(classLabels).T,classEst)#計算e的指數項

        D=np.multiply(D,np.exp(expon))
        D=D/D.sum()#更新樣本權值公式
        aggClassEst +=alpha*classEst    #計算類別估計值
        #print("aggClassEst",aggClassEst.T)
        aggErrors = np.multiply(np.sign(aggClassEst)!=np.mat(classLabels).T,np.ones((m,1)))#計算誤差
        errorRate = aggErrors.sum()/m
        #print('total error: ',errorRate,"\n",)
        if errorRate == 0.0:
            break
    return weakClassArr,aggClassEst

這段代碼的作用是首先根據產生弱分類器然後將弱分類器裝在一個集合中,然後更新樣本的權值

測試算法:基於AdaBoost的分類

'''
AdaBoost分類器
函數參數:
    datToClass - 待分類樣例
    classifierArr - 訓練好的分類器
返回值:
    分類結果
'''

def adaClassify(datToClass,classifierArr):
    datMatrix = np.mat(datToClass)
    m=np.shape(datToClass)[0]
    aggClassEst = np.mat(np.zeros((m,1)))
    for i in range(len(classifierArr)):#遍歷所有的分類器
        #構建單層決策樹
        classEst = stumpClassify(datMatrix,classifierArr[i]['dim'],\
            classifierArr[i]['thresh'],classifierArr[i]['ineq'])
        aggClassEst+=classifierArr[i]['alpha']*classEst #將分類器合併
    return np.sign(aggClassEst)

利用已經有的分類器求出分類結果,最後將分類結果進行合併。
github地址:https://github.com/geroge-gao/MachineLeaning
參考資料:

  • 機器學習實戰
  • 統計學習方法
發佈了71 篇原創文章 · 獲贊 255 · 訪問量 43萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章