機器學習筆記(十二)——集成學習方法之AdaBoost

集成學習方法

本文參考於《機器學習實戰》和《機器學習》

在此以前一共介紹了五種分類算法,分別爲KNN、決策樹、樸素貝葉斯、邏輯迴歸、支持向量機,能夠看到每一種算法都有各自的優缺點,以及適合的數據集。集成學習方法能夠將不一樣分類算法構建的分類器組合在一塊兒,更加高效準確的分類。python

使用集成學習方法時能夠有多種形式:能夠是不一樣算法的集成,也能夠是同一算法在不一樣設置下的集成,還能夠是數據集不一樣部分分配給不一樣分類器以後的集成。算法

對於集成方法中分類器的組合也能夠將集成分爲「同質」和「異質」。例如「決策樹集成」中全部的分類器都爲決策樹,同質集成中的個體分類器能夠稱做「基分類器」;而「異質」就是集成中包含不一樣分類算法,異質集成中的個體分類器常稱爲「組件分類器」。app

集成學習經過將若干個弱學習器組合成一個強分類器,這個強分類器能夠得到比單一學習器更加顯著的準確率。弱分類器常指準確率略優於隨機猜想的學習器,好比在二分類問題上準確率略高於50%的分類器,在同質集成學習中基分類器有時也能夠直接稱爲弱分類器。less

集成學習方法大體可分爲Bagging和Boosting兩種方法,這兩種方法的思想都是將多種分類和迴歸算法經過某種方式組合在一塊兒,從而獲得一個強分類器。下面分別介紹這兩種方法,比較兩種方法的異同。機器學習

Bagging

能夠將Bagging方法的主要原理總結成如下幾點:分佈式

  • 抽樣:在原始數據中有放回的抽取N次獲得一個新的採樣數據集。新數據集與原始數據集大小相等,因爲是有放回抽樣,因此容許採樣數據集中有重複值。
  • 訓練:總共選取M個採樣數據集,每個數據集對應一種學習算法,從而能夠獲得M個分類器,利用這些分類器對數據進行分類。
  • 票選:M個分類器會獲得M個分類結果,因此最後經過投票將分類結果中最多的類別做爲最終的分類結果。

隨機森林就是Bagging方法的典型表明,它是由不少棵決策樹與Bagging方法思想相結合而成。函數

Boosting

Boosting方法與Bagging方法很相似,但前者主要是經過將權重與串行迭代結合的方式進行學習,Boosting更加關注每次分類結束後被分錯的樣本,在下次分類以前,Boosting會增大分錯樣本的權重,這樣下一個分類器在訓練時就會更加註重對分錯樣本的訓練。學習

在決定最終的分類時,因爲每一輪分類權重都會發生改變,因此Boosting方法經過加權求和的方式獲得分類結果。Boosting方法的典型表明爲Adaboost,它是由不少棵決策樹與Boosting方法思想相結合而成,也被稱做提高樹,除此以外還有GBDT和XGBoost。下文會給出以AdaBoost的流程圖,其原理就是Boosting的主要思想。測試

兩種方法區別

兩種算法雖然思想上都是經過將若干個弱分類器組合成一個強分類器,讓最終的分類準確率獲得提高,可是兩者也是有很大區別的,具體以下:ui

  • Bagging在原始數據集中以隨機放回抽樣的方式獲得新採樣集;而Boosting每一輪的訓練集都爲原始數據集,只是訓練集中每一個樣本的權重在發生變化。
  • Bagging是採用並行的方式學習,每一個分類器學習時互不影響;而Boosting是採用串行的方式學習,上一次訓練結果會影響下一次訓練集中樣本的權重。
  • Bagging因爲每一個分類器權重都一致,因此在斷定分類最終結果時採用投票式;而Boosting每一個分類器都有相應的權重,因此在斷定分類最終結果時採用加權求和的方式。

AdaBoost算法介紹

算法原理

AdaBoost的全稱爲adaptive boosting(自適應boosting),能夠將其運行過程歸結爲如下幾點:

  • 訓練數據集中的每一個樣本都會被賦予一個相同的權重值,這些權重會構成一個向量,暫稱爲D。
  • 分類器在訓練數據集以後會計算出該分類器的錯誤率,即分錯樣本的機率,而且依據錯誤率更新分類器的權重。
  • 在下一個分類器訓練以前,會依據上一個分類器的權重調整每個樣本的權重,上一次分對的樣本權重下降,分錯的樣本權重提升。
  • 迭代次數是人爲設置的,在達到迭代次數或者知足某個條件後,以加權求和的方式肯定最終的分類結果。

相關公式

一、樣本權重

對於一個含有N個樣本的數據集,在初始化訓練數據的權重分佈式,每一個樣本的權重都應該是相等的。

二、錯誤率

若是咱們將基於不一樣權重$D(M)$訓練出的分類器稱做$h_M(x)$,每一個樣本實際對應類別爲$f(x)$,則錯誤率$\epsilon$的定義以下:$$\epsilon=\frac{未正確分類的樣本數目}{全部樣本數目}$$ $$\epsilon_M=P(h_M(x)\neq f(x))$$

三、分類器權重

除樣本權重外,每個分類器也會有一個權重$\alpha_M$: $$\alpha_M=\frac{1}{2}ln(\frac{1-\epsilon_M}{\epsilon_M})$$
能夠看下面這張流程圖:

左邊是數據集,黑條的長度表明每一個樣本的權重,每個分類器訓練以後後,都會獲得一個分類器權重$\alpha$,最後全部分類器加權結果(三角形)會在圓形中求和,以獲得最終的分類結果。

四、更新權重D

在下次訓練以前,爲了使正確分類的樣本權重下降而錯分樣本的權重提升,須要對權重向量D更新:

在兩個式子合併時,這裏通常針對二分類問題,即類別標籤爲+1和-1。當分類器預測結果和真實類別相同時,兩者乘積爲+1,不一樣時,兩者乘積爲-1。其中$Z_t$稱做規範化因子,它的做用就是確保$D_{M+1}$是一個機率分佈。

五、最終分類器輸出

能夠利用每一個弱分類器的線性組合達到加權求和的目的,而且經過符號函數肯定最終的分類結果。$$H(x)=sign(\sum_{m=1}^M\alpha_m h_m(x))$$

結合策略

能夠看到AdaBoost的流程圖中有一個「結合策略」,顧名思義,就是經過某種策略將全部的弱分類器的分類結果結合在一塊兒,繼而判斷出最終的分類結果。結合策略可分爲三類:平均法、投票法和學習法。

  • 平均法

對於數值型的迴歸預測問題,最多見的結合策略是使用平均法,平均法又能夠分爲簡單平均法和加權平均法。

假設有n個弱分類器$(h_1(x),h_2(x),...,h_n(x))$,簡單平均法公式以下: $$H(x)=\frac{1}{n}\sum_{i=1}^nh_i(x)$$

加權平均法公式以下:$$H(x)=\sum_{i=1}^nw_ih_i(x)$$
其中$w_i$是弱分類器$h_i(x)$的權重,一般要求$w_i\geq0$,$\sum_{i=1}^nw_i=1$。能夠看出簡單平均法是加權平均法令$w_i=\frac{1}{n}$的特例。

  • 投票法

對分類的預測,一般使用的是投票法,投票法能夠分爲相對多數投票法、絕對多數投票法和加權投票法。

相對多數投票法就是常說起的少數服從多數,在預測結果中選擇出現次數最多的類別做爲最終的分類類別,若是不止一個類別得到最高票數,則在其中隨機選取一個作最終類別。

絕對多數投票法就是在相對多數投票法的基礎上進行改進,不光要求要得到最高票數,而且票數還要超過50%,不然拒絕預測。

加權投票法與加權平均法相似,即每一個若分類器獲得的分類票數要乘以一個權重,最終將各個類別的票數加權求和,最大值對應類別做爲最終分類類別。

  • 學習法

學習法相對於前兩種方法較爲複雜,但獲得的效果可能會更優,學習法的表明是Stacking,其總體思想是在弱分類器以後再加上一個分類器,將弱分類器的輸出當成輸入再次訓練。

咱們將弱分類器稱做初級分類器,將用於結合的分類器稱做次級分類器,利用學習法結合策略時,先經過初級分類器對數據集訓練,而後再經過次級分類器在訓練一次,纔會獲得最終的預測結果。

AdaBoost代碼實現

單層決策樹

單層決策樹是一種簡單的決策樹,它僅僅基於單個特徵來作決策,也就是獲得一個信息增益以後會捨棄其餘特徵,因此單層決策樹只有一次分裂過程,實際上也是一個樹樁。

之因此用單層決策樹來構建弱分類器,是由於在下面這份數據集中,單層決策樹能更好的體現"弱"這個性質。

爲何這麼說呢?在某個座標軸上選擇一個值,若是要利用垂直座標軸而且通過該值的一條直線將兩類樣本分隔開,這顯然是不可能的。可是能夠經過屢次劃分,即經過使用多棵單層決策樹構建出一個徹底正確分類的分類器。

咱們暫且將上圖樣本的分類規定爲正類和負類,那麼在劃分數據集時候能夠給定相應的閾值,以下圖:

假如選取X1特徵的閾值爲1.4,能夠規定小於這個閾值的樣本爲負類,相應大於這個閾值的樣本爲正類;但規定小於閾值的樣本爲正類,大於爲負類也一樣合理,因此對於一個特徵有兩種狀況,這也是下面這個函數中惟一比較彆扭的點。

def buildStump(dataMatrix, classLabels, D):
    labelMat = np.mat(classLabels).T # 標籤列表矩陣化並轉置
    m, n = np.shape(dataMatrix) # 獲取輸入數據矩陣行數和列數
    numSteps = 10.0 # 閾值改變次數
    bestStump = {} # 用於存樹的空字典
    bestClasEst = np.mat(np.zeros((m, 1)))
    minError = float('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): # 閾值改變次數
            # lt(less than)指小於該閾值,分類爲-1
            # gt(greater than)指大於該閾值,分類爲-1
            for inequal in ['lt', 'gt']: # 兩種狀況,都須要遍歷
                threshVal = (rangeMin + float(j) * stepSize) # 計算閾值
                predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal) # 獲得分類結果
                errArr = np.mat(np.ones((m, 1))) # 建立一個存偏差值的矩陣
                errArr[predictedVals == labelMat] = 0 # 將分類正確的樣本賦值爲0
                weightedError = D.T * errArr # 利用權重計算出偏差
                if weightedError < minError: # 若是本輪偏差比上一次小,則替換
                    minError = weightedError
                    bestClasEst = predictedVals.copy()
                    # 將單層決策樹存入字典
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal
    return bestStump, minError, bestClasEst

這部分代碼中須要瞭解如下點:numSteps就是遍歷某個特徵全部取值的次數;bestStump這個字典用來存儲最佳單層決策樹的相關信息;minError爲最小偏差,暫定正無窮大,每次循環都會更新。

三層for循環是關鍵部分,首先第一層是遍歷全部特徵,而且設置了每次閾值改變的長度;第二層是以步長爲基礎,遍歷特徵上全部可取值;第三層則是小於和大於之間切換不等式。

在第三層循環內調用了一個函數,基於給定變量,會返回分類預測結果。首先建立了一個全爲1的矩陣,若是預測結果和真實結果一致則對應位置調整爲0,而且利用這個矩陣和權重矩陣的轉置相乘可獲得一個新的偏差,接着會經過比較更新最小偏差,並儲存最佳單層決策樹的相關信息,即特徵、閾值和(lt or gt)。

第三層循環調用函數以下:

def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
    # 建立一個與輸入矩陣行數相同、列數爲1的矩陣
    retArray = np.ones((np.shape(dataMatrix)[0], 1))
    # 若是小於閾值,分類爲-1
    if threshIneq == 'lt':
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
    # 若是大於閾值,分類爲-1
    else:
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0
    return retArray

這個函數很是簡單,就是以前說起兩種狀況,小於閾值將其分類爲-1或者大於閾值時將其分類爲-1都是合理狀況,因此須要遍歷考慮這兩種狀況。

能夠看到對於第一個特徵來講最小偏差爲0.2,也就是說垂直於x軸劃分,最優狀況下會分錯一個樣本,下面利用AdaBoost方法將多個單側決策樹結合在一塊兒,看一下樣本是否能所有分類正確。

結合AdaBoost方法

前文已經給出了AdaBoost運行流程及所需公式,只須要設定弱分類器的個數或者稱爲迭代次數,每一個弱分類器都實現一遍流程便可,代碼部分以下:

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))) #初始化一個值爲0的行向量
    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))) # 計算弱分類器的權重
        bestStump['alpha'] = alpha # 將弱分類器權重存入樹中
        weakClassArr.append(bestStump) # 將最佳單層決策樹存入一個列表
        # 更新樣本權重
        expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst)
        D = np.multiply(D, np.exp(expon))
        D = D / D.sum()
        # 計算AdaBoost的偏差
        aggClassEst += alpha * classEst                       
        aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m,1)))
        errorRate = aggErrors.sum() / m
        # 若是偏差爲0,則退出循環
        if errorRate == 0.0:
            break
    return weakClassArr, aggClassEst

首先對全部樣本初始化一個權重D,在設置的迭代次數內,獲得存儲最佳單層決策樹相關信息的字典、最小偏差、預測結果。在最小偏差的基礎上更新分類器權重alpha,依據公式當最小偏差爲0時,程序會發生報錯,即除數爲0,利用max方法便可防止這種狀況發生。

而後依據alpha更新樣本的權重,而expon中的classEst也就是上文公式中弱分類器預測結果$h_M(x)$,classLabels也就是真實結果$f(x)$。

由於咱們沒法猜得一個數據集須要幾棵單層決策樹才能徹底正確分類,若是迭代次數過多,則會發生過擬合的現象,分類反而不許了,因此上述代碼最後這部分就是爲了防止迭代次數過多,在偏差爲0時即便未達到迭代次數也退出迭代。

這裏運用了上文公式的$$H(x)=sign(\sum_{m=1}^M\alpha_m h_m(x))$$
先計算每一個弱分類器預測類別的估計值,累加後再利用符號函數進行分類。這裏科普一下sign方法,當值大於0時返回1.0,當值小於0時返回-1.0,當值等於0時返回0,知道了sign方法,這部分代碼就很容易理解。


最後AdaBoost方法經過三個最佳單層決策樹實現了對數據集的徹底正確分類,好比下面這種切分方式:

可能表達的比較抽象,可是在理解的時候你要拋開一個刀就能劃分的思想,咱們利用的是集成方法求解分類問題,這是三刀組合在一塊兒才獲得的徹底正確分類結果。

測試算法

算法模型已經建成了,因此須要對該模型進行測試,代碼的思路比較簡單,就是遍歷全部訓練獲得的弱分類器,計算每一個弱分類器預測類別的估計值,累加後再經過符號函數對類別進行判斷,代碼以下:

def adaClassify(datToClass,classifierArr):
    dataMatrix = np.mat(datToClass)
    m = np.shape(dataMatrix)[0]
    aggClassEst = np.mat(np.zeros((m,1)))
    for i in range(len(classifierArr)):
        classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'], classifierArr[i]['thresh'], classifierArr[i]['ineq'])
        aggClassEst += classifierArr[i]['alpha'] * classEst
    return np.sign(aggClassEst)

自己咱們的訓練數據集就很是小,因此不會計算模型準確率,只是輸入兩個樣本對其類別進行判斷,這裏樣本選擇[[5,5],[0,0]],運行截圖以下:

能夠看到,隨着迭代次數的增長,類別的估計值的絕對值愈來愈大(離0愈來愈遠),由於最後是利用符號函數對分類結果進行判斷,因此這也表明着輸入樣本的分類結果愈來愈強。

總結

AdaBoost的優勢就是分類的準確率高,與其餘複雜算法相比更容易編碼,而且能夠應用在大部分分類器上,沒有什麼參數須要調整。缺點就是對離羣點敏感,由於離羣點通常都是錯誤數據,每每應該被捨棄,對於這類樣本分類器很容易分錯,而分錯的樣本的下次迭代時權重會增長,分類器對這類樣本會更加劇視,就可能會致使一錯再錯。

關注公衆號【奶糖貓】後臺回覆"AdaBoost"可獲取源碼供參考,感謝閱讀。

相關文章
相關標籤/搜索