機器學習—決策樹(ID3算法)及其python實現

一、決策樹(ID3算法)及其python實現

總結一下前一陣對於決策樹的學習。爲了從算法的層次理解並熟練運用機器學習的多種算法,我首先從用Python編寫算法入手:

(一)決策樹的概念

決策樹屬於機器學習中的監督學習,也就是說,其數據集需要人工對feature與label標定,比如表1,天氣的各種屬性與天氣好壞的關係表:

項目 Outlook(Feature1) Temperature(Feature2) Humidity(Feature3) Windy(Feature4) Good_State?(Label)
1 sunny hot high FALSE no
2 sunny hot high TRUE no
3 sunny hot high TRUE no
4 overcast hot high FALSE yes
5 rainy mild high FALSE yes
6 rainy cool normal FALSE yes
7 rainy cool normal TRUE no
8 overcast cool normal TRUE yes
9 sunny mild high FALSE no
10 sunny cool normal FALSE yes
11 rainy mild normal FALSE yes
12 sunny mild normal TRUE yes
13 overcast mild high TRUE yes
14 overcast hot normal FALSE yes
15 rainy mild high TRUE no

我們需要決策樹做的就是找到一種分類器根據天氣的四個feature判斷出其所屬State的標籤。
下面介紹決策樹:它從一組無次序、無規則的實例中推理出以決策樹表示的分類規則[1]。採用自頂向下的遞歸方式,在決策樹的內部節點(feature)進行屬性的比較(比如根據feature2比較天氣的溫度高低),並根據不同屬性值判斷從該節點向下的分支,在決策樹的葉節點得到結論。
如下圖1,我們可以根據此決策樹來表示決策邏輯。
這裏寫圖片描述

(二)生成決策樹的算法

  • ID3 (Iterative Dichotomiser 3) (基於信息論)
  • C4.5(對ID3算法的改進,增加了對決策樹的修剪等)
  • 隨機森林(Random Forest)
  • 分類及迴歸樹(Classification And Regression Tree, CART) (基於最小GNIN指數)
  • 多元自適應迴歸樣條(MARS)
  • 梯度推進機(Gradient Boosting Machine, GBM)

(三)ID3算法介紹

1、過程
 (1)出發點:哪一個屬性將在樹的根節點被測試?--> 分類能力最好的屬性
 (2)對於這個屬性的每個可能值產生一個分支,然後將訓練樣例分配到樣例屬性值所對應的分支之下
 (3)對每個節點不斷測試對其而言分類能力最好的屬性
 (4)重複4過程直到所有屬性被測試
2、如何選擇分類能力最好的屬性?(ID3算法中利用信息論中的熵與信息增益)
(1)熵
   高中物理我們學過熱力學第二定律:在孤立系統中,體系與環境沒有能量交換,體系總是自發地像混亂度增大的方向變化,總使整個系統的熵值增大。熵就是表示分子狀態混亂程度的物理量。
   在信息論中,香農用熵的概念來描述信源的不確定度。我們可以用熵來表示數據集的純度,與熱熵類似,信息熵越小,數據集越純淨,即越多的數據具有相同的類別。
信息熵定義爲:Entropy(D) = -Σp(i)*log(p(i))
   其中p(i)是數據集D中標籤label取值爲i的數據所佔比例(概率)。在這裏定義0log0=0,這裏的log以2爲底。

至於爲什麼信息熵要定義爲如上形式,可以參考《Pattern Recognition and Machine Learning》[2]

(2)信息增益
   熵:表示隨機變量的不確定性。
   條件熵:在一個條件下,隨機變量的不確定性。(類似條件概率的理解)
   信息增益即爲 **熵 - 條件熵** ,即在一個條件下,信息不確定性減少的程度。

詳細的解釋可以參考這篇博客[3]

於是信息增益定義爲:Gain(D,A) = Entropy(D) - Σ( |D(v)|/|D| * Entropy(D(v)))
   其中,v屬於某個Feature(屬性)A的所有可能值的集合,D(v)是數據集D中FeatureA值爲v的子集。Entropy(D)是D未用FeatureA分割之前的熵,Entropy(D(v))是D用FeatureA值爲v分割之後的熵。
   FeatureA的每一個可能取值都有一個熵(Entropy(D(v))),該熵的權重是取該FeatureA的可能值所佔數據在所有數據集D中的比例(|D(v)|/|D|)。
(3)選擇分類能力最好的節點(Feature)
   選擇信息增益最大的屬性A作爲當前節點的決策Feature。
   原因:熵刻畫了數據集的純度,數據集越混亂,熵就越大。熵越小,數據集越純淨,越多的數據具有相同的類別。當熵爲0時,數據集中的數據都相等。
   FeatureA的信息增益就是按A來劃分數據集時,數據集能比原來純淨多少。通過不斷地劃分,得到儘可能純的節點,相當於降低數據集的熵。
   當決策樹劃分到葉子節點時,其熵大多爲0(除了部分葉子節點中標籤還未一致,但數據集中所有Feature均已劃分完畢)。
3、僞代碼及例題
   如果上述講解仍覺得不甚理解,可以去找一道關於決策樹的例題,手工計算信息熵及信息增益便會有更深的理解。
   至於僞代碼及具體的從算法層面的編程流程,推薦《人工智能導論》鮑軍鵬、張選平編著。
4、Python實現(具體代碼及註釋已經附上)
        # -*- coding: utf-8 -*-
"""
Created on Sun Nov 19 19:58:10 2017

@author: Lesley
"""
from math import log
#------------------------數據集-------------------------------
#四個因變量(屬性) -> feature : "Outlook","Temperature","Humidity","Windy"
#標籤 -> 是否出門 -> 'no','yes'
#-------------------------------------------------------------
def createDataSet():
    dataSet=[["sunny","hot","high",'FALSE','no'],
            ["sunny","hot","high",'TRUE','no'],
            ["overcast","hot","high",'FALSE','yes'],
            ["rainy","mild","high",'FALSE','yes'],
            ["rainy","cool","normal",'FALSE','yes'],
            ["rainy","cool","normal",'TRUE','no'],
            ["overcast","cool","normal",'TRUE','yes'],
            ["sunny","mild","high",'FALSE','no'],
            ["sunny","cool","normal",'FALSE','yes'],
            ["rainy","mild","normal",'FALSE','yes'],
            ["sunny","mild","normal",'TRUE','yes'],
            ["overcast","mild","high",'TRUE','yes'],
            ["overcast","hot","normal",'FALSE','yes'],
            ["rainy","mild","high",'TRUE','no']
            ]
    feature = ["Outlook","Temperature","Humidity","Windy"]
    return dataSet, feature

#-------------計算數據集整體及各個feature的不同值的熵------------
def Entropy(dataSet):
    sample_num = len(dataSet)    
    label_category = {}
    for sample in dataSet:
        if sample[-1] not in label_category:
            label_category[sample[-1]] = 1
        else:
            label_category[sample[-1]] += 1  #各類別的數量
    #print (label_category)
    entropy = 0
    for i in label_category:
        temp = label_category[i]/sample_num
        entropy -= temp * log(temp,2)
    return entropy

#-------------根據不同feature的不同值篩選對應的數據集------------
def Extractdata(dataSet, axis, value):       #篩選出的是第axis個feature中值爲value的sample集合 且此集合中不含第axis個featur對應的數據
    extDataSet = []
    for sample in dataSet:
        if sample[axis] == value:
            extSample = sample[:axis]        #複製第axis個featur之前的數據
            extSample.extend(sample[axis+1:])#複製第axis個featur之後的數據
            extDataSet.append(extSample)     #刪除第axis個featur對應的數據
    return extDataSet

#--------對於每一節點得到最大的信息增益及對應的feature-----------
def BestFeature(dataSet):
    feature_num = len(dataSet[0]) - 1     #數據集的最後一項是標籤
    entropy = Entropy(dataSet)
    bestEntropy = 0
    bestInfoGain = 0
    bestFeature = -1
    for i in range(feature_num):
        featuresample = [example[i] for example in dataSet]
        uniqueVals = set(featuresample)   #去除list中的重複元素
        newEntropy = 0
        for value in uniqueVals:
            extDataSet = Extractdata(dataSet, i, value)
            prob = len(extDataSet) / float(len(dataSet))  #當前feature中各個值所佔比重
            newEntropy += prob * Entropy(extDataSet)      #Entropy(extDataSet) 得到的是第i個feature中的第j個值的熵

        gain = entropy - newEntropy       #信息增益越大越好 -> 熵越小,數據越純淨

        if gain > bestInfoGain:
            bestInfoGain = gain
            bestFeature = i
            bestEntropy = entropy - gain
    return bestInfoGain, bestFeature, bestEntropy

#--------------------------多數表決---------------------------
def majorityCnt(classList):  
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    return max(classCount)     #classlist中出現最多的元素      

#----------------------------建樹-----------------------------
def CreateTree(dataSet,feature):
    classList = [example[-1] for example in dataSet]  #標籤項
    best_infogain, best_feature, best_entropy= BestFeature(dataSet)  #最大信息增益增益值與其所對應的feature的順序

    #--------------------停止劃分的條件------------------------
    #數據集中的最後一項:標籤爲同一個值(類別相同)   best_entropy-> 最大熵爲0
    if classList.count(classList[0]) ==len(classList): 
        #print(best_entropy)
        return classList[0]

    #最大信息熵小於等於0
    if best_infogain < 0 or best_infogain == 0:
        return majorityCnt(classList)

    #所有特徵已經用完 -> 剩餘標籤
    if len(dataSet[0]) == 1:
        return majorityCnt(classList)          #剩餘的list中所有feature已被用完,但標籤中仍沒有一致,這是採用多數表決
    #---------------------------------------------------------

    best_feature_name = feature[best_feature]  #最大信息增益對應的feature的名稱
    myTree = {best_feature_name:{}}            #根節點 -> 某一個feature

    del(feature[best_feature])                 #刪去已經處理過的feature屬性
    featuresample = [example[best_feature] for example in dataSet]
    uniqueVals = set(featuresample)            #對於每一個feature的各個值

    for value in uniqueVals:
        subfeature = feature[:]#複製,使進一步的調用函數不會對原feature產生影響。 python中函數傳入的list參數若修改會影響其原始值
        myTree[best_feature_name][value] = CreateTree( Extractdata(dataSet, best_feature, value),subfeature)
    return myTree

def main():
    data, feature = createDataSet()
    myTree = CreateTree(data,feature)
    print (myTree)

if __name__=='__main__':
    main()
5、運行結果
{'Outlook': {'rainy': {'Windy': {'FALSE': 'yes', 'TRUE': 'no'}}, 'overcast': 'yes', 'sunny': {'Humidity': {'high': 'no', 'normal': 'yes'}}}}

即生成了圖1中所得到的決策樹。

6、問題
    不知道大家有沒有發現上述算法中存在的問題。當隨着決策樹的生長,其深度在不斷增加,這時就會出現過擬合問題。過擬合會導致數據的預測效果不好。而如何防止過擬合及欠擬合的問題呢?我們下次再講。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章