1. 程式人生 > >《機器學習》(周誌華)第4章 決策樹 筆記 理論及實現——“西瓜樹”

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

取數據 dataset 結點 (六) eight dot 都是 ret 集合

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

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

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

第一部分 理論基礎

1. 純度(purity)

對於一個分支結點,如果該結點所包含的樣本都屬於同一類,那麽它的純度為1,而我們總是希望純度越高越好,也就是盡可能多的樣本屬於同一類別。那麽如何衡量“純度”呢?由此引入“信息熵”的概念。

2. 信息熵(information entropy)

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

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

顯然,Ent(D)值越小,D的純度越高。因為0<=pk<= 1,故log2 pk<=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天!(??ヽ(°▽°)ノ?)

技術分享圖片

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

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