機(jī)器學(xué)習(xí)實(shí)戰(zhàn)-利用AdaBoost元算法提高分類性能

元算法是對其他算法進(jìn)行組合的一種方式闸拿。本章首先討論不同分類器的集成方法,然后主要關(guān)注boosting方法及其代表分類器Adaboost峻凫。

Adaboost
優(yōu)點(diǎn):泛化錯(cuò)誤率低矮湘,易編碼,可以應(yīng)用在大部分分類器上醋寝,無參數(shù)調(diào)整
缺點(diǎn):對離群點(diǎn)敏感
適用數(shù)據(jù)類型:數(shù)值型和標(biāo)稱型數(shù)據(jù)

bagging:自舉匯聚法(bootstrap aggregating)搞挣,也成為bagging方法,是從原始數(shù)據(jù)集選擇S次吼得到S個(gè)新數(shù)據(jù)集的一種技術(shù)音羞。新數(shù)據(jù)集大小和原始數(shù)據(jù)集的大小相等囱桨。
boosting:通過集中關(guān)注被已有分類器錯(cuò)分的那些數(shù)據(jù)來獲得新的分類器。

單層決策樹(decision stump嗅绰,也稱決策樹樁)舍肠,是一種簡單的決策樹搀继。

#adaboost.py
from numpy import *

def loadSimpData():
    datMat = 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

并加入

import adaboost
datMat,classLabels = adaboost.loadSimpData()

接下來可以通過構(gòu)建多個(gè)函數(shù)來建立單層決策樹,偽代碼如下

將最小錯(cuò)誤率minError設(shè)為正無窮
對數(shù)據(jù)集中的每一個(gè)特征(每一層循環(huán)):
  對每一個(gè)步長(第二層循環(huán)):
    對每一個(gè)不等號(第三層循環(huán)):
      建立一顆單層決策樹并利用加權(quán)數(shù)據(jù)集對它進(jìn)行測試
      如果錯(cuò)誤率低于minError貌夕,則將當(dāng)前單層決策樹設(shè)為最佳單層決策樹
返回最佳單層決策樹

接下來開始構(gòu)造這個(gè)函數(shù)

#7-1 單層決策樹生成函數(shù)
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):#閾值比較分類
    retArray = ones((shape(dataMatrix)[0],1))
    if threshIneq == "lt":
        retArray[dataMatrix[:,dimen] <= threshVal] = -1.0
    else:
        retArray[dataMatrix[:,dimen] > threshVal] = -1.0
    return retArray

def buildStump(dataArr,classLabels,D):#遍歷所有可能輸入值,找到最佳單層決策樹
    dataMatrix = mat(dataArr); labelMat = mat(classLabels).T
    m,n = shape(dataMatrix)
    numSteps = 10.0; bestStump = {}; bestClasEst = mat(zeros((m,1)))
    minError = 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 = mat(ones((m,1)))
                errArr[predictedVals == labelMat] = 0
                weightedError = D.T*errArr
                print "split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f" % (i, threshVal, inequal, weightedError)
                #將當(dāng)前錯(cuò)誤率與已有的最小錯(cuò)誤率進(jìn)行對比民镜,如果當(dāng)前的值比較小啡专,那么就在詞典bestStump中保存該單層決策樹
                if weightedError < minError:
                    minError = weightedError
                    bestClasEst = predictedVals.copy()
                    bestStump["dim"] = i
                    bestStump["thresh"] = threshVal
                    bestStump['ineq'] = inequal
    return bestStump,minError,bestClasEst
#開始運(yùn)行
D = mat(ones((5,1))/5)
adaboost.buildStump(datMat, classLabels,D)
#省略部分
split: dim 1, thresh 1.88, thresh ineqal: gt, the weighted error is 0.600
split: dim 1, thresh 1.99, thresh ineqal: lt, the weighted error is 0.600
split: dim 1, thresh 1.99, thresh ineqal: gt, the weighted error is 0.600
split: dim 1, thresh 2.10, thresh ineqal: lt, the weighted error is 0.400
split: dim 1, thresh 2.10, thresh ineqal: gt, the weighted error is 0.400
Out[26]: 
({'dim': 0, 'ineq': 'lt', 'thresh': 2.0}, matrix([[ 0.4]]), array([[ 1.],
        [ 1.],
        [ 1.],
        [ 1.],
        [ 1.]]))

上述單層決策樹的生成函數(shù)時(shí)決策樹的簡化版本,也是所謂的弱學(xué)習(xí)器制圈。
下面實(shí)現(xiàn)一個(gè)完整AdaBoost算法所需要的所有信息们童,偽代碼如下:

對每次迭代:
    利用buildStump()函數(shù)找到最佳的單詞決策樹
    將最佳單層決策樹加入到單層決策樹數(shù)組
    計(jì)算alpha
    計(jì)算新的權(quán)重向量D
    更新累計(jì)類別估計(jì)值
    如果錯(cuò)誤率等于0.0,則退出循環(huán)

繼續(xù)補(bǔ)充adaboost.py

#7-2 基于單層決策樹的AdaBoost訓(xùn)練過程
def adaBoostTrainDS(dataArr,classLabels,numIt=40):#數(shù)據(jù)集鲸鹦,類別標(biāo)簽慧库,迭代次數(shù)
    weakClassArr = []
    m = shape(dataArr)[0]
    D = mat(ones((m,1))/m)
    aggClassEst = mat(zeros((m,1)))
    for i in range(numIt):
        #找到最佳決策樹
        bestStump,error,classEst = buildStump(dataArr,classLabels,D)
        print "D:",D.T
        alpha = float(0.5*log((1.0-error)/max(error,1e-16)))#確保沒有除0溢出
        bestStump["alpha"] = alpha
        weakClassArr.append(bestStump)
        print "classEst:",classEst.T
        expon = multiply(-1*alpha*mat(classLabels).T,classEst)
        D = multiply(D,exp(expon))
        D = D/D.sum()
        aggClassEst += alpha*classEst#更新累計(jì)估計(jì)值
        print "aggClassEst:", aggClassEst.T
        aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1)))
        errorRate = aggErrors.sum()/m
        print "total error:",errorRate
        if errorRate == 0.0:break
    return weakClassArr

并使用該函數(shù)

In [48]: runfile('E:/上學(xué)/機(jī)器學(xué)習(xí)實(shí)戰(zhàn)/7.利用AdaBoost元算法提高分類性能/adaboost.py', wdir='E:/上學(xué)/機(jī)器學(xué)習(xí)實(shí)戰(zhàn)/7.利用AdaBoost元算法提高分類性能')
Reloaded modules: adaboost
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
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
#觀察classifierArray的值
In [62]: classifierArray
Out[62]: 
([{'alpha': 0.6931471805599453, 'dim': 0, 'ineq': 'lt', 'thresh': 1.3},
  {'alpha': 0.9729550745276565, 'dim': 1, 'ineq': 'lt', 'thresh': 1.0},
  {'alpha': 0.8958797346140273,
   'dim': 0,
   'ineq': 'lt',
   'thresh': 0.90000000000000002}],
 matrix([[ 1.17568763],
         [ 2.56198199],
         [-0.77022252],
         [-0.77022252],
         [ 0.61607184]]))

我們已經(jīng)實(shí)際寫完了大部分的代碼,現(xiàn)在需要將弱分類器的訓(xùn)練過程從程序中抽出來馋嗜,然后應(yīng)用到某個(gè)具體的實(shí)例上去齐板。

def adaClassify(datToClass,classifierArr):#待分類樣本,多個(gè)弱分類器組成的數(shù)組
    dataMatrix = mat(datToClass)
    m = shape(dataMatrix)[0]
    aggClassEst = mat(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
        print aggClassEst
    return sign(aggClassEst)#返回符號
datArr,labelArr = adaboost.loadSimpData()
classifierArr = adaboost.adaBoostTrainDS(datArr,labelArr,30)

In [75]: adaboost.adaClassify([0,0],classifierArr)
[[-0.69314718]]
[[-1.66610226]]
[[-2.56198199]]
Out[75]: matrix([[-1.]])
In [76]: adaboost.adaClassify([[5,5],[0,0]],classifierArr)
[[ 0.69314718]
 [-0.69314718]]
[[ 1.66610226]
 [-1.66610226]]
[[ 2.56198199]
 [-2.56198199]]
Out[76]: 
matrix([[ 1.],
        [-1.]])

我們可以看到葛菇,數(shù)據(jù)點(diǎn)的分類結(jié)果也會(huì)隨著迭代的進(jìn)行而越來越強(qiáng)甘磨,接下來我們將會(huì)將該分類器應(yīng)用到一個(gè)規(guī)模更大,難度也更大的真實(shí)數(shù)據(jù)集中眯停。
首先我們向文件加載數(shù)據(jù)

#自適應(yīng)加載函數(shù)
def loadDataSet(fileName):
    numFeat = len(open(fileName).readline().split('\t'))
    dataMat = []; labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = []
        curLine = line.strip().split('\t')#\t是tab鍵
        for i in range(numFeat-1):
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        labelMat.append(float(curLine[-1]))
    return dataMat,labelMat

并且測試該函數(shù)

In [18]: import adaboost
    ...: datArr, labelArr = loadDataSet("horseColicTraining2.txt")  
    ...: classifierArray  = adaBoostTrainDS(datArr, labelArr, 10)
    ...: 
total error:  0.284280936455 

total error:  0.284280936455 

total error:  0.247491638796 

total error:  0.247491638796 

total error:  0.254180602007 

total error:  0.240802675585 

total error:  0.240802675585 

total error:  0.220735785953 

total error:  0.247491638796 

total error:  0.230769230769
In [19]: testArr,testLabelArr = adaboost.loadDataSet('horseColicTest2.txt')

In [20]: prediction10 = adaboost.adaClassify(testArr,classifierArray)
[[ 0.46166238]
 [ 0.46166238]
 [-0.46166238]
 ..., 
#省略部分
 ..., 
 [ 0.80958618]
 [ 0.54030781]
 [ 0.5273375 ]]

In [21]: errArr = mat(ones((67,1)))

In [22]: errArr[prediction10!=mat(testLabelArr).T].sum()
Out[22]: 16.0

如圖7-1所示济舆,使用50個(gè)分類器就可以獲得較高的性能。但是錯(cuò)誤率在達(dá)到一個(gè)最小值以后又開始上升莺债,這類現(xiàn)象稱為過擬合滋觉。


表7-1

很多人認(rèn)為AdaBoost和SVM是監(jiān)督機(jī)器學(xué)習(xí)中最強(qiáng)大的兩種方法。實(shí)際上齐邦,這兩者之間有不少相似之處椎侠。我們可以吧弱分類器想象成SVM中的一個(gè)核函數(shù),也可以按照最大化某個(gè)最小間隔的方式重寫AdaBoost算法措拇。而他們的不同就在于其所定義的間隔計(jì)算方式有所不同肺蔚,因此導(dǎo)致的結(jié)果也不同。
ROC曲線代表接受者特征儡羔。在最佳的分類器下宣羊,點(diǎn)應(yīng)該盡可能在左上角,不同的ROC曲線進(jìn)行比較的一個(gè)參數(shù)是曲線下面積汰蜘。一個(gè)完美的分類器的AUC為1.0仇冯,而隨機(jī)猜測的未0.5。

def plotROC(predStrengths, classLabels):#分類器的預(yù)測強(qiáng)度
    import matplotlib.pyplot as plt
    cur = (1.0,1.0)#繪制光標(biāo)的位置
    ySum = 0.0#計(jì)算AUC的值
    numPosClas = sum(array(classLabels)==1.0)
    yStep = 1/float(numPosClas); xStep = 1/float(len(classLabels)-numPosClas)#步長
    sortedIndicies = predStrengths.argsort()
    fig = plt.figure()
    fig.clf()
    ax = plt.subplot(111)
    for index in sortedIndicies.tolist()[0]:
        if classLabels[index] == 1.0:
            delX = 0; delY =yStep;
        else:
            delX = xStep; delY = 0;
            ySum += cur[1]
        ax.plot([cur[0],cur[0]-delX],[cur[1],cur[1]-delY], c='b')
        cur = (cur[0]-delX,cur[1]-delY)
    ax.plot([0,1],[0,1],'b--')
    plt.xlabel('False positive rate'); plt.ylabel('True positive rate')
    plt.title('ROC curve for AdaBoost horse colic detection system')
    ax.axis([0,1,0,1])
    plt.show()
    print "the Area Under the Curve is:",ySum*xStep
datArr, labelArr = loadDataSet("horseColicTraining2.txt")  
classifierArray,aggClassEst = adaboost.adaBoostTrainDS(datArr,labelArr,10)
plotROC(aggClassEst.T,labelArr)
ROC圖

the Area Under the Curve is: 0.858296963506

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末族操,一起剝皮案震驚了整個(gè)濱河市苛坚,隨后出現(xiàn)的幾起案子比被,更是在濱河造成了極大的恐慌,老刑警劉巖泼舱,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件等缀,死亡現(xiàn)場離奇詭異,居然都是意外死亡娇昙,警方通過查閱死者的電腦和手機(jī)尺迂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冒掌,“玉大人噪裕,你說我怎么就攤上這事」珊粒” “怎么了膳音?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铃诬。 經(jīng)常有香客問我祭陷,道長,這世上最難降的妖魔是什么趣席? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任颗胡,我火速辦了婚禮,結(jié)果婚禮上吩坝,老公的妹妹穿的比我還像新娘毒姨。我一直安慰自己,他們只是感情好钉寝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布弧呐。 她就那樣靜靜地躺著,像睡著了一般嵌纲。 火紅的嫁衣襯著肌膚如雪俘枫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天逮走,我揣著相機(jī)與錄音鸠蚪,去河邊找鬼。 笑死师溅,一個(gè)胖子當(dāng)著我的面吹牛茅信,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播墓臭,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蘸鲸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了窿锉?” 一聲冷哼從身側(cè)響起酌摇,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤膝舅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后窑多,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仍稀,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年埂息,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了技潘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡耿芹,死狀恐怖崭篡,靈堂內(nèi)的尸體忽然破棺而出挪哄,到底是詐尸還是另有隱情吧秕,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布迹炼,位于F島的核電站砸彬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏斯入。R本人自食惡果不足惜砂碉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望刻两。 院中可真熱鬧增蹭,春花似錦、人聲如沸磅摹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽户誓。三九已至饼灿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間帝美,已是汗流浹背碍彭。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悼潭,地道東北人庇忌。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像舰褪,于是被迫代替她去往敵國和親漆枚。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容