機器學習實戰(zhàn)教程(三):決策樹實戰(zhàn)篇(a)

一、前言

上篇文章機器學習實戰(zhàn)教程(二):決策樹基礎篇講述了機器學習決策樹的原理,以及如何選擇最優(yōu)特征作為分類特征。本篇文章將在此基礎上進行介紹。主要包括:

決策樹構建

決策樹可視化

使用決策樹進行分類預測

決策樹的存儲和讀取

sklearn實戰(zhàn)之預測隱形眼睛類型


二、決策樹構建

上篇文章也粗略提到過,構建決策樹的算法有很多。篇幅原因,本篇文章只使用ID3算法構建決策樹。

1、ID3算法

ID3算法的核心是在決策樹各個結點上對應信息增益準則選擇特征,遞歸地構建決策樹。具體方法是:從根結點(root node)開始,對結點計算所有可能的特征的信息增益,選擇信息增益最大的特征作為結點的特征,由該特征的不同取值建立子節(jié)點;再對子結點遞歸地調(diào)用以上方法,構建決策樹;直到所有特征的信息增益均很小或沒有特征可以選擇為止。最后得到一個決策樹。ID3相當于用極大似然法進行概率模型的選擇。

在使用ID3構造決策樹之前,我們再分析下數(shù)據(jù)。

免費視頻教程:www.mlxs.top? ??

利用上篇文章求得的結果,由于特征A3(有自己的房子)的信息增益值最大,所以選擇特征A3作為根結點的特征。它將訓練集D劃分為兩個子集D1(A3取值為"是")和D2(A3取值為"否")。由于D1只有同一類的樣本點,所以它成為一個葉結點,結點的類標記為“是”。

對D2則需要從特征A1(年齡),A2(有工作)和A4(信貸情況)中選擇新的特征,計算各個特征的信息增益:

免費視頻教程:www.mlxs.top? ??

根據(jù)計算,選擇信息增益最大的特征A2(有工作)作為結點的特征。由于A2有兩個可能取值,從這一結點引出兩個子結點:一個對應"是"(有工作)的子結點,包含3個樣本,它們屬于同一類,所以這是一個葉結點,類標記為"是";另一個是對應"否"(無工作)的子結點,包含6個樣本,它們也屬于同一類,所以這也是一個葉結點,類標記為"否"。

這樣就生成了一個決策樹,該決策樹只用了兩個特征(有兩個內(nèi)部結點),生成的決策樹如下圖所示。

免費視頻教程:www.mlxs.top? ??

這樣我們就使用ID3算法構建出來了決策樹,接下來,讓我們看看如何進行代實現(xiàn)。

2、編寫代碼構建決策樹

我們使用字典存儲決策樹的結構,比如上小節(jié)我們分析出來的決策樹,用字典可以表示為:

# -*- coding: UTF-8 -*-

from math import log

import operator

"""

函數(shù)說明:計算給定數(shù)據(jù)集的經(jīng)驗熵(香農(nóng)熵)

Parameters:

? ? dataSet - 數(shù)據(jù)集

Returns:

? ? shannonEnt - 經(jīng)驗熵(香農(nóng)熵)

Author:

? ? Jack Cui

Modify:

? ? 2017-07-24

"""

def calcShannonEnt(dataSet):

? ? numEntires = len(dataSet)? ? ? ? ? ? ? ? ? ? ? ? #返回數(shù)據(jù)集的行數(shù)

? ? labelCounts = {}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #保存每個標簽(Label)出現(xiàn)次數(shù)的字典

? ? for featVec in dataSet:? ? ? ? ? ? ? ? ? ? ? ? ? ? #對每組特征向量進行統(tǒng)計

? ? ? ? currentLabel = featVec[-1]? ? ? ? ? ? ? ? ? ? #提取標簽(Label)信息

? ? ? ? if currentLabel not in labelCounts.keys():? ? #如果標簽(Label)沒有放入統(tǒng)計次數(shù)的字典,添加進去

? ? ? ? ? ? labelCounts[currentLabel] = 0

? ? ? ? labelCounts[currentLabel] += 1? ? ? ? ? ? ? ? #Label計數(shù)

? ? shannonEnt = 0.0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #經(jīng)驗熵(香農(nóng)熵)

? ? for key in labelCounts:? ? ? ? ? ? ? ? ? ? ? ? ? ? #計算香農(nóng)熵

? ? ? ? prob = float(labelCounts[key]) / numEntires? ? #選擇該標簽(Label)的概率

? ? ? ? shannonEnt -= prob * log(prob, 2)? ? ? ? ? ? #利用公式計算

? ? return shannonEnt? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #返回經(jīng)驗熵(香農(nóng)熵)

"""

函數(shù)說明:創(chuàng)建測試數(shù)據(jù)集

Parameters:

? ? 無

Returns:

? ? dataSet - 數(shù)據(jù)集

? ? labels - 特征標簽

Author:

? ? Jack Cui

Modify:

? ? 2017-07-20

"""

def createDataSet():

? ? dataSet = [[0, 0, 0, 0, 'no'],? ? ? ? ? ? ? ? ? ? ? ? #數(shù)據(jù)集

? ? ? ? ? ? [0, 0, 0, 1, 'no'],

? ? ? ? ? ? [0, 1, 0, 1, 'yes'],

? ? ? ? ? ? [0, 1, 1, 0, 'yes'],

? ? ? ? ? ? [0, 0, 0, 0, 'no'],

? ? ? ? ? ? [1, 0, 0, 0, 'no'],

? ? ? ? ? ? [1, 0, 0, 1, 'no'],

? ? ? ? ? ? [1, 1, 1, 1, 'yes'],

? ? ? ? ? ? [1, 0, 1, 2, 'yes'],

? ? ? ? ? ? [1, 0, 1, 2, 'yes'],

? ? ? ? ? ? [2, 0, 1, 2, 'yes'],

? ? ? ? ? ? [2, 0, 1, 1, 'yes'],

? ? ? ? ? ? [2, 1, 0, 1, 'yes'],

? ? ? ? ? ? [2, 1, 0, 2, 'yes'],

? ? ? ? ? ? [2, 0, 0, 0, 'no']]

? ? labels = ['年齡', '有工作', '有自己的房子', '信貸情況']? ? ? ? #特征標簽

? ? return dataSet, labels? ? ? ? ? ? ? ? ? ? ? ? ? ? #返回數(shù)據(jù)集和分類屬性

"""

函數(shù)說明:按照給定特征劃分數(shù)據(jù)集

Parameters:

? ? dataSet - 待劃分的數(shù)據(jù)集

? ? axis - 劃分數(shù)據(jù)集的特征

? ? value - 需要返回的特征的值

Returns:

? ? 無

Author:

? ? Jack Cui

Modify:

? ? 2017-07-24

"""

def splitDataSet(dataSet, axis, value):? ? ?

? ? retDataSet = []? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #創(chuàng)建返回的數(shù)據(jù)集列表

? ? for featVec in dataSet:? ? ? ? ? ? ? ? ? ? ? ? ? ? #遍歷數(shù)據(jù)集

? ? ? ? if featVec[axis] == value:

? ? ? ? ? ? reducedFeatVec = featVec[:axis]? ? ? ? ? ? ? ? #去掉axis特征

? ? ? ? ? ? reducedFeatVec.extend(featVec[axis+1:])? ? #將符合條件的添加到返回的數(shù)據(jù)集

? ? ? ? ? ? retDataSet.append(reducedFeatVec)

? ? return retDataSet? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #返回劃分后的數(shù)據(jù)集

"""

函數(shù)說明:選擇最優(yōu)特征

Parameters:

? ? dataSet - 數(shù)據(jù)集

Returns:

? ? bestFeature - 信息增益最大的(最優(yōu))特征的索引值

Author:

? ? Jack Cui

Modify:

? ? 2017-07-20

"""

def chooseBestFeatureToSplit(dataSet):

? ? numFeatures = len(dataSet[0]) - 1? ? ? ? ? ? ? ? ? ? #特征數(shù)量

? ? baseEntropy = calcShannonEnt(dataSet)? ? ? ? ? ? ? ? #計算數(shù)據(jù)集的香農(nóng)熵

? ? bestInfoGain = 0.0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #信息增益

? ? bestFeature = -1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #最優(yōu)特征的索引值

? ? for i in range(numFeatures):? ? ? ? ? ? ? ? ? ? ? ? #遍歷所有特征

? ? ? ? #獲取dataSet的第i個所有特征

? ? ? ? featList = [example[i] for example in dataSet]

? ? ? ? uniqueVals = set(featList)? ? ? ? ? ? ? ? ? ? ? ? #創(chuàng)建set集合{},元素不可重復

? ? ? ? newEntropy = 0.0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #經(jīng)驗條件熵

? ? ? ? for value in uniqueVals:? ? ? ? ? ? ? ? ? ? ? ? #計算信息增益

? ? ? ? ? ? subDataSet = splitDataSet(dataSet, i, value)? ? ? ? #subDataSet劃分后的子集

? ? ? ? ? ? prob = len(subDataSet) / float(len(dataSet))? ? ? ? ? #計算子集的概率

? ? ? ? ? ? newEntropy += prob * calcShannonEnt(subDataSet)? ? #根據(jù)公式計算經(jīng)驗條件熵

? ? ? ? infoGain = baseEntropy - newEntropy? ? ? ? ? ? ? ? ? ? #信息增益

? ? ? ? # print("第%d個特征的增益為%.3f" % (i, infoGain))? ? ? ? ? ? #打印每個特征的信息增益

? ? ? ? if (infoGain > bestInfoGain):? ? ? ? ? ? ? ? ? ? ? ? ? ? #計算信息增益

? ? ? ? ? ? bestInfoGain = infoGain? ? ? ? ? ? ? ? ? ? ? ? ? ? #更新信息增益,找到最大的信息增益

? ? ? ? ? ? bestFeature = i? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #記錄信息增益最大的特征的索引值

? ? return bestFeature? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #返回信息增益最大的特征的索引值

"""

函數(shù)說明:統(tǒng)計classList中出現(xiàn)此處最多的元素(類標簽)

Parameters:

? ? classList - 類標簽列表

Returns:

? ? sortedClassCount[0][0] - 出現(xiàn)此處最多的元素(類標簽)

Author:

? ? Jack Cui

Modify:

? ? 2017-07-24

"""

def majorityCnt(classList):

? ? classCount = {}

? ? for vote in classList:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #統(tǒng)計classList中每個元素出現(xiàn)的次數(shù)

? ? ? ? if vote not in classCount.keys():classCount[vote] = 0?

? ? ? ? classCount[vote] += 1

? ? sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True)? ? ? ? #根據(jù)字典的值降序排序

? ? return sortedClassCount[0][0]? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #返回classList中出現(xiàn)次數(shù)最多的元素

"""

函數(shù)說明:創(chuàng)建決策樹

Parameters:

? ? dataSet - 訓練數(shù)據(jù)集

? ? labels - 分類屬性標簽

? ? featLabels - 存儲選擇的最優(yōu)特征標簽

Returns:

? ? myTree - 決策樹

Author:

? ? Jack Cui

Modify:

? ? 2017-07-25

"""

def createTree(dataSet, labels, featLabels):

? ? classList = [example[-1] for example in dataSet]? ? ? ? ? ? #取分類標簽(是否放貸:yes or no)

? ? if classList.count(classList[0]) == len(classList):? ? ? ? ? ? #如果類別完全相同則停止繼續(xù)劃分

? ? ? ? return classList[0]

? ? if len(dataSet[0]) == 1 or len(labels) == 0:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #遍歷完所有特征時返回出現(xiàn)次數(shù)最多的類標簽

? ? ? ? return majorityCnt(classList)

? ? bestFeat = chooseBestFeatureToSplit(dataSet)? ? ? ? ? ? ? ? #選擇最優(yōu)特征

? ? bestFeatLabel = labels[bestFeat]? ? ? ? ? ? ? ? ? ? ? ? ? ? #最優(yōu)特征的標簽

? ? featLabels.append(bestFeatLabel)

? ? myTree = {bestFeatLabel:{}}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #根據(jù)最優(yōu)特征的標簽生成樹

? ? del(labels[bestFeat])? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #刪除已經(jīng)使用特征標簽

? ? featValues = [example[bestFeat] for example in dataSet]? ? ? ? #得到訓練集中所有最優(yōu)特征的屬性值

? ? uniqueVals = set(featValues)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #去掉重復的屬性值

? ? for value in uniqueVals:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #遍歷特征,創(chuàng)建決策樹。? ? ? ?

? ? ? ? subLabels = labels[:]? ? ? ? ? ? ?

? ? ? ? myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels, featLabels)

? ? return myTree

if __name__ == '__main__':

? ? dataSet, labels = createDataSet()

? ? featLabels = []

? ? myTree = createTree(dataSet, labels, featLabels)

? ? print(myTree)

遞歸創(chuàng)建決策樹時,遞歸有兩個終止條件:第一個停止條件是所有的類標簽完全相同,則直接返回該類標簽;第二個停止條件是使用完了所有特征,仍然不能將數(shù)據(jù)劃分僅包含唯一類別的分組,即決策樹構建失敗,特征不夠用。此時說明數(shù)據(jù)緯度不夠,由于第二個停止條件無法簡單地返回唯一的類標簽,這里挑選出現(xiàn)數(shù)量最多的類別作為返回值。

運行上述代碼,我們可以看到如下結果:

免費視頻教程:www.mlxs.top? ??

可見,我們的決策樹已經(jīng)構建完成了。這時候,有的朋友可能會說,這個決策樹看著好別扭,雖然這個能看懂,但是如果多點的結點,就不好看了。能直觀點嗎?完全沒有問題,我們可以使用強大的Matplotlib繪制決策樹。免費視頻教程:www.mlxs.top??

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容