本文介紹如何利用決策樹/判定樹(decision tree)中決策樹歸納算法(ID3)解決機器學習中的回歸問題。文中介紹基於有監督的學習方式,如何利用年齡、收入、身份、收入、信用等級等特征值來判定用戶是否購買電腦的行為,最后利用python和sklearn庫實現了該應用。
1、 決策樹歸納算法(ID3)實例介紹
2、 如何利用python實現決策樹歸納算法(ID3)
1、決策樹歸納算法(ID3)實例介紹
首先介紹下算法基本概念,判定樹是一個類似於流程圖的樹結構:其中,每個內部結點表示在一個屬性上的測試,每個分支代表一個屬性輸出,而每個樹葉結點代表類或類分布。樹的最頂層是根結點。
決策樹的優點: 直觀,便於理解,小規模數據集有效
決策樹的缺點:處理連續變量不好,類別較多時,錯誤增加的比較快,可規模性一般
以如下測試數據為例:

我們有一組已知訓練集數據,顯示用戶購買電腦行為與各個特征值的關系,我們可以繪制出如下決策樹圖像(繪制方法后面介紹)

此時,輸入一個新的測試數據,就能根據該決策樹很容易判定出用戶是否購買電腦的行為。
有兩個關鍵點需要考慮:1、如何決定分支終止;2如何決定各個節點的位置,例如根節點如何確定。
1、如何決定分支終止
如果某個節點所有標簽均為同一類,我們將不再繼續繪制分支,直接標記結果。或者分支過深,可以基於少數服從多數的算法,終止該分支直接繪制結果。

例如,通過年齡划分,所有middle_aged對象,對應的標簽都為yes,盡管還有其它特征值,例如收入、身份、信用等級等,但由於標簽所有都為一類,所以該分支直接標注結果為yes,不再往下細分。
2、如何決定各個節點的位置,例如根節點如何確定。
在說明這個問題之前,我們先討論一個熵的概,信息和抽象,如何度量?1948年,香農提出了 “信息熵(entropy)”的概念,一條信息的信息量大小和它的不確定性有直接的關系,要搞清楚一件非常非常不確定的事情,或者是我們一無所知的事情,需要了解大量信息==>信息量的度量就等於不確定性的多少
例子:猜世界杯冠軍,假如一無所知,猜多少次?每個隊奪冠的幾率不是相等的,也就說明該信息熵較大。
當前樣本集合 D 中第 k 類樣本所占的比例為 pk ,則 D 的信息熵定義為

離散屬性 a 有 V 個可能的取值 {a1,a2,…,aV};樣本集合中,屬性 a 上取值為 av 的樣本集合,記為 Dv。用屬性 a 對樣本集 D 進行划分所獲得的“信息增益”,表示得知屬性 a 的信息而使得樣本集合不確定度減少的程,公式如下:

ID3算法首先計算各個特征向量的信息增益量,取最大的作為決策樹的根,利用上面公式,以計算age年齡的信息增益量為例:

於是,我們選取age年齡作為決策樹的根節點,依次畫出最后的決策樹,最后的算法如下:
-
- 樹以代表訓練樣本的單個結點開始(步驟1)。
- 如果樣本都在同一個類,則該結點成為樹葉,並用該類標號(步驟2 和3)。
- 否則,算法使用稱為信息增益的基於熵的度量作為啟發信息,選擇能夠最好地將樣本分類的屬性(步驟6)。該屬性成為該結點的“測試”或“判定”屬性(步驟7)。在算法的該版本中,
- 所有的屬性都是分類的,即離散值。連續屬性必須離散化。
- 對測試屬性的每個已知的值,創建一個分枝,並據此划分樣本(步驟8-10)。
- 算法使用同樣的過程,遞歸地形成每個划分上的樣本判定樹。一旦一個屬性出現在一個結點上,就不必該結點的任何后代上考慮它(步驟13)。
- 遞歸划分步驟僅當下列條件之一成立停止:
- (a) 給定結點的所有樣本屬於同一類(步驟2 和3)。
- (b) 沒有剩余屬性可以用來進一步划分樣本(步驟4)。在此情況下,使用多數表決(步驟5)。
- 這涉及將給定的結點轉換成樹葉,並用樣本中的多數所在的類標記它。替換地,可以存放結
- 點樣本的類分布。
- (c) 分枝
- test_attribute = a i 沒有樣本(步驟11)。在這種情況下,以 samples 中的多數類
- 創建一個樹葉(步驟12)
除了ID3算法,還有一些其它算法可以用來繪制決策樹,此處暫不討論,例如:C4.5: Quinlan,Classification and Regression Trees (CART): (L. Breiman, J. Friedman, R. Olshen, C. Stone)。共同點:都是貪心算法,自上而下(Top-down approach)。區別:屬性選擇度量方法不同: C4.5 (gain ratio), CART(gini index), ID3 (Information Gain)
2、 如何利用python實現決策樹歸納算法(ID3)
用Python實現上面實例前,我們需要先安裝一個科學庫Anaconda,anaconda指的是一個開源的Python發行版本,其包含了conda、Python等180多個科學包及其依賴項。
同時,還需要安裝一個決策樹圖形繪制工具graphviz
下面是具體的代碼實現
# 導入庫 from sklearn.feature_extraction import DictVectorizer import csv from sklearn import tree from sklearn import preprocessing from sklearn.externals.six import StringIO import math # 測試,沒有age時的信息熵 result = -(5/14*math.log2(5/14) + 9/14*math.log2(9/14)) print(result) #測試,有age時的信息熵 result1 = 5/14*(-3/5*math.log2(3/5) - 2/5*math.log(2/5)) + 4/14*(-4/4*math.log2(4/4)) + 5/14*(-3/5*math.log2(3/5) - 2/5*math.log2(2/5)) print(result1) # 1 打開測試數據集 allElectronicsData = open(r'D:\test.csv','r') reader = csv.reader(allElectronicsData) # 讀取文件頭 headers = next(reader) #print(headers) featureList = [] labelList = [] # 2 為每個測試樣例,建立一個特征名和值的字典,然后加入到featureList中 for row in reader: labelList.append(row[len(row) -1]) rowDict = {} for i in range(1,len(row) -1): rowDict[headers[i]] = row[i] featureList.append(rowDict) print(labelList) print(featureList) # 3 把輸入特征集進行轉換,例如 ''' {'age': 'youth', 'income': 'high', 'student': 'no', 'credit_rating': 'fair'} [ 0. 0. 1. 0. 1. 1. 0. 0. 1. 0.] ''' vec = DictVectorizer() dummyX = vec.fit_transform(featureList).toarray() #print(type(dummyX)) print(dummyX) #print(vec.get_feature_names()) # 4 對標簽值進行0,1轉換 lb = preprocessing.LabelBinarizer() dummyY = lb.fit_transform(labelList) print(dummyY) # 5 直接調用庫的決策樹分類器,entropy表示信息熵 clf = tree.DecisionTreeClassifier(criterion='entropy') clf = clf.fit(dummyX,dummyY) #print(clf) # 6 填入feature_names,還原原有的值,寫入test.dot文件 with open("test.dot",'w') as f: f = tree.export_graphviz(clf,feature_names=vec.get_feature_names(),out_file=f) # 7 此時dumyX為一個n維的向量,取出第一行,修改一下作為一個新的測試數據 # 'age': 'middle_aged', 'income': 'high', 'student': 'no', 'credit_rating': 'fair' 正確結果應該是 買 為1 oneRowX = dummyX[0,:] print("oneRowX: ",oneRowX) newRowX = oneRowX newRowX[0] = 1 newRowX[2] = 0 print(newRowX) # 8 使用新數據進行測試驗證 predictedY = clf.predict(newRowX.reshape(1, -1)) print(predictedY) # 使用graphviz工具,通過命令dot -Tpdf test.dot -o output.pdf生成決策樹文檔
