(一)《機器學習》(周志華)第4章 決策樹 筆記 理論及實現——「西瓜樹」

參考書籍:《機器學習》(周志華)算法

說       明:本篇內容爲讀書筆記,主要參考教材爲《機器學習》(周志華)。詳細內容請參閱書籍——第4章 決策樹。部份內容參考網絡資源,在此感謝全部原創者的工做。網絡

=================================================================app

第一部分 理論基礎

1. 純度(purity)

      對於一個分支結點,若是該結點所包含的樣本都屬於同一類,那麼它的純度爲1,而咱們老是但願純度越高越好,也就是儘量多的樣本屬於同一類別。那麼如何衡量「純度」呢?由此引入「信息熵」的概念。框架

2. 信息熵(information entropy)

      假定當前樣本集合D中第k類樣本所佔的比例爲pk(k=1,,2,...,|y|),則D的信息熵定義爲:機器學習

                 Ent(D) = -∑k=1 pk·log2 pk    (約定若p=0,則logp=0)函數

      顯然,Ent(D)值越小,D的純度越高。由於0<=pk<= 1,故logpk<=0,Ent(D)>=0. 極限狀況下,考慮D中樣本同屬於同一類,則此時的Ent(D)值爲0(取到最小值)。當D中樣本都分別屬於不一樣類別時,Ent(D)取到最大值log2 |y|.工具

3. 信息增益(information gain)

      假定離散屬性a有V個可能的取值{a1,a2,...,aV}. 若使用a對樣本集D進行分類,則會產生V個分支結點,記Dv爲第v個分支結點包含的D中全部在屬性a上取值爲av的樣本。不一樣分支結點樣本數不一樣,咱們給予分支結點不一樣的權重:|Dv|/|D|, 該權重賦予樣本數較多的分支結點更大的影響、由此,用屬性a對樣本集D進行劃分所得到的信息增益定義爲:
學習

               Gain(D,a) = Ent(D)-∑v=1 |Dv|/|D|·Ent(Dv)編碼

其中,Ent(D)是數據集D劃分前的信息熵,∑v=1 |Dv|/|D|·Ent(Dv)能夠表示爲劃分後的信息熵。「前-後」的結果代表了本次劃分所得到的信息熵減小量,也就是純度的提高度。顯然,Gain(D,a) 越大,得到的純度提高越大,這次劃分的效果越好。spa

4. 增益率(gain ratio)

      基於信息增益的最優屬性劃分原則——信息增益準則,對可取值數據較多的屬性有所偏好。C4.5算法使用增益率替代信息增益來選擇最優劃分屬性,增益率定義爲:

               Gain_ratio(D,a) = Gain(D,a)/IV(a)

其中

               IV(a) = -∑v=1 |Dv|/|D|·log2 |Dv|/|D|

稱爲屬性a的固有值。屬性a的可能取值數目越多(即V越大),則IV(a)的值一般會越大。這在必定程度上消除了對可取值數據較多的屬性的偏好。

      事實上,增益率準則對可取值數目較少的屬性有所偏好,C4.5算法並非直接使用增益率準則,而是先從候選劃分屬性中找出信息增益高於平均水平的屬性,再從中選擇增益率最高的。

5. 基尼指數(Gini index)

     CART決策樹算法使用基尼指數來選擇劃分屬性,基尼指數定義爲:

              Gini(D) = ∑k=1 k'≠1 pk·pk' = 1- ∑k=1  pk·pk

      能夠這樣理解基尼指數:從數據集D中隨機抽取兩個樣本,其類別標記不一致的機率。Gini(D)越小,純度越高。

      屬性a的基尼指數定義:

             Gain_index(D,a) = ∑v=1 |Dv|/|D|·Gini(Dv)

      使用基尼指數選擇最優劃分屬性,即選擇使得劃分後基尼指數最小的屬性做爲最優劃分屬性。

 

第二部分  編碼實現——基於信息增益準則的決策樹

     採用Python做爲實現工具,以書籍中的西瓜數據爲例,構造一棵「watermelon tree」。這裏,咱們構建的是一棵基於信息增益準則的決策樹,比較簡單,適合初學。

1. 算法

      此處先略。^_^

2. Python代碼實現

    代碼框架參考了部分網絡資源,而後就是悶頭去寫了。本質上都是大同小異,重要的仍是抱着學習的心態,去自主實現一下,才能對決策樹有更多的思考。

 2.1 數據樣本說明

     本案例基於教材《機器學習》P76表4.1 西瓜數據集2.0,嘗試用Python實現決策樹構建。一共17條樣本數據。理論上,創建的樹應該和P78圖4.4一致。

    樣本數據截圖以下:

 

 2.2 實現代碼

(一)導入模塊部分

#導入模塊
import pandas as pd
import numpy as np
from collections import Counter
from math import log2

    用pandas模塊的read_excel()函數讀取數據文本;用numpy模塊將dataframe轉換爲list(列表);用Counter來完成計數;用math模塊的log2函數計算對數。後邊代碼中會有對應體現。

 

(二)數據獲取與處理函數

#數據獲取與處理
def getData(filePath):
    data = pd.read_excel(filePath)
    return data

def dataDeal(data):
    dataList = np.array(data).tolist()
    dataSet = [element[1:] for element in dataList]
    return dataSet

    getData()經過pandas模塊中的read_excel()函數讀取樣本數據。嘗試過將數據文件保存爲csv格式,可是對於中文的處理不是很好,因此選擇了使用xls格式文件。

    dataDeal()函數將dataframe轉換爲list,而且去掉了編號列。編號列並非西瓜的屬性,事實上,若是把它當作屬性,會得到最大的信息增益。

    這兩個函數是徹底能夠合併爲同一個函數的,可是由於我想分別使用data(dataframe結構,帶屬性標籤)和dataSet(list)數據樣本,因此分開寫了兩個函數。

 

(三)獲取屬性名稱

#獲取屬性名稱
def getLabels(data):
    labels = list(data.columns)[1:-1]
    return labels

     很簡單,獲取屬性名稱:紋理,色澤,根蒂,敲聲,臍部,觸感。

 

(四)獲取類別標記

#獲取類別標記
def targetClass(dataSet):
    classification = set([element[-1] for element in dataSet])
    return classification

    獲取一個樣本是否好瓜的標記(是與否)。

 

(五)葉結點標記

#將分支結點標記爲葉結點,選擇樣本數最多的類做爲類標記
def majorityRule(dataSet):
    mostKind = Counter([element[-1] for element in dataSet]).most_common(1)
    majorityKind = mostKind[0][0]
    return majorityKind

     

(六)計算信息熵

#計算信息熵
def infoEntropy(dataSet):
    classColumnCnt = Counter([element[-1] for element in dataSet])
    Ent = 0
    for symbol in classColumnCnt:
        p_k = classColumnCnt[symbol]/len(dataSet)
        Ent = Ent-p_k*log2(p_k)
    return Ent

 

(七)子數據集構建

#子數據集構建
def makeAttributeData(dataSet,value,iColumn):
    attributeData = []
    for element in dataSet:
        if element[iColumn]==value:
            row = element[:iColumn]
            row.extend(element[iColumn+1:])
            attributeData.append(row)
    return attributeData

    在某一個屬性值下的數據,好比紋理爲清晰的數據集。

 

(八)計算信息增益

#計算信息增益
def infoGain(dataSet,iColumn):
    Ent = infoEntropy(dataSet)
    tempGain = 0.0
    attribute = set([element[iColumn] for element in dataSet])
    for value in attribute:
        attributeData = makeAttributeData(dataSet,value,iColumn)
        tempGain = tempGain+len(attributeData)/len(dataSet)*infoEntropy(attributeData)
        Gain = Ent-tempGain
    return Gain

 

(九)選擇最優屬性

#選擇最優屬性                
def selectOptimalAttribute(dataSet,labels):
    bestGain = 0
    sequence = 0
    for iColumn in range(0,len(labels)):#不計最後的類別列
        Gain = infoGain(dataSet,iColumn)
        if Gain>bestGain:
            bestGain = Gain
            sequence = iColumn
        print(labels[iColumn],Gain)
    return sequence

    

(十)創建決策樹

#創建決策樹
def createTree(dataSet,labels):
    classification = targetClass(dataSet) #獲取類別種類(集合去重)
    if len(classification) == 1:
        return list(classification)[0]
    if len(labels) == 1:
        return majorityRule(dataSet)#返回樣本種類較多的類別
    sequence = selectOptimalAttribute(dataSet,labels)
    print(labels)
    optimalAttribute = labels[sequence]
    del(labels[sequence])
    myTree = {optimalAttribute:{}}
    attribute = set([element[sequence] for element in dataSet])
    for value in attribute:
        
        print(myTree)
        print(value)
        subLabels = labels[:]
        myTree[optimalAttribute][value] =  \
                createTree(makeAttributeData(dataSet,value,sequence),subLabels)
    return myTree

    樹自己並不複雜,採用遞歸的方式實現。

 

(十一)定義主函數

def main():
    filePath = 'watermelonData.xls'
    data = getData(filePath)
    dataSet = dataDeal(data)
    labels = getLabels(data)
    myTree = createTree(dataSet,labels)
    return myTree

    主函數隨便寫寫了,主要是實現功能。

 

(十二)生成樹

if __name__ == '__main__':
    myTree = main()

 

3.幾點說明

    Python實現並無很複雜的東西,只要能很好的理解遞歸在這裏是如何體現的就足夠了。

    在構造樹的時候,這裏的樹定義爲一個嵌套的字典(dict)結構,樹根對應的屬性是字典最外層的關鍵字,其值是仍一個字典。遞歸就是這樣用下一層返回的樹做爲上一層樹某個分支(字典的關鍵字)的值,一層層往下(一棵倒樹)填充,直至遇到葉結點。在定義的構造樹函數中,終止條件(兩個if)是很重要的,決定了遞歸在何時中止,也就是樹在何時中止生長。

    生成的樹(字典結構)以下:

 

    一個字典結構的樹是極其不友好的,暫時沒有將其可視化,後續會學習一下。 

    從結果看,根節點的屬性是紋理。紋理爲稍糊的,下一個結點的屬性是觸感,觸感爲軟粘的瓜,判斷爲好瓜(紋理爲稍糊且觸感爲軟粘的瓜),觸感爲硬滑的瓜,斷定爲壞瓜(紋理爲稍糊且觸感爲硬滑的瓜)。紋理爲模糊的,直接斷定爲壞瓜(買瓜的要注意了);紋理爲清晰的情形較爲複雜。紋理爲清晰的,下一個結點屬性爲根蒂,對於根蒂爲硬挺的,判斷爲壞瓜(紋理爲清晰且根蒂爲硬挺的瓜),根蒂爲蜷縮的,判斷爲好瓜(紋理爲清晰且根蒂爲蜷縮的瓜)。根蒂爲稍蜷的,下一個結點的屬性是色澤,對於色澤爲青綠的,判斷爲好瓜(紋理爲清晰,根蒂爲稍蜷且色澤爲青綠的瓜),對於色澤爲烏黑的,下一個結點屬性是觸感,對於觸感爲軟粘的,斷定爲壞瓜(紋理爲清晰,根蒂爲稍蜷,色澤爲烏黑且觸感爲軟粘的瓜),對於觸感爲硬滑的,斷定爲好瓜(紋理爲清晰,根蒂爲稍蜷,色澤爲烏黑且觸感爲硬滑的瓜)。這裏有一個小的問題,一下子再說。

    先看《機器學習》教材上給出的樹:

 

 

 

    咱們得到的結果和書本中的結果基本是一致的,惟一的一個區別是咱們缺乏一個葉——色澤爲淺白的葉。這是由於,樣本數據中不存在紋理爲清晰、根蒂爲稍蜷且色澤爲淺白的瓜,致使在生成樹的時候少了一個葉。這種狀況須要特殊處理,好比處理成父類的類別。這裏沒有多作處理,該機制添加進去並不難。

 

4.寫在最後

    熱烈慶祝在帝都正式工做的第100天!(✿✿ヽ(°▽°)ノ✿)

          

(晚上加個班的好處就是能夠看看書,作點本身喜歡的事——回家吃飯(#^.^#))

相關文章
相關標籤/搜索