當作重要決定時,你們可能都會考慮吸收多個專家而不僅是一我的的意見。機器學習處理問題時又未嘗不是如此?這就是元算法(meta-algorithm)背後的思路。元算法是對其餘算法進行組合的一種方式。接下來咱們將集中關注一個稱做AdaBoost的最流行的元算法。因爲某些人認爲AdaBoost是最好的監督學習的方法,因此該方法是機器學習工具箱中最強有力的工具之一。
本章首先討論不一樣分類器的集成方法,而後主要關注boosting方法及其表明分類器Adaboost。再接下來,咱們就會創建一個單層決策樹(decision stump)分類器。實際上,它是一個單節點的決策樹。AdaBoost算法將應用在上述單層決策樹分類器之上。咱們將在一個難數據集上應用AdaBoost分類器,以瞭解該算法是如何迅速超越其餘分類器的。
最後,在結束分類話題以前,咱們將討論全部分類器都會遇到的一個通用問題:非均衡分類問題。當咱們試圖對樣例數目不均衡的數據進行分類時,就會遇到這個問題。信用卡使用中的欺詐檢測就是非均衡問題中的一個極好的例子,此時咱們可能會對每個正例樣本都有1000個反例樣本。在這種狀況下,分類器將如何工做?讀者將會了解到,可能須要利用修改後的指標來評價分類器的性能。而就這個問題而言,並不是AdaBoost所獨用,只是由於這是分類的最後一章,所以到了討論這個問題的最佳時機。算法
優勢:泛化錯誤率低,易編碼,能夠應用在大部分分類器上,無參數調整。bootstrap
缺點:對離羣點敏感。數組
適用數據類型:數值型和標稱型數據。app
前面已經介紹了五種不一樣的分類算法,它們各有優缺點。咱們天然能夠將不一樣的分類器組合起來,而這種組合結果則被稱爲集成方法(ensemble method)或者元算法(meta-algorithm)。使用集成方法時會有多種形式:能夠是不一樣算法的集成,也能夠是同一算法在不一樣設置下的集成,還能夠是數據集不一樣部分分配給不一樣分類器以後的集成。接下來,咱們將介紹基於同一種分類器多個不一樣實例的兩種計算方法。在這些方法當中,數據集也會不斷變化,然後應用於不一樣的實例分類器上。最後,咱們會討論如何利用機器學習問題的通用框架來應用AdaBoost算法。框架
一、bagging:基於數據隨機重抽樣的分類器構建方法less
自舉匯聚法(bootstrap aggregating),也稱爲bagging方法,是在從原始數據集選擇S次後獲得S個新數據集的一種技術。新數據集和原數據集的大小相等。每一個數據集都是經過在原始數據集中隨機選擇一個樣原本進行替換而獲得的。這裏的替換就意味着能夠屢次地選擇同同樣本。這一性質就容許新數據集中能夠有重複的值,而原始數據集的某些值在新集合中則再也不出現。在S個數據集建好以後,將某個學習算法分別做用於每一個數據集就獲得了S個分類器。當咱們要對新數據進行分類時,就能夠應用這S個分類器進行分類。與此同時,選擇分類器投票結果中最多的類別做爲最後的分類結果。固然,還有一些更先進的bagging方法,好比隨機森林(random forest)。接下來咱們將注意力轉向一個與bagging相似的集成分類器方法boosting。dom
二、boosting機器學習
boosting是一種與bagging很相似的技術。不管是在boost-ing仍是bagging當中,所使用的多個分類器的類型都是一致的。可是在前者當中,不一樣的分類器是經過串行訓練而得到的,每一個新分類器都根據已訓練出的分類器的性能來進行訓練。boosting是經過集中關注被已有分類器錯分的那些數據來得到新的分類器。因爲boosting分類的結果是基於全部分類器的加權求和結果的,所以boosting與bagging不太同樣。bagging中的分類器權重是相等的,而boosting中的分類器權重並不相等,每一個權重表明的是其對應分類器在上一輪迭代中的成功度。boosting方法擁有多個版本,本章將只關注其中一個最流行的版本AdaBoost。函數
AdaBoost的通常流程工具
(1)收集數據:可使用任意方法。
(2)準備數據:依賴於所使用的弱分類器類型,本章使用的是單層決策樹,這種分類器能夠處理任何數據類型。固然也可使用任意分類器做爲弱分類器,第2章到第6章中的任一分類器均可以充當弱分類器。做爲弱分類器,簡單分類器的效果更好。
(3)分析數據:可使用任意方法。
(4)訓練算法:AdaBoost的大部分時間都用在訓練上,分類器將屢次在同一數據集上訓練弱分類器。
(5)測試算法:計算分類的錯誤率。
(6)使用算法:同SVM同樣,AdaBoost預測兩個類別中的一個。若是想把它應用到多個類別的場合,那麼就要像多類SVM中的作法同樣對AdaBoost進行修改。
可否使用弱分類器和多個實例來構建一個強分類器?這是一個很是有趣的理論問題。這裏的「弱」意味着分類器的性能比隨機猜想要略好,可是也不會好太多。這就是說,在二分類狀況下弱分類器的錯誤率會高於50%,而「強」分類器的錯誤率將會低不少。AdaBoost算法即脫胎於上述理論問題。
AdaBoost是adaptive boosting(自適應boosting)的縮寫,其運行過程以下:
訓練數據中的每一個樣本,並賦予其一個權重,這些權重構成了向量D。一開始,這些權重都初始化成相等值。首先在訓練數據上訓練出一個弱分類器並計算該分類器的錯誤率,而後在同一數據集上再次訓練弱分類器。在分類器的第二次訓練當中,將會從新調整每一個樣本的權重,其中第一次分對的樣本的權重將會下降,而第一次分錯的樣本的權重將會提升。爲了從全部弱分類器中獲得最終的分類結果,AdaBoost爲每一個分類器都分配了一個權重值alpha,這些alpha值是基於每一個弱分類器的錯誤率進行計算的。其中,錯誤率ε的定義爲:
而alpha的計算公式以下:
AdaBoost算法的流程如圖7-1所示。
圖7-1 AdaBoost算法的示意圖。左邊是數據集,其中直方圖的不一樣寬度表示每一個樣例上的不一樣權重。在通過一個分類器以後,
加權的預測結果會經過三角形中的alpha值進行加權。每一個三角形中輸出的加權結果在圓形中求和,從而獲得最終的輸出結果
計算出alpha值以後,能夠對權重向量D進行更新,以使得那些正確分類的樣本的權重下降而錯分樣本的權重升高。D的計算方法以下。
若是某個樣本被正確分類,那麼該樣本的權重更改成:
而若是某個樣本被錯分,那麼該樣本的權重更改成:
在計算出D以後,AdaBoost又開始進入下一輪迭代。Ad-aBoost算法會不斷地重複訓練和調整權重的過程,直到訓練錯誤率爲0或者弱分類器的數目達到用戶的指定值爲止。
單層決策樹(decision stump,也稱決策樹樁)是一種簡單的決策樹。前面咱們已經介紹了決策樹的工做原理,接下來將構建一個單層決策樹,而它僅基於單個特徵來作決策。因爲這棵樹只有一次分裂過程,所以它實際上就是一個樹樁。
在構造AdaBoost的代碼時,咱們將首先經過一個簡單數據集來確保在算法實現上一切就緒。而後,創建一個叫adaboost.py的新文件並加入以下代碼:
1 def loadSimpData(): 2 datMat = matrix([[ 1. , 2.1], 3 [ 2. , 1.1], 4 [ 1.3, 1. ], 5 [ 1. , 1. ], 6 [ 2. , 1. ]]) 7 classLabels = [1.0, 1.0, -1.0, -1.0, 1.0] #注意 這裏的分類 爲 1 和 -1 8 return datMat,classLabels
圖7-2給出了上述數據集的示意圖。若是想要試着從某個座標軸上選擇一個值(即選擇一條與座標軸平行的直線)來將全部的圓形點和方形點分開,這顯然是不可能的。這就是單層決策樹難以處理的一個著名問題。經過使用多棵單層決策樹,咱們就能夠構建出一個可以對該數據集徹底正確分類的分類器。
圖7-2 用於檢測AdaBoost構建函數的簡單數據。這不可能僅僅經過在某個座標軸上選擇某個閾值來將圓形點和方形點分開。AdaBoost須要將多個單層決策樹組合起來才能對該數據集進行正確分類
有了數據,接下來就能夠經過構建多個函數來創建單層決策樹。
第一個函數將用於測試是否有某個值小於或者大於咱們正在測試的閾值。第二個函數則更加複雜一些,它會在一個加權數據集中循環,並找到具備最低錯誤率的單層決策樹。這個程序的僞代碼看起來大體以下:
將最小錯誤率minError設爲+∞
對數據集中的每個特徵(第一層循環):
對每一個步長(第二層循環):
對每一個不等號(第三層循環):
創建一棵單層決策樹並利用加權數據集對它進行測試
若是錯誤率低於minError,則將當前單層決策樹設爲最佳單層決策樹
返回最佳單層決策樹
程序清單7-1 單層決策樹生成函數
# 是經過閾值比較對數據進行分類的。全部在閾值一邊的數據會分到類別-1,而在另一邊的數據分到類別+1。該函數能夠經過數組過濾來實現,首先將返回數組的所有元素設置爲1,而後將全部不知足不等式要求的元素設置爲-1。
#能夠基於數據集中的任一元素進行比較,同時也能夠將不等號在大於、小於之間切換。
1 def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):# 返回一個列向量-------- 分正確的爲1, 分錯的爲-1 , 2 retArray = ones((shape(dataMatrix)[0],1)) 3 if threshIneq == 'lt': 4 retArray[dataMatrix[:,dimen] <= threshVal] = -1.0 5 else: 6 retArray[dataMatrix[:,dimen] > threshVal] = -1.0 7 #print (retArray) 8 return retArray 9 10 #遍歷stumpClassify()函數全部的可能輸入值,並找到數據集上最佳的單層決策樹。這裏的「最佳」是基於數據的權重向量D來定義的 11 def buildStump(dataArr,classLabels,D): #權重向量D classLabels=[1.0, 1.0, -1.0, -1.0, 1.0] 12 dataMatrix = mat(dataArr); labelMat = mat(classLabels).T #labelMat = 列矩陣 13 m,n = shape(dataMatrix) 14 numSteps = 10.0; bestStump = {}; bestClasEst = mat(zeros((m,1))) 15 minError = inf #init error sum, to +infinity 16 for i in range(n):#loop over all dimensions 17 rangeMin = dataMatrix[:,i].min(); rangeMax = dataMatrix[:,i].max(); 18 stepSize = (rangeMax-rangeMin)/numSteps 19 for j in range(-1,int(numSteps)+1):#loop over all range in current dimension 20 for inequal in ['lt', 'gt']: #go over less than and greater than 21 threshVal = (rangeMin + float(j) * stepSize) 22 predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)#列向量 23 #print (predictedVals) 24 errArr = mat(ones((m,1))) #構建一個列向量errArr,若是predict-edVals中的值不等於labelMat中的真正類別標籤值,那麼er-rArr的相應位置爲1。 25 errArr[predictedVals == labelMat] = 0 26 print(errArr) 27 weightedError = D.T*errArr #將錯誤向量errArr和權重向量D的相應元素相乘並求和 -----分類錯誤率 28 #print "split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f" % (i, threshVal, inequal, weightedError) 29 if weightedError < minError: 30 minError = weightedError 31 bestClasEst = predictedVals.copy() 32 bestStump['dim'] = i 33 bestStump['thresh'] = threshVal 34 bestStump['ineq'] = inequal 35 return bestStump,minError,bestClasEst #返回分類的最小錯誤率
爲了解實際運行過程,在Python提示符下輸入以下命令:
1 >>> import adaboost 2 >>> from numpy import * 3 >>> D=mat(ones((5,1))/5) 4 >>> datMat,classLabels=adaboost.loadSimpData() 5 >>> adaboost.buildStump(datMat,classLabels,D) 6 split: dim 0, thresh 0.90, thresh ineqal: lt, the weighted error is 0.400 7 split: dim 0, thresh 0.90, thresh ineqal: gt, the weighted error is 0.600 8 split: dim 0, thresh 1.00, thresh ineqal: lt, the weighted error is 0.400 9 split: dim 0, thresh 1.00, thresh ineqal: gt, the weighted error is 0.600 10 split: dim 0, thresh 1.10, thresh ineqal: lt, the weighted error is 0.400 11 split: dim 0, thresh 1.10, thresh ineqal: gt, the weighted error is 0.600 12 split: dim 0, thresh 1.20, thresh ineqal: lt, the weighted error is 0.400 13 split: dim 0, thresh 1.20, thresh ineqal: gt, the weighted error is 0.600 14 split: dim 0, thresh 1.30, thresh ineqal: lt, the weighted error is 0.200 15 split: dim 0, thresh 1.30, thresh ineqal: gt, the weighted error is 0.800 16 split: dim 0, thresh 1.40, thresh ineqal: lt, the weighted error is 0.200 17 split: dim 0, thresh 1.40, thresh ineqal: gt, the weighted error is 0.800 18 split: dim 0, thresh 1.50, thresh ineqal: lt, the weighted error is 0.200 19 split: dim 0, thresh 1.50, thresh ineqal: gt, the weighted error is 0.800 20 split: dim 0, thresh 1.60, thresh ineqal: lt, the weighted error is 0.200 21 split: dim 0, thresh 1.60, thresh ineqal: gt, the weighted error is 0.800 22 split: dim 0, thresh 1.70, thresh ineqal: lt, the weighted error is 0.200 23 split: dim 0, thresh 1.70, thresh ineqal: gt, the weighted error is 0.800 24 split: dim 0, thresh 1.80, thresh ineqal: lt, the weighted error is 0.200 25 split: dim 0, thresh 1.80, thresh ineqal: gt, the weighted error is 0.800 26 split: dim 0, thresh 1.90, thresh ineqal: lt, the weighted error is 0.200 27 split: dim 0, thresh 1.90, thresh ineqal: gt, the weighted error is 0.800 28 split: dim 0, thresh 2.00, thresh ineqal: lt, the weighted error is 0.600 29 split: dim 0, thresh 2.00, thresh ineqal: gt, the weighted error is 0.400 30 split: dim 1, thresh 0.89, thresh ineqal: lt, the weighted error is 0.400 31 split: dim 1, thresh 0.89, thresh ineqal: gt, the weighted error is 0.600 32 split: dim 1, thresh 1.00, thresh ineqal: lt, the weighted error is 0.200 33 split: dim 1, thresh 1.00, thresh ineqal: gt, the weighted error is 0.800 34 split: dim 1, thresh 1.11, thresh ineqal: lt, the weighted error is 0.400 35 split: dim 1, thresh 1.11, thresh ineqal: gt, the weighted error is 0.600 36 split: dim 1, thresh 1.22, thresh ineqal: lt, the weighted error is 0.400 37 split: dim 1, thresh 1.22, thresh ineqal: gt, the weighted error is 0.600 38 split: dim 1, thresh 1.33, thresh ineqal: lt, the weighted error is 0.400 39 split: dim 1, thresh 1.33, thresh ineqal: gt, the weighted error is 0.600 40 split: dim 1, thresh 1.44, thresh ineqal: lt, the weighted error is 0.400 41 split: dim 1, thresh 1.44, thresh ineqal: gt, the weighted error is 0.600 42 split: dim 1, thresh 1.55, thresh ineqal: lt, the weighted error is 0.400 43 split: dim 1, thresh 1.55, thresh ineqal: gt, the weighted error is 0.600 44 split: dim 1, thresh 1.66, thresh ineqal: lt, the weighted error is 0.400 45 split: dim 1, thresh 1.66, thresh ineqal: gt, the weighted error is 0.600 46 split: dim 1, thresh 1.77, thresh ineqal: lt, the weighted error is 0.400 47 split: dim 1, thresh 1.77, thresh ineqal: gt, the weighted error is 0.600 48 split: dim 1, thresh 1.88, thresh ineqal: lt, the weighted error is 0.400 49 split: dim 1, thresh 1.88, thresh ineqal: gt, the weighted error is 0.600 50 split: dim 1, thresh 1.99, thresh ineqal: lt, the weighted error is 0.400 51 split: dim 1, thresh 1.99, thresh ineqal: gt, the weighted error is 0.600 52 split: dim 1, thresh 2.10, thresh ineqal: lt, the weighted error is 0.600 53 split: dim 1, thresh 2.10, thresh ineqal: gt, the weighted error is 0.400 54 ({'ineq': 'lt', 'thresh': 1.3, 'dim': 0}, matrix([[ 0.2]]), array([[-1.], 55 [ 1.], 56 [-1.], 57 [-1.], 58 [ 1.]])) 59 >>>
上述單層決策樹的生成函數是決策樹的一個簡化版本。它就是所謂的弱學習器,即弱分類算法。到如今爲止,咱們已經構建了單層決策樹,並生成了程序,作好了過渡到完整AdaBoost算法的準備。在下一節當中,咱們將使用多個弱分類器來構建Ad-aBoost代碼。
整個實現的僞代碼以下:
對每次迭代:
利用buildStump()函數找到最佳的單層決策樹
將最佳單層決策樹加入到單層決策樹數組
計算alpha
計算新的權重向量D
更新累計類別估計值
若是錯誤率等於0.0,則退出循環
程序清單7-2 基於單層決策樹的AdaBoost訓練過程
1 def adaBoostTrainDS(dataArr,classLabels,numIt=40): 2 weakClassArr = [] 3 m = shape(dataArr)[0] 4 D = mat(ones((m,1))/m) #向量D很是重要,它包含了每一個數據點的權重 5 aggClassEst = mat(zeros((m,1))) #列向量aggClassEst,記錄每一個數據點的類別估計累計值。 6 for i in range(numIt): 7 bestStump,error,classEst = buildStump(dataArr,classLabels,D)#bestStump=字典,error=分類錯誤率,classEst=列向量,預測以後的分類列表 8 #print "D:",D.T 9 alpha = float(0.5*log((1.0-error)/max(error,1e-16)))#求alpha 值 上面的數學公式, throw in max(error,eps) to account for error=0 10 bestStump['alpha'] = alpha 11 weakClassArr.append(bestStump) #store Stump Params in Array 12 #print "classEst: ",classEst.T // ①(如下兩行)爲下一次迭代計算D 13 expon = multiply(-1*alpha*mat(classLabels).T,classEst) # 樣本被正確分類的話 expon 爲負,錯誤分類的話 爲正 其中第一次分對的樣本的權重將會下降,而第一次分錯的樣本的權重將會提升 14 D = multiply(D,exp(expon)) #迭代計算公式 15 D = D/D.sum() 16 # ②(如下五行)錯誤率累加計算 17 aggClassEst += alpha*classEst 18 #print "aggClassEst: ",aggClassEst.T 19 aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1))) 20 errorRate = aggErrors.sum()/m 21 print ("total error: ",errorRate) 22 if errorRate == 0.0: break 23 return weakClassArr,aggClassEst
測試代碼:
1 >>> imp.reload(adaboost) 2 <module 'adaboost' from 'F:\\99999_算法\\《機器學習實戰》源代碼\\machinelearninginaction\\Ch07\\adaboost.py'> 3 >>> classifierArray,aggClassEst=adaboost.adaBoostTrainDS(datMat,classLabels,9) 4 D: [[ 0.2 0.2 0.2 0.2 0.2]] 5 alpha: 0.6931471805599453 6 classEst: [[-1. 1. -1. -1. 1.]] 7 aggClassEst: [[-0.69314718 0.69314718 -0.69314718 -0.69314718 0.69314718]] 8 aggErrors : [[ 1. 0. 0. 0. 0.]] 9 total error: 0.2 10 D: [[ 0.5 0.125 0.125 0.125 0.125]] 11 alpha: 0.9729550745276565 12 classEst: [[ 1. 1. -1. -1. -1.]] 13 aggClassEst: [[ 0.27980789 1.66610226 -1.66610226 -1.66610226 -0.27980789]] 14 aggErrors : [[ 0. 0. 0. 0. 1.]] 15 total error: 0.2 16 D: [[ 0.28571429 0.07142857 0.07142857 0.07142857 0.5 ]] 17 alpha: 0.8958797346140273 18 classEst: [[ 1. 1. 1. 1. 1.]] 19 aggClassEst: [[ 1.17568763 2.56198199 -0.77022252 -0.77022252 0.61607184]] 20 aggErrors : [[ 0. 0. 0. 0. 0.]] 21 total error: 0.0 22 >>> classifierArray 23 [{'ineq': 'lt', 'thresh': 1.3, 'alpha': 0.6931471805599453, 'dim': 0}, {'ineq': 'lt', 'thresh': 1.0, 'alpha': 0.9729550745276565, 'dim': 1},
{'ineq': 'lt', 'thresh': 0.90000000000000002, 'alpha': 0.8958797346140273, 'dim': 0}] 24 >>> aggClassEst 25 matrix([[ 1.17568763], 26 [ 2.56198199], 27 [-0.77022252], 28 [-0.77022252], 29 [ 0.61607184]]) 30 >>>
AdaBoost算法的輸入參數包括數據集、類別標籤以及迭代次數numIt,其中numIt是在整個AdaBoost算法中惟一須要用戶指定的參數。
咱們假定迭代次數設爲9,若是算法在第三次迭代以後錯誤率爲0,那麼就會退出迭代過程,所以,此時就不須要執行全部的9次迭代過程。每次迭代的中間結果都會經過print語句進行輸出。後面,讀者能夠把print輸出語句註釋掉,可是如今能夠經過中間結果來了解AdaBoost算法的內部運行過程。
向量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循環。
classifierArray:該數組包含三部詞典,其中包含了分類所須要的全部信息。
此時,一個分類器已經構建成功,並且只要咱們願意,隨時均可以將訓練錯誤率降到0。那麼測試錯誤率會如何呢?爲了觀察測試錯誤率,咱們須要編寫分類的一些代碼。下一節咱們將討論分類。
一旦擁有了多個弱分類器以及其對應的alpha值,進行測試就變得至關容易了。在程序清單7-2的adaBoostTrainDS()中,咱們實際已經寫完了大部分的代碼。如今,須要作的就只是將弱分類器的訓練過程從程序中抽出來,而後應用到某個具體的實例上去。每一個弱分類器的結果以其對應的alpha值做爲權重。全部這些弱分類器的結果加權求和就獲得了最後的結果。在程序清單7-3中列出了實現這一過程的全部代碼。而後,將下列代碼添加到adaboost.py中,就能夠利用它基於adaboostTrainDS()中的弱分類器對數據進行分類。
程序清單7-3 AdaBoost分類函數
1 def adaClassify(datToClass,classifierArr): 2 dataMatrix = mat(datToClass)#do stuff similar to last aggClassEst in adaBoostTrainDS 3 m = shape(dataMatrix)[0] 4 aggClassEst = mat(zeros((m,1))) 5 for i in range(len(classifierArr)): 6 classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],\ 7 classifierArr[i]['thresh'],\ 8 classifierArr[i]['ineq'])#call stump classify 9 aggClassEst += classifierArr[i]['alpha']*classEst 10 print (aggClassEst) 11 return sign(aggClassEst) #返回-1 0 1
上述的adaClassify()函數就是利用訓練出的多個弱分類器進行分類的函數。該函數的輸入是由一個或者多個待分類樣例datToClass以及多個弱分類器組成的數組classifierArr。程序返回aggClassEst的符號,即若是aggClassEst大於0則返回+1,而若是小於0則返回-1。
咱們再看看實際中的運行效果。加入程序清單7-3中的代碼以後,在Python提示符下輸入:
1 >>> adaboost.adaClassify([0,0],classifierArray) 2 [[-0.69314718]] 3 [[-1.66610226]] 4 [[-2.56198199]] 5 matrix([[-1.]]) 6 >>>
能夠發現,隨着迭代的進行,數據點[0,0]的分類結果愈來愈強。
本節咱們將在第4章給出的馬疝病數據集上應用AdaBoost分類器。在第4章,咱們曾經利用Logistic迴歸來預測患有疝病的馬是否可以存活。而在本節,咱們則想要知道若是利用多個單層決策樹和AdaBoost能不能預測得更準。
示例:在一個難數據集上的AdaBoost應用
(1)收集數據:提供的文本文件。
(2)準備數據:確保類別標籤是+1和-1而非1和0。
(3)分析數據:手工檢查數據。
(4)訓練算法:在數據上,利用adaBoostTrainDS()函數訓練出一系列的分類器。
(5)測試算法:咱們擁有兩個數據集。在不採用隨機抽樣的方法下,咱們就會對AdaBoost和Logistic迴歸的結果進行徹底對等的比較。
(6)使用算法:觀察該例子上的錯誤率。不過,也能夠構建一個Web網站,讓馴馬師輸入馬的症狀而後預測馬是否會死去。
程序清單7-4 自適應數據加載函數
1 def loadDataSet(fileName): #general function to parse tab -delimited floats 2 numFeat = len(open(fileName).readline().split('\t')) #get number of fields 3 dataMat = []; labelMat = [] 4 fr = open(fileName) 5 for line in fr.readlines(): 6 lineArr =[] 7 curLine = line.strip().split('\t') 8 for i in range(numFeat-1): 9 lineArr.append(float(curLine[i])) 10 dataMat.append(lineArr) 11 labelMat.append(float(curLine[-1])) 12 return dataMat,labelMat
將上述代碼添加到adaboost.py文件中而且將其保存以後,就能夠輸入以下命令來使用上述函數:
1 >>> datArr,labelArr=adaboost.loadDataSet('horseColicTraining2.txt') 2 >>> classifierArray,b=adaboost.adaBoostTrainDS(datArr,labelArr,10) 3 total error:0.284280936455 4 total error:0.284280936455 5 . 6 . 7 total error:0.230769230769 8 >>> testArr,testLabelArr=adaboost.loadDataSet('horseColicTest2.txt') 9 >>> prediction10=adaboost.adaClassify(testArr,classifierArray) 10 To get the number of misclassified examples type in: 11 >>> errArr=mat(ones((67,1))) 12 >>>errArr[prediction10!=mat(testLabelArr).T].sum() 13 16.0
要獲得錯誤率,只需將上述錯分樣例的個數除以67便可。
將弱分類器的數目設定爲1到10000之間的幾個不一樣數字,並運行上述過程。這時,獲得的結果就會如表7-1所示。在該數據集上獲得的錯誤率至關低。若是沒忘的話,在第5章中,咱們在同一數據集上採用Logistic迴歸獲得的平均錯誤率爲0.35。而採用AdaBoost,獲得的錯誤率就永遠不會那麼高了。從表中能夠看出,咱們僅僅使用50個弱分類器,就達到了較高的性能。
表7-1 不一樣弱分類器數目狀況下的AdaBoost測試和分類錯誤率。該數據集是個難數據集。一般狀況下,AdaBoost會達到一個穩定的測試錯誤率,而並不會隨分類器數目的增多而提升
觀察表7-1中的測試錯誤率一欄,就會發現測試錯誤率在達到了一個最小值以後又開始上升了。這類現象稱之爲過擬合(overfitting,也稱過學習)。有文獻聲稱,對於表現好的數據集,AdaBoost的測試錯誤率就會達到一個穩定值,並不會隨着分類器的增多而上升。或許在本例子中的數據集也稱不上「表現好」。該數據集一開始有30%的缺失值,對於Logistic迴歸而言,這些缺失值的假設就是有效的,而對於決策樹卻可能並不合適。若是回到數據集,將全部的0值替換成其餘值,或者給定類別的平均值,那麼可否獲得更好的性能?
不少人都認爲,AdaBoost和SVM是監督機器學習中最強大的兩種方法。實際上,這二者之間擁有很多類似之處。咱們能夠把弱分類器想象成SVM中的一個核函數,也能夠按照最大化某個最小間隔的方式重寫AdaBoost算法。而它們的不一樣就在於其所定義的間隔計算方式有所不一樣,所以致使的結果也不一樣。特別是在高維空間下,這二者之間的差別就會更加明顯。
在下一節中,咱們再也不討論AdaBoost,而是轉而關注全部分類器中的一個廣泛問題。
在咱們結束分類這個主題以前,還必須討論一個問題。在前面六章的全部分類介紹中,咱們都假設全部類別的分類代價是同樣的。例如在第5章,咱們構建了一個用於檢測患疝病的馬匹是否存活的系統。在那裏,咱們構建了分類器,可是並無對分類後的情形加以討論。假如某人給咱們牽來一匹馬,他但願咱們能預測這匹馬可否生存。咱們說馬會死,那麼他們就可能會對馬實施安樂死,而不是經過給馬喂藥來延緩其不可避免的死亡過程。咱們的預測也許是錯誤的,馬原本是能夠繼續活着的。畢竟,咱們的分類器只有80%的精確率(accuracy)。若是咱們預測錯誤,那麼咱們將會錯殺了一個如此昂貴的動物,更不要說人對馬還存在情感上的依戀。
如何過濾垃圾郵件呢?若是收件箱中會出現某些垃圾郵件,但合法郵件永遠不會扔進垃圾郵件夾中,那麼人們是否會滿意呢?癌症檢測又如何呢?只要患病的人不會得不到治療,那麼再找一個醫生來看看會不會更好呢(即情願誤判也不漏判)?
還能夠舉出不少不少這樣的例子,坦白地說,在大多數狀況下不一樣類別的分類代價並不相等。在本節中,咱們將會考察一種新的分類器性能度量方法,並經過圖像技術來對在上述非均衡問題下不一樣分類器的性能進行可視化處理。而後,咱們考察這兩種分類器的變換算法,它們可以將不一樣決策的代價考慮在內。
一、其餘分類性能度量指標:正確率、召回率及ROC曲線
二、基於代價函數的分類器決策控制
三、處理非均衡問題的數據抽樣方法