日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

机器学习实战--决策树算法

發布時間:2023/12/15 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 机器学习实战--决策树算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

決策樹

決策樹(decision tree)是一種基本的分類與回歸方法。從判斷模塊引出的左右箭頭稱為分支,它可以達到另外一個判斷模塊或者終止模塊。分類決策樹模型是一種描述對實例進行分類的樹形結構。決策樹由節點(node)和有向邊(directed edge)組成。節點有內部節點(internal node)和葉節點(leaf node)。內部節點表示一個特征或者屬性,葉節點表示一個類。

1. 決策樹流程:

  • 收集數據:可以使用任何方法收集到的數據。
  • 準備數據:收集完數據之后,我們要對數據進行整理,將這些數據按照一定的要求進行整理出來,方便后續數據處理
  • 分析數據:可以使用任何方法,決策樹構建完成之后,可以檢查我們的決策樹圖形是否符合預期要求。
  • 訓練算法:這個過程也就是構建決策樹,同樣也叫做決策樹學習,就是構建一個決策樹的數據結構
  • 測試算法:使用經驗樹計算錯誤率,當錯誤率達到了可接受的范圍,這個決策樹就可以投放使用了。
  • 使用算法:此步驟可以適用于任何監督學習算法,而使用決策樹可以更好的理解數據的內在含義。
    決策樹一般構建分成三個步驟:特征選擇、決策樹的生成、決策樹的剪枝

1.1 特征選擇

特征選擇在于選擇對訓練數據具有分類的特征,這樣可以提高決策樹學習的效率,如果利用一個特征進行分類的結果與隨機分類的結果沒有多大的區別,則稱這個特征是沒有分類能力的。經驗上扔掉這樣的特征對決策樹學習的精度影響不大。通常特征選擇的標準是信息增益(information gain)或者信息增益比,為了簡單,本文使用信息增益作為選擇特征的標準。

希望通過所給的訓練數據學習一個貸款申請的決策樹,用于對未來的貸款申請進行分類,即當新的客戶提出貸款申請時,根據申請人的特征利用決策樹是否批準貸款申請。
特征選擇就是決定用哪個特征來劃分特征空間,比如上表兩個可能的決策樹,分別由兩個不同特征的根節點構成。年齡節點或者是否有工作兩個節點。不同的根節點,對應不同的決策樹。究竟哪個效果更好?就需要用到信息增益,可以很好的表示這一直觀的準則。
什么是信息增益?在劃分數據集之前之后發生的信息變化就是信息增益,知道如何計算信息增益,我們就可以計算每個特征值劃分數據集獲得的信息增益,獲得信息增益最高的特征就是最好的選擇。

1.2 計算經驗熵

在進行代碼編寫之前,我們需要先對數據集進行屬性標注

  • 年齡: 0代表青年,1代表中年,2代表老年;
  • 有工作:0代表否,1代表是;
  • 有自己的房子:0代表否,1代表是;
  • 信貸情況:0代表一般,1代表好,2代表非常好;
  • 類別(是否給貸款):no表示否,yes表示是。
    計算經驗熵
from math import log""" 函數說明:創建測試數據集Parameters:無 Returns:dataSet - 數據集labels - 分類屬性 """ def createDataSet():dataSet = [[0, 0, 0, 0, 'no'], #數據集[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 #返回數據集和分類屬性""" 函數說明:計算給定數據集的經驗熵(香農熵)Parameters:dataSet - 數據集 Returns:shannonEnt - 經驗熵(香農熵) """ def calcShannonEnt(dataSet):numEntires = len(dataSet) #返回數據集的行數labelCounts = {} #保存每個標簽(Label)出現次數的字典for featVec in dataSet: #對每組特征向量進行統計currentLabel = featVec[-1] #提取標簽(Label)信息if currentLabel not in labelCounts.keys(): #如果標簽(Label)沒有放入統計次數的字典,添加進去labelCounts[currentLabel] = 0labelCounts[currentLabel] += 1 #Label計數shannonEnt = 0.0 #經驗熵(香農熵)for key in labelCounts: #計算香農熵prob = float(labelCounts[key]) / numEntires #選擇該標簽(Label)的概率shannonEnt -= prob * log(prob, 2) #利用公式計算return shannonEnt #返回經驗熵(香農熵)if __name__ == '__main__':dataSet, features = createDataSet()print(dataSet)print(calcShannonEnt(dataSet))

代碼運行結果如下圖所示,代碼先打印訓練數據集,再打印經驗熵

1.3 計算信息增益

信息增益越大,特征對最終的分類結果的影響也就越大,我們選擇對最終分類結果影響最大的那個特征作為我們的分類特征

# -*- coding: UTF-8 -*- from math import log""" 函數說明:計算給定數據集的經驗熵(香農熵)Parameters:dataSet - 數據集 Returns:shannonEnt - 經驗熵(香農熵) """ def calcShannonEnt(dataSet):numEntires = len(dataSet) #返回數據集的行數labelCounts = {} #保存每個標簽(Label)出現次數的字典for featVec in dataSet: #對每組特征向量進行統計currentLabel = featVec[-1] #提取標簽(Label)信息if currentLabel not in labelCounts.keys(): #如果標簽(Label)沒有放入統計次數的字典,添加進去labelCounts[currentLabel] = 0labelCounts[currentLabel] += 1 #Label計數shannonEnt = 0.0 #經驗熵(香農熵)for key in labelCounts: #計算香農熵prob = float(labelCounts[key]) / numEntires #選擇該標簽(Label)的概率shannonEnt -= prob * log(prob, 2) #利用公式計算return shannonEnt #返回經驗熵(香農熵)""" 函數說明:創建測試數據集Parameters:無 Returns:dataSet - 數據集labels - 分類屬性 """ def createDataSet():dataSet = [[0, 0, 0, 0, 'no'], #數據集[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 #返回數據集和分類屬性""" 函數說明:按照給定特征劃分數據集Parameters:dataSet - 待劃分的數據集axis - 劃分數據集的特征value - 需要返回的特征的值 Returns:無 """ def splitDataSet(dataSet, axis, value): retDataSet = [] #創建返回的數據集列表for featVec in dataSet: #遍歷數據集if featVec[axis] == value:reducedFeatVec = featVec[:axis] #去掉axis特征reducedFeatVec.extend(featVec[axis+1:]) #將符合條件的添加到返回的數據集retDataSet.append(reducedFeatVec)return retDataSet #返回劃分后的數據集""" 函數說明:選擇最優特征Parameters:dataSet - 數據集 Returns:bestFeature - 信息增益最大的(最優)特征的索引值 """ def chooseBestFeatureToSplit(dataSet):numFeatures = len(dataSet[0]) - 1 #特征數量baseEntropy = calcShannonEnt(dataSet) #計算數據集的香農熵bestInfoGain = 0.0 #信息增益bestFeature = -1 #最優特征的索引值for i in range(numFeatures): #遍歷所有特征#獲取dataSet的第i個所有特征featList = [example[i] for example in dataSet]uniqueVals = set(featList) #創建set集合{},元素不可重復newEntropy = 0.0 #經驗條件熵for value in uniqueVals: #計算信息增益subDataSet = splitDataSet(dataSet, i, value) #subDataSet劃分后的子集prob = len(subDataSet) / float(len(dataSet)) #計算子集的概率newEntropy += prob * calcShannonEnt(subDataSet) #根據公式計算經驗條件熵infoGain = baseEntropy - newEntropy #信息增益print("第%d個特征的增益為%.3f" % (i, infoGain)) #打印每個特征的信息增益if (infoGain > bestInfoGain): #計算信息增益bestInfoGain = infoGain #更新信息增益,找到最大的信息增益bestFeature = i #記錄信息增益最大的特征的索引值return bestFeature #返回信息增益最大的特征的索引值if __name__ == '__main__':dataSet, features = createDataSet()print("最優特征索引值:" + str(chooseBestFeatureToSplit(dataSet)))

最優特征索引值為2

1.4決策樹生成

1.4.1 ID3算法

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

1.4.2 構建決策樹代碼

# -*- coding: UTF-8 -*- from math import log import operator""" 函數說明:計算給定數據集的經驗熵(香農熵)Parameters:dataSet - 數據集 Returns:shannonEnt - 經驗熵(香農熵) """ def calcShannonEnt(dataSet):numEntires = len(dataSet) #返回數據集的行數labelCounts = {} #保存每個標簽(Label)出現次數的字典for featVec in dataSet: #對每組特征向量進行統計currentLabel = featVec[-1] #提取標簽(Label)信息if currentLabel not in labelCounts.keys(): #如果標簽(Label)沒有放入統計次數的字典,添加進去labelCounts[currentLabel] = 0labelCounts[currentLabel] += 1 #Label計數shannonEnt = 0.0 #經驗熵(香農熵)for key in labelCounts: #計算香農熵prob = float(labelCounts[key]) / numEntires #選擇該標簽(Label)的概率shannonEnt -= prob * log(prob, 2) #利用公式計算return shannonEnt #返回經驗熵(香農熵)""" 函數說明:創建測試數據集Parameters:無 Returns:dataSet - 數據集labels - 特征標簽 """ def createDataSet():dataSet = [[0, 0, 0, 0, 'no'], #數據集[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 #返回數據集和分類屬性""" 函數說明:按照給定特征劃分數據集Parameters:dataSet - 待劃分的數據集axis - 劃分數據集的特征value - 需要返回的特征的值 Returns:無 """ def splitDataSet(dataSet, axis, value): retDataSet = [] #創建返回的數據集列表for featVec in dataSet: #遍歷數據集if featVec[axis] == value:reducedFeatVec = featVec[:axis] #去掉axis特征reducedFeatVec.extend(featVec[axis+1:]) #將符合條件的添加到返回的數據集retDataSet.append(reducedFeatVec)return retDataSet #返回劃分后的數據集""" 函數說明:選擇最優特征Parameters:dataSet - 數據集 Returns:bestFeature - 信息增益最大的(最優)特征的索引值 """ def chooseBestFeatureToSplit(dataSet):numFeatures = len(dataSet[0]) - 1 #特征數量baseEntropy = calcShannonEnt(dataSet) #計算數據集的香農熵bestInfoGain = 0.0 #信息增益bestFeature = -1 #最優特征的索引值for i in range(numFeatures): #遍歷所有特征#獲取dataSet的第i個所有特征featList = [example[i] for example in dataSet]uniqueVals = set(featList) #創建set集合{},元素不可重復newEntropy = 0.0 #經驗條件熵for value in uniqueVals: #計算信息增益subDataSet = splitDataSet(dataSet, i, value) #subDataSet劃分后的子集prob = len(subDataSet) / float(len(dataSet)) #計算子集的概率newEntropy += prob * calcShannonEnt(subDataSet) #根據公式計算經驗條件熵infoGain = baseEntropy - newEntropy #信息增益# print("第%d個特征的增益為%.3f" % (i, infoGain)) #打印每個特征的信息增益if (infoGain > bestInfoGain): #計算信息增益bestInfoGain = infoGain #更新信息增益,找到最大的信息增益bestFeature = i #記錄信息增益最大的特征的索引值return bestFeature #返回信息增益最大的特征的索引值""" 函數說明:統計classList中出現此處最多的元素(類標簽)Parameters:classList - 類標簽列表 Returns:sortedClassCount[0][0] - 出現此處最多的元素(類標簽) """ def majorityCnt(classList):classCount = {}for vote in classList: #統計classList中每個元素出現的次數if vote not in classCount.keys():classCount[vote] = 0 classCount[vote] += 1sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #根據字典的值降序排序return sortedClassCount[0][0] #返回classList中出現次數最多的元素""" 函數說明:創建決策樹Parameters:dataSet - 訓練數據集labels - 分類屬性標簽featLabels - 存儲選擇的最優特征標簽 Returns:myTree - 決策樹 """ def createTree(dataSet, labels, featLabels):classList = [example[-1] for example in dataSet] #取分類標簽(是否放貸:yes or no)if classList.count(classList[0]) == len(classList): #如果類別完全相同則停止繼續劃分return classList[0]if len(dataSet[0]) == 1: #遍歷完所有特征時返回出現次數最多的類標簽return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet) #選擇最優特征bestFeatLabel = labels[bestFeat] #最優特征的標簽featLabels.append(bestFeatLabel)myTree = {bestFeatLabel:{}} #根據最優特征的標簽生成樹del(labels[bestFeat]) #刪除已經使用特征標簽featValues = [example[bestFeat] for example in dataSet] #得到訓練集中所有最優特征的屬性值uniqueVals = set(featValues) #去掉重復的屬性值for value in uniqueVals: #遍歷特征,創建決策樹。 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), labels, featLabels)return myTreeif __name__ == '__main__':dataSet, labels = createDataSet()featLabels = []myTree = createTree(dataSet, labels, featLabels)print(myTree)

1.4.3決策樹可視化

# -*- coding: UTF-8 -*- from matplotlib.font_manager import FontProperties import matplotlib.pyplot as plt from math import log import operator""" 函數說明:計算給定數據集的經驗熵(香農熵)Parameters:dataSet - 數據集 Returns:shannonEnt - 經驗熵(香農熵) """ def calcShannonEnt(dataSet):numEntires = len(dataSet) #返回數據集的行數labelCounts = {} #保存每個標簽(Label)出現次數的字典for featVec in dataSet: #對每組特征向量進行統計currentLabel = featVec[-1] #提取標簽(Label)信息if currentLabel not in labelCounts.keys(): #如果標簽(Label)沒有放入統計次數的字典,添加進去labelCounts[currentLabel] = 0labelCounts[currentLabel] += 1 #Label計數shannonEnt = 0.0 #經驗熵(香農熵)for key in labelCounts: #計算香農熵prob = float(labelCounts[key]) / numEntires #選擇該標簽(Label)的概率shannonEnt -= prob * log(prob, 2) #利用公式計算return shannonEnt #返回經驗熵(香農熵)""" 函數說明:創建測試數據集Parameters:無 Returns:dataSet - 數據集labels - 特征標簽 """ def createDataSet():dataSet = [[0, 0, 0, 0, 'no'], #數據集[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 #返回數據集和分類屬性""" 函數說明:按照給定特征劃分數據集Parameters:dataSet - 待劃分的數據集axis - 劃分數據集的特征value - 需要返回的特征的值 Returns:無 """ def splitDataSet(dataSet, axis, value): retDataSet = [] #創建返回的數據集列表for featVec in dataSet: #遍歷數據集if featVec[axis] == value:reducedFeatVec = featVec[:axis] #去掉axis特征reducedFeatVec.extend(featVec[axis+1:]) #將符合條件的添加到返回的數據集retDataSet.append(reducedFeatVec)return retDataSet #返回劃分后的數據集""" 函數說明:選擇最優特征Parameters:dataSet - 數據集 Returns:bestFeature - 信息增益最大的(最優)特征的索引值 """ def chooseBestFeatureToSplit(dataSet):numFeatures = len(dataSet[0]) - 1 #特征數量baseEntropy = calcShannonEnt(dataSet) #計算數據集的香農熵bestInfoGain = 0.0 #信息增益bestFeature = -1 #最優特征的索引值for i in range(numFeatures): #遍歷所有特征#獲取dataSet的第i個所有特征featList = [example[i] for example in dataSet]uniqueVals = set(featList) #創建set集合{},元素不可重復newEntropy = 0.0 #經驗條件熵for value in uniqueVals: #計算信息增益subDataSet = splitDataSet(dataSet, i, value) #subDataSet劃分后的子集prob = len(subDataSet) / float(len(dataSet)) #計算子集的概率newEntropy += prob * calcShannonEnt(subDataSet) #根據公式計算經驗條件熵infoGain = baseEntropy - newEntropy #信息增益# print("第%d個特征的增益為%.3f" % (i, infoGain)) #打印每個特征的信息增益if (infoGain > bestInfoGain): #計算信息增益bestInfoGain = infoGain #更新信息增益,找到最大的信息增益bestFeature = i #記錄信息增益最大的特征的索引值return bestFeature #返回信息增益最大的特征的索引值""" 函數說明:統計classList中出現此處最多的元素(類標簽)Parameters:classList - 類標簽列表 Returns:sortedClassCount[0][0] - 出現此處最多的元素(類標簽) """ def majorityCnt(classList):classCount = {}for vote in classList: #統計classList中每個元素出現的次數if vote not in classCount.keys():classCount[vote] = 0 classCount[vote] += 1sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #根據字典的值降序排序return sortedClassCount[0][0] #返回classList中出現次數最多的元素""" 函數說明:創建決策樹Parameters:dataSet - 訓練數據集labels - 分類屬性標簽featLabels - 存儲選擇的最優特征標簽 Returns:myTree - 決策樹 """ def createTree(dataSet, labels, featLabels):classList = [example[-1] for example in dataSet] #取分類標簽(是否放貸:yes or no)if classList.count(classList[0]) == len(classList): #如果類別完全相同則停止繼續劃分return classList[0]if len(dataSet[0]) == 1: #遍歷完所有特征時返回出現次數最多的類標簽return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet) #選擇最優特征bestFeatLabel = labels[bestFeat] #最優特征的標簽featLabels.append(bestFeatLabel)myTree = {bestFeatLabel:{}} #根據最優特征的標簽生成樹del(labels[bestFeat]) #刪除已經使用特征標簽featValues = [example[bestFeat] for example in dataSet] #得到訓練集中所有最優特征的屬性值uniqueVals = set(featValues) #去掉重復的屬性值for value in uniqueVals: #遍歷特征,創建決策樹。 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), labels, featLabels)return myTree""" 函數說明:獲取決策樹葉子結點的數目Parameters:myTree - 決策樹 Returns:numLeafs - 決策樹的葉子結點的數目 """ def getNumLeafs(myTree):numLeafs = 0 #初始化葉子firstStr = next(iter(myTree)) #python3中myTree.keys()返回的是dict_keys,不在是list,所以不能使用myTree.keys()[0]的方法獲取結點屬性,可以使用list(myTree.keys())[0]secondDict = myTree[firstStr] #獲取下一組字典for key in secondDict.keys():if type(secondDict[key]).__name__=='dict': #測試該結點是否為字典,如果不是字典,代表此結點為葉子結點numLeafs += getNumLeafs(secondDict[key])else: numLeafs +=1return numLeafs""" 函數說明:獲取決策樹的層數Parameters:myTree - 決策樹 Returns:maxDepth - 決策樹的層數 """ def getTreeDepth(myTree):maxDepth = 0 #初始化決策樹深度firstStr = next(iter(myTree)) #python3中myTree.keys()返回的是dict_keys,不在是list,所以不能使用myTree.keys()[0]的方法獲取結點屬性,可以使用list(myTree.keys())[0]secondDict = myTree[firstStr] #獲取下一個字典for key in secondDict.keys():if type(secondDict[key]).__name__=='dict': #測試該結點是否為字典,如果不是字典,代表此結點為葉子結點thisDepth = 1 + getTreeDepth(secondDict[key])else: thisDepth = 1if thisDepth > maxDepth: maxDepth = thisDepth #更新層數return maxDepth""" 函數說明:繪制結點Parameters:nodeTxt - 結點名centerPt - 文本位置parentPt - 標注的箭頭位置nodeType - 結點格式 Returns:無 """ def plotNode(nodeTxt, centerPt, parentPt, nodeType):arrow_args = dict(arrowstyle="<-") #定義箭頭格式font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) #設置中文字體createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction', #繪制結點xytext=centerPt, textcoords='axes fraction',va="center", ha="center", bbox=nodeType, arrowprops=arrow_args, FontProperties=font)""" 函數說明:標注有向邊屬性值Parameters:cntrPt、parentPt - 用于計算標注位置txtString - 標注的內容 Returns:無 """ def plotMidText(cntrPt, parentPt, txtString):xMid = (parentPt[0]-cntrPt[0])/2.0 + cntrPt[0] #計算標注位置 yMid = (parentPt[1]-cntrPt[1])/2.0 + cntrPt[1]createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30)""" 函數說明:繪制決策樹Parameters:myTree - 決策樹(字典)parentPt - 標注的內容nodeTxt - 結點名 Returns:無 """ def plotTree(myTree, parentPt, nodeTxt):decisionNode = dict(boxstyle="sawtooth", fc="0.8") #設置結點格式leafNode = dict(boxstyle="round4", fc="0.8") #設置葉結點格式numLeafs = getNumLeafs(myTree) #獲取決策樹葉結點數目,決定了樹的寬度depth = getTreeDepth(myTree) #獲取決策樹層數firstStr = next(iter(myTree)) #下個字典 cntrPt = (plotTree.xOff + (1.0 + float(numLeafs))/2.0/plotTree.totalW, plotTree.yOff) #中心位置plotMidText(cntrPt, parentPt, nodeTxt) #標注有向邊屬性值plotNode(firstStr, cntrPt, parentPt, decisionNode) #繪制結點secondDict = myTree[firstStr] #下一個字典,也就是繼續繪制子結點plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD #y偏移for key in secondDict.keys(): if type(secondDict[key]).__name__=='dict': #測試該結點是否為字典,如果不是字典,代表此結點為葉子結點plotTree(secondDict[key],cntrPt,str(key)) #不是葉結點,遞歸調用繼續繪制else: #如果是葉結點,繪制葉結點,并標注有向邊屬性值 plotTree.xOff = plotTree.xOff + 1.0/plotTree.totalWplotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalD""" 函數說明:創建繪制面板Parameters:inTree - 決策樹(字典) Returns:無 """ def createPlot(inTree):fig = plt.figure(1, facecolor='white') #創建figfig.clf() #清空figaxprops = dict(xticks=[], yticks=[])createPlot.ax1 = plt.subplot(111, frameon=False, **axprops) #去掉x、y軸plotTree.totalW = float(getNumLeafs(inTree)) #獲取決策樹葉結點數目plotTree.totalD = float(getTreeDepth(inTree)) #獲取決策樹層數plotTree.xOff = -0.5/plotTree.totalW; plotTree.yOff = 1.0; #x偏移plotTree(inTree, (0.5,1.0), '') #繪制決策樹plt.show() #顯示繪制結果 if __name__ == '__main__':dataSet, labels = createDataSet()featLabels = []myTree = createTree(dataSet, labels, featLabels)print(myTree) createPlot(myTree)

1.4.4 決策樹可視化

# -*- coding: UTF-8 -*- from matplotlib.font_manager import FontProperties import matplotlib.pyplot as plt from math import log import operator""" 函數說明:計算給定數據集的經驗熵(香農熵)Parameters:dataSet - 數據集 Returns:shannonEnt - 經驗熵(香農熵)""" def calcShannonEnt(dataSet):numEntires = len(dataSet) #返回數據集的行數labelCounts = {} #保存每個標簽(Label)出現次數的字典for featVec in dataSet: #對每組特征向量進行統計currentLabel = featVec[-1] #提取標簽(Label)信息if currentLabel not in labelCounts.keys(): #如果標簽(Label)沒有放入統計次數的字典,添加進去labelCounts[currentLabel] = 0labelCounts[currentLabel] += 1 #Label計數shannonEnt = 0.0 #經驗熵(香農熵)for key in labelCounts: #計算香農熵prob = float(labelCounts[key]) / numEntires #選擇該標簽(Label)的概率shannonEnt -= prob * log(prob, 2) #利用公式計算return shannonEnt #返回經驗熵(香農熵)""" 函數說明:創建測試數據集Parameters:無 Returns:dataSet - 數據集labels - 特征標簽""" def createDataSet():dataSet = [[0, 0, 0, 0, 'no'], #數據集[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 #返回數據集和分類屬性""" 函數說明:按照給定特征劃分數據集Parameters:dataSet - 待劃分的數據集axis - 劃分數據集的特征value - 需要返回的特征的值 Returns:無""" def splitDataSet(dataSet, axis, value): retDataSet = [] #創建返回的數據集列表for featVec in dataSet: #遍歷數據集if featVec[axis] == value:reducedFeatVec = featVec[:axis] #去掉axis特征reducedFeatVec.extend(featVec[axis+1:]) #將符合條件的添加到返回的數據集retDataSet.append(reducedFeatVec)return retDataSet #返回劃分后的數據集""" 函數說明:選擇最優特征Parameters:dataSet - 數據集 Returns:bestFeature - 信息增益最大的(最優)特征的索引值""" def chooseBestFeatureToSplit(dataSet):numFeatures = len(dataSet[0]) - 1 #特征數量baseEntropy = calcShannonEnt(dataSet) #計算數據集的香農熵bestInfoGain = 0.0 #信息增益bestFeature = -1 #最優特征的索引值for i in range(numFeatures): #遍歷所有特征#獲取dataSet的第i個所有特征featList = [example[i] for example in dataSet]uniqueVals = set(featList) #創建set集合{},元素不可重復newEntropy = 0.0 #經驗條件熵for value in uniqueVals: #計算信息增益subDataSet = splitDataSet(dataSet, i, value) #subDataSet劃分后的子集prob = len(subDataSet) / float(len(dataSet)) #計算子集的概率newEntropy += prob * calcShannonEnt(subDataSet) #根據公式計算經驗條件熵infoGain = baseEntropy - newEntropy #信息增益# print("第%d個特征的增益為%.3f" % (i, infoGain)) #打印每個特征的信息增益if (infoGain > bestInfoGain): #計算信息增益bestInfoGain = infoGain #更新信息增益,找到最大的信息增益bestFeature = i #記錄信息增益最大的特征的索引值return bestFeature #返回信息增益最大的特征的索引值""" 函數說明:統計classList中出現此處最多的元素(類標簽)Parameters:classList - 類標簽列表 Returns:sortedClassCount[0][0] - 出現此處最多的元素(類標簽)""" def majorityCnt(classList):classCount = {}for vote in classList: #統計classList中每個元素出現的次數if vote not in classCount.keys():classCount[vote] = 0 classCount[vote] += 1sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #根據字典的值降序排序return sortedClassCount[0][0] #返回classList中出現次數最多的元素""" 函數說明:創建決策樹Parameters:dataSet - 訓練數據集labels - 分類屬性標簽featLabels - 存儲選擇的最優特征標簽 Returns:myTree - 決策樹""" def createTree(dataSet, labels, featLabels):classList = [example[-1] for example in dataSet] #取分類標簽(是否放貸:yes or no)if classList.count(classList[0]) == len(classList): #如果類別完全相同則停止繼續劃分return classList[0]if len(dataSet[0]) == 1: #遍歷完所有特征時返回出現次數最多的類標簽return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet) #選擇最優特征bestFeatLabel = labels[bestFeat] #最優特征的標簽featLabels.append(bestFeatLabel)myTree = {bestFeatLabel:{}} #根據最優特征的標簽生成樹del(labels[bestFeat]) #刪除已經使用特征標簽featValues = [example[bestFeat] for example in dataSet] #得到訓練集中所有最優特征的屬性值uniqueVals = set(featValues) #去掉重復的屬性值for value in uniqueVals: #遍歷特征,創建決策樹。 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), labels, featLabels)return myTree""" 函數說明:獲取決策樹葉子結點的數目Parameters:myTree - 決策樹 Returns:numLeafs - 決策樹的葉子結點的數目""" def getNumLeafs(myTree):numLeafs = 0 #初始化葉子firstStr = next(iter(myTree)) #python3中myTree.keys()返回的是dict_keys,不在是list,所以不能使用myTree.keys()[0]的方法獲取結點屬性,可以使用list(myTree.keys())[0]secondDict = myTree[firstStr] #獲取下一組字典for key in secondDict.keys():if type(secondDict[key]).__name__=='dict': #測試該結點是否為字典,如果不是字典,代表此結點為葉子結點numLeafs += getNumLeafs(secondDict[key])else: numLeafs +=1return numLeafs""" 函數說明:獲取決策樹的層數Parameters:myTree - 決策樹 Returns:maxDepth - 決策樹的層數""" def getTreeDepth(myTree):maxDepth = 0 #初始化決策樹深度firstStr = next(iter(myTree)) #python3中myTree.keys()返回的是dict_keys,不在是list,所以不能使用myTree.keys()[0]的方法獲取結點屬性,可以使用list(myTree.keys())[0]secondDict = myTree[firstStr] #獲取下一個字典for key in secondDict.keys():if type(secondDict[key]).__name__=='dict': #測試該結點是否為字典,如果不是字典,代表此結點為葉子結點thisDepth = 1 + getTreeDepth(secondDict[key])else: thisDepth = 1if thisDepth > maxDepth: maxDepth = thisDepth #更新層數return maxDepth""" 函數說明:繪制結點Parameters:nodeTxt - 結點名centerPt - 文本位置parentPt - 標注的箭頭位置nodeType - 結點格式 Returns:無""" def plotNode(nodeTxt, centerPt, parentPt, nodeType):arrow_args = dict(arrowstyle="<-") #定義箭頭格式font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) #設置中文字體createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction', #繪制結點xytext=centerPt, textcoords='axes fraction',va="center", ha="center", bbox=nodeType, arrowprops=arrow_args, FontProperties=font)""" 函數說明:標注有向邊屬性值Parameters:cntrPt、parentPt - 用于計算標注位置txtString - 標注的內容 Returns:無""" def plotMidText(cntrPt, parentPt, txtString):xMid = (parentPt[0]-cntrPt[0])/2.0 + cntrPt[0] #計算標注位置 yMid = (parentPt[1]-cntrPt[1])/2.0 + cntrPt[1]createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30)""" 函數說明:繪制決策樹Parameters:myTree - 決策樹(字典)parentPt - 標注的內容nodeTxt - 結點名 Returns:無""" def plotTree(myTree, parentPt, nodeTxt):decisionNode = dict(boxstyle="sawtooth", fc="0.8") #設置結點格式leafNode = dict(boxstyle="round4", fc="0.8") #設置葉結點格式numLeafs = getNumLeafs(myTree) #獲取決策樹葉結點數目,決定了樹的寬度depth = getTreeDepth(myTree) #獲取決策樹層數firstStr = next(iter(myTree)) #下個字典 cntrPt = (plotTree.xOff + (1.0 + float(numLeafs))/2.0/plotTree.totalW, plotTree.yOff) #中心位置plotMidText(cntrPt, parentPt, nodeTxt) #標注有向邊屬性值plotNode(firstStr, cntrPt, parentPt, decisionNode) #繪制結點secondDict = myTree[firstStr] #下一個字典,也就是繼續繪制子結點plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD #y偏移for key in secondDict.keys(): if type(secondDict[key]).__name__=='dict': #測試該結點是否為字典,如果不是字典,代表此結點為葉子結點plotTree(secondDict[key],cntrPt,str(key)) #不是葉結點,遞歸調用繼續繪制else: #如果是葉結點,繪制葉結點,并標注有向邊屬性值 plotTree.xOff = plotTree.xOff + 1.0/plotTree.totalWplotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalD""" 函數說明:創建繪制面板Parameters:inTree - 決策樹(字典) Returns:無""" def createPlot(inTree):fig = plt.figure(1, facecolor='white') #創建figfig.clf() #清空figaxprops = dict(xticks=[], yticks=[])createPlot.ax1 = plt.subplot(111, frameon=False, **axprops) #去掉x、y軸plotTree.totalW = float(getNumLeafs(inTree)) #獲取決策樹葉結點數目plotTree.totalD = float(getTreeDepth(inTree)) #獲取決策樹層數plotTree.xOff = -0.5/plotTree.totalW; plotTree.yOff = 1.0; #x偏移plotTree(inTree, (0.5,1.0), '') #繪制決策樹plt.show() #顯示繪制結果 if __name__ == '__main__':dataSet, labels = createDataSet()featLabels = []myTree = createTree(dataSet, labels, featLabels)print(myTree) createPlot(myTree)

1.4.5決策樹執行分類

# -*- coding: UTF-8 -*- from math import log import operator""" 函數說明:計算給定數據集的經驗熵(香農熵)Parameters:dataSet - 數據集 Returns:shannonEnt - 經驗熵(香農熵)""" def calcShannonEnt(dataSet):numEntires = len(dataSet) #返回數據集的行數labelCounts = {} #保存每個標簽(Label)出現次數的字典for featVec in dataSet: #對每組特征向量進行統計currentLabel = featVec[-1] #提取標簽(Label)信息if currentLabel not in labelCounts.keys(): #如果標簽(Label)沒有放入統計次數的字典,添加進去labelCounts[currentLabel] = 0labelCounts[currentLabel] += 1 #Label計數shannonEnt = 0.0 #經驗熵(香農熵)for key in labelCounts: #計算香農熵prob = float(labelCounts[key]) / numEntires #選擇該標簽(Label)的概率shannonEnt -= prob * log(prob, 2) #利用公式計算return shannonEnt #返回經驗熵(香農熵)""" 函數說明:創建測試數據集Parameters:無 Returns:dataSet - 數據集labels - 特征標簽""" def createDataSet():dataSet = [[0, 0, 0, 0, 'no'], #數據集[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 #返回數據集和分類屬性""" 函數說明:按照給定特征劃分數據集Parameters:dataSet - 待劃分的數據集axis - 劃分數據集的特征value - 需要返回的特征的值 Returns:無""" def splitDataSet(dataSet, axis, value): retDataSet = [] #創建返回的數據集列表for featVec in dataSet: #遍歷數據集if featVec[axis] == value:reducedFeatVec = featVec[:axis] #去掉axis特征reducedFeatVec.extend(featVec[axis+1:]) #將符合條件的添加到返回的數據集retDataSet.append(reducedFeatVec)return retDataSet #返回劃分后的數據集""" 函數說明:選擇最優特征Parameters:dataSet - 數據集 Returns:bestFeature - 信息增益最大的(最優)特征的索引值""" def chooseBestFeatureToSplit(dataSet):numFeatures = len(dataSet[0]) - 1 #特征數量baseEntropy = calcShannonEnt(dataSet) #計算數據集的香農熵bestInfoGain = 0.0 #信息增益bestFeature = -1 #最優特征的索引值for i in range(numFeatures): #遍歷所有特征#獲取dataSet的第i個所有特征featList = [example[i] for example in dataSet]uniqueVals = set(featList) #創建set集合{},元素不可重復newEntropy = 0.0 #經驗條件熵for value in uniqueVals: #計算信息增益subDataSet = splitDataSet(dataSet, i, value) #subDataSet劃分后的子集prob = len(subDataSet) / float(len(dataSet)) #計算子集的概率newEntropy += prob * calcShannonEnt(subDataSet) #根據公式計算經驗條件熵infoGain = baseEntropy - newEntropy #信息增益# print("第%d個特征的增益為%.3f" % (i, infoGain)) #打印每個特征的信息增益if (infoGain > bestInfoGain): #計算信息增益bestInfoGain = infoGain #更新信息增益,找到最大的信息增益bestFeature = i #記錄信息增益最大的特征的索引值return bestFeature #返回信息增益最大的特征的索引值""" 函數說明:統計classList中出現此處最多的元素(類標簽)Parameters:classList - 類標簽列表 Returns:sortedClassCount[0][0] - 出現此處最多的元素(類標簽)""" def majorityCnt(classList):classCount = {}for vote in classList: #統計classList中每個元素出現的次數if vote not in classCount.keys():classCount[vote] = 0 classCount[vote] += 1sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True) #根據字典的值降序排序return sortedClassCount[0][0] #返回classList中出現次數最多的元素""" 函數說明:創建決策樹Parameters:dataSet - 訓練數據集labels - 分類屬性標簽featLabels - 存儲選擇的最優特征標簽 Returns:myTree - 決策樹""" def createTree(dataSet, labels, featLabels):classList = [example[-1] for example in dataSet] #取分類標簽(是否放貸:yes or no)if classList.count(classList[0]) == len(classList): #如果類別完全相同則停止繼續劃分return classList[0]if len(dataSet[0]) == 1: #遍歷完所有特征時返回出現次數最多的類標簽return majorityCnt(classList)bestFeat = chooseBestFeatureToSplit(dataSet) #選擇最優特征bestFeatLabel = labels[bestFeat] #最優特征的標簽featLabels.append(bestFeatLabel)myTree = {bestFeatLabel:{}} #根據最優特征的標簽生成樹del(labels[bestFeat]) #刪除已經使用特征標簽featValues = [example[bestFeat] for example in dataSet] #得到訓練集中所有最優特征的屬性值uniqueVals = set(featValues) #去掉重復的屬性值for value in uniqueVals: #遍歷特征,創建決策樹。 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), labels, featLabels)return myTree""" 函數說明:使用決策樹分類Parameters:inputTree - 已經生成的決策樹featLabels - 存儲選擇的最優特征標簽testVec - 測試數據列表,順序對應最優特征標簽 Returns:classLabel - 分類結果""" def classify(inputTree, featLabels, testVec):firstStr = next(iter(inputTree)) #獲取決策樹結點secondDict = inputTree[firstStr] #下一個字典featIndex = featLabels.index(firstStr) for key in secondDict.keys():if testVec[featIndex] == key:if type(secondDict[key]).__name__ == 'dict':classLabel = classify(secondDict[key], featLabels, testVec)else: classLabel = secondDict[key]return classLabelif __name__ == '__main__':dataSet, labels = createDataSet()featLabels = []myTree = createTree(dataSet, labels, featLabels)testVec = [0,1] #測試數據result = classify(myTree, featLabels, testVec)if result == 'yes':print('放貸')if result == 'no':print('不放貸')

2.Sklearn–使用決策樹預測隱形眼鏡類型

Label的類型是age(年齡)、prescript(癥狀)、astigmatic(是否散光)、tearRate(眼淚數量)、class(最終分類)

2.1 Sklearn決策樹代碼

# -*- coding: UTF-8 -*- from sklearn.preprocessing import LabelEncoder, OneHotEncoder from sklearn.externals.six import StringIO from sklearn import tree import pandas as pd import numpy as np import pydotplusif __name__ == '__main__':with open('lenses.txt', 'r') as fr: #加載文件lenses = [inst.strip().split('\t') for inst in fr.readlines()] #處理文件lenses_target = [] #提取每組數據的類別,保存在列表里for each in lenses:lenses_target.append(each[-1])print(lenses_target)lensesLabels = ['age', 'prescript', 'astigmatic', 'tearRate'] #特征標簽 lenses_list = [] #保存lenses數據的臨時列表lenses_dict = {} #保存lenses數據的字典,用于生成pandasfor each_label in lensesLabels: #提取信息,生成字典for each in lenses:lenses_list.append(each[lensesLabels.index(each_label)])lenses_dict[each_label] = lenses_listlenses_list = []# print(lenses_dict) #打印字典信息lenses_pd = pd.DataFrame(lenses_dict) #生成pandas.DataFrame# print(lenses_pd) #打印pandas.DataFramele = LabelEncoder() #創建LabelEncoder()對象,用于序列化 for col in lenses_pd.columns: #序列化lenses_pd[col] = le.fit_transform(lenses_pd[col])# print(lenses_pd) #打印編碼信息clf = tree.DecisionTreeClassifier(max_depth = 4) #創建DecisionTreeClassifier()類clf = clf.fit(lenses_pd.values.tolist(), lenses_target) #使用數據,構建決策樹dot_data = StringIO()tree.export_graphviz(clf, out_file = dot_data, #繪制決策樹feature_names = lenses_pd.keys(),class_names = clf.classes_,filled=True, rounded=True,special_characters=True)graph = pydotplus.graph_from_dot_data(dot_data.getvalue())graph.write_pdf("tree.pdf") #保存繪制好的決策樹,以PDF的形式存儲。

3.總結

決策樹的一些優點:

  • 易于理解和解釋,決策樹可以可視化。
  • 幾乎不需要數據預處理。其他方法經常需要數據標準化,創建虛擬變量和刪除缺失值。決策樹還不支持缺失值。
  • 使用樹的花費(例如預測數據)是訓練數據點(data points)數量的對數。
  • 可以同時處理數值變量和分類變量。其他方法大都適用于分析一種變量的集合。
  • 可以處理多值輸出變量問題。
  • 使用白盒模型。如果一個情況被觀察到,使用邏輯判斷容易表示這種規則。相反,如果是黑盒模型(例如人工神經網絡),結果會非常難解釋。
  • 即使對真實模型來說,假設無效的情況下,也可以較好的適用。

決策樹的一些缺點:

  • 決策樹學習可能創建一個過于復雜的樹,并不能很好的預測數據。也就是過擬合。修剪機制(現在不支持),設置一個葉子節點需要的最小樣本數量,或者數的最大深度,可以避免過擬合。
  • 決策樹可能是不穩定的,因為即使非常小的變異,可能會產生一顆完全不同的樹。這個問題通過decision trees with an ensemble來緩解。
  • 學習一顆最優的決策樹是一個NP-完全問題under several aspects of optimality and even for simple concepts。因此,傳統決策樹算法基于啟發式算法,例如貪婪算法,即每個節點創建最優決策。這些算法不能產生一個全家最優的決策樹。對樣本和特征隨機抽樣可以降低整體效果偏差。
  • 概念難以學習,因為決策樹沒有很好的解釋他們,例如,XOR, parity or multiplexer problems.
  • 如果某些分類占優勢,決策樹將會創建一棵有偏差的樹。因此,建議在訓練之前,先抽樣使樣本均衡。

總結

以上是生活随笔為你收集整理的机器学习实战--决策树算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。