決策樹

理論

什么是決策樹写隶?

決策樹內(nèi)部節(jié)點表示一個特征或?qū)傩裕~節(jié)點表示一個類讲仰,進行分類時慕趴,從根節(jié)點開始,對實例的某一特征進行測試鄙陡,根據(jù)測試結(jié)果冕房,將實例分配到其子節(jié)點,這時每一個子節(jié)點對應(yīng)著該特征的一個取值趁矾,如此遞歸地對實例進行測試并分配耙册,直至到達葉節(jié)點,最后將實例分到葉節(jié)點地類中毫捣。
可以將決策樹看成一個if-then規(guī)則的集合详拙,每一個實例都被一條路徑或一條規(guī)則所覆蓋,而且只被一條路徑或一條規(guī)則所覆蓋蔓同,互斥并且完備溪厘。同時,決策樹還表示給定特征條件下類的條件概率分布牌柄,定義了特征空間的一個劃分,分為互不相交的幾個單元侧甫,決策樹的一條路徑就對應(yīng)劃分中的一個單元珊佣,決策樹分類時將實例分到條件概率大的一類去。

決策樹學(xué)習(xí)

決策樹學(xué)習(xí)通常包括3個步驟:特征選擇披粟、決策樹的生成和決策樹的剪枝咒锻。決策樹學(xué)習(xí)的算法通常是一個遞歸地選擇最優(yōu)特征,并根據(jù)該特征對訓(xùn)練數(shù)據(jù)進行分割守屉,使得對各個子數(shù)據(jù)集有一個最好的分類的過程惑艇,這一過程對應(yīng)著特征空間的劃分,也對應(yīng)著決策樹的構(gòu)建拇泛。為了防止過擬合滨巴,我們需要對生成的決策樹自下而上進行剪枝,去掉過于細分的葉節(jié)點俺叭,使其回退到父節(jié)點恭取,甚至更高的節(jié)點,然后將父節(jié)點或更高的節(jié)點改為新的葉節(jié)點熄守。

前提知識

設(shè) X 是一個取有限值的離散隨機變量蜈垮,其概率分布為
P(X=x_i) = p_i, i=1,2,\cdots,n
隨機變量(X,Y)耗跛,其聯(lián)合概率分布為
P(X=x_i,Y=y_j) = p_{i} \cdot p_{j}, i=1,2,\cdots,n; j=1,2,\cdots,m;

我們使用熵來衡量事件所包含的信息量,事件發(fā)生的不確定性越大攒发,包含的信息量就越多调塌,因此熵是事件發(fā)生概率的函數(shù)h(x) = -log(p(x)。對于兩個獨立(不相關(guān))事件 x 和 y惠猿,有h(x,y) = h(x) + h(y)羔砾,同時有 p(x,y) = p(x)p(y),這也符合對數(shù)函數(shù)的性質(zhì)紊扬。
隨機變量 X 的熵是隨機變量 X 中包含的平均信息量蜒茄,因此隨機變量的熵定義為
H(X) = -\sum_{i=1}^{n} p_i log(p_i)

條件熵H(Y|X)表示在已知X的條件下隨機變量Y的不確定性,定義為在X給定的條件下餐屎,Y的條件概率分布的熵對X的數(shù)學(xué)期望
H(Y|X) = \sum_{i=1}^{n} p_iH(Y|X=x_i)
這里檀葛,p_i =P(X=x_i)
當熵和條件熵的概率由數(shù)據(jù)估計得到時,稱為經(jīng)驗熵和經(jīng)驗條件熵腹缩,如果有0概率屿聋,則令 0log0=0

特征選擇

特征選擇在于選取對訓(xùn)練數(shù)據(jù)具有分類能力的特征,準則是信息增益或信息增益比藏鹊。

信息增益表示得知特征X的信息而使得類Y的信息的不確定性減少的程度润讥,將熵H(Y)與條件熵H(Y|X)之差稱為互信息,決策樹學(xué)習(xí)中的信息增益等價于訓(xùn)練數(shù)據(jù)集中類與特征的互信息盘寡。

給定訓(xùn)練數(shù)據(jù)集D和特征A楚殿,經(jīng)驗熵H(D)表示對數(shù)據(jù)集D進行分類的不確定性,經(jīng)驗條件熵H(D|A)表示在特征A給定的條件下對數(shù)據(jù)集D進行分類的不確定性竿痰,它們的差脆粥,即信息增益,表示由于特征A而使得對數(shù)據(jù)集D的分類的不確定性減少的程度影涉。在進行特征選擇的時候变隔。我們選擇信息增益最大的特征,因為信息增益大的特征具有更強的分類能力蟹倾。
信息增益定義為
g(D,A) = H(D) - H(D|A)

設(shè)訓(xùn)練數(shù)據(jù)集為D匣缘,|D|表示其樣本容量。設(shè)有K個類C_k鲜棠,k=1,2,\cdots,K, |C_k|為屬于類C_k的樣本個數(shù)肌厨,\sum_{k=1}^{K}|C_k| = D。設(shè)特征An個不同的取值\{a_1,a_2,\cdots,a_n\}豁陆,根據(jù)特征A的取值將D劃分為n個子集D_1,D_2,\cdots,D_n夏哭,|D_i|D_i的樣本個數(shù),\sum_{i=1}^{n}|D_i| = |D|献联。記子集|D_i|中屬于類|C_k|的樣本的集合為|D_{ik}|竖配,即D_{ik} = D_i \bigcap C_k何址,|D_{ik}|D_{ik}的樣本個數(shù)。
計算信息增益的算法如下:

輸入:訓(xùn)練數(shù)據(jù)D和特征A进胯;
輸出:特征A對訓(xùn)練數(shù)據(jù)集D的信息增益g(D,A)用爪。

  1. 計算數(shù)據(jù)集的經(jīng)驗熵H(D)
    H(D) = -\sum_{k=1}^{K} \frac{|C_k|}{|D|}log_2\frac{|C_k|}{|D|}
  2. 計算特征A對數(shù)據(jù)集D的經(jīng)驗條件熵H(D|A)
    H(D|A) = \sum_{i=1}^{n} \frac{|D_i|}{|D|} H(D_i) = -\sum_{i=1}^{n} \frac{|D_i|}{|D|} \sum_{k=1}^{K} \frac{|D_{ik}|}{|D_i|}log_2\frac{|D_{ik}|}{|D_i|}
  3. 計算信息增益
    g(D,A) = H(D) - H(D|A)

使用信息增益作為劃分訓(xùn)練數(shù)據(jù)集的特征,存在偏向于選擇取值較多的特征的問題胁镐,使用信息增益比可以進行校正偎血。
信息增益比g_R(D,A)定義為其信息增益g(D,A)與訓(xùn)練數(shù)據(jù)集D關(guān)于特征A的值的熵H_A(D)之比,即
g_R(D,A) = \frac{g(D,A)}{H_A(D)}
其中盯漂, H_A(D) = -\sum_{i=1}^{n} \frac{|D_i|}{|D|}log_2\frac{|D_i|}{|D|}颇玷。

決策樹生成

ID3算法

應(yīng)用信息增益準則選擇特征,遞歸地構(gòu)建決策樹就缆。從根節(jié)點開始帖渠,對結(jié)點計算所有可能的特征的信息增益,選擇信息增益最大的特征作為結(jié)點的特征竭宰,由該特征的不同取值建立子結(jié)點空郊;再對子結(jié)點遞歸地調(diào)用以上方法,構(gòu)建決策樹切揭;直到所有特征的信息增益均很小或沒有特征可以選擇為止狞甚。

輸入:訓(xùn)練數(shù)據(jù)集D,特征集A廓旬,閾值\epsilon哼审;
輸出:決策樹T

  1. D中所有實例屬于同一類C_k孕豹,則T為單結(jié)點樹棺蛛,并將類C_k作為該結(jié)點的類標記,返回T巩步;
  2. A = \emptyset,則T為單結(jié)點樹桦踊,并將D中實例數(shù)最大的類C_k作為該結(jié)點的類標記椅野,返回T
  3. 否則籍胯,計算A中各特征對D的信息增益竟闪,選擇信息增益最大的特征A_g
  4. 如果A_g的信息增益小于閾值\epsilon杖狼,則置T為單結(jié)點樹炼蛤,并將D中實例數(shù)最大的類C_k作為該結(jié)點的類標記,返回T蝶涩;
  5. 否則理朋,對A_g的每一可能值a_i絮识,依A_g=a_iD分割為若干非空子集D_i,將D_i中實例數(shù)最大的類作為標記嗽上,構(gòu)建子結(jié)點次舌,由結(jié)點及其子結(jié)點構(gòu)成樹T,返回T;
  6. 對第i個子結(jié)點兽愤,以D_i為訓(xùn)練集彼念,以A-\{A_g\}為特征集,遞歸地調(diào)用1-5步浅萧,得到子樹T_i逐沙,返回T_i
C4.5算法

使用信息增益比來選擇特征洼畅,其他與ID3類似吩案。

實現(xiàn)

例:有一個由15個樣本組成的貸款申請訓(xùn)練數(shù)據(jù),數(shù)據(jù)包括4個特征:年齡土思,是否有工作务热,是否有房子,信貸情況己儒,最后一列是類別崎岂,是否同意貸款。
數(shù)據(jù)train_data.csv如下:

ID,age,hasWork,hasOwnHouse,credit,clazz
1,青年,否,否,一般,否
2,青年,否,否,好,否
3,青年,是,否,好,是
4,青年,是,是,一般,是
5,青年,否,否,一般,否
6,中年,否,否,一般,否
7,中年,否,否,好,否
8,中年,是,是,好,是
9,中年,否,是,非常好,是
10,中年,否,是,非常好,是
11,老年,否,是,非常好,是
12,老年,否,是,好,是
13,老年,是,否,好,是
14,老年,是,否,非常好,是
15,老年,否,否,一般,否

決策樹實現(xiàn)代碼decisionTree.py如下:

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
import queue

class Node:
    def __init__(self, label):
        self.label = label
        self.attribute = None
        self.children = None
        self.x = None
        self.y = None

    def setAttribute(self, attribute):
        self.attribute = attribute

    def setChildren(self, children):
        self.children = children

def entropy(datas, clazz):
    C = datas[clazz].value_counts().values
    D = float(datas.index.size)
    H = -np.sum(C/D * np.log2(C/D))
    return H

def conditional_entropy(datas, clazz, attr):
    sub_d = datas[attr].value_counts()
    label = sub_d.index
    Di = sub_d.values
    D = float(datas.index.size)
    H = []
    for i in range(label.size):
        H.append(entropy(datas[datas[attr]==label[i]], clazz))
    CH = np.sum(Di * H / D)
    return CH

def info_gain(datas, clazz, attr):
    H = entropy(datas, clazz)
    CH = conditional_entropy(datas, clazz, attr)
    G = H - CH
    return G

def info_gain_ratio(datas, clazz, attr):
    G = info_gain(datas, clazz, attr)
    Di = datas[attr].value_counts().values
    D = float(datas.index.size)
    HA = -np.sum(Di / D * np.log2(Di / D))
    GR = G / HA
    return GR


def decisionTree(datas, attrs, clazz, threshold, method = 'ID3'):
    clazz_value_cnts = datas[clazz].value_counts()
    label = clazz_value_cnts.index[0]
    node = Node(label)
    if clazz_value_cnts.size == 1 or attrs.size == 0:
        return node

    IG = np.zeros(attrs.size)
    for i in range(attrs.size):
        if method == 'ID3':
            IG[i] = info_gain(datas, clazz, attrs[i])
        if method == 'C4.5':
            IG[i] = info_gain_ratio(datas, clazz, attrs[i])
    index = IG.argmax()
    attr = attrs[index]
    node.setAttribute(attr)

    if IG.max() < threshold:
        return node

    attrs_value_cnts = datas[attr].value_counts()
    attrs = attrs.drop(attr)
    children = dict()

    for i in range(attrs_value_cnts.size):
        children[attrs_value_cnts.index[i]] = decisionTree(datas[datas[attr]==attrs_value_cnts.index[i]], attrs, clazz, threshold, method)

    node.setChildren(children)
    return node

"""
return tree depth
"""
def getTreeDepth(tree):
    if tree.children == None:
        return 1

    ds = [None] * len(tree.children)
    i = 0
    for node in tree.children.values():
        ds[i] = getTreeDepth(node)
        i = i + 1

    return max(ds) + 1


"""
return the tree's leaves's number
"""

def getLeavesNum(tree):
    if tree.children == None:
        return 1

    num = 0
    for node in tree.children.values():
        num += getLeavesNum(node)

    return num

df = pd.read_csv('train_data.csv', encoding='utf-8')
tree = decisionTree(df, df.columns.drop('clazz').drop('ID'), 'clazz', 0, 'ID3')
printTree(tree)

進行可視化代碼plotTree.py如下:

import matplotlib.pyplot as plt
import pandas as pd
import decisionTree as dt


def xy(tree, y):
    global n
    tree.y = y

    if tree.x != None:
        return tree.x

    if tree.children == None:
        x = n * x0
        n += 1
    else:
        xSum = 0
        k = len(tree.children)
        for node in tree.children.values():
            xSum += xy(node, y - y0)
        x = float(xSum) / k

    tree.x = x
    return x


xx = 0.05
yy = 0.08


def pp(tree, ax):
    if tree == None:
        return

    if tree.children != None:
        for key in tree.children.keys():
            ax.annotate(tree.attribute,
                        xy=(tree.children[key].x + xx, tree.children[key].y + yy),
                        xytext=(tree.x, tree.y),
                        xycoords='figure fraction',
                        textcoords='figure fraction',
                        arrowprops=dict(arrowstyle='->')
                        )
            ax.text((tree.children[key].x + tree.x) / 2, (tree.children[key].y + tree.y) / 2, key,
                    transform=ax.transAxes)
            pp(tree.children[key], ax)
    else:
        ax.text(tree.x, tree.y, tree.label, transform=ax.transAxes)


df = pd.read_csv('train_data.csv', encoding='utf-8')
tree = dt.decisionTree(df, df.columns.drop('clazz').drop('ID'), 'clazz', 0, 'ID3')

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)

h = dt.getTreeDepth(tree)
w = dt.getLeavesNum(tree)

y0 = float(1) / (h + 1)
x0 = float(1) / (w + 1)

n = 1

xy(tree, 1 - y0)
pp(tree, ax)

plt.show()

結(jié)果展示如下:


plotTree.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闪湾,一起剝皮案震驚了整個濱河市冲甘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌途样,老刑警劉巖江醇,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異何暇,居然都是意外死亡陶夜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進店門裆站,熙熙樓的掌柜王于貴愁眉苦臉地迎上來条辟,“玉大人,你說我怎么就攤上這事宏胯∮鸬眨” “怎么了?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵肩袍,是天一觀的道長杭棵。 經(jīng)常有香客問我,道長氛赐,這世上最難降的妖魔是什么魂爪? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任先舷,我火速辦了婚禮,結(jié)果婚禮上甫窟,老公的妹妹穿的比我還像新娘密浑。我一直安慰自己,他們只是感情好粗井,可當我...
    茶點故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布尔破。 她就那樣靜靜地躺著,像睡著了一般浇衬。 火紅的嫁衣襯著肌膚如雪懒构。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天耘擂,我揣著相機與錄音胆剧,去河邊找鬼。 笑死醉冤,一個胖子當著我的面吹牛秩霍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚁阳,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼铃绒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了螺捐?” 一聲冷哼從身側(cè)響起颠悬,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎定血,沒想到半個月后赔癌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡澜沟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年灾票,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茫虽。...
    茶點故事閱讀 40,918評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡刊苍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出席噩,到底是詐尸還是另有隱情,我是刑警寧澤贤壁,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布悼枢,位于F島的核電站,受9級特大地震影響脾拆,放射性物質(zhì)發(fā)生泄漏馒索。R本人自食惡果不足惜莹妒,卻給世界環(huán)境...
    茶點故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绰上。 院中可真熱鬧旨怠,春花似錦、人聲如沸蜈块。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽百揭。三九已至爽哎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間器一,已是汗流浹背课锌。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留祈秕,地道東北人渺贤。 一個月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像请毛,于是被迫代替她去往敵國和親志鞍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,926評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 看過一個很經(jīng)典的比喻获印,喜歡跟愛是不一樣的述雾,喜歡是蕩秋千可以自得其樂,不需別人的回應(yīng)兼丰;愛是蹺蹺板需要一個人坐在對面與...
    我是憨憨你是誰閱讀 87評論 0 0
  • 媽媽鳍征,我感謝您對我的關(guān)愛和照顧黍翎。我更感謝您對我的付出。 但是艳丛,媽媽我有一件重要的事要對您說:期末考試...
    子銘媽媽閱讀 410評論 1 1