1. 程式人生 > >機器學習實戰--決策樹(一)

機器學習實戰--決策樹(一)

決策樹是一種通過推斷分解,逐步縮小待推測事物範圍的演算法結構,重要任務就是理解資料中所蘊含的知識資訊,可以使用不熟悉的資料集合,並從中提取出一系列規則,根據資料集建立規則的過程就是機器學習的過程。 優點:計算複雜度不高,輸出結果易於理解,對中間值的缺失不敏感,可以處理不相關特徵的資料。 缺點:可能產生過度匹配的問題。

### 決策樹的構造 使用資訊理論劃分資料集,要知道當前資料集的哪個特徵起決定性作用,為找到決定性特徵,必須評估每個特徵。原始資料集被劃分成幾個子集,分佈在第一個決策點的所有分支上。如果某個分支下的所有資料屬於同一個型別,則無需進一步劃分,反之則重複劃分資料子集。採用--ID3--演算法劃分資料集,每次劃分資料集時只選取一個特徵。|不浮出水面可以生存| 是否有腳蹼 |屬於魚類 |1-是----------------------|-是------------|是 |2-是---------------------  |-是------------|是 |3-是---------------------  |-否------------|否 |4-否---------------------  |-是------------|否 |5-否---------------------  |-是------------|否

劃分資料集最大原則:將無序資料變得更加有序,劃分資料集之前之後資訊的變化成為資訊增益,計算每個特徵值劃分資料集獲得的資訊增益。計算增益的資訊度量方式稱為夏農熵。 符號的資訊定義為:l(xi) = -\log {_{2}}^{p(xi))},需要計算所有類別的所有可能值包含的期望。通過下面的公式得到:

H = -\sum_{1}^{n}p(xi){\log_{2}}^{p(xi)} ,p(xi)指該類出現的概率。

計算給定資料集的夏農熵

from math import log
import operator
#sys.path.append('G:/BasicPython/ML')
#print(sys.path)
def calcShannonEnt(dataSet):
    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:
        #Prob代表該標籤類出現的概率,為該標籤出現次數/總次數
        prob = float(labelCounts[key])/numEntries
        shannonEnt -= prob*log(prob,2)
    return shannonEnt

建立一個數據字典,來統計資料集中最後一列即目標值的大小與次數,然後對目標值進行遍歷,統計每類目標值的頻率,和該目標值對應的資訊熵。最後累加得到輸入資料集的熵,熵越高則混合的資料越多。

劃分資料集

劃分資料集為了度量劃分資料集的熵,以便判斷是否正確劃分。將對每個特徵劃分資料集的結果計算一次資訊熵,然後判斷哪個特徵劃分資料集是最好的方式。

按照給定特徵劃分資料集:

def splitDataSet(dataSet,axis,value):
    retDataSet = []
    for featVec in dataSet:
        #判斷特徵值是否與給定值匹配
        if featVec[axis] == value:
            #將對應的特徵項挖去
            reducedFeatVec = featVec[:axis]
            reducedFeatVec.extend(featVec[axis+1:])
            retDataSet.append(reducedFeatVec)
    return retDataSet

三個引數分別為:待劃分資料集,劃分資料集的特徵,需要返回的特徵的值,在if語句中將每個樣本的特徵項與給定特徵值進行比較,相等的話,則挖去該特徵項,並重新新增到新的資料集中。最後返回的資料集滿足:都歸屬於同一個特徵值劃分的資料集。

接下來將遍歷整個資料集,迴圈計算夏農熵和splitDataSet()函式,找到做好的特徵劃分方式。熵計算告訴我們如何劃分資料集是最好的資料劃分方式。

選擇最好的資料集劃分方式:

#選擇最好的資料集劃分方式
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
len(dataSet[0])-1可以得到資料集中特徵的個數,然後計算得到初始的夏農熵作為初始的比較值。遍歷特徵列表,得到特徵對應的特徵值列表featList,然後對特徵值集合進行遍歷,求得每個特徵值的出現概率即權重,對每個特徵值劃分的資料集求夏農熵,最後累加求和得到的是該特徵對應的所有特徵值的平均夏農熵,也就是該特徵對應的夏農熵,將初始夏農熵與之相減的含義是熵降低的顯著程度,最後得到熵降低程度最大的的特徵的索引。

遞迴構建決策樹

前面都是從決策及構造決策樹演算法所需要的子功能模組。第一次劃分後,資料集向下傳遞到分支的下一個節點,在該點上可以繼續劃分資料。因此可以用遞迴原則處理資料集。遞迴結束的條件:程式遍歷完所有劃分資料集的屬性,每個分支下的所有例項具有相同分類。當資料集已經處理了所有的屬性,但類標籤仍不是唯一的,通常採用投票表決。

投票表決:

#對於所有特徵已經消耗完,但類標籤不是唯一時的投票表決
def majorityCnt(classList):
    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)
    #遍歷特徵值,不同特徵值代表當前節點的不同分支
    for value in uniqueVals:
        #由於列表作為引數傳遞是引用的,因此將其複製到新的列表,防止改變原列表
        subLabels = labels[:]
        #在符合特徵對應的特徵值和去除特徵項的資料集的情況下開啟下一層遞迴
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
    #返回的值插入到上一層的樹字典中
    return myTree

兩個輸入引數:資料集,標籤列表。對資料集的類標籤的值放入列表中,然後判斷是否所有類標籤的值都相同,是的話則返回該標籤值;倘若特徵消耗完但是仍未判斷出類,則將類標籤傳給majorityCnt()的投票表決函式,返回“大多數”的類標籤。以上都是兩個終止條件。遞迴時,得倒當前資料集的最優特徵的索引和特徵名,構建決策樹一個樹的分支字典,鍵為判斷分支條件的特徵項,值為相應的分支。然後得到最優特徵對應的所有特徵值。遍歷這些特徵值,這些特徵值作為分支,其值等於在該特徵值條件下的資料集,即通過該特徵值劃分的資料集,需要下一層遞迴。