1. 程式人生 > >ID3和C4.5決策樹演算法總結

ID3和C4.5決策樹演算法總結

1.決策樹的演算法流程

決策樹的演算法流程主要是:
1.如果當前樣本集全部為同一類別,則返回這一類標籤
2.如果當前屬性集為空集或者D中樣本在屬性集中的取值全部相同,那麼採用多數表決法,返回樣本數最多的類標籤
3.如果不滿足上面三個條件,說明當前結點還可以繼續劃分,這時候要選擇最優的屬性
4.選擇完屬性之後根據屬性值劃分樣本,如果在某個取值下樣本集為空,那麼標記為父節點中樣本最多的類,否則遞迴產生子節點
5.返回根節點

2.ID3決策樹

ID3決策樹選擇最優屬性的方式是選擇能使劃分後的樣本集合資訊增益最大的屬性
假設樣本第k類的樣本所佔的比例是pk,樣本一共有C
資訊熵的定義為
E

nt(D)=Ck=1pklogpk2
假設屬性a有V個取值
用屬性a劃分集合後的資訊增益定義為
Gain(D,a)=Ent(D)Vv=1|Dv|/|D|Ent(Dv)
選擇能使資訊增益最大的屬性
但是注意到,ID3演算法可能對取值較少的屬性有所偏好,所以C4.5演算法對這點進行了改變。

3.C4.5決策樹

C4.5演算法使用增益率來衡量屬性的優劣,增益率定義為:
Gainratio(D,a)=Gain(D,a)/IV(a)
IV(a)=Vv=1|Dv|/|D|log|Dv|/|D|2
其中IV(a)稱為屬性a的固有值,注意到這個固有值的計算公式和資訊熵的很類似。
屬性a的可能取值數目越多,IV(a)的值通常會越大,這同樣帶來了一個問題,這可能對屬性取值較少的屬性有所偏好,所以C4.5演算法採用了一個啟發式的方案,首先先從候選劃分屬性中找出資訊增益高於平均水平的屬性,再從中選擇增益率最高的。

連續值處理

C4.5演算法採用二分法對連續屬性進行處理,假設當前樣本集合在某個連續屬性上有n個取值,首先對這n個屬性值排序,然後取每兩個相鄰值的平均值作為劃分值來考察。
與離散屬性不同,若當前結點劃分屬性為連續屬性,那麼該屬性還可以作為後代結點的劃分屬性。

缺失值處理

現實中常常遇到不完整樣本,也就是樣本在某些屬性的取值缺失,此時需要解決兩個問題:
1.如何在屬性值缺失的情況下進行屬性選擇
2.給定劃分屬性,如何對缺失該屬性的樣本進行劃分
解決這兩個問題的方法是給每一個樣本一個權值,計算資訊增益的時候只選用沒有缺失值的樣本,選擇完屬性後,讓每一個缺失該屬性值的樣本以不同的概率進入到每個子節點中,也就是將進入這些子節點的該樣本的權值置為不同的值。

4.Python實現

下面是用python實現ID3的程式碼,沒有新定義結點的資料結構,而是使用了內建的dict型別表示非葉結點結點,對於葉子節點用一個字串(類標籤)。對於每一個非葉結點,只有一個鍵值為當前結點的劃分屬性,然後是屬性值對應的葉子節點。
例如:
一棵典型的數的根節點是這個樣子的:
{‘a’: {0: ‘no’, 1: {‘b’: {0: ‘no’, 1: ‘yes’}}}}
其中a,b為屬性名,0,1為屬性值,yes,no為類別名

from numpy import *
from math import log
import operator

# ID3演算法

def createDataSet():
    dataSet = [[1, 1, 'yes'],
               [1, 0, 'no'],
               [0, 1, 'no'],
               [0, 0, 'no']]
    feature = ['a', 'b']
    featureValues = [[0, 1], [0, 1]]
    return dataSet, feature, featureValues

# 計算熵
def calEnt(dataSet):
    num = len(dataSet)
    labelCount = {}
    for data in dataSet:
        label = data[-1]
        if label not in labelCount:
            labelCount[label] = 0
        labelCount[label] += 1
    ent = 0
    for label in labelCount:
        p = float(labelCount[label])/num
        ent -= p * log(p, 2)
    return ent

# 劃分資料集
def splitByVal(dataSet, axis, val):
    retDataSet = []
    for data in dataSet:
        if data[axis] == val:
            newData = data[:axis]
            newData.extend(data[axis+1:])
            retDataSet.append(newData)
    return retDataSet

# 選擇劃分方式
def chooseBestFeature(dataSet):
    numFeatures = len(dataSet[0]) - 1
    numVector = len(dataSet)
    baseEnt = calEnt(dataSet)
    bestInfoGain = 0
    bestFeature = -1
    for i in range(numFeatures):
        featureList = [data[i] for data in dataSet]
        featureList = set(featureList)
        currentEnt = 0
        for feature in featureList:
            tmpDataSet = splitByVal(dataSet, i, feature)
            tmpLen = len(tmpDataSet)
            tmpEnt = calEnt(tmpDataSet)
            currentEnt += tmpEnt * tmpLen / numVector
        currentInfoGain = baseEnt - currentEnt
        if (currentInfoGain > bestInfoGain):
            bestFeature = i
            bestInfoGain = currentInfoGain
    return bestFeature

# 找出多數類別
def findMajoritiyClass(dataSet):
    classCount = {}
    for data in dataSet:
        currentClass = data[-1]
        if currentClass not in classCount.keys():
            classCount[currentClass] = 0
        else:
            classCount[currentClass] += 1
    sortedClassCount = sorted(classCount.items(), key=lambda d : d[1], reverse=True)
    return sortedClassCount[0][0]

# 建樹操作
def createTreeNode(dataSet, labels, featureValues):
    classList = [example[-1] for example in dataSet]
    majorityClass = findMajoritiyClass(dataSet)

    # 如果類別相同,將當前節點標記為這個類別
    if classList.count(classList[0]) == len(classList):
        return classList[0]

    # 如果當前屬性集為空或者樣本在屬性集中的取值相同
    # 採用多數表決的方法將當前節點標記為數量最多的類別
    if len(dataSet[0]) == 1 or dataSet.count(dataSet[0]) == len(dataSet):
        return majorityClass

    # 其他情況
    bestFeature = chooseBestFeature(dataSet)
    bestFeatureLabel = labels[bestFeature]
    del(labels[bestFeature])
    node = {bestFeatureLabel:{}}
    for val in featureValues[bestFeature]:
        subLabels = labels[:]
        subFeatureValues = featureValues[:bestFeature]
        subFeatureValues.extend(featureValues[bestFeature+1:])
        subDataSet = splitByVal(dataSet, bestFeature, val)

        #如果子集為空,那麼輸出父節點多數類的標籤
        if len(subDataSet) == 0:
            node[bestFeatureLabel][val] = majorityClass
        else:
            node[bestFeatureLabel][val]  = createTreeNode(subDataSet, subLabels, subFeatureValues)
    labels.insert(bestFeature, bestFeatureLabel)
    return node

def classify(node, featureLabels, featureValues, inx):
    while type(node) is dict:
        firstFeature = list(node.keys())[0]
        featureIndex = featureLabels.index(firstFeature)
        featureValue = inx[featureIndex]
        node = node[firstFeature][featureValue]
    return node


#自己構造資料集 ,屬性集,屬性的取值集合
dataSet, labels, featureValues = createDataSet()
#構造決策樹
root = createTreeNode(dataSet, labels, featureValues)
print(root)
#分類,最後一個是要預測的向量
classOfX = classify(root, labels, featureValues, [1, 1])
print(classOfX)
print(root)