[機器學習]-Adaboost提升算法從原理到實踐


1.基本思想:

綜合某些專家的判斷,往往要比一個專家單獨的判斷要好。在”強可學習”和”弱可學習”的概念上來說就是我們通過對多個弱可學習的算法進行”組合提升或者說是強化”得到一個性能趕超強可學習算法的算法。如何地這些弱算法進行提升是關鍵!AdaBoost算法是其中的一個代表。

2.分類算法提升的思路:

    1.找到一個弱分類器,分類器簡單,快捷,易操作(如果它本身就很復雜,而且效果還不錯,那么進行提升無疑是錦上添花,增加復雜度,甚至上性能並沒有得到提升,具體情況具體而論)。

    2.迭代尋找N個最優的分類器(最優的分類器,就是說這N個分類器分別是每一輪迭代中分類誤差最小的分類器,並且這N個分類器組合之后是分類效果最優的。)。

    在迭代求解最優的過程中我們需要不斷地修改數據的權重(AdaBoost中是每一輪迭代得到一個分類結果與正確分類作比較,修改那些錯誤分類數據的權重,減小正確分類數據的權重 ),后一個分類器根據前一個分類器的結果修改權重在進行分類,因此可以看出,迭代的過程中分類器的效果越來越好,所以需要給每個分類器賦予不同的權重。最終我們得到了N個分類器和每個分類器的權重,那么最終的分類器也得到了。

3.算法流程:(數據默認:M*N,M行N列,M條數據,N維 )

    輸入:訓練數據集,:弱學習算法(xi表示數據i[數據i是個N列/維的],yi表示數據的分類為yi,Y={-1,1}表示xi在某種規則約束下的分類可能為-1或+1)

    輸出:最終分類器G(x)

   1)初始化訓練數據的權值分布(初始化的時候每一條數據權重均等)

     ,M表示數據的個數,i=1,2,3…M 

   2)j=1,2,3,…,J(表示迭代的次數/或者最終分類器的個數,取決於是否能夠使分類誤差為0)

     a)使用具有權值分布Dj的訓練數據集學習,得到基本的分類器

        Gj(x):X->{-1,+1}

     b)計算Gj(x)在訓練集上的分類誤差率

        

         求的是分錯類別的數據的權重值和,表示第i個數據的權重Dj[i]

      c)計算Gj(x)第j個分類器的系數(權重),ln表示以E為底的自然對數跟ej沒什么關系,ej表示的是分類錯誤率。

       

     d)更新訓練數據集的權重Dj+1,數據集的權重是根據上一次權重進行更新的, i=1,2,3…M(xi表示第i條數據)

        

         

         

        Z是規范化因子,他表示所有數據權重之和,它使Dj+1成為一個概率分布。

    3)構建基本分類器的線性組合

       

      得到最終的分類器:

          

4.用一組數據來具體解說一下Adaboost的實現過程:

   Data5*2

  

   原始類別:

  

   1.初始化數據權重D1=1/5,1/5,1/5,1/5,1/5,五條數據所以是5列,w=1/m

   2.分類器

      

    通過計算得到誤差率最小時V的值,但是最小誤差率是由分類結果Gx)得到的,所以這個V值我們只有通過窮舉得到。

   1).按第一維度來分類:   

    我們找到第一維所有數據的極值(min=1,max=2,我們從最小的數據1開始,每次增加0.5,即V=min+0.5*n,n表示次數。

    v=1+0.5*1=1.5

    分類結果G(x):

      G(x)=[1<1.5->1,2>1.5->-1,1.3<1.5->1,1<1.5->1,2>1.5->-1]

      G(x)=[1,-1,1,1,-1]

       誤差率為e1:

            e1=sum(D[G(xi)!=yi])誤分類點的權重和

            我們來比較一下分類器的分類結果和原始類別就知道那些分錯了:

       G(x)=[1,-1,1,1,-1]

       Lables=[1,1,-1,-1,1]

            對比一下可以發現第2,3,4,5都分錯了。

            e1=D[2]+D[3]+D[4]+D[5]=0.8

            交換一下符號:即

       

     分類結果G(x):

       G(x)=[1<1.5->-1,2>1.5->1,1.3<1.5->-1,1<1.5->-1,2>1.5->1]

       G(x)=[-1,1,-1,-1,1]

        誤差率為e1:

       G(x)=[-1,1,-1,-1,1]

        Lables=[1,1,-1,-1,1]

            對比一下可以發現第1個錯了。

           e1=D[1]=0.2

        分類器權重alpha:

            Alpha = 0.5*ln((1-0.2)/0.2)

        更新數據權重D

         sum(D1)=1

            D2=((D1[1]*e(-alpha*-1))/sum(D1), (D1[1]*e(-alpha*1))/1,..)

          e的系數最后的+-1取決於是否正確分類,分正確了就是1,分錯誤了就是-1,前面公式中也有寫到。

    這里的計算公式是統計學習方法中的,跟機器學習實戰中的D的計算有一點出入,在機器學習實戰中D是這么計算的:

            D2= D1[1]*e(-alpha*-1)

            D2=D2/sum(D2)

            但是就結果而言,好像影響不大,只是對這個加權誤差有影響。

     我們得到兩個分類器:

         

          

   v=1+0.5*2=1.5時,

         重復以上步驟得到兩個分類器。

   v=1+0.5*s時,一共尋找了2s

   當我們從最小值找分類閾值直到最大值時,我們得到了2s個分類器,s表示尋找的次數。我們記錄效果最好的分類器即分類誤差最小的分類器。那么我們在一個維度上的尋找就完成了

     2).接下來在第二個維度上尋找,同樣得到2s個分類器

     。。。

    3).直到第N,總共得到N*2s個分類器,最終在這么多分類器找到一個最優的分類器。一次迭代完成。

 3.接下來將上面這個過程重復J(J表示迭代次數,如果h(h<J)就得到了誤差為0的分類器那么提前結束迭代。)

  按所給數據,迭代三次就能夠找到誤差為零的分類器

    看到這里應該對整個過程有了一個了解,對於數據權重D和分類器的權重alpha,以及分類誤差率e的計算都有了一個了解,看一下代碼:

源碼:(源碼是按照《機器學習實戰》來寫的,因為個人對於python不太熟,機器學習實戰中的代碼運用矩陣來做很多公式中的乘法,有很大的技巧性,可能開始看的時候沒法理解這樣做,需要和理論結合,理論則是是來自《統計學習方法》)

 1 # -*- coding:utf-8 -*-
 2 # Filename: AdaBoost.py
 3 # Author:Ljcx
 4 
 5 """
 6  AdaBoost提升算法:(自適應boosting)  7  優點:泛化錯誤率低,易編碼,可以應用在大部分分類器上,無參數調整  8  缺點:對離群點敏感  9 
 10  bagging:自舉匯聚法(bootstrap aggregating)  11  基於數據隨機重抽樣的分類器構建方法  12  原始數據集中重新選擇S次得到S個新數據集,將磨溝算法分別作用於這個數據集,  13  最后進行投票,選擇投票最多的類別作為分類類別  14 
 15  boosting:類似於bagging,多個分類器類型都是相同的  16 
 17  boosting是關注那些已有分類器錯分的數據來獲得新的分類器,  18  bagging則是根據已訓練的分類器的性能來訓練的。  19 
 20  boosting分類器權重不相等,權重對應與上一輪迭代成功度  21  bagging分類器權重相等  22 """
 23 from numpy import*
 24 
 25 
 26 class Adaboosting(object):  27 
 28     def loadSimpData(self):  29         datMat = matrix(  30             [[1., 2.1],  31              [2., 1.1],  32              [1.3, 1.],  33              [1., 1.],  34              [2., 1.]])  35         classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]  36         return datMat, classLabels  37 
 38     def stumpClassify(self, datMat, dimen, threshVal, threshIneq):  39         """
 40  通過閾值比較進行分類  41  dataMat:數據矩陣  42  dimen:表示列下標  43  threshVal:閾值  44  threshIneq:不等號 lt, gt  45  只是簡單的將數據分為兩類-1,1,初始化了一個全1的矩陣,我們判斷一下閾值第i列小於/大於閾值的就為-1,(因為我們並不清楚這個划分標准,所以要大於小於都試一次)  46 
 47  每一個維度的所有數據跟閾值比較,就相當於找到一個點划分所有數據。  48 
 49         """
 50         # print "-----data-----"
 51         # print datMat
 52         retArr = ones((shape(datMat)[0], 1))  # m(數據量)行,1列,列向量
 53         if threshIneq == 'lt':  54             retArr[datMat[:, dimen] <= threshVal] = -1.0  # 小於閾值的列都為-1
 55         else:  56             retArr[datMat[:, dimen] > threshVal] = -1.0  # 大於閾值的列都為-1
 57         # print "---------retArr------------"
 58         # print retArr
 59         return retArr  60 
 61     def buildStump(self, dataArr, classLables, D):  62         """
 63  單層決策樹生成函數  64         """
 65         dataMatrix = mat(dataArr)  66         lableMat = mat(classLables).T  67         m, n = shape(dataMatrix)  68         numSteps = 10.0  # 步數,影響的是迭代次數,步長
 69         bestStump = {}  # 存儲分類器的信息
 70         bestClassEst = mat(zeros((m, 1)))  # 最好的分類器
 71         minError = inf  # 迭代尋找最小錯誤率
 72         for i in range(n):  73             # 求出每一列數據的最大最小值計算步長
 74             rangeMin = dataMatrix[:, i].min()  75             rangeMax = dataMatrix[:, i].max()  76             stepSize = (rangeMax - rangeMin) / numSteps  77             # j唯一的作用用步數去生成閾值,從最小值大最大值都與數據比較一邊了一遍
 78             for j in range(-1, int(numSteps) + 1):  79                 threshVal = rangeMin + float(j) * stepSize  # 閾值
 80                 for inequal in ['lt', 'gt']:  81                     predictedVals = self.stumpClassify(  82  dataMatrix, i, threshVal, inequal)  83                     errArr = mat(ones((m, 1)))  84                     errArr[predictedVals == lableMat] = 0  # 為1的 表示i分錯的
 85                     weightedError = D.T * errArr  # 分錯的個數*權重(開始權重=1/M行)
 86                     # print "split: dim %d, thresh %.2f, thresh ineqal:\
 87 #%s,the weighted error is %.3f" % (i, threshVal, inequal, weightedError)
 88                     if weightedError < minError:  # 尋找最小的加權錯誤率然后保存當前的信息
 89                         minError = weightedError  90                         bestClassEst = predictedVals.copy()  # 分類結果
 91                         bestStump['dim'] = i  92                         bestStump['thresh'] = threshVal  93                         bestStump['ineq'] = inequal  94         # print bestStump
 95         # print minError
 96         # print bestClassEst # 類別估計
 97         return bestStump, minError, bestClassEst  98 
 99     def adaBoostingDs(self, dataArr, classLables, numIt=40): 100         """
101  基於單層決策樹的AdaBoosting訓練過程: 102         """
103         weakClassArr = []  # 最佳決策樹數組
104         m = shape(dataArr)[0] 105         D = mat(ones((m, 1)) / m) 106         aggClassEst = mat(zeros((m, 1))) 107         for i in range(numIt): 108             bestStump, minError, bestClassEst = self.buildStump( 109  dataArr, classLables, D) 110             print "bestStump:", bestStump 111             print "D:", D.T 112             alpha = float( 113                 0.5 * log((1.0 - minError) / max(minError, 1e-16))) 114             bestStump['alpha'] = alpha 115  weakClassArr.append(bestStump) 116             print "alpha:", alpha 117             print "classEst:", bestClassEst.T  # 類別估計
118 
119             expon = multiply(-1 * alpha * mat(classLables).T, bestClassEst) 120             D = multiply(D, exp(expon)) 121             D = D / D.sum() 122 
123             aggClassEst += alpha * bestClassEst 124             print "aggClassEst ;", aggClassEst.T 125             # 累加錯誤率
126             aggErrors = multiply(sign(aggClassEst) !=
127                                  mat(classLables).T, ones((m, 1))) 128             # 錯誤率平均值
129             errorsRate = aggErrors.sum() / m 130             print "total error:", errorsRate, "\n"
131             if errorsRate == 0.0: 132                 break
133         print "weakClassArr:", weakClassArr 134         return weakClassArr 135 
136     def adClassify(self, datToClass, classifierArr): 137         """
138  預測分類: 139  datToClass:待分類數據 140  classifierArr: 訓練好的分類器數組 141         """
142         dataMatrix = mat(datToClass) 143         m = shape(dataMatrix)[0] 144         aggClassEst = mat(zeros((m, 1))) 145         print
146         for i in range(len(classifierArr)):  # 有多少個分類器迭代多少次
147             # 調用第一個分類器進行分類
148             classEst = self.stumpClassify(dataMatrix, classifierArr[i]['dim'], 149                                           classifierArr[i]['thresh'], 150                                           classifierArr[i]['ineq'] 151  ) 152             # alpha 表示每個分類器的權重,
153             print classEst 154             aggClassEst += classifierArr[i]['alpha'] * classEst 155             print aggClassEst 156         return sign(aggClassEst) 157 
158 
159 if __name__ == "__main__": 160     adaboosting = Adaboosting() 161     D = mat(ones((5, 1)) / 5) 162     dataMat, lableMat = adaboosting.loadSimpData() 163     # 訓練分類器
164     classifierArr = adaboosting.adaBoostingDs(dataMat, lableMat, 40) 165     # 預測數據
166     result = adaboosting.adClassify([0, 0], classifierArr) 167     print result

運行結果:可以看到迭代三次加權錯誤率為0

最后有一個對數據[0,0]的預測:weakClassArr表示保存的三個分類器的信息,我們用這個分類器對數據進行預測

三個小數對應的是三個分類器前N個分類加權分類結果累加。對應的-1,-1,-1表示三個分類器對這個數據分類是-1,最后一個表示增強分類器對這個數據的加權求和分類結果為-1

   


免責聲明!

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



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