1. 程式人生 > >機器學習--DIY筆記與感悟--②決策樹(1)

機器學習--DIY筆記與感悟--②決策樹(1)

lis ... 編寫代碼 需要 總結 初始化 對數 三分 xtend

在完成了K臨近之後,今天我們開始下一個算法--->決策樹算法。

一、決策樹基礎知識

如果突然問你"有一個陌生人叫X,Ta今天需要帶傘嗎?", 你一定會覺得這個問題就像告訴你"兩千米外有一個超市,問超市裏面有多少卷衛生紙"一樣突兀. 可能幾秒鐘之後你會說"這要依情況而定, 如果今天烈日炎炎並且X是一個皮膚白皙的中國姑娘,或者外面下著大雨並且X是一個不得不去步行上班的屌絲碼農, 那麽Ta今天需要帶傘, 否則不要."

當談及這個看似無聊的問題的時候我們在談什麽?恩,在談方法. 我們經常會遇到簡單但又困難的抉擇. 說簡單,是因為候選方案簡單而且清晰. 例如,要不要出國留學,要不要辭職創業,要不要投資黃金等等. 但是這些看似簡單的選項背後卻是困難的抉擇. 面對這些困難的抉擇, 一開始, 很可能不知所措. 值得慶幸的是, 很多時候, 這些看似不可能解決的問題, 最終可以被化解成一連串越來越簡單的問題, 然後解決掉. 這個方法可以粗俗總結為: "如果這樣這樣這樣, 那麽選擇A; 如果那樣那樣那樣, 那麽選擇B", 當然這種方法有一個更加簡潔形象的名字--決策樹(Decision Tree)。

現在大家應該對決策樹應該有了一個大致的了解。簡單來說,決策樹就是內心的一個決策過程,既:如果我要達成某個結果,那麽我需要先怎麽樣,然後怎麽樣,滿足什麽樣的條件balabala.....

舉一個通俗的栗子,各位立誌於脫單的單身男女在找對象的時候就已經完完全全使用了決策樹的思想。假設一位母親在給女兒介紹對象時,有這麽一段對話:

母親:給你介紹個對象。
女兒:年紀多大了?
母親:26。
女兒:長的帥不帥?
母親:挺帥的。
女兒:收入高不?
母親:不算很高,中等情況。
女兒:是公務員不?
母親:是,在稅務局上班呢。
女兒:那好,我去見見。

畫成樹狀圖即為:

技術分享圖片

上圖則為一個簡單的決策樹。

二、決策樹理論--信息論

大家不要被高大上的信息論給嚇到了,決策樹算法中涉及信息論的知識很少,也就是幾個概念。所以了解了對應的概念就沒有問題了。

這裏我準備使用一個例子來講述相應理論。

ID年齡有工作有自己的房子信貸情況類別(是否發放貸款)
1 青年 一般
2 青年
3 青年
4 青年 一般
5 青年 一般
6 中年 一般
7 中年
8 中年
9 中年 非常好
10 中年 非常好
11 老年 非常好
12 老年
13 老年
14 老年 非常好
15 老年 一般

上面我加載了一張數據表格。這張表格的內容為貸款申請樣本數據表。也就是說這張表格中每一行的結果均為是否成功申請到貸款。而能否成功申請到貸款取決於一些神秘的力量。

神秘的力量是什麽呢?這個力量與四個特征有某些神奇的關系(這個關系就是我們要找的決策樹)--申請人的年齡、是否有工作、是否有房子、信貸情況。

之後我們就要使用信息論中--熵的概念來提煉出哪一個特征對結果影響更大。

這裏引入熵的概念:

通常,一個信源發送出什麽符號是不確定的,衡量它可以根據其出現的概率來度量。概率大,出現機會多,不確定性小;反之就大。

通俗來說,在這個例子中,計算“是否發放貸款”這個信息的熵(這裏表示為H),得到的H值的大小表示為該結果的不確定性。而H的值越大,不確定性越大。

對應的計算公式為:技術分享圖片

根據此公式計算經驗熵H(D),分析貸款申請樣本數據表中的數據。最終分類結果只有兩類,即放貸和不放貸。根據表中的數據統計可知,在15個數據中,9個數據的結果為放貸,6個數據的結果為不放貸。所以數據集D的經驗熵H(D)為:

技術分享圖片

經過計算可知,數據集D的經驗熵H(D)的值為0.971。不確定度也就是為0.971Z這麽大(這裏不是概率)

三、編寫代碼計算熵

在編寫代碼之前,我們先對數據集進行屬性標註。

  • 年齡:0代表青年,1代表中年,2代表老年;
  • 有工作:0代表否,1代表是;
  • 有自己的房子:0代表否,1代表是;
  • 信貸情況:0代表一般,1代表好,2代表非常好;
  • 類別(是否給貸款):no代表否,yes代表是。

這樣上述表格就可以規範化為

dataSet = [[0, 0, 0, 0, no],         #數據集
            [0, 0, 0, 1, no],
            [0, 1, 0, 1, yes],
            [0, 1, 1, 0, yes],
            [0, 0, 0, 0, no],
            [1, 0, 0, 0, no],
            [1, 0, 0, 1, no],
            [1, 1, 1, 1, yes],
            [1, 0, 1, 2, yes],
            [1, 0, 1, 2, yes],
            [2, 0, 1, 2, yes],
            [2, 0, 1, 1, yes],
            [2, 1, 0, 1, yes],
            [2, 1, 0, 2, yes],
            [2, 0, 0, 0, no]]

這裏放上代碼:

#coding:utf-8

"""基於信息量的決策樹算法(Decision Tree),計算當前數據的香農熵"""

from math import log

def createData():
    data = [[0, 0, 0, 0, no],         #數據集
            [0, 0, 0, 1, no],
            [0, 1, 0, 1, yes],
            [0, 1, 1, 0, yes],
            [0, 0, 0, 0, no],
            [1, 0, 0, 0, no],
            [1, 0, 0, 1, no],
            [1, 1, 1, 1, yes],
            [1, 0, 1, 2, yes],
            [1, 0, 1, 2, yes],
            [2, 0, 1, 2, yes],
            [2, 0, 1, 1, yes],
            [2, 1, 0, 1, yes],
            [2, 1, 0, 2, yes],
            [2, 0, 0, 0, no]]

    labels = [年齡, 有工作, 有房子, 信貸情況]
#分類標簽
    return data,labels

def calcu_Entropy(data):
    dataCount = len(data)
#計算data的個數
    labelCounts = {}
#用來計算每個標簽出現的次數
    for everVec in data:
        clac_label = everVec[-1]
#取出所需計算的標簽(-1代表倒數第一列的值)
        if clac_label not in labelCounts.keys():
            labelCounts[clac_label] = 0
#.keys()函數為:返回當前字典裏的鍵
#上述判斷是為了查看當前的字典裏是否有此鍵值,若沒有將此鍵的值更新為0,為了方便以後計算數量
        labelCounts[clac_label] += 1
#當前的鍵每出現一次,鍵值就加1(用來計算出現的次數)
    shannonEn = 0.0
#初始化香農熵
    for i in labelCounts:
        prob = float(labelCounts[i]) / float(dataCount)
#prob為每一個鍵出現的概率
        shannonEn -= prob * log(prob, 2)
#計算香農熵,H = -P1 * log(2,p1) - P2 * log(2,p2)....- Pn * log(2,pn)
    return shannonEn



if __name__ == __main__:
    data,labels = createData()
    print calcu_Entropy(data)

經過計算,得到“是否發放貸款”的香農熵為:

技術分享圖片

四、決策樹理論--互信息

互信息(Mutual Information)是信息論裏一種有用的信息度量,它可以簡單的形容為一個隨機變量由於已知另一個隨機變量而減少的不肯定性。

它的公式為:技術分享圖片

而H(D|A)為條件熵,既在已知A的前提下D的熵。而H(D) - H(D|A)則可以理解為什麽都不知道的前提下求得的D的熵 - 已知A的前提下D的熵。又由於熵表示不確定程度,所以這個公式表示了不確定程度的減少,既已知A對D的不確定程度的減少量。

而條件熵的公式為:技術分享圖片

這裏放入一個鏈接,如果不是很明白這個概念的同學可以跳轉學習下。

http://blog.csdn.net/xwd18280820053/article/details/70739368

這下是不是好理解了一些呢?對於例子來說,看下年齡這一列的數據,也就是特征A1,一共有三個類別,分別是:青年、中年和老年。

我們只看年齡是青年的數據,年齡是青年的數據一共有5個,所以年齡是青年的數據在訓練數據集出現的概率是十五分之五,也就是三分之一。

同理,年齡是中年和老年的數據在訓練數據集出現的概率也都是三分之一。

現在我們只看年齡是青年的數據的最終得到貸款的概率為五分之二,因為在五個數據中,只有兩個數據顯示拿到了最終的貸款,同理,年齡是中年和老年的數據最終得到貸款的概率分別為五分之三、五分之四。所以計算年齡的信息增益,過程如下:

技術分享圖片

同理,計算其余特征的信息增益g(D,A2)、g(D,A3)和g(D,A4)。分別為:

技術分享圖片

技術分享圖片

技術分享圖片

得到A3的互信息最大,既在知道A3的條件下對我們得到結果最有幫助(減少可能性最大)。

五、計算互信息量的代碼

這裏仍然使用了python 2.7.10版本。

from math import log

def createData():
    data = [[0, 0, 0, 0, no],         #數據集
            [0, 0, 0, 1, no],
            [0, 1, 0, 1, yes],
            [0, 1, 1, 0, yes],
            [0, 0, 0, 0, no],
            [1, 0, 0, 0, no],
            [1, 0, 0, 1, no],
            [1, 1, 1, 1, yes],
            [1, 0, 1, 2, yes],
            [1, 0, 1, 2, yes],
            [2, 0, 1, 2, yes],
            [2, 0, 1, 1, yes],
            [2, 1, 0, 1, yes],
            [2, 1, 0, 2, yes],
            [2, 0, 0, 0, no]]

    labels = [年齡, 有工作, 有房子, 信貸情況]
#分類標簽
    return data,labels

def calcu_Entropy(data):
    dataCount = len(data)
#計算data的個數
    labelCounts = {}
#用來計算每個標簽出現的次數
    for everVec in data:
        clac_label = everVec[-1]
#取出所需計算的標簽
        if clac_label not in labelCounts.keys():
            labelCounts[clac_label] = 0
#.keys()函數為:返回當前字典裏的鍵
#上述判斷是為了查看當前的字典裏是否有此鍵值,若沒有將此鍵的值更新為0,為了方便以後計算數量
        labelCounts[clac_label] += 1
#當前的鍵每出現一次,鍵值就加1(用來計算出現的次數)
    shannonEn = 0.0
#初始化香農熵
    for i in labelCounts:
        prob = float(labelCounts[i]) / float(dataCount)
#prob為每一個鍵出現的概率
        shannonEn -= prob * log(prob, 2)
#計算香農熵,H = -P1 * log(2,p1) - P2 * log(2,p2)....- Pn * log(2,pn)
    return shannonEn

def choose_column_in_data(data,column,value):
#該函數意義為選擇當前數據集中以某一列(例如選擇年齡、或者選擇是否有房子等)作為依據的數據集合
#其中data是所有數據集,column是所選擇列的編號,value為所需要選定的值(例如我需要年齡為青年的人,那麽value就為:青年)
    dataConsequence = []
    for everVec in data:
        if everVec[column] == value:
#對每一條數據進行處理,若該數據對應位置的值為value,則進行下一步
            mid = everVec[:column]
            mid.extend(everVec[column+1:])
            dataConsequence.append(mid)
    return dataConsequence


def Choose_Best_Mutua_Information(data):
#此函數含義為選擇當前數據中使熵增最大的特征
    bestFeature = -1
#預設最優解
    bestFeatureValue = -1000.0
#預設最大增益
    Featurenum = len(data[0]) - 1
#提取單個數據的特征數量
    baseEntropy = calcu_Entropy(data)
#計算總的熵
    for i in range(Featurenum):
        midList = [example[i] for example in data]
#中間變量暫存所有個體的第i個特征的數據(既將表格豎著存入數組)
        columnType = set(midList)
#打印出每一列特征所包含的數據有哪些類型,例如[0,0,1,1,2,2] -> [0,1,2]
        conditionsEnt = 0.0
#條件熵
        for value in columnType:
#取當前特征下每一個類型出來
            currentData = choose_column_in_data(data,i,value)
#既在data中,選擇第i列中值為value的所有數據並組合到一起
            prob = len(currentData) / float(len(data))
#類似於那個例子中的 5/15 * 2/5
            conditionsEnt += prob * calcu_Entropy(currentData)
#H = -P(a) * P(D|a) * log(2,P(D|a))
        increaseEntropy = baseEntropy - conditionsEnt
#得出:已知當前特征(i)時,得到的熵增益
        print "第%d個特征熵的增益為:%.3f" %(i,increaseEntropy)
        if increaseEntropy > bestFeatureValue:
            bestFeatureValue = increaseEntropy
            bestFeature = i
    return bestFeature




data,labels = createData()
final = Choose_Best_Mutua_Information(data)
print "當前數據中熵增益最大的特征的編號為:  編號",final,

得到了結果為:

技術分享圖片

跟我們計算的值相同。

下一篇我將繼續講解如何構建具體的決策樹。

機器學習--DIY筆記與感悟--②決策樹(1)