1. 程式人生 > >決策樹基本理論學習以及Python程式碼實現和詳細註釋

決策樹基本理論學習以及Python程式碼實現和詳細註釋

首先是樹的概念我們都比較熟悉了,然後決策樹其實就是一棵樹,通過在每一個幾點通過特徵的不同,走向不同的子樹直到走到葉子節點找到分類的標籤,算是完成了分類的過程。分類的過程不難理解,主要的是資料構造過程。
首先是構造的依據是什麼呢,以什麼依據作為特徵使用的選擇條件呢。這裡使用的資訊增益,通過計算資訊增益的方式來選擇特徵作為劃分資料集合的依據,資訊增益最高的特徵就是劃分資料的最佳方式。

資訊增益和熵的計算

資訊增益(information gain)就是選擇一個特徵之後所計算的熵(entropy),與原來的熵的差值即 infoGain=newEntropyoldEntropy
這裡熵的計算方式是 H

=ni=1p(xi)log2p(xi)
計算熵的python程式碼如下

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=float(labelCounts[key])/numEntries #計算每種類別出現的概率 shannonEnt-=prob*log(prob,2) #計算夏農熵 return shannonEnt

劃分資料集合

計算熵之後通過計算每個特徵的資訊增益,來找到最大的資訊增益,選擇出最佳分類特徵,劃分資料集合。
劃分資料集合利用該特徵的不同資料分類,比如選擇人的視力作為分類標準,就可以有近視和正常兩類。劃分資料集合的程式碼如下

def splitDataSet(dataSet,axis,value):
    #這個函式的作用就是從dataSet的第axis維中找到值維value的行,
    #然後將axis這個位置的值去掉後返回
    retDataSet=[]#儲存返回結果
    for featVec in dataSet:
        if featVec[axis]==value:#找到在axis列上值為value的行
            reduceFeatVec=featVec[:axis]
         #   print reduceFeatVec
            reduceFeatVec.extend(featVec[axis+1:])#構造去掉axis位置值的list
            retDataSet.append(reduceFeatVec)#新增到結果list
    return retDataSet

通過計算每一個特徵作為分類依據劃分資料集合後計算響應的熵,減去原來的熵計算資訊增益,遍歷完所有的特徵後選擇資訊增益最大的特徵作為劃分資料集合的依據。
實現:

def chooseBestFeatureToSplit(dataSet):
    numFeatures=len(dataSet[0])-1
    #除去最後一列分類標籤,得到所有特徵的數量
    baseEntropy=calcShannonEnt(dataSet)
    #最初的夏農熵
    baseInfoGain=0.0#基礎資訊增益初始化
    bestFeature=-1#選擇的特徵初始化
    for i in range(numFeatures):
        #遍歷所有的特徵選擇計算每個特徵的增益
        featlist=[example[i] for example in dataSet]
        #抽取出該維的所有特徵
        uniqueVals=set(featlist)
#         print i,uniqueVals
        #對特徵進行驅蟲,建立唯一分類標籤列表
        newEntropy=0.0#初始化去掉該特徵之後的夏農熵
        for value in uniqueVals:
            #計算新的夏農熵
            subDataSet=splitDataSet(dataSet, i, value)
            #利用前邊的劃分函式抽取出該維特徵
            prob=len(subDataSet)/float(len(dataSet))
            #計算value類在資料中的概率
            newEntropy+=prob*calcShannonEnt(subDataSet)
            #計算新的熵
        infoGain=baseEntropy-newEntropy
        #計算該維特徵的資訊增益
#         print infoGain,baseEntropy,newEntropy
        if infoGain>baseInfoGain:
            #如果大於原來的資訊增益,則進行替換,並標記增益最大的特徵維
            baseInfoGain=infoGain
            bestFeature=i

    return bestFeature 
    #返回增益最大的特徵的列

構建決策樹

能夠選擇出最佳的劃分資料集合的特徵之後,要做的就是構建決策樹,這裡使用遞迴的方式來構建決策樹,每次選擇資訊增益最大的特徵作為一次分類的根節點,根據特徵的不同值來繼續遞迴構建子樹。
遞迴的結束條件有兩個:
1、集合內所有的資料都是一個類別
2、用完了所有的特徵依然沒有能夠得到分類,這個時候選擇出現次數最多的類別作為返回結果。

def createTree(dataSet,labels):
    classList=[example[-1] for example in dataSet]
    if classList.count(classList[0])==len(classList):
        #count計算classlist【0】這個標籤出現的數量,
        #如果和總數一致,則說明所有的都是這一個類的,可以作為終止條件
        return classList[0]
    if len(dataSet[0])==1:
        #dataset[0]是資料集的第一行,
        #如果第一行只有一列則說明已經使用完了所有的特徵
        #這時候室友出現次數最多的類別作為返回
        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:
        #對特徵列的每一個類別建立子樹
        subLables=labels[:]
        myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet, bestFeat, value), subLables)
    return myTree

構建決策樹後可以進行儲存,減少以後的計算。

def storeTree(inputTree,filename):
    import pickle
    fw=open(filename,'w')
    pickle.dump(inputTree, fw)
    #將樹序列化之後儲存到檔案中
    fw.close()

def grabTree(filename):
    import pickle
    fr=open(filename,'r')
    return pickle.load(fr)
#將序列化的數解析成樹的形式,返回結果

利用決策樹進行分類

利用決策樹進行分類,通過已有的訓練好的決策樹,給未知類別但是知道不同特徵資料的變數進行分類

def classify(inputTree,featLabels,testVec):
    firstStr=inputTree.keys()[0]
    secondDict=inputTree[firstStr]
    featIndex=featLabels.index(firstStr)
    #確頂當前特徵在資料矩陣中的第幾維
    for key in secondDict.keys():
        if testVec[featIndex]==key:
            #testVex[featIndex]是待分類資料在該特徵出的資料值,
            #通過這個資料值找到子樹的內容
            if type(secondDict[key]).__name__=='dict':
                #如果是字典則需要繼續向下進行從他的子樹中找到分類結果
                classLabel=classify(secondDict[key], featLabels, testVec)
            else:
                #走到葉子節點則返回分類型別
                classLabel=secondDict[key]
    return classLabel

到此決策樹的功能基本實現,決策樹的好處在於我們能夠看到其工作過程,每個特徵在決策書中所在的位置也是可以看到的,所以可以用視覺化的方式繪製決策樹。
完整版的程式碼見https://code.csdn.net/snippets/2593867.git
測試生成的決策樹結果
眼鏡分類結果

決策樹的優缺點
優點:計算複雜度不高,輸出結果利於理解,對中間值缺失不敏感,可以處理不相關的特徵資料
缺點:可能會產生過度匹配的問題
適用資料型別:數值型和標稱型