機器學習中的那些樹——決策樹(二)

前言

在上篇文章中介紹了決策樹的一些基本概念,在這篇文章中,將介紹決策樹ID3和C4.5算法的代碼實現以及一些優化。html

ID3實現

ID3算法的核心是在決策樹各個節點上應用信息增益準則選擇特徵,遞歸地構建決策樹,算法僞代碼在上篇文章中給出了。在這裏將給出其代碼實現。python

代碼

import pandas as pd
from math import log

def load(filename):
    '''
        input: 文件
        output: DataFrame數據
    '''
    df = pd.read_csv(filename)
    return df

def calEnt(df):
    '''
        input: 數據集
        output: 熵
        descripion: 計算給定數據集的熵
    '''
    # 返回數據行數
    numEntries = df.shape[0]
    # 字典,用於保存每一個標籤(label)出現次數
    labelCounts = {}
    cols = df.columns.tolist()
    # 獲取標籤
    classlabel = df[cols[-1]].tolist()
    # 對每組特徵向量進行統計
    for currentlabel in classlabel:
        if currentlabel not in labelCounts.keys():
            labelCounts[currentlabel] = 1
        else:
            labelCounts[currentlabel] += 1

    Ent = 0.0
    # 計算熵
    for key in labelCounts:
        prob = labelCounts[key]/numEntries
        Ent -= prob*log(prob, 2)

    return Ent

def splitDatsSet(df, axis, value):
    '''
        input: 數據集,所佔列,選擇值
        output: 劃分數據集
        description: 按照給定特徵劃分數據集;選擇所佔列中等於選擇值的項
    '''
    cols = df.columns.tolist()
    axisFeat = df[axis].tolist()
    # L = []
    # L.append(axis)

    # retDataset = df[list(set(cols)-set(L))]
    retDataset = pd.concat([df[feaVec] for feaVec in cols if feaVec != axis], axis=1)

    i = 0
    # 須要丟棄的行
    dropIndex = []
    for feaVec in axisFeat:
        if feaVec != value:
            dropIndex.append(i)
            i += 1
        else:
            i += 1
    # 劃分數據集
    newDataset = retDataset.drop(dropIndex)
    return newDataset.reset_index(drop=True)


def chooseBestFeatureToSplit(df):
    '''
        input: 數據集
        output: 最優特徵和信息增益
    '''
    # 獲取特徵數量
    numFeatures = len(df.columns) - 1
    # 計算熵
    Ent = calEnt(df)
    # 信息增益
    bestInfoGain = 0.0
    # 最優特徵索引值
    bestFeature = -1
    cols = df.columns.tolist()
    # 遍歷全部特徵
    for i in range(numFeatures):
        # 獲取第i個特徵的全部不一樣的取值
        equalVals = set(df[cols[i]].tolist())
        # 經驗條件熵
        newEnt = 0.0
        # 計算信息增益
        for value in equalVals:
            # 獲取劃分後的子集
            subDataset = splitDatsSet(df, cols[i], value)
            # 計算子集機率
            prob = subDataset.shape[0] / df.shape[0]
            # 根據公式計算條件熵
            newEnt += prob * calEnt(subDataset)
        infoGain = Ent - newEnt
        print(cols[i], infoGain)
        # 更新信息增益,找到最大的信息增益
        if infoGain > bestInfoGain:
            bestInfoGain = infoGain
            bestFeature = cols[i]
    return bestFeature, bestInfoGain



def majorityCnt(classList):
    '''
        input: 類別列表
        output: 子節點的分類
        description: 數據集已經處理了全部屬性,可是類標籤依然不是惟一的,
          採用多數判決的方法決定該子節點的分類
    '''
    classCount = {}
    # 統計classList中每一個元素出現的次數
    for clas in classList:
        if clas not in classCount.keys():
            classCount[clas] = 0
        classCount[clas] += 1
    # 根據字典值降序排列
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reversed=True)
    return sortedClassCount[0][0]

def createTree(df, dropcol):
    '''
        input: 數據集和需刪除特徵
        output: 決策樹
        description: 遞歸實現決策樹構建
    '''
    # cols = df.columns.tolist()[:-1]
    # 取分類標籤
    classList = df[df.columns.tolist()[-1]].tolist()

    # 若數據集中全部實例屬於同一類,將Ck做爲該節點的類標記
    if classList.count(classList[0]) == len(classList):
        return classList[0]

    # 若特徵集爲空集,將數據集中實例數最大的類Ck做爲該節點的類標記
    if len(df[0:1]) == 0:
        return majorityCnt(classList)

    # 獲取最優特徵與信息增益
    bestFeature, bestInfoGain = chooseBestFeatureToSplit(df)

    print('特徵集和類別:',df.columns.tolist())
    print('bestFeature:',bestFeature)
    # 根據最優特徵的標籤生成樹
    myTree = {bestFeature:{}}
    # 獲得最優特徵的屬性值
    featValues = df[bestFeature]
    # 去掉重複屬性值
    uniqueVals = set(featValues)
    # 遍歷建立決策樹
    for value in uniqueVals:
        myTree[bestFeature][value] = createTree(splitDatsSet(df, bestFeature, value), bestFeature)
    return myTree

def main():
    filename = "data.csv"
    dataset = load(filename)
    dropCol = []
    myTree = createTree(dataset, dropCol)
    print(myTree)

if __name__ == '__main__':
    main()

輸入

輸入選取統計學習方法中給出的數據集算法

age,work,hourse,loan,class
青年,否,否,通常,否
青年,否,否,好,否
青年,是,否,好,是
青年,是,是,通常,是
青年,否,否,通常,否
中年,否,否,通常,否
中年,否,否,好,否
中年,是,是,好,是
中年,否,是,很是好,是
中年,否,是,很是好,是
老年,否,是,很是好,是
老年,否,是,好,是
老年,是,否,好,是
老年,是,否,很是好,是
老年,否,否,通常,否

輸出

age 0.08300749985576883
work 0.32365019815155627
hourse 0.4199730940219749
loan 0.36298956253708536
特徵集和類別: ['age', 'work', 'hourse', 'loan', 'class']
bestFeature: hourse
age 0.2516291673878229
work 0.9182958340544896
loan 0.47385138961004514
特徵集和類別: ['age', 'work', 'loan', 'class']
bestFeature: work
{'hourse': {'是': '是', '否': {'work': {'是': '是', '否': '否'}}}}

ID3算法的缺點

  1. ID3算法對於缺失值沒有進行考慮
  2. 沒有考慮過擬合的狀況
  3. ID3沒有考慮連續特徵
  4. 上篇文章中有提到,ID3以信息增益做爲劃分訓練數據集的特徵,存在偏向於選擇取值較多的特徵的問題。
  5. ID3採用信息增益大的特徵優先創建決策樹的節點。很快就被人發現,在相同條件下,取值比較多的特徵比取值少的特徵信息增益大。

C4.5實現

C4.5就是在上面提出ID3的缺點中的第4條進行的改正,大部分代碼都相同,除了在劃分數據集的時候以信息增益比爲劃分標準,這在上篇文章中也提到過。app

def chooseBestFeatureToSplit(df):
    '''
        input: 數據集
        output: 最優特徵和信息增益
    '''
    # 獲取特徵數量
    numFeatures = len(df.columns) - 1
    # 計算熵
    Ent = calEnt(df)
    # 信息增益
    bestInfoGain = 0.0
    splitInfo = 0.0
    # 最優特徵索引值
    bestFeature = -1
    cols = df.columns.tolist()
    # 遍歷全部特徵
    for i in range(numFeatures):
        # 獲取第i個特徵的全部不一樣的取值
        equalVals = set(df[cols[i]].tolist())
        # 經驗條件熵
        newEnt = 0.0
        # 計算信息增益
        for value in equalVals:
            # 獲取劃分後的子集
            subDataset = splitDatsSet(df, cols[i], value)
            # 計算子集機率
            prob = subDataset.shape[0] / df.shape[0]
            # 根據公式計算條件熵
            newEnt += prob * calEnt(subDataset)
            # 計算特徵熵
            splitInfo += -prob * log(prob, 2)
        infoGain = Ent - newEnt
        print(cols[i], infoGain)
        # 計算信息增益比
        infoGainRatio = infoGain / splitInfo
        # 更新信息增益比,找到最大的信息增益比
        if infoGainRatio > bestInfoGain:
            bestInfoGain = infoGainRatio
            bestFeature = cols[i]
    return bestFeature, infoGainRatio

結語

這篇文章給出了ID3與C4.5的代碼實現,同時也提到了其中的一些缺點,對於缺點2中提到的過擬合現象,咱們能夠經過剪枝來簡化模型,從而下降這一現象的產生。對於剪枝的實現及介紹,將在後面介紹CART算法時再講述。機器學習

參考:學習

  1. 《統計學習方法》——李航
  2. 《機器學習實戰》
  3. 魚佬的文章 https://zhuanlan.zhihu.com/p/30352406
  4. http://www.javashuo.com/article/p-slgattie-gm.html
相關文章
相關標籤/搜索