0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

決策樹的原理和決策樹構(gòu)建的準(zhǔn)備工作,機(jī)器學(xué)習(xí)決策樹的原理

電子工程師 ? 來源:未知 ? 作者:李倩 ? 2018-10-08 14:26 ? 次閱讀

一、前言

本篇討論決策樹的原理和決策樹構(gòu)建的準(zhǔn)備工作,機(jī)器學(xué)習(xí)決策樹的原理,以及如何選擇最優(yōu)特征作為分類特征,決策樹構(gòu)建,決策樹可視化,使用決策樹進(jìn)行分類預(yù)測,決策樹的存儲(chǔ)和讀取以及sklearn實(shí)戰(zhàn)之預(yù)測隱形眼睛類型。

本文出現(xiàn)的所有代碼,均可在github上下載,歡迎Follow、Star:Github地址:https://github.com/yaoguangju/machine_learning

二、決策樹的基礎(chǔ)

1、決策樹是什么

決策樹是什么?決策樹(decision tree)是一種基本的分類與回歸方法。舉個(gè)通俗易懂的例子,如下圖所示的流程圖就是一個(gè)決策樹,長方形代表判斷模塊(decision block),橢圓形成代表終止模塊(terminating block),表示已經(jīng)得出結(jié)論,可以終止運(yùn)行。從判斷模塊引出的左右箭頭稱作為分支(branch),它可以達(dá)到另一個(gè)判斷模塊或者終止模塊。我們還可以這樣理解,分類決策樹模型是一種描述對實(shí)例進(jìn)行分類的樹形結(jié)構(gòu)。決策樹由結(jié)點(diǎn)(node)和有向邊(directed edge)組成。結(jié)點(diǎn)有兩種類型:內(nèi)部結(jié)點(diǎn)(internal node)和葉結(jié)點(diǎn)(leaf node)。內(nèi)部結(jié)點(diǎn)表示一個(gè)特征或?qū)傩裕~結(jié)點(diǎn)表示一個(gè)類。蒙圈沒??

如下圖所示的決策樹,長方形和橢圓形都是結(jié)點(diǎn)。長方形的結(jié)點(diǎn)屬于內(nèi)部結(jié)點(diǎn),橢圓形的結(jié)點(diǎn)屬于葉結(jié)點(diǎn),從結(jié)點(diǎn)引出的左右箭頭就是有向邊。而最上面的結(jié)點(diǎn)就是決策樹的根結(jié)點(diǎn)(root node)。這樣,結(jié)點(diǎn)說法就與模塊說法對應(yīng)上了。

我們回到這個(gè)流程圖,對,你沒看錯(cuò),這就是一個(gè)假想的相親對象分類系統(tǒng)。它首先檢測相親對方是否有房。如果有房,則對于這個(gè)相親對象可以考慮進(jìn)一步接觸。如果沒有房,則觀察相親對象是否有上進(jìn)心,如果沒有,直接Say Goodbye,此時(shí)可以說:”你人很好,但是我們不合適?!比绻?,同樣也值得認(rèn)真考慮。

不過這只是個(gè)簡單的相親對象分類系統(tǒng),只是做了簡單的分類。真實(shí)情況可能要復(fù)雜得多,考慮因素也可以是五花八門。脾氣好嗎?會(huì)做飯嗎?愿意做家務(wù)嗎?家里幾個(gè)孩子?父母是干什么的?等等各種因素。

我們可以把決策樹看成一個(gè)if-then規(guī)則的集合,將決策樹轉(zhuǎn)換成if-then規(guī)則的過程是這樣的:由決策樹的根結(jié)點(diǎn)(root node)到葉結(jié)點(diǎn)(leaf node)的每一條路徑構(gòu)建一條規(guī)則;路徑上內(nèi)部結(jié)點(diǎn)的特征對應(yīng)著規(guī)則的條件,而葉結(jié)點(diǎn)的類對應(yīng)著規(guī)則的結(jié)論。決策樹的路徑或其對應(yīng)的if-then規(guī)則集合具有一個(gè)重要的性質(zhì):互斥并且完備。這就是說,每一個(gè)實(shí)例都被一條路徑或一條規(guī)則所覆蓋,而且只被一條路徑或一條規(guī)則所覆蓋。這里所覆蓋是指實(shí)例的特征與路徑上的特征一致或?qū)嵗凉M足規(guī)則的條件。

使用決策樹做預(yù)測需要以下過程:

收集數(shù)據(jù):可以使用任何方法。比如想構(gòu)建一個(gè)相親系統(tǒng),我們可以從媒婆那里,或者通過采訪相親對象獲取數(shù)據(jù)。根據(jù)他們考慮的因素和最終的選擇結(jié)果,就可以得到一些供我們利用的數(shù)據(jù)了。

準(zhǔn)備數(shù)據(jù):收集完的數(shù)據(jù),我們要進(jìn)行整理,將這些所有收集的信息按照一定規(guī)則整理出來,并排版,方便我們進(jìn)行后續(xù)處理。

分析數(shù)據(jù):可以使用任何方法,決策樹構(gòu)造完成之后,我們可以檢查決策樹圖形是否符合預(yù)期。

訓(xùn)練算法:這個(gè)過程也就是構(gòu)造決策樹,同樣也可以說是決策樹學(xué)習(xí),就是構(gòu)造一個(gè)決策樹的數(shù)據(jù)結(jié)構(gòu)。

測試算法:使用經(jīng)驗(yàn)樹計(jì)算錯(cuò)誤率。當(dāng)錯(cuò)誤率達(dá)到了可接收范圍,這個(gè)決策樹就可以投放使用了。

使用算法:此步驟可以使用適用于任何監(jiān)督學(xué)習(xí)算法,而使用決策樹可以更好地理解數(shù)據(jù)的內(nèi)在含義。

2、決策樹的構(gòu)建的準(zhǔn)備工作

使用決策樹做預(yù)測的每一步驟都很重要,數(shù)據(jù)收集不到位,將會(huì)導(dǎo)致沒有足夠的特征讓我們構(gòu)建錯(cuò)誤率低的決策樹。數(shù)據(jù)特征充足,但是不知道用哪些特征好,將會(huì)導(dǎo)致無法構(gòu)建出分類效果好的決策樹模型。從算法方面看,決策樹的構(gòu)建是我們的核心內(nèi)容。

決策樹要如何構(gòu)建呢?通常,這一過程可以概括為3個(gè)步驟:特征選擇、決策樹的生成和決策樹的修剪。

特征選擇

特征選擇在于選取對訓(xùn)練數(shù)據(jù)具有分類能力的特征。這樣可以提高決策樹學(xué)習(xí)的效率,如果利用一個(gè)特征進(jìn)行分類的結(jié)果與隨機(jī)分類的結(jié)果沒有很大差別,則稱這個(gè)特征是沒有分類能力的。經(jīng)驗(yàn)上扔掉這樣的特征對決策樹學(xué)習(xí)的精度影響不大。通常特征選擇的標(biāo)準(zhǔn)是信息增益(information gain)或信息增益比,為了簡單,本文使用信息增益作為選擇特征的標(biāo)準(zhǔn)。那么,什么是信息增益?在講解信息增益之前,讓我們看一組實(shí)例,貸款申請樣本數(shù)據(jù)表。

希望通過所給的訓(xùn)練數(shù)據(jù)學(xué)習(xí)一個(gè)貸款申請的決策樹,用于對未來的貸款申請進(jìn)行分類,即當(dāng)新的客戶提出貸款申請時(shí),根據(jù)申請人的特征利用決策樹決定是否批準(zhǔn)貸款申請。

特征選擇就是決定用哪個(gè)特征來劃分特征空間。比如,我們通過上述數(shù)據(jù)表得到兩個(gè)可能的決策樹,分別由兩個(gè)不同特征的根結(jié)點(diǎn)構(gòu)成。

圖(a)所示的根結(jié)點(diǎn)的特征是年齡,有3個(gè)取值,對應(yīng)于不同的取值有不同的子結(jié)點(diǎn)。圖(b)所示的根節(jié)點(diǎn)的特征是工作,有2個(gè)取值,對應(yīng)于不同的取值有不同的子結(jié)點(diǎn)。兩個(gè)決策樹都可以從此延續(xù)下去。問題是:究竟選擇哪個(gè)特征更好些?這就要求確定選擇特征的準(zhǔn)則。直觀上,如果一個(gè)特征具有更好的分類能力,或者說,按照這一特征將訓(xùn)練數(shù)據(jù)集分割成子集,使得各個(gè)子集在當(dāng)前條件下有最好的分類,那么就更應(yīng)該選擇這個(gè)特征。信息增益就能夠很好地表示這一直觀的準(zhǔn)則。

什么是信息增益呢?在劃分?jǐn)?shù)據(jù)集之后信息發(fā)生的變化稱為信息增益,知道如何計(jì)算信息增益,我們就可以計(jì)算每個(gè)特征值劃分?jǐn)?shù)據(jù)集獲得的信息增益,獲得信息增益最高的特征就是最好的選擇。

(1)香農(nóng)熵

在可以評測哪個(gè)數(shù)據(jù)劃分方式是最好的數(shù)據(jù)劃分之前,我們必須學(xué)習(xí)如何計(jì)算信息增益。集合信息的度量方式成為香農(nóng)熵或者簡稱為熵(entropy),這個(gè)名字來源于信息論之父克勞德·香農(nóng)。

如果看不明白什么是信息增益和熵,請不要著急,因?yàn)樗麄冏哉Q生的那一天起,就注定會(huì)令世人十分費(fèi)解??藙诘隆は戕r(nóng)寫完信息論之后,約翰·馮·諾依曼建議使用”熵”這個(gè)術(shù)語,因?yàn)榇蠹叶疾恢浪鞘裁匆馑肌?/p>

熵定義為信息的期望值。在信息論與概率統(tǒng)計(jì)中,熵是表示隨機(jī)變量不確定性的度量。如果待分類的事物可能劃分在多個(gè)分類之中,則符號(hào)xi的信息定義為 :

其中p(xi)是選擇該分類的概率。有人可能會(huì)問,信息為啥這樣定義?。看鹪唬呵拜叺贸龅慕Y(jié)論。這就跟1+1等于2一樣,記住并且會(huì)用即可。上述式中的對數(shù)以2為底,也可以e為底(自然對數(shù))。

通過上式,我們可以得到所有類別的信息。為了計(jì)算熵,我們需要計(jì)算所有類別所有可能值包含的信息期望值(數(shù)學(xué)期望),通過下面的公式得到:

其中n是分類的數(shù)目。熵越大,隨機(jī)變量的不確定性就越大。

當(dāng)熵中的概率由數(shù)據(jù)估計(jì)(特別是最大似然估計(jì))得到時(shí),所對應(yīng)的熵稱為經(jīng)驗(yàn)熵(empirical entropy)。什么叫由數(shù)據(jù)估計(jì)?比如有10個(gè)數(shù)據(jù),一共有兩個(gè)類別,A類和B類。其中有7個(gè)數(shù)據(jù)屬于A類,則該A類的概率即為十分之七。其中有3個(gè)數(shù)據(jù)屬于B類,則該B類的概率即為十分之三。淺顯的解釋就是,這概率是我們根據(jù)數(shù)據(jù)數(shù)出來的。我們定義貸款申請樣本數(shù)據(jù)表中的數(shù)據(jù)為訓(xùn)練數(shù)據(jù)集D,則訓(xùn)練數(shù)據(jù)集D的經(jīng)驗(yàn)熵為H(D),|D|表示其樣本容量,及樣本個(gè)數(shù)。設(shè)有K個(gè)類Ck, = 1,2,3,…,K,|Ck|為屬于類Ck的樣本個(gè)數(shù),因此經(jīng)驗(yàn)熵公式就可以寫為 :

根據(jù)此公式計(jì)算經(jīng)驗(yàn)熵H(D),分析貸款申請樣本數(shù)據(jù)表中的數(shù)據(jù)。最終分類結(jié)果只有兩類,即放貸和不放貸。根據(jù)表中的數(shù)據(jù)統(tǒng)計(jì)可知,在15個(gè)數(shù)據(jù)中,9個(gè)數(shù)據(jù)的結(jié)果為放貸,6個(gè)數(shù)據(jù)的結(jié)果為不放貸。所以數(shù)據(jù)集D的經(jīng)驗(yàn)熵H(D)為:

經(jīng)過計(jì)算可知,數(shù)據(jù)集D的經(jīng)驗(yàn)熵H(D)的值為0.971。

(2)編寫代碼計(jì)算經(jīng)驗(yàn)熵

在編寫代碼之前,我們先對數(shù)據(jù)集進(jìn)行屬性標(biāo)注。

年齡:0代表青年,1代表中年,2代表老年;

有工作:0代表否,1代表是;

有自己的房子:0代表否,1代表是;

信貸情況:0代表一般,1代表好,2代表非常好;

類別(是否給貸款):no代表否,yes代表是。

確定這些之后,我們就可以創(chuàng)建數(shù)據(jù)集,并計(jì)算經(jīng)驗(yàn)熵了,代碼編寫如下:

代碼運(yùn)行結(jié)果如下圖所示,代碼是先打印訓(xùn)練數(shù)據(jù)集,然后打印計(jì)算的經(jīng)驗(yàn)熵H(D),程序計(jì)算的結(jié)果與我們統(tǒng)計(jì)計(jì)算的結(jié)果是一致的,程序沒有問題。

(3) 信息增益

在上面,我們已經(jīng)說過,如何選擇特征,需要看信息增益。也就是說,信息增益是相對于特征而言的,信息增益越大,特征對最終的分類結(jié)果影響也就越大,我們就應(yīng)該選擇對最終分類結(jié)果影響最大的那個(gè)特征作為我們的分類特征。

在講解信息增益定義之前,我們還需要明確一個(gè)概念,條件熵。

熵我們知道是什么,條件熵又是個(gè)什么鬼?條件熵H(Y|X)表示在已知隨機(jī)變量X的條件下隨機(jī)變量Y的不確定性,隨機(jī)變量X給定的條件下隨機(jī)變量Y的條件熵(conditional entropy)H(Y|X),定義為X給定條件下Y的條件概率分布的熵對X的數(shù)學(xué)期望:

這里,

同理,當(dāng)條件熵中的概率由數(shù)據(jù)估計(jì)(特別是極大似然估計(jì))得到時(shí),所對應(yīng)的條件熵成為條件經(jīng)驗(yàn)熵(empirical conditional entropy)。

明確了條件熵和經(jīng)驗(yàn)條件熵的概念。接下來,讓我們說說信息增益。前面也提到了,信息增益是相對于特征而言的。所以,特征A對訓(xùn)練數(shù)據(jù)集D的信息增益g(D,A),定義為集合D的經(jīng)驗(yàn)熵H(D)與特征A給定條件下D的經(jīng)驗(yàn)條件熵H(D|A)之差,即:

一般地,熵H(D)與條件熵H(D|A)之差成為互信息(mutual information)。決策樹學(xué)習(xí)中的信息增益等價(jià)于訓(xùn)練數(shù)據(jù)集中類與特征的互信息。

設(shè)特征A有n個(gè)不同的取值{a1,a2,···,an},根據(jù)特征A的取值將D劃分為n個(gè)子集{D1,D2,···,Dn},|Di|為Di的樣本個(gè)數(shù)。記子集Di中屬于Ck的樣本的集合為Dik,即Dik = Di ∩ Ck,|Dik|為Dik的樣本個(gè)數(shù)。于是經(jīng)驗(yàn)條件熵的公式可以些為:

說了這么多概念性的東西,沒有聽懂也沒有關(guān)系,舉幾個(gè)例子,再回來看一下概念,就懂了。

以貸款申請樣本數(shù)據(jù)表為例進(jìn)行說明??聪履挲g這一列的數(shù)據(jù),也就是特征A1,一共有三個(gè)類別,分別是:青年、中年和老年。我們只看年齡是青年的數(shù)據(jù),年齡是青年的數(shù)據(jù)一共有5個(gè),所以年齡是青年的數(shù)據(jù)在訓(xùn)練數(shù)據(jù)集出現(xiàn)的概率是十五分之五,也就是三分之一。同理,年齡是中年和老年的數(shù)據(jù)在訓(xùn)練數(shù)據(jù)集出現(xiàn)的概率也都是三分之一?,F(xiàn)在我們只看年齡是青年的數(shù)據(jù)的最終得到貸款的概率為五分之二,因?yàn)樵谖鍌€(gè)數(shù)據(jù)中,只有兩個(gè)數(shù)據(jù)顯示拿到了最終的貸款,同理,年齡是中年和老年的數(shù)據(jù)最終得到貸款的概率分別為五分之三、五分之四。所以計(jì)算年齡的信息增益,過程如下:

同理,計(jì)算其余特征的信息增益g(D,A2)、g(D,A3)和g(D,A4)。分別為:

最后,比較特征的信息增益,由于特征A3(有自己的房子)的信息增益值最大,所以選擇A3作為最優(yōu)特征。

(4) 編寫代碼計(jì)算信息增益

我們已經(jīng)學(xué)會(huì)了通過公式計(jì)算信息增益,接下來編寫代碼,計(jì)算信息增益。

#-*-coding:UTF-8-*-frommathimportlog"""函數(shù)說明:計(jì)算給定數(shù)據(jù)集的經(jīng)驗(yàn)熵(香農(nóng)熵)Parameters:dataSet-數(shù)據(jù)集Returns:shannonEnt-經(jīng)驗(yàn)熵(香農(nóng)熵)"""defcalcShannonEnt(dataSet):numEntires=len(dataSet)#返回?cái)?shù)據(jù)集的行數(shù)labelCounts={}#保存每個(gè)標(biāo)簽(Label)出現(xiàn)次數(shù)的字典forfeatVecindataSet:#對每組特征向量進(jìn)行統(tǒng)計(jì)currentLabel=featVec[-1]#提取標(biāo)簽(Label)信息ifcurrentLabelnotinlabelCounts.keys():#如果標(biāo)簽(Label)沒有放入統(tǒng)計(jì)次數(shù)的字典,添加進(jìn)去labelCounts[currentLabel]=0labelCounts[currentLabel]+=1#Label計(jì)數(shù)shannonEnt=0.0#經(jīng)驗(yàn)熵(香農(nóng)熵)forkeyinlabelCounts:#計(jì)算香農(nóng)熵prob=float(labelCounts[key])/numEntires#選擇該標(biāo)簽(Label)的概率shannonEnt-=prob*log(prob,2)#利用公式計(jì)算returnshannonEnt#返回經(jīng)驗(yàn)熵(香農(nóng)熵)"""函數(shù)說明:創(chuàng)建測試數(shù)據(jù)集Parameters:無Returns:dataSet-數(shù)據(jù)集labels-特征標(biāo)簽"""defcreateDataSet():dataSet=[[0,0,0,0,'no'],#數(shù)據(jù)集[0,0,0,1,'no'],[0,1,0,1,'yes'],[0,1,1,0,'yes'],[0,0,0,0,'no'],[1,0,0,0,'no'],[1,0,0,1,'no'],[1,1,1,1,'yes'],[1,0,1,2,'yes'],[1,0,1,2,'yes'],[2,0,1,2,'yes'],[2,0,1,1,'yes'],[2,1,0,1,'yes'],[2,1,0,2,'yes'],[2,0,0,0,'no']]labels=['年齡','有工作','有自己的房子','信貸情況']#特征標(biāo)簽returndataSet,labels#返回?cái)?shù)據(jù)集和分類屬性"""函數(shù)說明:選擇最優(yōu)特征Parameters:dataSet-數(shù)據(jù)集Returns:bestFeature-信息增益最大的(最優(yōu))特征的索引值"""defchooseBestFeatureToSplit(dataSet):numFeatures=len(dataSet[0])-1#特征數(shù)量baseEntropy=calcShannonEnt(dataSet)#計(jì)算數(shù)據(jù)集的香農(nóng)熵bestInfoGain=0.0#信息增益bestFeature=-1#最優(yōu)特征的索引值foriinrange(numFeatures):#遍歷所有特征#獲取dataSet的第i個(gè)所有特征featList=[example[i]forexampleindataSet]uniqueVals=set(featList)#創(chuàng)建set集合{},元素不可重復(fù)newEntropy=0.0#經(jīng)驗(yàn)條件熵forvalueinuniqueVals:#計(jì)算信息增益subDataSet=splitDataSet(dataSet,i,value)#subDataSet劃分后的子集prob=len(subDataSet)/float(len(dataSet))#計(jì)算子集的概率newEntropy+=prob*calcShannonEnt(subDataSet)#根據(jù)公式計(jì)算經(jīng)驗(yàn)條件熵infoGain=baseEntropy-newEntropy#信息增益print("第%d個(gè)特征的增益為%.3f"%(i,infoGain))#打印每個(gè)特征的信息增益if(infoGain>bestInfoGain):#計(jì)算信息增益bestInfoGain=infoGain#更新信息增益,找到最大的信息增益bestFeature=i#記錄信息增益最大的特征的索引值returnbestFeature#返回信息增益最大的特征的索引值if__name__=='__main__':dataSet,features=createDataSet()print("最優(yōu)特征索引值:"+str(chooseBestFeatureToSplit(dataSet)))

splitDataSet函數(shù)是用來選擇各個(gè)特征的子集的,比如選擇年齡(第0個(gè)特征)的青年(用0代表)的自己,我們可以調(diào)用splitDataSet(dataSet,0,0)這樣返回的子集就是年齡為青年的5個(gè)數(shù)據(jù)集。chooseBestFeatureToSplit是選擇選擇最優(yōu)特征的函數(shù)。運(yùn)行代碼結(jié)果如下:

對比我們自己計(jì)算的結(jié)果,發(fā)現(xiàn)結(jié)果完全正確!最優(yōu)特征的索引值為2,也就是特征A3(有自己的房子)。

決策樹生成和修剪

我們已經(jīng)學(xué)習(xí)了從數(shù)據(jù)集構(gòu)造決策樹算法所需要的子功能模塊,包括經(jīng)驗(yàn)熵的計(jì)算和最優(yōu)特征的選擇,其工作原理如下:得到原始數(shù)據(jù)集,然后基于最好的屬性值劃分?jǐn)?shù)據(jù)集,由于特征值可能多于兩個(gè),因此可能存在大于兩個(gè)分支的數(shù)據(jù)集劃分。第一次劃分之后,數(shù)據(jù)集被向下傳遞到樹的分支的下一個(gè)結(jié)點(diǎn)。在這個(gè)結(jié)點(diǎn)上,我們可以再次劃分?jǐn)?shù)據(jù)。因此我們可以采用遞歸的原則處理數(shù)據(jù)集。

構(gòu)建決策樹的算法有很多,比如C4.5、ID3和CART,這些算法在運(yùn)行時(shí)并不總是在每次劃分?jǐn)?shù)據(jù)分組時(shí)都會(huì)消耗特征。由于特征數(shù)目并不是每次劃分?jǐn)?shù)據(jù)分組時(shí)都減少,因此這些算法在實(shí)際使用時(shí)可能引起一定的問題。目前我們并不需要考慮這個(gè)問題,只需要在算法開始運(yùn)行前計(jì)算列的數(shù)目,查看算法是否使用了所有屬性即可。

決策樹生成算法遞歸地產(chǎn)生決策樹,直到不能繼續(xù)下去未為止。這樣產(chǎn)生的樹往往對訓(xùn)練數(shù)據(jù)的分類很準(zhǔn)確,但對未知的測試數(shù)據(jù)的分類卻沒有那么準(zhǔn)確,即出現(xiàn)過擬合現(xiàn)象。過擬合的原因在于學(xué)習(xí)時(shí)過多地考慮如何提高對訓(xùn)練數(shù)據(jù)的正確分類,從而構(gòu)建出過于復(fù)雜的決策樹。解決這個(gè)問題的辦法是考慮決策樹的復(fù)雜度,對已生成的決策樹進(jìn)行簡化。

三、決策樹構(gòu)建

上篇文章也粗略提到過,構(gòu)建決策樹的算法有很多。篇幅原因,本篇文章只使用ID3算法構(gòu)建決策樹。

1、ID3算法

ID3算法的核心是在決策樹各個(gè)結(jié)點(diǎn)上對應(yīng)信息增益準(zhǔn)則選擇特征,遞歸地構(gòu)建決策樹。具體方法是:從根結(jié)點(diǎn)(root node)開始,對結(jié)點(diǎn)計(jì)算所有可能的特征的信息增益,選擇信息增益最大的特征作為結(jié)點(diǎn)的特征,由該特征的不同取值建立子節(jié)點(diǎn);再對子結(jié)點(diǎn)遞歸地調(diào)用以上方法,構(gòu)建決策樹;直到所有特征的信息增益均很小或沒有特征可以選擇為止。最后得到一個(gè)決策樹。ID3相當(dāng)于用極大似然法進(jìn)行概率模型的選擇。

在使用ID3構(gòu)造決策樹之前,我們再分析下數(shù)據(jù)。

利用上篇文章求得的結(jié)果,由于特征A3(有自己的房子)的信息增益值最大,所以選擇特征A3作為根結(jié)點(diǎn)的特征。它將訓(xùn)練集D劃分為兩個(gè)子集D1(A3取值為”是”)和D2(A3取值為”否”)。由于D1只有同一類的樣本點(diǎn),所以它成為一個(gè)葉結(jié)點(diǎn),結(jié)點(diǎn)的類標(biāo)記為“是”。

對D2則需要從特征A1(年齡),A2(有工作)和A4(信貸情況)中選擇新的特征,計(jì)算各個(gè)特征的信息增益:

根據(jù)計(jì)算,選擇信息增益最大的特征A2(有工作)作為結(jié)點(diǎn)的特征。由于A2有兩個(gè)可能取值,從這一結(jié)點(diǎn)引出兩個(gè)子結(jié)點(diǎn):一個(gè)對應(yīng)”是”(有工作)的子結(jié)點(diǎn),包含3個(gè)樣本,它們屬于同一類,所以這是一個(gè)葉結(jié)點(diǎn),類標(biāo)記為”是”;另一個(gè)是對應(yīng)”否”(無工作)的子結(jié)點(diǎn),包含6個(gè)樣本,它們也屬于同一類,所以這也是一個(gè)葉結(jié)點(diǎn),類標(biāo)記為”否”。

這樣就生成了一個(gè)決策樹,該決策樹只用了兩個(gè)特征(有兩個(gè)內(nèi)部結(jié)點(diǎn)),生成的決策樹如下圖所示。

這樣我們就使用ID3算法構(gòu)建出來了決策樹,接下來,讓我們看看如何進(jìn)行代實(shí)現(xiàn)。

2、編寫代碼構(gòu)建決策樹

我們使用字典存儲(chǔ)決策樹的結(jié)構(gòu),比如上小節(jié)我們分析出來的決策樹,用字典可以表示為:

{'有自己的房子':{0:{'有工作':{0:'no',1:'yes'}},1:'yes'}}

創(chuàng)建函數(shù)majorityCnt統(tǒng)計(jì)classList中出現(xiàn)此處最多的元素(類標(biāo)簽),創(chuàng)建函數(shù)createTree用來遞歸構(gòu)建決策樹。編寫代碼如下:

#-*-coding:UTF-8-*-frommathimportlogimportoperator"""函數(shù)說明:計(jì)算給定數(shù)據(jù)集的經(jīng)驗(yàn)熵(香農(nóng)熵)Parameters:dataSet-數(shù)據(jù)集Returns:shannonEnt-經(jīng)驗(yàn)熵(香農(nóng)熵)"""defcalcShannonEnt(dataSet):numEntires=len(dataSet)#返回?cái)?shù)據(jù)集的行數(shù)labelCounts={}#保存每個(gè)標(biāo)簽(Label)出現(xiàn)次數(shù)的字典forfeatVecindataSet:#對每組特征向量進(jìn)行統(tǒng)計(jì)currentLabel=featVec[-1]#提取標(biāo)簽(Label)信息ifcurrentLabelnotinlabelCounts.keys():#如果標(biāo)簽(Label)沒有放入統(tǒng)計(jì)次數(shù)的字典,添加進(jìn)去labelCounts[currentLabel]=0labelCounts[currentLabel]+=1#Label計(jì)數(shù)shannonEnt=0.0#經(jīng)驗(yàn)熵(香農(nóng)熵)forkeyinlabelCounts:#計(jì)算香農(nóng)熵prob=float(labelCounts[key])/numEntires#選擇該標(biāo)簽(Label)的概率shannonEnt-=prob*log(prob,2)#利用公式計(jì)算returnshannonEnt#返回經(jīng)驗(yàn)熵(香農(nóng)熵)"""函數(shù)說明:創(chuàng)建測試數(shù)據(jù)集Parameters:無Returns:dataSet-數(shù)據(jù)集labels-特征標(biāo)簽"""defcreateDataSet():dataSet=[[0,0,0,0,'no'],#數(shù)據(jù)集[0,0,0,1,'no'],[0,1,0,1,'yes'],[0,1,1,0,'yes'],[0,0,0,0,'no'],[1,0,0,0,'no'],[1,0,0,1,'no'],[1,1,1,1,'yes'],[1,0,1,2,'yes'],[1,0,1,2,'yes'],[2,0,1,2,'yes'],[2,0,1,1,'yes'],[2,1,0,1,'yes'],[2,1,0,2,'yes'],[2,0,0,0,'no']]labels=['年齡','有工作','有自己的房子','信貸情況']#特征標(biāo)簽returndataSet,labels#返回?cái)?shù)據(jù)集和分類屬性"""函數(shù)說明:按照給定特征劃分?jǐn)?shù)據(jù)集Parameters:dataSet-待劃分的數(shù)據(jù)集axis-劃分?jǐn)?shù)據(jù)集的特征value-需要返回的特征的值Returns:無"""defsplitDataSet(dataSet,axis,value):retDataSet=[]#創(chuàng)建返回的數(shù)據(jù)集列表forfeatVecindataSet:#遍歷數(shù)據(jù)集iffeatVec[axis]==value:reducedFeatVec=featVec[:axis]#去掉axis特征reducedFeatVec.extend(featVec[axis+1:])#將符合條件的添加到返回的數(shù)據(jù)集retDataSet.append(reducedFeatVec)returnretDataSet#返回劃分后的數(shù)據(jù)集"""函數(shù)說明:選擇最優(yōu)特征Parameters:dataSet-數(shù)據(jù)集Returns:bestFeature-信息增益最大的(最優(yōu))特征的索引值"""defchooseBestFeatureToSplit(dataSet):numFeatures=len(dataSet[0])-1#特征數(shù)量baseEntropy=calcShannonEnt(dataSet)#計(jì)算數(shù)據(jù)集的香農(nóng)熵bestInfoGain=0.0#信息增益bestFeature=-1#最優(yōu)特征的索引值foriinrange(numFeatures):#遍歷所有特征#獲取dataSet的第i個(gè)所有特征featList=[example[i]forexampleindataSet]uniqueVals=set(featList)#創(chuàng)建set集合{},元素不可重復(fù)newEntropy=0.0#經(jīng)驗(yàn)條件熵forvalueinuniqueVals:#計(jì)算信息增益subDataSet=splitDataSet(dataSet,i,value)#subDataSet劃分后的子集prob=len(subDataSet)/float(len(dataSet))#計(jì)算子集的概率newEntropy+=prob*calcShannonEnt(subDataSet)#根據(jù)公式計(jì)算經(jīng)驗(yàn)條件熵infoGain=baseEntropy-newEntropy#信息增益#print("第%d個(gè)特征的增益為%.3f"%(i,infoGain))#打印每個(gè)特征的信息增益if(infoGain>bestInfoGain):#計(jì)算信息增益bestInfoGain=infoGain#更新信息增益,找到最大的信息增益bestFeature=i#記錄信息增益最大的特征的索引值returnbestFeature#返回信息增益最大的特征的索引值"""函數(shù)說明:統(tǒng)計(jì)classList中出現(xiàn)此處最多的元素(類標(biāo)簽)Parameters:classList-類標(biāo)簽列表Returns:sortedClassCount[0][0]-出現(xiàn)此處最多的元素(類標(biāo)簽)"""defmajorityCnt(classList):classCount={}forvoteinclassList:#統(tǒng)計(jì)classList中每個(gè)元素出現(xiàn)的次數(shù)ifvotenotinclassCount.keys():classCount[vote]=0classCount[vote]+=1sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)#根據(jù)字典的值降序排序returnsortedClassCount[0][0]#返回classList中出現(xiàn)次數(shù)最多的元素"""函數(shù)說明:創(chuàng)建決策樹Parameters:dataSet-訓(xùn)練數(shù)據(jù)集labels-分類屬性標(biāo)簽featLabels-存儲(chǔ)選擇的最優(yōu)特征標(biāo)簽Returns:myTree-決策樹"""defcreateTree(dataSet,labels,featLabels):classList=[example[-1]forexampleindataSet]#取分類標(biāo)簽(是否放貸:yesorno)ifclassList.count(classList[0])==len(classList):#如果類別完全相同則停止繼續(xù)劃分returnclassList[0]iflen(dataSet[0])==1:#遍歷完所有特征時(shí)返回出現(xiàn)次數(shù)最多的類標(biāo)簽returnmajorityCnt(classList)bestFeat=chooseBestFeatureToSplit(dataSet)#選擇最優(yōu)特征bestFeatLabel=labels[bestFeat]#最優(yōu)特征的標(biāo)簽featLabels.append(bestFeatLabel)myTree={bestFeatLabel:{}}#根據(jù)最優(yōu)特征的標(biāo)簽生成樹del(labels[bestFeat])#刪除已經(jīng)使用特征標(biāo)簽featValues=[example[bestFeat]forexampleindataSet]#得到訓(xùn)練集中所有最優(yōu)特征的屬性值uniqueVals=set(featValues)#去掉重復(fù)的屬性值forvalueinuniqueVals:#遍歷特征,創(chuàng)建決策樹。myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),labels,featLabels)returnmyTreeif__name__=='__main__':dataSet,labels=createDataSet()featLabels=[]myTree=createTree(dataSet,labels,featLabels)print(myTree)

遞歸創(chuàng)建決策樹時(shí),遞歸有兩個(gè)終止條件:第一個(gè)停止條件是所有的類標(biāo)簽完全相同,則直接返回該類標(biāo)簽;第二個(gè)停止條件是使用完了所有特征,仍然不能將數(shù)據(jù)劃分僅包含唯一類別的分組,即決策樹構(gòu)建失敗,特征不夠用。此時(shí)說明數(shù)據(jù)緯度不夠,由于第二個(gè)停止條件無法簡單地返回唯一的類標(biāo)簽,這里挑選出現(xiàn)數(shù)量最多的類別作為返回值。

運(yùn)行上述代碼,我們可以看到如下結(jié)果:

可見,我們的決策樹已經(jīng)構(gòu)建完成了。這時(shí)候,有的朋友可能會(huì)說,這個(gè)決策樹看著好別扭,雖然這個(gè)能看懂,但是如果多點(diǎn)的結(jié)點(diǎn),就不好看了。能直觀點(diǎn)嗎?完全沒有問題,我們可以使用強(qiáng)大的Matplotlib繪制決策樹。

3、決策樹可視化

這里代碼都是關(guān)于Matplotlib的,如果對于Matplotlib不了解的,可以先學(xué)習(xí)下,Matplotlib的內(nèi)容這里就不再累述。可視化需要用到的函數(shù):

getNumLeafs:獲取決策樹葉子結(jié)點(diǎn)的數(shù)目

getTreeDepth:獲取決策樹的層數(shù)

plotNode:繪制結(jié)點(diǎn)

plotMidText:標(biāo)注有向邊屬性值

plotTree:繪制決策樹

createPlot:創(chuàng)建繪制面板

對可視化決策樹的程序進(jìn)行了詳細(xì)的注釋,直接看代碼,調(diào)試查看即可。為了顯示中文,需要設(shè)置FontProperties,代碼編寫如下:

(點(diǎn)擊圖片查看大圖)

不出意外的話,我們就可以得到如下結(jié)果,可以看到?jīng)Q策樹繪制完成。plotNode函數(shù)的工作就是繪制各個(gè)結(jié)點(diǎn),比如有自己的房子、有工作、yes、no,包括內(nèi)結(jié)點(diǎn)和葉子結(jié)點(diǎn)。plotMidText函數(shù)的工作就是繪制各個(gè)有向邊的屬性。

4、使用決策樹執(zhí)行分類

依靠訓(xùn)練數(shù)據(jù)構(gòu)造了決策樹之后,我們可以將它用于實(shí)際數(shù)據(jù)的分類。在執(zhí)行數(shù)據(jù)分類時(shí),需要決策樹以及用于構(gòu)造樹的標(biāo)簽向量。然后,程序比較測試數(shù)據(jù)與決策樹上的數(shù)值,遞歸執(zhí)行該過程直到進(jìn)入葉子結(jié)點(diǎn);最后將測試數(shù)據(jù)定義為葉子結(jié)點(diǎn)所屬的類型。在構(gòu)建決策樹的代碼,可以看到,有個(gè)featLabels參數(shù)。它是用來干什么的?它就是用來記錄各個(gè)分類結(jié)點(diǎn)的,在用決策樹做預(yù)測的時(shí)候,我們按順序輸入需要的分類結(jié)點(diǎn)的屬性值即可。舉個(gè)例子,比如我用上述已經(jīng)訓(xùn)練好的決策樹做分類,那么我只需要提供這個(gè)人是否有房子,是否有工作這兩個(gè)信息即可,無需提供冗余的信息。

用決策樹做分類的代碼很簡單,編寫代碼如下:

(點(diǎn)擊圖片,查看大圖)

這里只增加了classify函數(shù),用于決策樹分類。輸入測試數(shù)據(jù)[0,1],它代表沒有房子,但是有工作,分類結(jié)果如下所示:

看到這里,細(xì)心的朋友可能就會(huì)問了,每次做預(yù)測都要訓(xùn)練一次決策樹?這也太麻煩了吧?有什么好的解決嗎?

5、決策樹的存儲(chǔ)

構(gòu)造決策樹是很耗時(shí)的任務(wù),即使處理很小的數(shù)據(jù)集,如前面的樣本數(shù)據(jù),也要花費(fèi)幾秒的時(shí)間,如果數(shù)據(jù)集很大,將會(huì)耗費(fèi)很多計(jì)算時(shí)間。然而用創(chuàng)建好的決策樹解決分類問題,則可以很快完成。因此,為了節(jié)省計(jì)算時(shí)間,最好能夠在每次執(zhí)行分類時(shí)調(diào)用已經(jīng)構(gòu)造好的決策樹。為了解決這個(gè)問題,需要使用Python模塊pickle序列化對象。序列化對象可以在磁盤上保存對象,并在需要的時(shí)候讀取出來。

假設(shè)我們已經(jīng)得到?jīng)Q策樹

{'有自己的房子':{0:{'有工作':{0:'no',1:'yes'}},1:'yes'}}

使用pickle.dump存儲(chǔ)決策樹。

#-*-coding:UTF-8-*-importpickle"""函數(shù)說明:存儲(chǔ)決策樹Parameters:inputTree-已經(jīng)生成的決策樹filename-決策樹的存儲(chǔ)文件名Returns:無"""defstoreTree(inputTree,filename):withopen(filename,'wb')asfw:pickle.dump(inputTree,fw)if__name__=='__main__':myTree={'有自己的房子':{0:{'有工作':{0:'no',1:'yes'}},1:'yes'}}storeTree(myTree,'classifierStorage.txt')

運(yùn)行代碼,在該P(yáng)ython文件的相同目錄下,會(huì)生成一個(gè)名為classifierStorage.txt的txt文件,這個(gè)文件二進(jìn)制存儲(chǔ)著我們的決策樹。我們可以使用VScode打開看下存儲(chǔ)結(jié)果。

看不懂?沒錯(cuò),因?yàn)檫@個(gè)是個(gè)二進(jìn)制存儲(chǔ)的文件,我們也無需看懂里面的內(nèi)容,會(huì)存儲(chǔ),會(huì)用即可。那么問題來了。將決策樹存儲(chǔ)完這個(gè)二進(jìn)制文件,然后下次使用的話,怎么用呢?

很簡單使用pickle.load進(jìn)行載入即可,編寫代碼如下:

#-*-coding:UTF-8-*-importpickle"""函數(shù)說明:讀取決策樹Parameters:filename-決策樹的存儲(chǔ)文件名Returns:pickle.load(fr)-決策樹字典"""defgrabTree(filename):fr=open(filename,'rb')returnpickle.load(fr)if__name__=='__main__':myTree=grabTree('classifierStorage.txt')print(myTree)

如果在該P(yáng)ython文件的相同目錄下,有一個(gè)名為classifierStorage.txt的文件,那么我們就可以運(yùn)行上述代碼,運(yùn)行結(jié)果如下圖所示:

從上述結(jié)果中,我們可以看到,我們順利加載了存儲(chǔ)決策樹的二進(jìn)制文件。

四、Sklearn之使用決策樹預(yù)測隱形眼睛類型

1、實(shí)戰(zhàn)背景

進(jìn)入本文的正題:眼科醫(yī)生是如何判斷患者需要佩戴隱形眼鏡的類型的?一旦理解了決策樹的工作原理,我們甚至也可以幫助人們判斷需要佩戴的鏡片類型。

隱形眼鏡數(shù)據(jù)集是非常著名的數(shù)據(jù)集,它包含很多換著眼部狀態(tài)的觀察條件以及醫(yī)生推薦的隱形眼鏡類型。隱形眼鏡類型包括硬材質(zhì)(hard)、軟材質(zhì)(soft)以及不適合佩戴隱形眼鏡(no lenses)。數(shù)據(jù)來源與UCI數(shù)據(jù)庫,數(shù)據(jù)集下載地址:點(diǎn)擊進(jìn)入鏈接

一共有24組數(shù)據(jù),數(shù)據(jù)的Labels依次是age、prescript、astigmatic、tearRate、class,也就是第一列是年齡,第二列是癥狀,第三列是是否散光,第四列是眼淚數(shù)量,第五列是最終的分類標(biāo)簽。數(shù)據(jù)如下圖所示:

可以使用已經(jīng)寫好的Python程序構(gòu)建決策樹,不過出于繼續(xù)學(xué)習(xí)的目的,本文使用Sklearn實(shí)現(xiàn)。

2、使用Sklearn構(gòu)建決策樹

官方英文文檔地址:

http://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html

sklearn.tree模塊提供了決策樹模型,用于解決分類問題和回歸問題。方法如下圖所示:

本次實(shí)戰(zhàn)內(nèi)容使用的是DecisionTreeClassifier和export_graphviz,前者用于決策樹構(gòu)建,后者用于決策樹可視化。

DecisionTreeClassifier構(gòu)建決策樹:

讓我們先看下DecisionTreeClassifier這個(gè)函數(shù),一共有12個(gè)參數(shù):

參數(shù)說明如下:

criterion:特征選擇標(biāo)準(zhǔn),可選參數(shù),默認(rèn)是gini,可以設(shè)置為entropy。gini是基尼不純度,是將來自集合的某種結(jié)果隨機(jī)應(yīng)用于某一數(shù)據(jù)項(xiàng)的預(yù)期誤差率,是一種基于統(tǒng)計(jì)的思想。entropy是香農(nóng)熵,也就是上篇文章講過的內(nèi)容,是一種基于信息論的思想。Sklearn把gini設(shè)為默認(rèn)參數(shù),應(yīng)該也是做了相應(yīng)的斟酌的,精度也許更高些?ID3算法使用的是entropy,CART算法使用的則是gini。

splitter:特征劃分點(diǎn)選擇標(biāo)準(zhǔn),可選參數(shù),默認(rèn)是best,可以設(shè)置為random。每個(gè)結(jié)點(diǎn)的選擇策略。best參數(shù)是根據(jù)算法選擇最佳的切分特征,例如gini、entropy。random隨機(jī)的在部分劃分點(diǎn)中找局部最優(yōu)的劃分點(diǎn)。默認(rèn)的”best”適合樣本量不大的時(shí)候,而如果樣本數(shù)據(jù)量非常大,此時(shí)決策樹構(gòu)建推薦”random”。

max_features:劃分時(shí)考慮的最大特征數(shù),可選參數(shù),默認(rèn)是None。尋找最佳切分時(shí)考慮的最大特征數(shù)(n_features為總共的特征數(shù)),有如下6種情況:

如果max_features是整型的數(shù),則考慮max_features個(gè)特征;

如果max_features是浮點(diǎn)型的數(shù),則考慮int(max_features * n_features)個(gè)特征;

如果max_features設(shè)為auto,那么max_features = sqrt(n_features);

如果max_features設(shè)為sqrt,那么max_featrues = sqrt(n_features),跟auto一樣;

如果max_features設(shè)為log2,那么max_features = log2(n_features);

如果max_features設(shè)為None,那么max_features = n_features,也就是所有特征都用。

一般來說,如果樣本特征數(shù)不多,比如小于50,我們用默認(rèn)的”None”就可以了,如果特征數(shù)非常多,我們可以靈活使用剛才描述的其他取值來控制劃分時(shí)考慮的最大特征數(shù),以控制決策樹的生成時(shí)間。

max_depth:決策樹最大深,可選參數(shù),默認(rèn)是None。這個(gè)參數(shù)是這是樹的層數(shù)的。層數(shù)的概念就是,比如在貸款的例子中,決策樹的層數(shù)是2層。如果這個(gè)參數(shù)設(shè)置為None,那么決策樹在建立子樹的時(shí)候不會(huì)限制子樹的深度。一般來說,數(shù)據(jù)少或者特征少的時(shí)候可以不管這個(gè)值。或者如果設(shè)置了min_samples_slipt參數(shù),那么直到少于- - - min_smaples_split個(gè)樣本為止。如果模型樣本量多,特征也多的情況下,推薦限制這個(gè)最大深度,具體的取值取決于數(shù)據(jù)的分布。常用的可以取值10-100之間。

min_samples_split:內(nèi)部節(jié)點(diǎn)再劃分所需最小樣本數(shù),可選參數(shù),默認(rèn)是2。這個(gè)值限制了子樹繼續(xù)劃分的條件。如果min_samples_split為整數(shù),那么在切分內(nèi)部結(jié)點(diǎn)的時(shí)候,min_samples_split作為最小的樣本數(shù),也就是說,如果樣本已經(jīng)少于min_samples_split個(gè)樣本,則停止繼續(xù)切分。如果min_samples_split為浮點(diǎn)數(shù),那么min_samples_split就是一個(gè)百分比,ceil(min_samples_split * n_samples),數(shù)是向上取整的。如果樣本量不大,不需要管這個(gè)值。如果樣本量數(shù)量級(jí)非常大,則推薦增大這個(gè)值。

min_samples_leaf:葉子節(jié)點(diǎn)最少樣本數(shù),可選參數(shù),默認(rèn)是1。這個(gè)值限制了葉子節(jié)點(diǎn)最少的樣本數(shù),如果某葉子節(jié)點(diǎn)數(shù)目小于樣本數(shù),則會(huì)和兄弟節(jié)點(diǎn)一起被剪枝。葉結(jié)點(diǎn)需要最少的樣本數(shù),也就是最后到葉結(jié)點(diǎn),需要多少個(gè)樣本才能算一個(gè)葉結(jié)點(diǎn)。如果設(shè)置為1,哪怕這個(gè)類別只有1個(gè)樣本,決策樹也會(huì)構(gòu)建出來。如果min_samples_leaf是整數(shù),那么min_samples_leaf作為最小的樣本數(shù)。如果是浮點(diǎn)數(shù),那么min_samples_leaf就是一個(gè)百分比,同上,celi(min_samples_leaf * n_samples),數(shù)是向上取整的。如果樣本量不大,不需要管這個(gè)值。如果樣本量數(shù)量級(jí)非常大,則推薦增大這個(gè)值。

min_weight_fraction_leaf:葉子節(jié)點(diǎn)最小的樣本權(quán)重和,可選參數(shù),默認(rèn)是0。這個(gè)值限制了葉子節(jié)點(diǎn)所有樣本權(quán)重和的最小值,如果小于這個(gè)值,則會(huì)和兄弟節(jié)點(diǎn)一起被剪枝。一般來說,如果我們有較多樣本有缺失值,或者分類樹樣本的分布類別偏差很大,就會(huì)引入樣本權(quán)重,這時(shí)我們就要注意這個(gè)值了。

max_leaf_nodes:最大葉子節(jié)點(diǎn)數(shù),可選參數(shù),默認(rèn)是None。通過限制最大葉子節(jié)點(diǎn)數(shù),可以防止過擬合。如果加了限制,算法會(huì)建立在最大葉子節(jié)點(diǎn)數(shù)內(nèi)最優(yōu)的決策樹。如果特征不多,可以不考慮這個(gè)值,但是如果特征分成多的話,可以加以限制,具體的值可以通過交叉驗(yàn)證得到。

class_weight:類別權(quán)重,可選參數(shù),默認(rèn)是None,也可以字典、字典列表、balanced。指定樣本各類別的的權(quán)重,主要是為了防止訓(xùn)練集某些類別的樣本過多,導(dǎo)致訓(xùn)練的決策樹過于偏向這些類別。類別的權(quán)重可以通過{class_label:weight}這樣的格式給出,這里可以自己指定各個(gè)樣本的權(quán)重,或者用balanced,如果使用balanced,則算法會(huì)自己計(jì)算權(quán)重,樣本量少的類別所對應(yīng)的樣本權(quán)重會(huì)高。當(dāng)然,如果你的樣本類別分布沒有明顯的偏倚,則可以不管這個(gè)參數(shù),選擇默認(rèn)的None。

random_state:可選參數(shù),默認(rèn)是None。隨機(jī)數(shù)種子。如果是證書,那么random_state會(huì)作為隨機(jī)數(shù)生成器的隨機(jī)數(shù)種子。隨機(jī)數(shù)種子,如果沒有設(shè)置隨機(jī)數(shù),隨機(jī)出來的數(shù)與當(dāng)前系統(tǒng)時(shí)間有關(guān),每個(gè)時(shí)刻都是不同的。如果設(shè)置了隨機(jī)數(shù)種子,那么相同隨機(jī)數(shù)種子,不同時(shí)刻產(chǎn)生的隨機(jī)數(shù)也是相同的。如果是RandomState instance,那么random_state是隨機(jī)數(shù)生成器。如果為None,則隨機(jī)數(shù)生成器使用np.random。

min_impurity_split:節(jié)點(diǎn)劃分最小不純度,可選參數(shù),默認(rèn)是1e-7。這是個(gè)閾值,這個(gè)值限制了決策樹的增長,如果某節(jié)點(diǎn)的不純度(基尼系數(shù),信息增益,均方差,絕對差)小于這個(gè)閾值,則該節(jié)點(diǎn)不再生成子節(jié)點(diǎn)。即為葉子節(jié)點(diǎn) 。

presort:數(shù)據(jù)是否預(yù)排序,可選參數(shù),默認(rèn)為False,這個(gè)值是布爾值,默認(rèn)是False不排序。一般來說,如果樣本量少或者限制了一個(gè)深度很小的決策樹,設(shè)置為true可以讓劃分點(diǎn)選擇更加快,決策樹建立的更加快。如果樣本量太大的話,反而沒有什么好處。問題是樣本量少的時(shí)候,我速度本來就不慢。所以這個(gè)值一般懶得理它就可以了。

除了這些參數(shù)要注意以外,其他在調(diào)參時(shí)的注意點(diǎn)有:

當(dāng)樣本數(shù)量少但是樣本特征非常多的時(shí)候,決策樹很容易過擬合,一般來說,樣本數(shù)比特征數(shù)多一些會(huì)比較容易建立健壯的模型

如果樣本數(shù)量少但是樣本特征非常多,在擬合決策樹模型前,推薦先做維度規(guī)約,比如主成分分析(PCA),特征選擇(Losso)或者獨(dú)立成分分析(ICA)。這樣特征的維度會(huì)大大減小。再來擬合決策樹模型效果會(huì)好。

推薦多用決策樹的可視化,同時(shí)先限制決策樹的深度,這樣可以先觀察下生成的決策樹里數(shù)據(jù)的初步擬合情況,然后再?zèng)Q定是否要增加深度。

在訓(xùn)練模型時(shí),注意觀察樣本的類別情況(主要指分類樹),如果類別分布非常不均勻,就要考慮用class_weight來限制模型過于偏向樣本多的類別。

決策樹的數(shù)組使用的是numpy的float32類型,如果訓(xùn)練數(shù)據(jù)不是這樣的格式,算法會(huì)先做copy再運(yùn)行。

如果輸入的樣本矩陣是稀疏的,推薦在擬合前調(diào)用csc_matrix稀疏化,在預(yù)測前調(diào)用csr_matrix稀疏化。

sklearn.tree.DecisionTreeClassifier()提供了一些方法供我們使用,如下圖所示:

了解到這些,我們就可以編寫代碼了。

注意一點(diǎn),由于fit()函數(shù)不能接收string類型的數(shù)據(jù),通過打印的信息可以看到,數(shù)據(jù)都是string類型的。在使用fit()函數(shù)之前,我們需要對數(shù)據(jù)集進(jìn)行編碼,這里可以使用兩種方法:

LabelEncoder :將字符串轉(zhuǎn)換為增量值

OneHotEncoder:使用One-of-K算法將字符串轉(zhuǎn)換為整數(shù)

為了對string類型的數(shù)據(jù)序列化,需要先生成pandas數(shù)據(jù),這樣方便我們的序列化工作。這里我使用的方法是,原始數(shù)據(jù)->字典->pandas數(shù)據(jù),編寫代碼如下:

importpandasaspdif__name__=='__main__':withopen('lenses.txt','r')asfr:#加載文件lenses=[inst.strip().split(' ')forinstinfr.readlines()]#處理文件lenses_target=[]#提取每組數(shù)據(jù)的類別,保存在列表里foreachinlenses:lenses_target.append(each[-1])lensesLabels=['age','prescript','astigmatic','tearRate']#特征標(biāo)簽lenses_list=[]#保存lenses數(shù)據(jù)的臨時(shí)列表lenses_dict={}#保存lenses數(shù)據(jù)的字典,用于生成pandasforeach_labelinlensesLabels:#提取信息,生成字典foreachinlenses:lenses_list.append(each[lensesLabels.index(each_label)])lenses_dict[each_label]=lenses_listlenses_list=[]print(lenses_dict)#打印字典信息lenses_pd=pd.DataFrame(lenses_dict)#生成pandas.DataFrameprint(lenses_pd)

從運(yùn)行結(jié)果可以看出,順利生成pandas數(shù)據(jù)。

接下來,將數(shù)據(jù)序列化,編寫代碼如下:

#-*-coding:UTF-8-*-importpandasaspdfromsklearn.preprocessingimportLabelEncoderimportpydotplusfromsklearn.externals.siximportStringIOif__name__=='__main__':withopen('lenses.txt','r')asfr:#加載文件lenses=[inst.strip().split(' ')forinstinfr.readlines()]#處理文件lenses_target=[]#提取每組數(shù)據(jù)的類別,保存在列表里foreachinlenses:lenses_target.append(each[-1])lensesLabels=['age','prescript','astigmatic','tearRate']#特征標(biāo)簽lenses_list=[]#保存lenses數(shù)據(jù)的臨時(shí)列表lenses_dict={}#保存lenses數(shù)據(jù)的字典,用于生成pandasforeach_labelinlensesLabels:#提取信息,生成字典foreachinlenses:lenses_list.append(each[lensesLabels.index(each_label)])lenses_dict[each_label]=lenses_listlenses_list=[]#print(lenses_dict)#打印字典信息lenses_pd=pd.DataFrame(lenses_dict)#生成pandas.DataFrameprint(lenses_pd)#打印pandas.DataFramele=LabelEncoder()#創(chuàng)建LabelEncoder()對象,用于序列化forcolinlenses_pd.columns:#為每一列序列化lenses_pd[col]=le.fit_transform(lenses_pd[col])print(lenses_pd)

從打印結(jié)果可以看到,我們已經(jīng)將數(shù)據(jù)順利序列化,接下來。我們就可以fit()數(shù)據(jù),構(gòu)建決策樹了。

3、使用Graphviz可視化決策樹

Graphviz的是AT&T Labs Research開發(fā)的圖形繪制工具,他可以很方便的用來繪制結(jié)構(gòu)化的圖形網(wǎng)絡(luò),支持多種格式輸出,生成圖片的質(zhì)量和速度都不錯(cuò)。它的輸入是一個(gè)用dot語言編寫的繪圖腳本,通過對輸入腳本的解析,分析出其中的點(diǎn),邊以及子圖,然后根據(jù)屬性進(jìn)行繪制。是使用Sklearn生成的決策樹就是dot格式的,因此我們可以直接利用Graphviz將決策樹可視化。

在講解編寫代碼之前,我們需要安裝兩樣?xùn)|西,即pydotplus和Grphviz。

(1)安裝Pydotplus

pydotplus可以在CMD窗口中,直接使用指令安裝:

pipinstallpydotplus

(2)安裝Graphviz

Graphviz不能使用pip進(jìn)行安裝,我們需要手動(dòng)安裝,下載地址:http://www.graphviz.org/Home.php

下載好安裝包,進(jìn)行安裝,安裝完畢之后,需要設(shè)置Graphviz的環(huán)境變量。

首先,按快捷鍵win+r,在出現(xiàn)的運(yùn)行對話框中輸入sysdm.cpl,點(diǎn)擊確定,出現(xiàn)如下對話框:

選擇高級(jí)->環(huán)境變量。在系統(tǒng)變量的Path變量中,添加Graphviz的環(huán)境變量,比如Graphviz安裝在了D盤的根目錄,則添加:D:Graphvizin;

添加好環(huán)境變量之后,我們就可以正常使用Graphviz了。

(3)編寫代碼

代碼如下,可視化部分的代碼不難,都是有套路的,直接填參數(shù)就好,詳細(xì)內(nèi)容可以查看官方教程:http://scikit-learn.org/stable/modules/tree.html#tree

運(yùn)行代碼,在該python文件保存的相同目錄下,會(huì)生成一個(gè)名為tree的PDF文件,打開文件,我們就可以看到?jīng)Q策樹的可視化效果圖了。

確定好決策樹之后,我們就可以做預(yù)測了??梢愿鶕?jù)自己的眼睛情況和年齡等特征,看一看自己適合何種材質(zhì)的隱形眼鏡。使用如下代碼就可以看到預(yù)測結(jié)果:

print(clf.predict([[1,1,1,0]]))#預(yù)測

代碼簡單,官方手冊都有,就不全貼出來了。

五、總結(jié)

決策樹的一些優(yōu)點(diǎn):

易于理解和解釋。決策樹可以可視化。

幾乎不需要數(shù)據(jù)預(yù)處理。其他方法經(jīng)常需要數(shù)據(jù)標(biāo)準(zhǔn)化,創(chuàng)建虛擬變量和刪除缺失值。決策樹還不支持缺失值。

使用樹的花費(fèi)(例如預(yù)測數(shù)據(jù))是訓(xùn)練數(shù)據(jù)點(diǎn)(data points)數(shù)量的對數(shù)。

可以同時(shí)處理數(shù)值變量和分類變量。其他方法大都適用于分析一種變量的集合。

可以處理多值輸出變量問題。

使用白盒模型。如果一個(gè)情況被觀察到,使用邏輯判斷容易表示這種規(guī)則。相反,如果是黑盒模型(例如人工神經(jīng)網(wǎng)絡(luò)),結(jié)果會(huì)非常難解釋。

即使對真實(shí)模型來說,假設(shè)無效的情況下,也可以較好的適用。

決策樹的一些缺點(diǎn):

決策樹學(xué)習(xí)可能創(chuàng)建一個(gè)過于復(fù)雜的樹,并不能很好的預(yù)測數(shù)據(jù)。也就是過擬合。修剪機(jī)制(現(xiàn)在不支持),設(shè)置一個(gè)葉子節(jié)點(diǎn)需要的最小樣本數(shù)量,或者數(shù)的最大深度,可以避免過擬合。

決策樹可能是不穩(wěn)定的,因?yàn)榧词狗浅P〉淖儺?,可能?huì)產(chǎn)生一顆完全不同的樹。這個(gè)問題通過decision trees with an ensemble來緩解。

概念難以學(xué)習(xí),因?yàn)闆Q策樹沒有很好的解釋他們,例如,XOR, parity or multiplexer problems。

如果某些分類占優(yōu)勢,決策樹將會(huì)創(chuàng)建一棵有偏差的樹。因此,建議在訓(xùn)練之前,先抽樣使樣本均衡。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 機(jī)器學(xué)習(xí)

    關(guān)注

    66

    文章

    8418

    瀏覽量

    132630
  • 決策樹
    +關(guān)注

    關(guān)注

    3

    文章

    96

    瀏覽量

    13551

原文標(biāo)題:機(jī)器學(xué)習(xí)決策樹算法實(shí)戰(zhàn)——理論+詳細(xì)的Python3代碼實(shí)現(xiàn)

文章出處:【微信號(hào):rgznai100,微信公眾號(hào):rgznai100】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    關(guān)于決策樹,這些知識(shí)點(diǎn)不可錯(cuò)過

    `隨著科學(xué)技術(shù)的發(fā)展,AI愛好者越來越多,除了一些精通AI的大神,還有很多的技術(shù)小白也對這方面感興趣,他們想學(xué)習(xí)一些機(jī)器學(xué)習(xí)的入門知識(shí)。今天,訊飛開放平臺(tái)就帶來機(jī)器
    發(fā)表于 05-23 09:38

    決策樹機(jī)器學(xué)習(xí)的理論學(xué)習(xí)與實(shí)踐

    決策樹機(jī)器學(xué)習(xí)的理論學(xué)習(xí)與實(shí)踐
    發(fā)表于 09-20 12:48

    分類與回歸方法之決策樹

    統(tǒng)計(jì)學(xué)習(xí)方法決策樹
    發(fā)表于 11-05 13:40

    機(jī)器學(xué)習(xí)決策樹介紹

    機(jī)器學(xué)習(xí)——決策樹算法分析
    發(fā)表于 04-02 11:48

    ML之決策樹與隨機(jī)森林

    ML--決策樹與隨機(jī)森林
    發(fā)表于 07-08 12:31

    決策樹的生成資料

    在本文中,我們將討論一種監(jiān)督式學(xué)習(xí)算法。最新一代意法半導(dǎo)體 MEMS 傳感器內(nèi)置一個(gè)基于決策樹分類器的機(jī)器學(xué)習(xí)核心(MLC)。這些產(chǎn)品很容易通過后綴中的 X 來識(shí)別(例如,LSM6DS
    發(fā)表于 09-08 06:50

    決策樹的介紹

    關(guān)于決策樹的介紹,是一些很基礎(chǔ)的介紹,不過是英文介紹。
    發(fā)表于 09-18 14:55 ?0次下載

    決策樹構(gòu)建設(shè)計(jì)并用Graphviz實(shí)現(xiàn)決策樹的可視化

    最近打算系統(tǒng)學(xué)習(xí)機(jī)器學(xué)習(xí)的基礎(chǔ)算法,避免眼高手低,決定把常用的機(jī)器學(xué)習(xí)基礎(chǔ)算法都實(shí)現(xiàn)一遍以便加深印象。本文為這系列博客的第一篇,關(guān)于
    發(fā)表于 11-15 13:10 ?1.5w次閱讀
    <b class='flag-5'>決策樹</b>的<b class='flag-5'>構(gòu)建</b>設(shè)計(jì)并用Graphviz實(shí)現(xiàn)<b class='flag-5'>決策樹</b>的可視化

    機(jī)器學(xué)習(xí)決策樹--python

    今天,我們介紹機(jī)器學(xué)習(xí)里比較常用的一種分類算法,決策樹決策樹是對人類認(rèn)知識(shí)別的一種模擬,給你一堆看似雜亂無章的數(shù)據(jù),如何用盡可能少的特征,對這些數(shù)據(jù)進(jìn)行有效的分類。
    發(fā)表于 11-16 01:50 ?1622次閱讀

    決策樹和隨機(jī)森林模型

    我們知道決策樹容易過擬合。換句話說,單個(gè)決策樹可以很好地找到特定問題的解決方案,但如果應(yīng)用于以前從未見過的問題則非常糟糕。俗話說三個(gè)臭皮匠賽過諸葛亮,隨機(jī)森林就利用了多個(gè)決策樹,來應(yīng)對多種不同場景。
    的頭像 發(fā)表于 04-19 14:38 ?7993次閱讀
    <b class='flag-5'>決策樹</b>和隨機(jī)森林模型

    決策樹的構(gòu)成要素及算法

    決策樹是一種解決分類問題的算法,決策樹算法采用樹形結(jié)構(gòu),使用層層推理來實(shí)現(xiàn)最終的分類。
    發(fā)表于 08-27 09:52 ?4370次閱讀

    決策樹的基本概念/學(xué)習(xí)步驟/算法/優(yōu)缺點(diǎn)

    本文將介紹決策樹的基本概念、決策樹學(xué)習(xí)的3個(gè)步驟、3種典型的決策樹算法、決策樹的10個(gè)優(yōu)缺點(diǎn)。
    發(fā)表于 01-27 10:03 ?2648次閱讀
    <b class='flag-5'>決策樹</b>的基本概念/<b class='flag-5'>學(xué)習(xí)</b>步驟/算法/優(yōu)缺點(diǎn)

    什么是決策樹模型,決策樹模型的繪制方法

    決策樹是一種解決分類問題的算法,本文將介紹什么是決策樹模型,常見的用途,以及如何使用“億圖圖示”軟件繪制決策樹模型。
    發(fā)表于 02-18 10:12 ?1.3w次閱讀
    什么是<b class='flag-5'>決策樹</b>模型,<b class='flag-5'>決策樹</b>模型的繪制方法

    決策樹的結(jié)構(gòu)/優(yōu)缺點(diǎn)/生成

    決策樹(DecisionTree)是機(jī)器學(xué)習(xí)中一種常見的算法,它的思想非常樸素,就像我們平時(shí)利用選擇做決策的過程。決策樹是一種基本的分類與回
    發(fā)表于 03-04 10:11 ?8330次閱讀

    大數(shù)據(jù)—決策樹

    大數(shù)據(jù)————決策樹(decision tree) 決策樹(decision tree):是一種基本的分類與回歸方法,主要討論分類的決策樹。 在分類問題中,表示基于特征對實(shí)例進(jìn)行分類的過程,可以
    的頭像 發(fā)表于 10-20 10:01 ?1215次閱讀