1. 程式人生 > >機器學習實戰之決策樹

機器學習實戰之決策樹


學習《機器學習實戰》

1、決策樹的構造

1、決策樹理解

決策樹是一種分類器,根據已知的特徵,做一個最純淨的劃分。
例子:現在想構建一個郵件分類系統,第一步:先檢測傳送郵件的域名的地址,若地址是myEmployer.com,就把郵件放在無聊時需要閱讀的郵件,若域名不是。第二步:就檢測該郵件是否 含有曲棍球的關鍵字,若含有曲棍球的關鍵字,則放在需要及時處理的朋友郵件分類裡,沒有曲棍球的關鍵字,就放在無需閱讀的垃圾郵件裡面。整個分類的步驟如下圖所示。
在這裡插入圖片描述

2、決策樹優缺點

優點:計算得複雜度不高,輸出結果易於理解,對中間值的缺失不敏感,可以處理不相干的特徵資料
缺點:可能產生匹配過度的問題

3、構建決策樹的一般流程

  1. 收集資料
  2. 準備資料:適用於標稱型資料,數值型資料需要離散化。(標稱型:一般在有限的資料中取,而且只存在是和否兩種結果(一般用於分類))
  3. 分析資料:用於檢測畫出的圖和自己理解的是否一致
  4. 訓練演算法:構建決策樹,主要是剪枝和構造樹
  5. 測試資料:用於計算錯誤率
  6. 使用演算法

2、ID3

1、資訊增益和熵

在劃分資料集之前之後資訊發生的變化稱為資訊增益,在ID3演算法中,使用資訊增益作為決策點。
集合資訊的度量方式稱為夏農熵或者簡稱為熵。
熵的定義為資訊的期望值。
資訊定義:若待分類的事物可能存在於多個分類之中,則符號Xi的資訊定義為:
在這裡插入圖片描述
其中p(xi)是選擇該分類的概率。
熵的計算方式,所有類別所有可能值包含的資訊期望值。公式如下:
在這裡插入圖片描述

2、例項

計算夏農熵
下面有五個樣本。特徵是不浮出水面可以生存和是否有腳蹼,label是屬於魚類,如下表

不浮出水面可以生存 是否有腳蹼 屬於魚類
1
2
3
4
5

第一步:建立資料特徵矩陣:

def createDataSet():
    dataSet = [[1, 1, 'yes'],
               [1, 1, 'yes'],
               [1, 0, 'no'],
               [0, 1, 'no'],
               [
0, 1, 'no']] labels = ['no surfacing','flippers'] return dataSet, labels

第二步,根據已知特徵計算熵

def calcShannonEnt(dataSet):
    """
    :param dataSet: 特徵列表,dataSet
    :return:
    """
    numEntries = len(dataSet)
    labelCounts = {}
    for featVec in dataSet:  
        currentLabel = featVec[-1] # 獲取特徵
        if currentLabel not in labelCounts.keys(): # 儲存特徵出現的次數
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1
    shannonEnt = 0.0
    for key in labelCounts: # 遍歷儲存所有的特徵列表,也就是yes:3 no :2
        prob = float(labelCounts[key]) / numEntries # 計算每個特徵出現的概率
        shannonEnt -= prob * log(prob, 2)  # 計算總的資訊熵
    return shannonEnt

劃分資料集
分類演算法處理計算夏農熵,還需要劃分資料集,度量劃分資料集的熵,以確定是否正確的劃分資料。對每個特徵劃分資料集的結果計算一次資訊熵,判斷按照那種方式是最好的劃分資料集的方式,比如在二維座標軸上,按照x軸劃分好還是y軸劃分好。
根據跟定的資料集的劃分資料集的程式碼

def splitDataSet(dataSet, axis, value):
    """
    :param dataSet: 資料集列表 dataSet
    :param axis: 劃分的特徵 0-->不浮出水面可以生存和1-->是否有腳蹼
    :param value: 需要返回特徵的值
    :return:
    這個函式很有意思的,尤其是if裡面三行,特徵抽取的程式碼。
    隨著axis的增大,特徵featVec也在移動,reducedFeatVec也隨著移動。這兩個分別佔領的一行資料的兩部分
    """
    retDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]
            reducedFeatVec.extend(featVec[axis+1:])
            retDataSet.append(reducedFeatVec)
    return retDataSet

接下來遍歷整個資料集,迴圈計算夏農熵,找到最好的資料劃分方式。

def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1   # 總的特徵數
    baseEntropy = calcShannonEnt(dataSet) # 計算當前資料集的資訊熵
    bestInfoGain = 0.0
    bestFeature = -1
    for i in range(numFeatures):        # 特徵迭代
        featList = [example[i] for example in dataSet]#獲取當前當前特徵列的所有值
        uniqueVals = set(featList)       # 構建當前列的特徵唯一化
        newEntropy = 0.0
        for value in uniqueVals: #計算每種分類方式的資訊熵
            subDataSet = splitDataSet(dataSet, i, value)
            prob = len(subDataSet)/float(len(dataSet))
            newEntropy += prob * calcShannonEnt(subDataSet)
        infoGain = baseEntropy - newEntropy     
        
        if (infoGain > bestInfoGain):       #計算最好的資訊增益
            bestInfoGain = infoGain         
            bestFeature = i
    return bestFeature

構建決策樹

def majorityCnt(classList):
    """
    :param classList: 分類名稱的列表
    :return: 返回出現次數最多的分類名稱
    """
    classCount={}
    for vote in classList:
        if vote not in classCount.keys(): 
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]
def createTree(dataSet,labels):
    classList = [example[-1] for example in dataSet]
    
    # 類別相同則停止劃分
    if classList.count(classList[0]) == len(classList): 
        return classList[0]
    
    # 遍歷所有特徵,返回出現次數最多的類別
    if len(dataSet[0]) == 1: 
        return majorityCnt(classList)
    
    # 尋找最好的作為特徵劃分的特徵編號
    bestFeat = chooseBestFeatureToSplit(dataSet)
    
    # 獲取該編號對應的值
    bestFeatLabel = labels[bestFeat] 
    
    # 初始化樹結構,儲存樹資訊
    myTree = {bestFeatLabel:{}}
    
    # 刪除當前最優的特徵值
    del(labels[bestFeat])
    
    # 得到列表包含的所有屬性值
    featValues = [example[bestFeat] for example in dataSet]
    uniqueVals = set(featValues)
    
    # 每個資料集上遞迴呼叫,將返回的值插入myTree
    for value in uniqueVals:
        subLabels = labels[:]     # 複製類別標籤   
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
    return myTree

結果列印

if __name__ == '__main__':
    myDat,labels = createDataSet()
    myTree = createTree(myDat,labels)
    print(myTree)

結果:

{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}