機器學習之Adaboost算法

一、算法原理

1.算法的基本思想

Adaboostadaptive boosting的簡寫,是自適應的boosting算法,基本思想爲:在前一個弱分類器的基礎上,增加誤分類樣本的權重,這些誤分類的樣本在下一個弱分類器那裏被重點關注,依次迭代進行,直到到達預定的足夠小的錯誤率或最大的迭代次數爲止。大概流程描述如下:

  1. 初始化訓練數據的權值分佈,假設樣本個數爲 NN,則每個樣本的權值爲 1N\frac{1}{N}
  2. 在初始訓練集上訓練出一個弱分類器,根據分類結果,被誤分類的樣本權重增加,正確分類的樣本的權重將減少,然後將權值更新過的訓練數據集用於訓練下一個弱分類器,不斷進行迭代。
  3. 將各個弱分類器進行組合形成強分類器。各個弱分類器也有自己的權重,加大分類誤差率小的弱分類器的權重,使其在最終的分類函數中有更大的決定權,同理,減少分類誤差率高的弱分類器的權重,使其在最終的分類函數中起着較小的決定權。
2.算法的流程

假設一個二分類的訓練數據集T={(x1,y1),(x2,y2), ,(xN,yN)}T=\{(x_{\scriptscriptstyle 1},y_{\scriptscriptstyle 1}),(x_{\scriptscriptstyle 2},y_{\scriptscriptstyle 2}),\cdots,(x_{\scriptscriptstyle N},y_{\scriptscriptstyle N})\}
其中,xiχRnx_{i}\in \chi \in R^{\scriptscriptstyle n},標記yi{1,1}y_{\scriptscriptstyle i}\in \{-1,1\}
(1)初始化訓練數據集的權值分佈
D1=(w11, ,w1i, ,w1N), w1i=1N, i=1,2, ,ND_{\scriptscriptstyle 1}=(w_{\scriptscriptstyle 11},\cdots,w_{\scriptscriptstyle 1i},\cdots,w_{\scriptscriptstyle 1N}),\ w_{\scriptscriptstyle 1i}=\frac{1}{N},\ i=1,2,\cdots,N
(2)訓練每個弱分類器,假設有 MM 個弱分類器,對於 m=1,2, ,Mm=1,2,\cdots,M
(a)使用具有權值分佈的訓練數據集學習,得到基本分類器
Gm(x)χ{1,1}G_{\scriptscriptstyle m}(x):\chi\to\{-1,1\}
(b)計算基分類器在訓練數據集上的分類誤差率
em=i=1NP(Gm(xiyi))=i=1NwmiI(P(Gm(xiyi))e_{\scriptscriptstyle m}=\sum\limits_{\scriptscriptstyle i=1}^{\scriptscriptstyle N}P(G_{m}(x_{i}\ne y_{i}))=\sum\limits_{\scriptscriptstyle i=1}^{\scriptscriptstyle N}w_{mi}I(P(G_{m}(x_{i}\ne y_{i}))
可以看出來,每個基分類器的誤差率其實就是誤分類樣本的權值之和。
©計算基分類器 Gm(x)G_{\scriptscriptstyle m}(x) 的係數
αm=12log1emem\alpha_{\scriptscriptstyle m}=\frac{1}{2}log\frac{1-e_{\scriptscriptstyle m}}{e_{\scriptscriptstyle m}}
這裏的對數是自然對數
我們知道,基分類器要滿足 “好而不同”,而 “好” 體現在每個基分類器的性能要比隨機猜測要好一些,
em0.5e_{\scriptscriptstyle m}\geq0.5時,1emem1\frac{1-e_{\scriptscriptstyle m}}{e_{\scriptscriptstyle m}}\leq1,則αm0\alpha_{\scriptscriptstyle m}\leq 0
em<0.5e_{\scriptscriptstyle m}<0.5時,1emem>1\frac{1-e_{\scriptscriptstyle m}}{e_{\scriptscriptstyle m}}>1,則αm>0\alpha_{\scriptscriptstyle m}> 0
給分類性能好的分類器較大的權重,使其在最終的分類函數中起到更大的決定作用。
(d)更新訓練集的權值分佈,
Dm+1=(wm+1,1, ,wm+1,i,wm+1,N)D_{\scriptscriptstyle m+1}=(w_{\scriptscriptstyle m+1,1},\cdots,w_{\scriptscriptstyle m+1,i},w_{\scriptscriptstyle m+1,N})wm+1,i=wmi e(αmyiGm(xi))Zmw_{\scriptscriptstyle m+1,i}=\frac{w_{\scriptscriptstyle mi}\ e^{(-\alpha_{\scriptscriptstyle m}y_{\scriptscriptstyle i}G_{m}(x_{\scriptscriptstyle i}))}}{Z_{\scriptscriptstyle m}}
其中,
Zm=i=1Nwm,ieαmyiGm(xi)Z_{\scriptscriptstyle m}=\sum\limits_{\scriptscriptstyle i=1}^{\scriptscriptstyle N}w_{\scriptscriptstyle m,i}e^{-\alpha_{\scriptscriptstyle m}y_{\scriptscriptstyle i}G_{\scriptscriptstyle m}(x_{\scriptscriptstyle i})}
ZmZ_{\scriptscriptstyle m}是一個規範化因子
觀察訓練樣本的權值更新公式,我們可以發現,
yiGm(xi)=1y_{\scriptscriptstyle i}G_{\scriptscriptstyle m}(x_{\scriptscriptstyle i})=1,即樣本點 ii 被正確分類時,有wm+1,i=wmi e(αm)Zmw_{\scriptscriptstyle m+1,i}=\frac{w_{\scriptscriptstyle mi}\ e^{(-\alpha_{\scriptscriptstyle m})}}{Z_{\scriptscriptstyle m}}
yiGm(xi)=1y_{\scriptscriptstyle i}G_{\scriptscriptstyle m}(x_{\scriptscriptstyle i})=-1,即樣本點被誤分類,有
wm+1,i=wmi e(αm)Zmw_{\scriptscriptstyle m+1,i}=\frac{w_{\scriptscriptstyle mi}\ e^{(\alpha_{\scriptscriptstyle m})}}{Z_{\scriptscriptstyle m}}
一句話總結,增加被誤分類樣本的權重,減少已被正確分類的樣本的權重。
(3)構建基本分類器的線性組合
f(x)=m=1MαmGm(x)f(x)=\sum\limits_{\scriptscriptstyle m=1}^{\scriptscriptstyle M}\alpha_{\scriptscriptstyle m}G_{\scriptscriptstyle m}(x)
最終的分類器函數爲,
G(x)=sign(f(x))=sign(m=1MαmGm(x))G(x)=sign(f(x))=sign(\sum\limits_{\scriptscriptstyle m=1}^{\scriptscriptstyle M}\alpha_{\scriptscriptstyle m}G_{\scriptscriptstyle m}(x))

二、實戰分析

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

加載數據以及可視化數據:

import numpy as np
import  matplotlib.pyplot as  plt
import matplotlib as mpl
def loadSimData():
    dataMat = np.matrix([[1.0,2.1],
                        [2.0,1.1],
                        [1.3,1.0],
                        [1.0,1.0],
                        [2.0,1.0]])
    classlabels = [1.0,1.0,-1.0,-1.0,1.0]
    return dataMat,classlabels
dataMat,classlabels = loadSimData()
def plotDecisionStu(dataMat,classlabes):
    n = np.shape(dataMat)[0]
    xcord1=[];ycord1=[]
    xcord2=[];ycord2=[]
    for i in range(n):
        if classlabels[i]==1:
            xcord1.append(dataMat[i,0]);ycord1.append(dataMat[i,1])
        else:
            xcord2.append(dataMat[i,0]);ycord2.append(dataMat[i,1])
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xcord1,ycord1,s=30,c='red',marker = 's')
    ax.scatter(xcord2,ycord2,s=30,c='blue',marker = '8')
    mpl.rcParams['font.sans-serif'] = ['simhei']
    plt.title('單層決策樹測試數據')
    plt.show()
print(plotDecisionStu(dataMat,classlabels))

結果:
在這裏插入圖片描述
單層決策樹生成函數:

僞代碼:
將最小錯誤率minError設爲++\infty
對數據集中的每一個特徵(第一層循環):   
  對每個步長(第二層 循環):
    對每個不等號(第三層循環):
       建立一棵單層決策樹並用加權數據集進行訓練
       如果錯誤率低於minError,則將當前的單層決策樹設爲最佳的單層決策樹
返回最佳的單層決策樹

# 單層決策樹生成函數
#單層決策樹的閾值過濾函數
# 參數說明:dataMatrix-訓練數據集
#                dimen-某一個特徵的索引
#            threshVal-閾值
#           threshIneq-不等式符號
def  stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
    # 初始化每一個樣本點的類標籤爲1
    reArray = np.ones((np.shape(dataMatrix)[0],1))
    # 判斷不等式的符號:lt-表示小於或等於閾值
    #                   gt-表示大於閾值
    if threshIneq == 'lt':
        # 如果是lt,表示特徵值小於或等於閾值,類標籤爲-1
        reArray[dataMatrix[:,dimen] <= threshVal] = -1.0
    else:
        # 否則,表示特徵值大於閾值,類標籤爲-1
        reArray[dataMatrix[:,dimen] > threshVal] = -1.0
    return  reArray
# 返回數據集上的最佳決策樹
def buildStump(dataMatrix,labelMat,D):
    # 返回訓練集的大小
    m,n = np.shape(dataMatrix)
    # 步數,最佳決策樹信息,最優單層決策樹的預測結果
    numSteps = 10.0;bestStemp = {};bestClassEst = 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)
                # 返回預測值
                predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)
                # 初始化誤分類的樣本矩陣
                errArr = np.mat(np.ones((m,1)))
                # 將誤分類矩陣中預測值與真實值相等的位置賦值爲0
                errArr[labelMat.T == predictedVals] = 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
                    # 將閾值添加到字典的threshVal鍵
                    bestStemp['threshVal'] = threshVal
                    # 將不等式添加到字典的threshIneq
                    bestStemp['threshIneq']= inequal
                    # 將預測值賦值給bestClassEst
                    bestClassEst = predictedVals.copy()
                    # 最佳的分類特徵
                    bestStemp['dim'] = i
    return  bestStemp,bestClassEst,minError

D = np.mat(np.ones((5,1))/5)
# bestStemp, bestClassEst, minError = buildStump(dataMat,classlabels,D)
print(buildStump(dataMat,classlabels,D))

結果:
在這裏插入圖片描述

2.完整的Adaboost算法
**上面的程序只是生成了單個基分類器,這裏,我們要生成多個弱分類器來構建完整的Adaboost算法:**

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

# 完整的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)))
    # 開始迭代
    for  i in range(numIt):
        # 得到最佳的單層決策樹
        bestStemp,ClassEst,Error = buildStump(dataArr,classlabels,D)
        print('D:',D.T)
        # 計算每一個單層決策樹的權重係數
        alpha = float(0.5*np.log((1-Error)/max(Error,1e-16)))
        # 將每一個弱分類器的係數alpha添加到字典 bestStemp
        bestStemp['alpha'] = alpha
        # 將該決策樹的信息存儲起來
        weakClassArr.append(bestStemp)
        print('ClassEst:',ClassEst.T)
        # 更新每一個樣本的權值向量
        expon = np.multiply(-alpha*np.mat(classlabels).T,ClassEst)
        D = np.multiply(D,np.exp(expon))
        D = D/D.sum()
        # 累加當前決策樹的加權預測值
        aggClassEst += alpha*ClassEst
        print('aggClassEst:',aggClassEst)
        # 返回一個 m×1 的矩陣,預測正確的位置爲0,誤分類的位置爲1
        aggErrors = np.multiply(np.sign(aggClassEst)!=np.mat(classlabels).T,np.ones((m,1)))
        # 計算誤分類率
        errorRate = aggErrors.sum()/m
        print('total error',errorRate)
        # 如果誤分類率爲0,則退出循環
        if errorRate == 0.0:
            break
    return  weakClassArr
print(adaBoostTrainDs(dataMat,classlabels,numIt=40))

結果:

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
[{'threshVal': 1.3, 'threshIneq': 'lt', 'dim': 0, 'alpha': 0.6931471805599453}, {'threshVal': 1.0, 'threshIneq': 'lt', 'dim': 1, 'alpha': 0.9729550745276565}, {'threshVal': 0.9, 'threshIneq': 'lt', 'dim': 0, 'alpha': 0.8958797346140273}]

其中,最後一行詳細記錄了每一個基學習器採用的閾值、不等式、特徵、權重。

3.基於Adaboost的分類

上一個代碼是在訓練集上訓練弱分類器,這裏,我們把訓練好的弱分類器抽離出來,在小數據集上進行測試,

# 利用訓練好的基學習器對數據進行分類
def adaClassifiy(dataToClass,classifierArr):
    dataMatrix = np.mat(dataToClass)
    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]['threshVal'],
                                 classifierArr[i]['threshIneq'])
        aggClassEst += classifierArr[i]['alpha']*classEst
    AggClassEst = np.sign(aggClassEst)
    return AggClassEst
classifierArr = adaBoostTrainDs(dataMat,classlabels,numIt=40)
AggClassEst = adaClassifiy([[5,5],[0,0]],classifierArr)
print(AggClassEst)

結果:

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
[[ 1.]
 [-1.]]

參考資料:
1.《統計學習方法》李航
2.《機器學習實戰》Peter Harrington
3.matplotlib.markers:https://matplotlib.org/api/markers_api.html
4.matplotlib繪圖之中文標題、座標軸標籤亂碼問題:https://blog.csdn.net/anmo1221/article/details/77746528
5.常用數學符號的 LaTeX 表示方法:http://mohu.org/info/symbols/symbols.htm

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章