《Machine Learning in Action》—— Taoye給你講講決策樹到底是支什麼“鬼”

玩世不恭的Coder發表於2020-11-19

《Machine Learning in Action》—— Taoye給你講講決策樹到底是支什麼“鬼”

前面我們已經詳細講解了線性SVM以及SMO的初步優化過程,具體可看:

關於SVM非線性相關的內容,我們留著下個星期來撕

這篇文章我們先來看看決策樹的內容,決策樹相對於SVM來講要簡單不少,也沒有多麼複雜的公式。我理解的決策樹,簡單來說就是通過已有的資料集訓練出一個樹形結構的模型,以便我們能夠依據該模型對不知分類結果的資料進行預測。簡單的決策樹就有點類似於我們在學習資料結構時候的二叉排序樹。當然了,決策樹除了能夠進行分類之外,還能進行迴歸,這裡主要討論的是分類問題。

關於決策樹相關的內容,可能會肝兩篇文章來介紹。第一篇主要是講解決策樹先關的基本知識,及其決策訓練的過程,也就是我們的這篇文章,重在理論及推導,也重在理解。而第二篇文章寫的主要內容是決策樹的程式碼實戰,而程式碼實戰是在熟悉理論基礎的前提之下來進行的,所以對於決策樹相關的理論知識還是有必要了解的。可能的話,還會有第三篇,主要是關羽防止決策樹過擬合的剪枝操作的內容。

一、什麼是決策樹

關於決策樹的定義,李航——《統計學習方法》(第二版)這本書中是這樣描述的:

分類決策樹模型是一種描述對例項進行分類的樹形結構,決策樹由節點(node)和有向邊(directed edge)組成。節點有兩種型別:內部節點(iternal node)和葉節點(leaf node)。內部節點表示一個特徵或屬性,葉節點表示一個類。

上述提到的內部節點可以理解成樣本的屬性特徵,而葉節點可以理解成樣本的標籤,而有向邊可以理解成不同的屬性特徵結果。一般我們在繪製決策樹的時候,內部節點用方框或長方形表示,而葉節點用圓形或橢圓表示。(注意:這個並不是絕對的,在不同資料中可能有不同的表示方法,比如《統計學習方法》一書中用圓表示內部節點,用方形表示葉子節點,而在《機器學習實戰》一書中表示方式正好相反

枯燥的文字說明總是會降低讀者的閱讀慾望,下面我們不妨通過一個實際的例子來說明下,從而加強讀者對決策樹的理解。

比如說,形容女生美與醜的時候一般會存在多個指標,這個例子感覺有點危險,還是換個吧。

例子來源:李航——《統計學習方法》(第二版)

比如說,我們沒Money用了,想要去銀行貸款,這個時候銀行就會根據你自己的個人附帶特徵來決定是否給你放款。假設這裡的屬性特徵有四個,分別是年紀、是否工作、是否有房子、信用情況,這些屬性特徵就相當於是內部節點,標籤(結果)是否放款相當於葉子節點,而不同屬性特徵的結果表示有向邊,像年紀中有青年、中年、老年,信用情況有好、一般、非常好等表示不同的有向邊。

對此,我們可以根據自己的感覺來手動繪製一個簡單的決策樹模型:

《Machine Learning in Action》—— Taoye給你講講決策樹到底是支什麼“鬼”

我們知道,上述決策樹僅僅是我們自己手動來實現的,不一定能夠運用於解決實際問題。而決策樹的任務,則是根據已有的資料樣本集訓練得到這麼一顆樹形結構模型,以此來對未知分類的資料進行類別預測。對此,我們要向得到這麼一顆儘可能理想的決策樹,則不得不考慮以下幾個問題:

  • 決策樹的指標(屬性特徵)選擇的順序應該是怎樣的的?哪個特徵應當被優先考慮呢?
  • 如果構建出的決策樹出現了過擬合問題,也就說我們的訓練的時候還不錯,但是一測試就GG,這個時候我們應當怎麼解決呢?

本篇文章,主要內容是屬性特徵的優先選取問題。

二、屬性特徵的選擇

對於屬性特徵的選擇,我們應當優先選取對訓練資料集具有明顯分類能力的特徵,這樣可以提高我們決策樹的學習效率。假如說一個屬性特徵的分類結果與隨機分類基本沒什麼差別,則我們可以說這個屬性特徵基本是沒有分類能力的。

比如說,我們這裡有12個關於女生美與醜的資料樣本,其中六個是醜女,另外六個是美女,六個醜女身高有三個是165cm、三個是185cm,而另外六個美女身高同樣如此,這樣的話,我們僅僅通過身高來對女生美貌的判斷基本是沒有分類能力的。(僅僅是個例子,不代表任何觀點

所以,我們理想情況下決策樹的生成理應是這樣的:隨著決策過程的不斷進行,我們希望決策樹的分支節點所包含的樣本儘可能的屬於同一類別(標籤相同),也就是節點的“純度”越來越高。

所以,決策樹的關鍵技術在於屬性特徵的選擇。對於屬性特徵的選擇,我們通常有三個準則:資訊增益、資訊增益比(增益率)和基尼指數。

2.1 資訊增益

上面提到了樣本濃度,比如說我這裡有10個女生,10個都是美女,那就說此時的樣本濃度高,假如美女和醜女五五分,那這個時候的濃度就比較低了。這裡的“濃度”表達的就是這個意思,它主要針對的是標籤(結果)

而表示樣本“濃度”的指標,最常用的就是“資訊熵”了。假定當前樣本集合\(D\)中第\(k\)類(總共\(n\)類)樣本所佔的比例\(p_k\),則此時樣本\(D\)的資訊熵為:

\[Ent(D) = -\sum_{k=1}^np_klog_2(p_k) \tag{2-1} \]

通過上述公式,我們可以知道10個美女(一個類別)的資訊熵為

\[Ent(D)=-1 * log_21=0 \]

美女丑女五五分(兩類)的資訊熵為:

\[Ent(D) = -\frac{1}{2}log_2\frac{1}{2}-\frac{1}{2}log_2\frac{1}{2}=1 \]

通過上面資訊熵的簡單計算,我們可以知道,資訊熵的值越小,則純度越高。

既然我們已經瞭解了資訊增益的計算公式及其所表達的意義,那麼對於一個資料樣本集,我們應當如何用程式碼來進行計算呢?下面我們來試試吧。

本次使用到的資料樣本來自 李航——《統計學習方法》(第二版),資料是關於貸款申請的,具體如下:

那麼現在我們來通過程式碼形式計算下這個資料樣本集的資訊熵吧:

首先建立一個establish_data方法來建立一個二維陣列用於儲存上述資料樣本集的相關資訊(關於屬性特徵所對於的值0,1,2之類的,大家可以參考上述表格對號入座,這裡就不做過多解釋了):

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:建立訓資料集
"""
def establish_data():
    data = [[0, 0, 0, 0, 'N'],         # 樣本資料集相關資訊,前面幾項代表屬性特徵,最後一項表示是否放款
            [0, 0, 0, 1, 'N'],
            [0, 1, 0, 1, 'Y'],
            [0, 1, 1, 0, 'Y'],
            [0, 0, 0, 0, 'N'],
            [1, 0, 0, 0, 'N'],
            [1, 0, 0, 1, 'N'],
            [1, 1, 1, 1, 'Y'],
            [1, 0, 1, 2, 'Y'],
            [1, 0, 1, 2, 'Y'],
            [2, 0, 1, 2, 'Y'],
            [2, 0, 1, 1, 'Y'],
            [2, 1, 0, 1, 'Y'],
            [2, 1, 0, 2, 'Y'],
            [2, 0, 0, 0, 'N']]
    return np.array(data)

隨後,我們建立一個calc_information_entropy方法用於計算資訊熵,計算過程的程式碼可結合上述的公式來看。另外,需要說一點的是,為了方便對放款結果進行分類,我們使用pandas包進行處理,關於pandas的使用,大夥可以參考文件,Taoye後期也會整理一份。當然了,不用pandas模組也能實現這個功能,有興趣的讀者可自行嘗試,也不難。calc_information_entropy具體程式碼如下:

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:計算資訊熵
"""
def calc_information_entropy(data):
    data_number, _ = data.shape
    information_entropy = 0
    for item in pd.DataFrame(data).groupby(_ - 1):
        print(item[1])
        proportion = item[1].shape[0] / data_number
        information_entropy += - proportion * np.log2(proportion)
    return information_entropy

我們執行上述程式碼,來看看具體的資訊熵結果:

《Machine Learning in Action》—— Taoye給你講講決策樹到底是支什麼“鬼”

相比大家都瞭解了資訊熵的概念,並能夠手動計算樣本集的資訊熵了,現在我們再來把資訊增益搬出來吧。

假設一個屬性特徵\(a\)\(V\)個可能的取值\(\{a^1, a^2,.., a^V\}\),若使用\(a\)來對樣本集\(D\)進行劃分,這樣的話就會產生\(V\)個分支節點,其中第\(v\)個 分支節點包含了\(D\)中所有在屬性\(a\)上取值為\(a^V\)的樣本,記為\(D^V\)。於是可計算出用屬性特徵\(a\)對樣本集\(D\)進行劃分所獲得的“資訊增益”(information gain)為:

\[Gain(D, a)=Ent(D)-\sum_{v=1}^V\frac{D^v}{D}Ent(D^v) \tag{2-2} \]

以上是周志華——《機器學習》一書中對資訊增益的相關說明。

枯燥的文字說明總是會降低讀者的閱讀慾望,上述符號也是有點多,我們不妨拿上面的貸款資料來進一步理解下上述資訊增益的內容:

我們單獨拿年齡這一個屬性特徵來計算其資訊增益吧,其有青年、中年、老年三個可能的值,也就是說上述的\(a=年齡\)\(\{a^1,a^2,...,a^V\}=\{青年,中年,老年\}\),而資料樣本集\(D\)為上述15個資料樣本,由於年齡有三個可能的值,所以此時會產生三個分支,即\(V=3\)。之前我們已經計算得到\(Ent(D)=0.971\),現在我們只要計算出後一項\(\sum_{v=1}^V\frac{D^v}{D}Ent(D^v)\)的值即可得到該屬性特徵所對應的資訊增益

通過資料,我們觀察得到如下資訊:

  • 對於年齡這個屬性特徵來講,青年、中年、老年分別有5個。
  • 5個青年中有2個允許貸款,有三個不允許貸款;中年中有3個允許貸款,2個不允許貸款;老年中有4個允許貸款,有1個不允許貸款。

對此,我們可以計算:

\[\begin{aligned} \sum_{v=1}^V\frac{D^v}{D}Ent(D^v) & = \frac{5}{15}(-\frac{2}{5}log_2\frac{2}{5}-\frac{3}{5}log_2\frac{3}{5})\\ & +\frac{5}{15}(-\frac{3}{5}log_2\frac{3}{5}-\frac{2}{5}log_2\frac{2}{5}) \\ & +\frac{5}{15}(-\frac{4}{5}log_2\frac{4}{5}-\frac{1}{5}log_2\frac{1}{5}) \\ & = 0.883 \end{aligned} \]

對此,我們的計算處年齡這個屬性特徵所對應的資訊增益值為:

\[Gain(D, 年齡) = 0.971-0.883=0.083 \]

我們可以把後一項的內容理解成條件概率。另外,資訊增益越大,則其所對應的屬性特徵的分類能力也就越強,也就是我們應當優先選取的特徵。

同理,我們可以計算出工作、房子、信用屬性特徵所對應的資訊增益:

\[\begin{aligned} & Gain(D, 工作)=0.324 \\ & Gain(D, 房子)=0.420 \\ & Gain(D, 信用)=0.363 \end{aligned} \]

我們比較哥哥屬性特徵的資訊增益值,可以發現房子這個屬性特徵最大,所以它應該是我們優先選擇的屬性特徵。

瞭解了資訊增益的計算及其意義,下面我們來通過程式碼計算一下(程式碼含義見註釋):

import numpy as np
import pandas as pd

np.__version__
pd.__version__

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:建立訓資料集
"""
def establish_data():
    data = [[0, 0, 0, 0, 'N'],         # 樣本資料集相關資訊,前面幾項代表屬性特徵,最後一項表示是否放款
            [0, 0, 0, 1, 'N'],
            [0, 1, 0, 1, 'Y'],
            [0, 1, 1, 0, 'Y'],
            [0, 0, 0, 0, 'N'],
            [1, 0, 0, 0, 'N'],
            [1, 0, 0, 1, 'N'],
            [1, 1, 1, 1, 'Y'],
            [1, 0, 1, 2, 'Y'],
            [1, 0, 1, 2, 'Y'],
            [2, 0, 1, 2, 'Y'],
            [2, 0, 1, 1, 'Y'],
            [2, 1, 0, 1, 'Y'],
            [2, 1, 0, 2, 'Y'],
            [2, 0, 0, 0, 'N']]
    return np.array(data)

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:計算資訊熵
"""
def calc_information_entropy(data):
    data_number, _ = data.shape
    information_entropy = 0
    for item in pd.DataFrame(data).groupby(_ - 1):
        proportion = item[1].shape[0] / data_number
        information_entropy += - proportion * np.log2(proportion)
    return information_entropy

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:找出對應屬性特徵值的樣本,比如找出所有年紀為青年的樣本資料集
"""
def handle_data(data, axis, value):
    result_data = list()
    for item in data:
        if item[axis] == value: result_data.append(item)
    return result_data

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:計算最大的資訊增益,並輸出其所對應的特徵索引
"""
def calc_information_gain(data):
    feature_number = data.shape[1] - 1                    # 屬性特徵的數量
    base_entropy = calc_information_entropy(data)                 # 計算總體資料集的資訊熵
    max_information_gain, best_feature = 0.0, -1                 # 初始化最大資訊增益和對應的特徵索引
    for index in range(feature_number):
        feat_list = [item[index] for item in data]
        feat_set = set(feat_list)
        new_entropy = 0.0
        for set_item in feat_set:                         # 計算屬性特徵劃分後的資訊增益
            sub_data = handle_data(data, index, set_item)
            proportion = len(sub_data) / float(data.shape[0])           # 計運算元集的比例
            new_entropy += proportion * calc_information_entropy(np.array(sub_data))
        temp_information_gain = base_entropy - new_entropy                     # 計算資訊增益
        print("第%d個屬性特徵所對應的的增益為%.3f" % (index + 1, temp_information_gain))            # 輸出每個特徵的資訊增益
        if (temp_information_gain > max_information_gain):
            max_information_gain, best_feature = temp_information_gain, index       # 更新資訊增益,確定的最大的資訊增益對應的索引
    return best_feature

if __name__ == "__main__":
    data = establish_data()
    print("屬性特徵對應的最大的資訊增益對應的索引為:%d" % calc_information_gain(data))

執行上述程式碼,可見輸出的各個屬性特徵的資訊增益,以及最大資訊增益對應的特徵索引:

可以發現,和我們手動計算的如出一轍,所以此時我們應當優先選取索引為2的屬性特徵作為決策標準,也就是房子。

2.2 資訊增益比(增益率)

我們使用資訊增益作為選取特徵指標的時候,存在偏向於選擇取值較多的特徵的問題。

比如我們將id作為上述資料集的一個分類屬性,通過計算可以發現該資訊增益最大,但實際上該id對類別不具有什麼分類能力,所以這樣得到的決策樹不具有泛化能力,無法對新樣本進行有效預測。

為了解決資訊增益存在的這個問題,我們就引入了資訊增益比(增益率)的概念,著名的C4.5演算法就是用“增益率”來作為選取最優劃分屬性。增益率定義為:

\[Gain_ratio(D, a)=\frac{Gain(D, a)}{IV(a)} \tag{2-3} \]

\[其中,IV(a)=-\sum_{v=1}^V\frac{|D^v|}{|D|}log_2\frac{|D^v|}{|D|} \tag{2-4} \]

\(IV(a)\)稱為\(a\)的“固有值”(intrinsic value)。通常\(a\)的取值數目越多,則\(IV(a)\)的值會越大。其中的\(a\)的取值比如說:上述的年紀有青年、中年、老年三種取值。

對於上述的貸款資料集來說,信用情況有一般、好和非常好三種,比例分為是\(\frac{5}{15}、\frac{6}{15}、\frac{4}{15}\)。毒刺,我們可以計算信用情況的“固有值”:

\[\begin{aligned} IV(信用) & =-\sum_{v=1}^V\frac{|D^v|}{|D|}log_2\frac{|D^v|}{|D|} \\ & =-\frac{5}{15}log_2\frac{5}{15}-\frac{6}{15}log_2\frac{6}{15}-\frac{4}{15}log_2\frac{4}{15} \\ & = 1.566 \end{aligned} \]

所以,對於信用屬性來講,其增益率為:

\[\begin{aligned} Gain\_ratio(D, 信用) & =\frac{Gain(D, 信用)}{IV(信用)} \\ & = \frac{0.363}{1.566} \\ & = 0.232 \end{aligned} \]

同理,我們可以計算出其他屬性特徵的增益率:

\[\begin{aligned} Gain\_ratio(D, 年齡) & =0.052 \\ Gain\_ratio(D, 工作) & =0.352 \\ Gain\_ratio(D, 房子) & =0.433 \\ \end{aligned} \]

計算增益率的具體程式碼可參考如下:

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:計算增益率
"""
def calc_gain_ratio(data):
    feature_number = data.shape[1] - 1                    # 屬性特徵的數量
    base_entropy = calc_information_entropy(data)                 # 計算總體資料集的資訊熵
    for index in range(feature_number):
        feat_list = [item[index] for item in data]
        feat_set = set(feat_list)
        new_entropy, iv = 0.0, 0.0
        for set_item in feat_set:                         # 計算屬性特徵劃分後的資訊增益
            sub_data = handle_data(data, index, set_item)
            proportion = len(sub_data) / float(data.shape[0])           # 計運算元集的比例
            new_entropy += proportion * calc_information_entropy(np.array(sub_data))
            iv += -proportion * np.log2(proportion)
        temp_information_gain = base_entropy - new_entropy                     # 計算資訊增益
        gain_ratio = temp_information_gain / iv
        print("第%d個屬性特徵所對應的增益率為%.3f,資訊增益為%.3f" % (index + 1, gain_ratio, temp_information_gain))            # 輸出每個特徵的資訊增益

執行結果如下:

在C4.5演算法中,並不是直接單一的使用增益率來對屬性特徵的選取進行決策,而是先在資訊增益中選取高於平均水平的屬性作為候選,然後從候選中選取增益率最高的。

關於C4.5演算法,我們後期會講到。

2.3 基尼指數

基尼指數是另外一種選取屬性的指標。

前面我們提到了,資訊熵是描述資料樣本純度一個標準,而在基尼指數中的基尼值同樣可以體現資料樣本的純度。資料樣本集\(D\)的基尼值可以用\(Gini(D)\)來表示(其中\(k\)表示有\(k\)個標籤結果):

\[Gini(D)=1-\sum_{k=1}^{|y|}p_k^2 \tag{2-5} \]

基尼值\(Gini(D)\)越小,說明資料樣本純度越高。而屬性\(a\)對應的基尼指數\(Gini\_index(D,a)\)可以定義為:

\[Gini\_index(D, a)=\sum_{v=1}^V\frac{|D^v|}{|D|}Gini(D^v) \tag{2-6} \]

我們同樣來分別計算下上述貸款資料集的基尼指數。

對於信用情況這一屬性特徵來講,其基尼指數的手動計算過程如下所示:

\[\begin{aligned} Gini\_index(D, 信用) & = \sum_{v=1}^{V}\frac{|D^v|}{|D|}Gini(D^v) \\ & = \frac{5}{15}(1-(\frac{4}{5})^2 - (\frac{1}{5})^2)+\frac{6}{15}(1-(\frac{2}{6})^2 - (\frac{4}{6})^2)+\frac{4}{15}(1-(\frac{4}{4})^2 - (\frac{0}{4})^2) \\ & = \end{aligned} \]

對於其他屬性的基尼指數,讀者可自行根據上述過程進行計算(真的很簡單)。關於基尼指數的計算程式碼,可參考如下:

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:計算基尼指數
"""
def calc_gini_index(data):
    feature_number = data.shape[1] - 1                    # 屬性特徵的數量
    base_entropy = calc_information_entropy(data)                 # 計算總體資料集的資訊熵
    for index in range(feature_number):
        feat_list = [item[index] for item in data]
        feat_set = set(feat_list)
        new_entropy, iv, gini_index = 0.0, 0.0, 0.0
        for set_item in feat_set:                         # 計算屬性特徵劃分後的資訊增益
            sub_data = handle_data(data, index, set_item)
            temp_df = pd.DataFrame(sub_data)
            yes_proportion = temp_df[temp_df[temp_df.shape[1]-1] == "Y"].shape[0] / len(sub_data)
            gini_value = 1 - (yes_proportion ** 2) - ((1- yes_proportion) ** 2)
            proportion = len(sub_data) / float(data.shape[0])           # 計運算元集的比例
            new_entropy += proportion * calc_information_entropy(np.array(sub_data))
            iv += -proportion * np.log2(proportion)
            gini_index += gini_value * proportion     # 計算基尼指數
        temp_information_gain = base_entropy - new_entropy                     # 計算資訊增益
        gain_ratio = temp_information_gain / iv
        print("第%d個屬性特徵所對應的資訊增益為%.3f,增益率為%.3f, 基尼指數為%.3f" % (index + 1, temp_information_gain, gain_ratio, gini_index))

執行結果:

以上就是基尼指數的計算及其相關程式碼,一般來講,基尼指數越小就優先被劃分。

上述內容的完整程式碼如下:

import numpy as np
import pandas as pd

np.__version__
pd.__version__

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:建立訓資料集
"""
def establish_data():
    data = [[0, 0, 0, 0, 'N'],         # 樣本資料集相關資訊,前面幾項代表屬性特徵,最後一項表示是否放款
            [0, 0, 0, 1, 'N'],
            [0, 1, 0, 1, 'Y'],
            [0, 1, 1, 0, 'Y'],
            [0, 0, 0, 0, 'N'],
            [1, 0, 0, 0, 'N'],
            [1, 0, 0, 1, 'N'],
            [1, 1, 1, 1, 'Y'],
            [1, 0, 1, 2, 'Y'],
            [1, 0, 1, 2, 'Y'],
            [2, 0, 1, 2, 'Y'],
            [2, 0, 1, 1, 'Y'],
            [2, 1, 0, 1, 'Y'],
            [2, 1, 0, 2, 'Y'],
            [2, 0, 0, 0, 'N']]
    return np.array(data)

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:計算資訊熵
"""
def calc_information_entropy(data):
    data_number, _ = data.shape
    information_entropy = 0
    for item in pd.DataFrame(data).groupby(_ - 1):
        proportion = item[1].shape[0] / data_number
        information_entropy += - proportion * np.log2(proportion)
    return information_entropy

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:找出對應屬性特徵值的樣本,比如找出所有年紀為青年的樣本資料集
"""
def handle_data(data, axis, value):
    result_data = list()
    for item in data:
        if item[axis] == value: result_data.append(item)
    return result_data

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:計算最大的資訊增益,並輸出其所對應的特徵索引
"""
def calc_information_gain(data):
    feature_number = data.shape[1] - 1                    # 屬性特徵的數量
    base_entropy = calc_information_entropy(data)                 # 計算總體資料集的資訊熵
    max_information_gain, best_feature = 0.0, -1                 # 初始化最大資訊增益和對應的特徵索引
    for index in range(feature_number):
        feat_list = [item[index] for item in data]
        feat_set = set(feat_list)
        new_entropy = 0.0
        for set_item in feat_set:                         # 計算屬性特徵劃分後的資訊增益
            sub_data = handle_data(data, index, set_item)
            proportion = len(sub_data) / float(data.shape[0])           # 計運算元集的比例
            new_entropy += proportion * calc_information_entropy(np.array(sub_data))
        temp_information_gain = base_entropy - new_entropy                     # 計算資訊增益
        print("第%d個屬性特徵所對應的的增益為%.3f" % (index + 1, temp_information_gain))            # 輸出每個特徵的資訊增益
        if (temp_information_gain > max_information_gain):
            max_information_gain, best_feature = temp_information_gain, index       # 更新資訊增益,確定的最大的資訊增益對應的索引
    return best_feature

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:計算增益率
"""
def calc_gain_ratio(data):
    feature_number = data.shape[1] - 1                    # 屬性特徵的數量
    base_entropy = calc_information_entropy(data)                 # 計算總體資料集的資訊熵
    for index in range(feature_number):
        feat_list = [item[index] for item in data]
        feat_set = set(feat_list)
        new_entropy, iv = 0.0, 0.0
        for set_item in feat_set:                         # 計算屬性特徵劃分後的資訊增益
            sub_data = handle_data(data, index, set_item)
            proportion = len(sub_data) / float(data.shape[0])           # 計運算元集的比例
            new_entropy += proportion * calc_information_entropy(np.array(sub_data))
            iv += -proportion * np.log2(proportion)
        temp_information_gain = base_entropy - new_entropy                     # 計算資訊增益
        gain_ratio = temp_information_gain / iv
        print("第%d個屬性特徵所對應的增益率為%.3f,資訊增益為%.3f" % (index + 1, gain_ratio, temp_information_gain))            # 輸出每個特徵的資訊增益

"""
    Author: Taoye
    微信公眾號: 玩世不恭的Coder
    Explain:計算基尼指數
"""
def calc_gini_index(data):
    feature_number = data.shape[1] - 1                    # 屬性特徵的數量
    base_entropy = calc_information_entropy(data)                 # 計算總體資料集的資訊熵
    for index in range(feature_number):
        feat_list = [item[index] for item in data]
        feat_set = set(feat_list)
        new_entropy, iv, gini_index = 0.0, 0.0, 0.0
        for set_item in feat_set:                         # 計算屬性特徵劃分後的資訊增益
            sub_data = handle_data(data, index, set_item)
            temp_df = pd.DataFrame(sub_data)
            yes_proportion = temp_df[temp_df[temp_df.shape[1]-1] == "Y"].shape[0] / len(sub_data)
            gini_value = 1 - (yes_proportion ** 2) - ((1- yes_proportion) ** 2)
            proportion = len(sub_data) / float(data.shape[0])           # 計運算元集的比例
            new_entropy += proportion * calc_information_entropy(np.array(sub_data))
            iv += -proportion * np.log2(proportion)
            gini_index += gini_value * proportion
        temp_information_gain = base_entropy - new_entropy                     # 計算資訊增益
        gain_ratio = temp_information_gain / iv
        print("第%d個屬性特徵所對應的資訊增益為%.3f,增益率為%.3f, 基尼指數為%.3f" % (index + 1, temp_information_gain, gain_ratio, gini_index))
        
if __name__ == "__main__":
    data = establish_data()
    calc_gini_index(data)

優先選取屬性特徵的指標主要有三個,分別是資訊增益、增益率、基尼指數。對上述內容做個簡單的總結吧:

  • 屬性特徵的資訊增益越高,按道理來講應當被優先選取,常用於\(ID3\)演算法
  • 屬性特徵的增益率越高,按道理來講應當被優先選取,常用與\(C4.5\)演算法
  • 屬性特徵的尼基指數低,按道理來講應當被優先選取,常用於\(CART\)演算法

本文主要是決策樹的理論部分內容,介紹了什麼決策樹,以及生成決策樹時所需要優先選取的三種決策標準。有學習的過SVM,或閱讀過Taoye之前寫的幾篇SVM內容的文章可以發現,決策樹相對於SVM來講要簡單很多,沒有太多且複雜的公式推導。關於決策樹的其他內容,比如決策樹的生成、視覺化、剪枝等,我們放在後面幾篇文章來寫。

我是Taoye,愛專研,愛分享,熱衷於各種技術,學習之餘喜歡下象棋、聽音樂、聊動漫,希望藉此一畝三分地記錄自己的成長過程以及生活點滴,也希望能結實更多志同道合的圈內朋友,更多內容歡迎來訪微信公主號:玩世不恭的Coder

相關文章