决策树归纳
分類(lèi)與監(jiān)督學(xué)習(xí)
現(xiàn)實(shí)中,我們經(jīng)常會(huì)遇到這樣的問(wèn)題:銀行收到用戶的信用卡申請(qǐng)表。當(dāng)然,這是一張帶有用戶豐富信息的申請(qǐng)表,比如年齡,學(xué)歷,收入,信用記錄等等。那么銀行的工作人員如何根據(jù)這些信息判別這個(gè)用戶是否是誠(chéng)信的,是否應(yīng)該通過(guò)他的信用卡申請(qǐng)呢?人工的判斷顯然耗時(shí)耗力,且不一定準(zhǔn)確,比較靠譜的辦法是通過(guò)已有的,大量用戶的使用記錄,分析得到一個(gè)模型(或一個(gè)方程,一種工具),利用這個(gè)模型,可以判別出大量用戶屬性與用戶是否誠(chéng)信的關(guān)聯(lián)關(guān)系。從而用一種科學(xué)的計(jì)算方式,得到一個(gè)相對(duì)準(zhǔn)確的判斷。而獲得這種模型的方法,就是分類(lèi)問(wèn)題的核心。
形式化的,分類(lèi)是這樣一種數(shù)據(jù)分析的形式:現(xiàn)在有大量的數(shù)據(jù)對(duì)象,構(gòu)成了一個(gè)數(shù)據(jù)集,數(shù)據(jù)集用D={obj1,obj2,…,objn}D={obj1,obj2,…,objn}來(lái)表示,每個(gè)數(shù)據(jù)對(duì)象objiobji都是由1個(gè)類(lèi)標(biāo)號(hào)和多個(gè)屬性構(gòu)成的。比如上面說(shuō)的信用卡的例子中,每個(gè)已經(jīng)申請(qǐng)過(guò)信用卡的用戶就是一個(gè)數(shù)據(jù)對(duì)象,他們的年齡,學(xué)歷,收入等等就是他們的屬性,而他們對(duì)于信用卡的使用情況(也就是是否誠(chéng)信)構(gòu)成了他們的類(lèi)標(biāo)號(hào)“誠(chéng)信”或者“不誠(chéng)信”。也就是說(shuō),分類(lèi)實(shí)際上由兩個(gè)過(guò)程組成:
我們工作的核心,在于使用某種算法,通過(guò)大量的訓(xùn)練集,“學(xué)習(xí)”出一種分類(lèi)器。之后,通過(guò)分類(lèi)器,對(duì)新的未分類(lèi)的數(shù)據(jù)做分類(lèi)預(yù)測(cè)。這里插一句閑話,預(yù)測(cè)問(wèn)題大致上分為兩類(lèi):
- 數(shù)值預(yù)測(cè)。比如商店通過(guò)顧客信息預(yù)測(cè)他大概能消費(fèi)多少錢(qián),這涉及到了具體的數(shù)值,常見(jiàn)的方法有“回歸分析”。
- 分類(lèi)。常見(jiàn)的方法有今天要說(shuō)的決策樹(shù),樸素貝葉斯,支持向量機(jī)等等
信用卡的例子中,一旦完成了這種分類(lèi)器的學(xué)習(xí),就可以根據(jù)新用戶的屬性處理新的用戶申請(qǐng),做出這個(gè)用戶可能“誠(chéng)信”或“不誠(chéng)信”的判別。可見(jiàn),分類(lèi)需要給出一定量的有類(lèi)標(biāo)號(hào)的訓(xùn)練集,這不同于之前我曾經(jīng)提到的聚類(lèi)(詳見(jiàn):聚類(lèi)分析: k-means算法)。聚類(lèi)沒(méi)有類(lèi)標(biāo)號(hào),完全是只有屬性的原始數(shù)據(jù),甚至連分成幾類(lèi)也是不確定的。而分類(lèi)的過(guò)程則似乎更“靠譜”一些。這種先給出訓(xùn)練集的學(xué)習(xí)模式,在機(jī)器學(xué)習(xí)中也叫“監(jiān)督學(xué)習(xí)”,它與聚類(lèi)所代表的“無(wú)監(jiān)督學(xué)習(xí)”有著本質(zhì)區(qū)別。
決策樹(shù)的結(jié)構(gòu)
本篇博客中,我將介紹分類(lèi)中最基礎(chǔ),也是最簡(jiǎn)單的學(xué)習(xí)過(guò)程:決策樹(shù)(Decision Tree)歸納。它從有類(lèi)標(biāo)號(hào)的訓(xùn)練元組中學(xué)習(xí)得到?jīng)Q策樹(shù)。決策樹(shù)是一種樹(shù)形結(jié)構(gòu),一個(gè)典型的決策樹(shù)如下圖所示,這張圖也是《數(shù)據(jù)挖掘》中的一個(gè)例子,根據(jù)顧客信息,對(duì)是否會(huì)”buys_computer”做判別。
下面對(duì)決策樹(shù)做幾點(diǎn)說(shuō)明:
每個(gè)內(nèi)部節(jié)點(diǎn)代表一個(gè)屬性上的測(cè)試,而他的每個(gè)分支代表這種屬性測(cè)試的結(jié)果,比如上圖中,根節(jié)點(diǎn)代表的就是對(duì)”age”這個(gè)屬性的測(cè)試,三個(gè)分支”youth”, “middle_aged”,”senior”,分別代表測(cè)試的結(jié)果
每個(gè)葉子節(jié)點(diǎn)代表一個(gè)類(lèi)標(biāo)號(hào),這里,只有”yes”和”no”兩種,即買(mǎi)計(jì)算機(jī)或者不買(mǎi)
做分類(lèi)預(yù)測(cè)時(shí),將新的數(shù)據(jù)對(duì)象自根節(jié)點(diǎn)向下遍歷決策樹(shù),比如現(xiàn)在有這樣的數(shù)據(jù)對(duì)象:{age: youth; student: yes; …}無(wú)論省略號(hào)所代表的屬性如何,都可以判斷出,這個(gè)用戶買(mǎi)計(jì)算機(jī)的概率很大。
需要注意的是,決策樹(shù)的分類(lèi)大多是對(duì)新的數(shù)據(jù)對(duì)象的類(lèi)做概率性的判斷,當(dāng)然,也有些時(shí)候做的是確定性的判斷,這需要根據(jù)具體問(wèn)題做具體分析。
決策樹(shù)歸納
1. 樹(shù)節(jié)點(diǎn)
當(dāng)前,主流的決策樹(shù)歸納算法有三種:ID3, C4.5, CART,雖然這3種算法在細(xì)節(jié)上有所差異,但主要的思想都是自頂向下,采用貪心方法,遞歸地分治構(gòu)造決策樹(shù)。算法將整個(gè)訓(xùn)練集同時(shí)讀入,隨著構(gòu)造的決策樹(shù)深度的增加,訓(xùn)練集被每個(gè)分支分裂成更多更小的子集,在講解具體的算法之前,先給出對(duì)一個(gè)決策樹(shù)節(jié)點(diǎn)類(lèi)的定義,如下:(注意:本文所有函數(shù)及類(lèi)的定義均用Python實(shí)現(xiàn))
class DecisionTreeNode(object):def __init__(self):# tag is the class nameself.tag = Noneself.isLeaf = True# pointers is a dictionary, {attributeValue: child, ...}self.pointers = {}# the best attribute that used to split datasetself.splitCriterion = None我們?cè)O(shè)定一個(gè)決策樹(shù)節(jié)點(diǎn)應(yīng)該有上面所示的四個(gè)屬性,下面具體說(shuō)一下:
splitCriterion:分裂準(zhǔn)則。即這個(gè)節(jié)點(diǎn)是按照哪個(gè)屬性對(duì)數(shù)據(jù)集劃分的,比如上面圖中根節(jié)點(diǎn)的分裂準(zhǔn)則是”age”。splitCriterion是只有內(nèi)部節(jié)點(diǎn)才擁有的屬性,葉子節(jié)點(diǎn)的splitCriterion設(shè)為None;
tag:葉子節(jié)點(diǎn)所對(duì)應(yīng)的類(lèi)標(biāo)號(hào),比如上圖中,從左至右,葉子節(jié)點(diǎn)的tag值分別為”no”,”yes”,”yes”,”no”,”yes”,所有內(nèi)部節(jié)點(diǎn)的tag值為None
isLeaf:節(jié)點(diǎn)是否為葉子,是葉子,設(shè)為T(mén)rue;不是,設(shè)為False
pointers:內(nèi)部節(jié)點(diǎn)的指針集合。在后面的代碼實(shí)現(xiàn)中,我將pointers以字典的形式表示,這個(gè)字典的鍵值對(duì)為{attribute_value: child, …},表示內(nèi)部節(jié)點(diǎn)的每個(gè)孩子是有分裂準(zhǔn)則下的哪個(gè)屬性值分裂而成的,比如上圖中,根節(jié)點(diǎn)的pointers為{“youth”: 最左的孩子, “middle_aged”: 中間的孩子, “senior”: 最右的孩子}。這樣設(shè)計(jì)的目的是為了決策樹(shù)生成之后,方便于對(duì)新的數(shù)據(jù)分類(lèi)。
2. 學(xué)習(xí)步驟
決策樹(shù)構(gòu)造的步驟如下:
輸入:數(shù)據(jù)集(每個(gè)數(shù)據(jù)對(duì)象都有一個(gè)或多個(gè)屬性);屬性列表(所有出現(xiàn)在數(shù)據(jù)集中的屬性)
輸出:一棵決策樹(shù)
新建一個(gè)決策樹(shù)節(jié)點(diǎn)uu。初始時(shí)生成的節(jié)點(diǎn)就是決策樹(shù)的根節(jié)點(diǎn),根節(jié)點(diǎn)對(duì)應(yīng)的是全體數(shù)據(jù)集。
根據(jù)當(dāng)前節(jié)點(diǎn)所對(duì)應(yīng)的數(shù)據(jù)集,選擇一個(gè)“最好的”屬性作為數(shù)據(jù)集的分裂準(zhǔn)則,按照這個(gè)屬性的不同屬性值(簡(jiǎn)單起見(jiàn),這里我假設(shè)屬性值都是離散的,連續(xù)的情況后面單另說(shuō)),對(duì)數(shù)據(jù)進(jìn)行分割。這里,所謂“最好的”屬性,是指按照這個(gè)屬性分割數(shù)據(jù)集之后,生成的每個(gè)數(shù)據(jù)子集都盡可能地“純”。也就是說(shuō),最好每個(gè)子集都屬于同一類(lèi)(擁有相同的類(lèi)標(biāo)號(hào))。
在屬性列表中,刪除當(dāng)前使用為分裂準(zhǔn)則的屬性。
根據(jù)數(shù)據(jù)分割后的數(shù)據(jù)子集,以及刪除了一個(gè)屬性的屬性列表,遞歸地執(zhí)行決策樹(shù)算法。新生成的決策樹(shù)(實(shí)際上是子樹(shù))的根節(jié)點(diǎn)被uu對(duì)應(yīng)的指針指向。
當(dāng)然,這里面有三種“觸底”生成葉子節(jié)點(diǎn)的情況:
如果此時(shí)對(duì)應(yīng)節(jié)點(diǎn)的數(shù)據(jù)集全部屬于同一類(lèi)C,那么此時(shí)的這個(gè)節(jié)點(diǎn)就是葉子節(jié)點(diǎn),其tag為C;
如果此時(shí)屬性列表為空,那么此時(shí)的這個(gè)節(jié)點(diǎn)就是葉子節(jié)點(diǎn)。然后采用“多數(shù)投票”的方法為這個(gè)葉子節(jié)點(diǎn)選擇tag。即此時(shí)節(jié)點(diǎn)所對(duì)應(yīng)的數(shù)據(jù)集中,擁有最多數(shù)據(jù)對(duì)象的類(lèi)為這個(gè)葉子節(jié)點(diǎn)的tag;
如果此時(shí)對(duì)應(yīng)節(jié)點(diǎn)的數(shù)據(jù)集為空,那么此時(shí)的這個(gè)節(jié)點(diǎn)就是葉子節(jié)點(diǎn)。且采用數(shù)據(jù)分割前(也就是其父親節(jié)點(diǎn))所對(duì)應(yīng)的數(shù)據(jù)集中“多數(shù)投票”的結(jié)果作為其tag;
從上面的過(guò)程可以看出,這是一個(gè)思路非常清晰的遞歸算法。先將全體數(shù)據(jù)集讀入,選擇“最好的”屬性,按照這個(gè)屬性,對(duì)數(shù)據(jù)分割。同時(shí),將已經(jīng)“用過(guò)”的屬性刪除出列表,再對(duì)于每個(gè)數(shù)據(jù)子集和此時(shí)剩余的屬性集合再做類(lèi)似的過(guò)程。最終,遇到上面三個(gè)“觸底”的條件時(shí),形成葉子,結(jié)束這一分支的分裂過(guò)程。
這里插一句閑話,如果讀者屬于Kd-tree構(gòu)建索引的過(guò)程(詳見(jiàn):Kd-tree原理與實(shí)現(xiàn)),會(huì)發(fā)現(xiàn),決策樹(shù)這種分裂的思路和Kd-tree非常類(lèi)似,只不過(guò)用途上就大相近庭了:Kd-tree是為了能實(shí)現(xiàn)對(duì)于多維數(shù)據(jù)庫(kù)的快速查詢,而決策樹(shù),是在學(xué)習(xí)數(shù)據(jù)的特征和類(lèi)別之間的關(guān)系。
在給出實(shí)現(xiàn)代碼之前,先解決一個(gè)棘手的問(wèn)題:怎樣選擇“最好的”屬性作為分裂準(zhǔn)則,讓分裂的結(jié)果盡可能地“純”呢?(其實(shí)就是構(gòu)建一棵平衡或者相對(duì)平衡的決策樹(shù))這樣做的目的有兩個(gè),一來(lái), 相關(guān)文獻(xiàn)表明平衡或者相對(duì)平衡的決策樹(shù)在預(yù)測(cè)分類(lèi)結(jié)果時(shí)會(huì)有更好的效果;二來(lái),顯然平衡的決策樹(shù)無(wú)論是在構(gòu)建還是在構(gòu)建完成后對(duì)于數(shù)據(jù)分類(lèi)的預(yù)測(cè)都更加高效。
目前,有三種比較主流的方法解決這個(gè)問(wèn)題,這三種方法恰好也對(duì)應(yīng)了上面說(shuō)的三種決策樹(shù)歸納的算法:1. 信息增益(ID3);2. 增益率(C4.5);3. 基尼指數(shù)(CART)。本文,我只介紹前兩種方法,至于基尼指數(shù),讀者們可參考《數(shù)據(jù)挖掘》,那里面有著詳細(xì)的介紹。
3. 分裂準(zhǔn)則的選擇
(1)信息增益
信息增益的基本思想來(lái)自于香農(nóng)的信息理論,當(dāng)中有一個(gè)非常重要的公式,就是信息熵的計(jì)算。信息熵表示的是一條消息所含信息量的多少,我這里簡(jiǎn)單說(shuō)說(shuō),比如現(xiàn)在兩條信息:
對(duì)于第一條消息來(lái)說(shuō),其信息量為0,因?yàn)檫@是必然事件,不用說(shuō),我們也知道的;但是對(duì)于事件2來(lái)說(shuō),就有點(diǎn)信息量了,因?yàn)榘l(fā)生的概率只有1/2。概率上的不確定性,才能給信息帶來(lái)了信息量。此外,如果一個(gè)事件有nn個(gè)結(jié)果,發(fā)生這些結(jié)果的概率分布越均勻,關(guān)于這個(gè)事件的信息量也就越大。比如“巴薩踢贏了皇馬”比“巴薩踢贏了北郵校隊(duì)”的信息量要大,因?yàn)榍罢叩母怕矢泳龋欢笳撸瑤缀跏潜厝皇录?/p>
為了定量的刻畫(huà)這種信息量的多少(計(jì)算信息熵),就出現(xiàn)了下面這個(gè)著名的公式:
Info(D)=?∑i=1mpilog2(pi)(1)(1)Info(D)=?∑i=1mpilog2?(pi)
其中,pipi表示出現(xiàn)第ii個(gè)結(jié)果的概率。可以證明,在所有pipi都相等時(shí),Info(D)Info(D)達(dá)到最大值。這個(gè)公式其實(shí)計(jì)算的是一個(gè)平均意義上的信息量(信息期望)。
把信息熵的概念用在決策樹(shù)算法的最佳分裂準(zhǔn)則(屬性)的選擇上,也能發(fā)揮作用。可以這樣理解,如果按照一個(gè)屬性分割數(shù)據(jù)集,那么可以針對(duì)每個(gè)數(shù)據(jù)集都按照信息熵的公式計(jì)算信息量,如果分得越“純”,那顯然每個(gè)數(shù)據(jù)子集的信息熵就越小,當(dāng)然,每個(gè)子集根據(jù)其大小不同,在原數(shù)據(jù)集上占據(jù)的權(quán)重也就不同,我們可以依照下式計(jì)算出,經(jīng)過(guò)屬性AA的分割后,數(shù)據(jù)子集全體的信息熵,記為InfoA(D)InfoA(D):
InfoA(D)=∑i=1v|Di||D|×Info(Dj)(2)(2)InfoA(D)=∑i=1v|Di||D|×Info(Dj)
其中,vv表示屬性值的個(gè)數(shù),比如上面的決策樹(shù)的圖示中,屬性age有3個(gè)屬性值。而|Di||D||Di||D|很明顯在這里表示數(shù)據(jù)子集DjDj的權(quán)重了。
根據(jù)信息論的原理,信息的作用是消除事件的不確定性,決策樹(shù)歸納中,每一次按照屬性對(duì)數(shù)據(jù)集的分割,都相當(dāng)于是我們借助了一些信息,最終到每個(gè)葉子節(jié)點(diǎn)歸為了同一類(lèi),則是完成了對(duì)這種不確定性的徹底消除。所以,InfoA(D)InfoA(D)實(shí)際上表示的是經(jīng)過(guò)屬性AA的劃分后,距離對(duì)數(shù)據(jù)集DD完全分類(lèi)還需要的信息期望。這個(gè)期望越低,則說(shuō)明越接近最終的分類(lèi)結(jié)果。而之前的Info(D)Info(D)也是這個(gè)意思,表示數(shù)據(jù)集還未經(jīng)AA分割時(shí),距離完全分類(lèi)的信息量,這個(gè)量肯定比InfoA(D)InfoA(D)大一些。他們兩個(gè)之間的差值,就叫做“信息增益”,用如下公式計(jì)算得到:
Gain(A)=Info(D)?InfoA(D)(3)(3)Gain(A)=Info(D)?InfoA(D)
顯然,應(yīng)該選擇信息增益最大的屬性作為分裂準(zhǔn)則,這樣,就能使分裂后的數(shù)據(jù)集都盡可能地“純”。下面,我們以《數(shù)據(jù)挖掘》中的例子來(lái)說(shuō)明選擇最佳分裂屬性的計(jì)算過(guò)程,首先我們給出數(shù)據(jù)集的形式:
| 1 | no | high | youth | fair | no |
| 2 | no | high | youth | excellent | no |
| 3 | no | high | middle_aged | fair | yes |
| 4 | no | medium | senior | fair | yes |
| 5 | yes | low | senior | fair | yes |
| 6 | yes | low | senior | excellent | no |
| 7 | yes | low | middle_aged | excellent | yes |
| 8 | no | medium | youth | fair | no |
| 9 | yes | low | youth | fair | yes |
| 10 | yes | medium | senior | fair | yes |
| 11 | yes | medium | youth | excellent | yes |
| 12 | no | medium | middle_aged | excellent | yes |
| 13 | yes | high | middle_aged | fair | yes |
| 14 | no | low | senior | excellent | no |
?
按照上面的公式(1),可以先計(jì)算出整個(gè)數(shù)據(jù)集DD的信息量:Info(D)=0.940Info(D)=0.940
接下來(lái),根據(jù)公式(2),(3)分別計(jì)算以屬性age,income,student,credit_rating分割數(shù)據(jù)集所產(chǎn)生的信息期望,以及信息增益。先看屬性age的:
Infoage(D)=0.694Gain(age)=0.940?0.694=0.246(4)(4)Infoage(D)=0.694Gain(age)=0.940?0.694=0.246
同理,可得:
Gain(income)=0.029Gain(student)=0.151Gain(credit_rating)=0.048(5)(5)Gain(income)=0.029Gain(student)=0.151Gain(credit_rating)=0.048
不難發(fā)現(xiàn),屬性age的信息增益最大,因此,對(duì)于根節(jié)點(diǎn)(原始全體數(shù)據(jù)集)的分割應(yīng)該首先以age為分割屬性。我們就得到了下面這張圖:
這時(shí)候,middle_aged屬性值所對(duì)應(yīng)的數(shù)據(jù)子集都是屬于同一類(lèi),那么根據(jù)上面講的遞歸的“觸底”條件,這個(gè)節(jié)點(diǎn)直接變成葉子,并且擁有類(lèi)標(biāo)記tag = yes。之后,將屬性age從屬性列表中刪除,對(duì)于此時(shí)youth所對(duì)的節(jié)點(diǎn)及數(shù)據(jù)子集再次進(jìn)行類(lèi)似的決策樹(shù)學(xué)習(xí),只不過(guò)用的數(shù)據(jù)集是age屬性為youth的數(shù)據(jù)子集,且屬性列表少了一個(gè)age,對(duì)于senior所對(duì)的節(jié)點(diǎn)及數(shù)據(jù)子集也進(jìn)行這樣的過(guò)程,最終得到的決策樹(shù)如本文最上面的圖所示。
當(dāng)然,上面所展示的方法,都是應(yīng)對(duì)這種最簡(jiǎn)單的離散屬性值的情況,對(duì)于連續(xù)屬性值的處理,會(huì)稍微復(fù)雜一點(diǎn),步驟如下:
將數(shù)據(jù)集中出現(xiàn)的屬性AA的值按遞增序排列,每個(gè)相鄰值得中點(diǎn)看做是可能的分裂點(diǎn)。假設(shè)數(shù)據(jù)集中,屬性AA一共出現(xiàn)了vv個(gè)值,那么需要比較的是這v?1v?1個(gè)分裂點(diǎn)。
比較的方法是計(jì)算InfoADInfoAD,按照分裂點(diǎn)將數(shù)據(jù)集分成兩部分,一部分屬性AA的值大于分裂點(diǎn),另一部分小于或等于分裂點(diǎn)。對(duì)于每個(gè)分裂點(diǎn)都做如此計(jì)算,找到信息增益最大的分裂點(diǎn)。
其實(shí),只要解決了最佳屬性的選擇問(wèn)題,決策樹(shù)歸納算法就算是完成了一大半的工作了。我們?cè)诒疚牡淖詈蠼o出了決策樹(shù)歸納的完整代碼。然而,這種信息增益的方法選擇分裂屬性在某些時(shí)候,也有它的問(wèn)題,所以,雖然早期的ID3算法使用了信息增益技術(shù),但是在隨后更加成熟的C4.5算法中,則采用了“增益率”的方法,具體如下。
(2)增益率
使用“信息增益”的算法會(huì)面臨這樣一個(gè)問(wèn)題,就是有些屬性會(huì)導(dǎo)致一些毫無(wú)意義的數(shù)據(jù)分割。比如上面的例子中,我們將數(shù)據(jù)對(duì)象的屬性”RID”排除在外了,如果沒(méi)有排除,那么”RID”一定會(huì)是第一個(gè)最佳的分裂屬性。他把大小為nn的數(shù)據(jù)集分裂成nn個(gè)數(shù)據(jù)子集,每個(gè)子集都只有一個(gè)對(duì)象,絕對(duì)夠“純”。但是這樣的分割是沒(méi)有什么意義的。所以,也就出現(xiàn)了對(duì)“增益率”的計(jì)算,實(shí)際上,就是將“信息增益”做規(guī)范化處理。
首先計(jì)算分裂后的數(shù)據(jù)子集的信息量,這實(shí)際上跟計(jì)算信息熵的公式是一樣的:
SplitInfoA(D)=?∑j=1v|Di||D|×log2(|Di||D|)(38)(38)SplitInfoA(D)=?∑j=1v|Di||D|×log2?(|Di||D|)
上式的計(jì)算結(jié)果可以當(dāng)做歸一化因子處理信息增益,也就得到了增益率:
GainRate(A)=Gain(A)SplitInfoA(D)(7)(7)GainRate(A)=Gain(A)SplitInfoA(D)
實(shí)際上就是一個(gè)簡(jiǎn)單的比率計(jì)算,但是卻克服了之前按”RID”劃分時(shí)產(chǎn)生的問(wèn)題。
程序設(shè)計(jì)
1. 數(shù)據(jù)類(lèi)型
首先,設(shè)計(jì)一下讀入的數(shù)據(jù)形式。我用的是Python中的字典。將上面表格中所示的數(shù)據(jù)對(duì)象讀成一個(gè)字典,再將所有的字典構(gòu)成一個(gè)元組,這個(gè)元組的所有元素如下:
{'RID': '1', 'student': 'no', 'class': 'no', 'income': 'high', 'age': 'youth', 'credit_rating': 'fair'}, {'RID': '2', 'student': 'no', 'class': 'no', 'income': 'high', 'age': 'youth', 'credit_rating': 'excellent'}, {'RID': '3', 'student': 'no', 'class': 'yes', 'income': 'high', 'age': 'middle_aged', 'credit_rating': 'fair'}, ...我不寫(xiě)全了,總之,每個(gè)數(shù)據(jù)對(duì)象被讀成一個(gè)字典,數(shù)據(jù)的每個(gè)特征(包括RID和class,這兩個(gè)不算數(shù)據(jù)屬性)都作為鍵,其對(duì)應(yīng)的特征值作為鍵對(duì)應(yīng)的值。
2. 分裂準(zhǔn)則(屬性)選擇
現(xiàn)在,根據(jù)前面說(shuō)的信息增益的原理,我們將分裂屬性選擇的過(guò)程(select Attribute)的代碼表示如下(非主要函數(shù)只是給出漢語(yǔ)的功能說(shuō)明):
def classCount(data):對(duì)數(shù)據(jù)集data中的數(shù)據(jù)對(duì)象做類(lèi)別統(tǒng)計(jì),得到一個(gè)字典{class_name: number, ...}def getAttributeList(data):得到數(shù)據(jù)集data的所有屬性的列表def info(data):"""calculate the entropy of dataset:param data: dataset that contains data objects, is a tuple: (obj_1, ..., obj_n):return: the entropy of dataset"""classRecord = classCount(data)n = len(data)result = 0for classTag in classRecord:pi = classRecord[classTag] / nresult += (-pi * math.log(pi, 2))return resultdef infoForAttribute(dataSize, attributeValue_subset):"""calculate the expectation of information if we split data with the attribute:param dataSize: the number of objects in dataset:param attributeValue_subset: a dictionary, as form as {attributeValue_1: subset_1,... }:return: the sum of weight x entropy"""result = 0for subset in attributeValue_subset.values():weight = len(subset) / dataSizeresult += weight * info(subset)return resultdef dataPartition(data, attribute):"""Partitioning the data according to an attribute:param data: dataset that contains data objects, is a tuple: (obj_1, ..., obj_n):param attribute: an attribute:return: a dictionary attributeValue_subset, as form as {attributeValue_1: subset_1,... }"""attributeValue_subset = {}for obj in data:attributeValue = obj[attribute]if attributeValue not in attributeValue_subset:attributeValue_subset[attributeValue] = [obj]else:attributeValue_subset[attributeValue].append(obj)for key in attributeValue_subset:attributeValue_subset[key] = tuple(attributeValue_subset[key])return attributeValue_subsetdef selectAttribute(data, attributeList):""":param data::param attributeList::return: the attribute that denotes the split criterion"""attributeValue_subset = dataPartition(data, attributeList[0])dataSize = len(data)maxGain = info(data) - infoForAttribute(dataSize, attributeValue_subset)result = attributeList[0]for attribute in attributeList[1:]:attributeValue_subset = dataPartition(data, attribute)temp = info(data) - infoForAttribute(dataSize, attributeValue_subset)if temp > maxGain:maxGain = tempresult = attributereturn result3. 決策樹(shù)構(gòu)建
到此,解決了屬性選擇的問(wèn)題,那就可以著手完成整個(gè)決策樹(shù)的構(gòu)建了,代碼如下,同樣的,我省略了有些簡(jiǎn)單函數(shù)的代碼,只是給出漢語(yǔ)的功能說(shuō)明。具體詳細(xì)的決策樹(shù)代碼請(qǐng)參考我的github主頁(yè):Decision_Tree
def isSameClass(data):判斷data中的數(shù)據(jù)對(duì)象是否屬于同一類(lèi)def majorityVoting(data):多數(shù)投票。返回data中,對(duì)象數(shù)量最多的類(lèi)def genDecisionTree(data, attributeList):""":param data: dataset that contains data objects, is a tuple: (obj_1, ..., obj_n):param attributeList: a list that contains all attributes occurred in data:return: the root of decision tree"""root = DecisionTreeNode()if isSameClass(data):root.tag = data[0]["class"]return root# majority votingif len(attributeList) == 0:root.tag = majorityVoting(data)return root# find the split criterion of rootroot.splitCriterion = selectAttribute(data, attributeList)root.isLeaf = False# Partitioning the data for several blocks# attributeValue_subset: a dictionary, {attributeValue: (data_object_1, data_object_2...)},# where data_object_i is also a dictionary: {attribute_1: value, attribute_2: value...}attributeValue_subset = dataPartition(data, root.splitCriterion)attributeList.remove(root.splitCriterion)for attributeValue in attributeValue_subset:child = DecisionTreeNode()subset = attributeValue_subset[attributeValue]if len(subset) == 0:child.tag = majorityVoting(data)else:attributeList_ForThisChild = copy.deepcopy(attributeList)child = genDecisionTree(subset, attributeList_ForThisChild)# pointers: a dictionary, as form as:# {"youth": child_1, "middle_aged": child_2, "senior": child_3}root.pointers[attributeValue] = childreturn root以上,就是決策樹(shù)構(gòu)建的全過(guò)程了,也是ID3算法的實(shí)現(xiàn)過(guò)程。C4.5算法原理與之類(lèi)似,不同點(diǎn)在于對(duì)最佳分裂屬性的選擇上。它使用了“增益率”來(lái)替代“信息增益”。
有關(guān)本文的詳細(xì)代碼請(qǐng)參考我的github:Decision_Tree
總結(jié)
- 上一篇: Xshell“所选的用户密钥未在远程主机
- 下一篇: angular8封装http服务