15.決策樹的測(cè)試和存儲(chǔ)分類器

《機(jī)器學(xué)習(xí)實(shí)戰(zhàn)》花了6頁(yè)半的篇幅,來表述如何用matplotlib畫一棵決策樹。雖然這件事也有意義,但當(dāng)你已經(jīng)較為充分的理解了代碼后,其實(shí)意義不算大。所以,這一段就此跳過,只留一張范例圖供大家參考:


我們來看一下,如何使用決策樹做分類。

def classify(inputTree, featLabels, testVec):

????firstStr = list(inputTree)[0]

????secondDict = inputTree[firstStr]

????featIndex = featLabels.index(firstStr)

????key = testVec[featIndex]

????valueOfFeat = secondDict[key]

????if isinstance(valueOfFeat, dict):

????????classLabel = classify(valueOfFeat, featLabels, testVec)

????else: classLabel = valueOfFeat

????return classLabel

代碼一共是10行。

由于我沒有用到畫圖的函數(shù),而運(yùn)行代碼的時(shí)候書本里調(diào)用的是畫圖的函數(shù)生成的樹,我就用之前的createTree()來生成樹。但是這里引發(fā)了一個(gè)bug,忽然提示“no surfacing”不存在,查了一會(huì)兒后發(fā)現(xiàn),其實(shí)是在運(yùn)行createTree()的時(shí)候,改寫了labels本身。因此就要在生成樹后,再重置一下labels即可。

代碼解釋如下:

def classify(inputTree, featLabels, testVec):

#定義輸入,為輸入的樹(這里就是之前create的樹),標(biāo)簽列表以及測(cè)試數(shù)據(jù)

????firstStr = list(inputTree)[0]

#取第一個(gè)特征值

????secondDict = inputTree[firstStr]

#將這個(gè)節(jié)點(diǎn)的分支生成一棵樹

????featIndex = featLabels.index(firstStr)

#獲取這個(gè)特征描述對(duì)應(yīng)的特征數(shù)組下標(biāo)

????key = testVec[featIndex]

#通過下標(biāo),找到特征對(duì)應(yīng)的值

????valueOfFeat = secondDict[key]

#根據(jù)值,查找子樹,確認(rèn)是在哪一個(gè)分支

????if isinstance(valueOfFeat, dict):

????????classLabel = classify(valueOfFeat, featLabels, testVec)

#isinstance是判斷變量類型,即確認(rèn)valueOfFeat是否是一個(gè)字典,如果是,繼續(xù)classify遞歸查找

????else: classLabel = valueOfFeat

#如果不是,即已經(jīng)摸到了沒有繼續(xù)分叉的子節(jié)點(diǎn),則終止并返回valueOfFeat,此時(shí)它是一個(gè)這個(gè)測(cè)試數(shù)據(jù)對(duì)應(yīng)的標(biāo)簽

????return classLabel

#返回這個(gè)標(biāo)簽


總體還比較好理解,難點(diǎn)在于前幾行的代碼,要對(duì)比生成的樹的結(jié)構(gòu),一個(gè)根節(jié)點(diǎn)或者還有分叉的子節(jié)點(diǎn),我們用secondDict取到它的分叉數(shù)據(jù),然后再通過查找到這是哪一個(gè)特征項(xiàng)并對(duì)應(yīng)的是什么數(shù)值結(jié)果,去找到對(duì)應(yīng)的分叉項(xiàng),并進(jìn)一步往下走。我也是依靠反復(fù)的打印信息,來確定每一行代碼的走向,可以說邏輯不難,但代碼實(shí)現(xiàn)上會(huì)有點(diǎn)繞(連書中也說到,涉及到字典的操作,會(huì)比較復(fù)雜)。

由于至此為止用到的決策樹數(shù)據(jù)集很簡(jiǎn)單,運(yùn)算也很快,我們可以每次都跑一遍createTree()生成樹,但是如果樹很復(fù)雜,這種方式肯定是不合適的。我們?cè)谏疃葘W(xué)習(xí)中也會(huì)遇到同樣的問題,即跑出來的模型,最好是把它存起來,不然每次都跑一遍,累死了(累死了電腦,除非你是頂配,但即便電腦是頂配,碰上復(fù)雜模型也是死路一條)。

保存和讀取的代碼比較簡(jiǎn)單,用到了pickle,我們看一下:

def storeTree(inputTree, filename):

????import pickle

????fw = open(filename, 'wb')

????pickle.dump(inputTree, fw)

????fw.close()


def grabTree(filename):

????import pickle

????fr = open(filename, 'rb')

????return pickle.load(fr)

這里就不解釋了吧,確實(shí)挺簡(jiǎn)單的。生成的pickle文件,雖然你可以用后綴名.txt(書中也是這么干的)來保存,但是事實(shí)上它并不是一個(gè)utf-8的文本格式,而是一個(gè)二進(jìn)制序列化格式。強(qiáng)行打開的話,你就會(huì)看到一堆亂碼:


亂碼當(dāng)中還是能看到幾個(gè)英文詞的,哪個(gè)就是特征的描述,看來這個(gè)還沒有被轉(zhuǎn)換,其他的就已經(jīng)看不出是什么了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容