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


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

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

=================================================================

第一部分 理論基礎

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) 越大,獲得的純度提升越大,此次划分的效果越好。

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天!(✿✿ヽ(°▽°)ノ✿)

          

(晚上加個班的好處就是可以看看書,做點自己喜歡的事——回家吃飯(#^.^#))


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM