决策树挑选西瓜
目錄
- 決策樹概念
- 信息熵
- CART算法
- ID3算法實現
- 使用sklearn庫實現
- 總結
- 參考
決策樹概念
決策樹(Decision Tree)是在已知各種情況發生概率的基礎上,通過構成決策樹來求取凈現值的期望值大于等于零的概率,評價項目風險,判斷其可行性的決策分析方法,是直觀運用概率分析的一種圖解法。由于這種決策分支畫成圖形很像一棵樹的枝干,故稱決策樹。在機器學習中,決策樹是一個預測模型,他代表的是對象屬性與對象值之間的一種映射關系。Entropy = 系統的凌亂程度,使用算法ID3, C4.5和C5.0生成樹算法使用熵。這一度量是基于信息學理論中熵的概念。
決策樹是一種樹形結構,其中每個內部節點表示一個屬性上的測試,每個分支代表一個測試輸出,每個葉節點代表一種類別。
分類樹(決策樹)是一種十分常用的分類方法。它是一種監督學習,所謂監督學習就是給定一堆樣本,每個樣本都有一組屬性和一個類別,這些類別是事先確定的,那么通過學習得到一個分類器,這個分類器能夠對新出現的對象給出正確的分類。這樣的機器學習就被稱之為監督學習。
信息熵
所謂信息熵,是一個數學上頗為抽象的概念,在這里不妨把信息熵理解成某種特定信息的出現概率。而信息熵和熱力學熵是緊密相關的。根據Charles H. Bennett對Maxwell’s Demon的重新解釋,對信息的銷毀是一個不可逆過程,所以銷毀信息是符合熱力學第二定律的。而產生信息,則是為系統引入負(熱力學)熵的過程。所以信息熵的符號與熱力學熵應該是相反的。
CART算法
Classification And Regression Tree,即分類回歸樹算法,簡稱CART算法,它是決策樹的一種實現,通常決策樹主要有三種實現,分別是ID3算法,CART算法和C4.5算法。 CART算法是一種二分遞歸分割技術,把當前樣本劃分為兩個子樣本,使得生成的每個非葉子結點都有兩個分支,因此CART算法生成的決策樹是結構簡潔的二叉樹。由于CART算法構成的是一個二叉樹,它在每一步的決策時只能是“是”或者“否”,即使一個feature有多個取值,也是把數據分為兩部分。在CART算法中主要分為兩個步驟
(1)將樣本遞歸劃分進行建樹過程
(2)用驗證數據進行剪枝
ID3算法實現
修改數據集為:
新建.ipynb:
代碼:
得到結果:
之后準備計算信息增益:
def splitDataSet(dataSet, axis, value):"""按照給定的特征值,將數據集劃分:param dataSet: 數據集:param axis: 給定特征值的坐標:param value: 給定特征值滿足的條件,只有給定特征值等于這個value的時候才會返回:return:"""# 創建一個新的列表,防止對原來的列表進行修改retDataSet = []# 遍歷整個數據集for featVec in dataSet:# 如果給定特征值等于想要的特征值if featVec[axis] == value:# 將該特征值前面的內容保存起來reducedFeatVec = featVec[:axis]# 將該特征值后面的內容保存起來,所以將給定特征值給去掉了reducedFeatVec.extend(featVec[axis + 1:])# 添加到返回列表中retDataSet.append(reducedFeatVec)return retDataSet確定數據集劃分
def chooseBestFeatureToSplit(dataSet, labels):"""選擇最好的數據集劃分特征,根據信息增益值來計算:param dataSet::return:"""# 得到數據的特征值總數numFeatures = len(dataSet[0]) - 1# 計算出基礎信息熵baseEntropy = calcShannonEnt(dataSet)# 基礎信息增益為0.0bestInfoGain = 0.0# 最好的特征值bestFeature = -1# 對每個特征值進行求信息熵for i in range(numFeatures):# 得到數據集中所有的當前特征值列表featList = [example[i] for example in dataSet]# 將當前特征唯一化,也就是說當前特征值中共有多少種uniqueVals = set(featList)# 新的熵,代表當前特征值的熵newEntropy = 0.0# 遍歷現在有的特征的可能性for value in uniqueVals:# 在全部數據集的當前特征位置上,找到該特征值等于當前值的集合subDataSet = splitDataSet(dataSet=dataSet, axis=i, value=value)# 計算出權重prob = len(subDataSet) / float(len(dataSet))# 計算出當前特征值的熵newEntropy += prob * calcShannonEnt(subDataSet)# 計算出“信息增益”infoGain = baseEntropy - newEntropy#print('當前特征值為:' + labels[i] + ',對應的信息增益值為:' + str(infoGain)+"i等于"+str(i))#如果當前的信息增益比原來的大if infoGain > bestInfoGain:# 最好的信息增益bestInfoGain = infoGain# 新的最好的用來劃分的特征值bestFeature = i#print('信息增益最大的特征為:' + labels[bestFeature])return bestFeature判斷屬性一致性:
def judgeEqualLabels(dataSet):"""判斷數據集的各個屬性集是否完全一致:param dataSet::return:"""# 計算出樣本集中共有多少個屬性,最后一個為類別feature_leng = len(dataSet[0]) - 1# 計算出共有多少個數據data_leng = len(dataSet)# 標記每個屬性中第一個屬性值是什么first_feature = ''# 各個屬性集是否完全一致is_equal = True# 遍歷全部屬性for i in range(feature_leng):# 得到第一個樣本的第i個屬性first_feature = dataSet[0][i]# 與樣本集中所有的數據進行對比,看看在該屬性上是否都一致for _ in range(1, data_leng):# 如果發現不相等的,則直接返回Falseif first_feature != dataSet[_][i]:return Falsereturn is_equal繪制決策樹并打印:
def createTree(dataSet, labels):"""創建決策樹:param dataSet: 數據集:param labels: 特征標簽:return:"""# 拿到所有數據集的分類標簽classList = [example[-1] for example in dataSet]# 統計第一個標簽出現的次數,與總標簽個數比較,如果相等則說明當前列表中全部都是一種標簽,此時停止劃分if classList.count(classList[0]) == len(classList):return classList[0]# 計算第一行有多少個數據,如果只有一個的話說明所有的特征屬性都遍歷完了,剩下的一個就是類別標簽,或者所有的樣本在全部屬性上都一致if len(dataSet[0]) == 1 or judgeEqualLabels(dataSet):# 返回剩下標簽中出現次數較多的那個return majorityCnt(classList)# 選擇最好的劃分特征,得到該特征的下標bestFeat = chooseBestFeatureToSplit(dataSet=dataSet, labels=labels)print(bestFeat)# 得到最好特征的名稱bestFeatLabel = labels[bestFeat]print(bestFeatLabel)# 使用一個字典來存儲樹結構,分叉處為劃分的特征名稱myTree = {bestFeatLabel: {}}# 將本次劃分的特征值從列表中刪除掉del(labels[bestFeat])# 得到當前特征標簽的所有可能值featValues = [example[bestFeat] for example in dataSet]# 唯一化,去掉重復的特征值uniqueVals = set(featValues)# 遍歷所有的特征值for value in uniqueVals:# 得到剩下的特征標簽subLabels = labels[:]subTree = createTree(splitDataSet(dataSet=dataSet, axis=bestFeat, value=value), subLabels)# 遞歸調用,將數據集中該特征等于當前特征值的所有數據劃分到當前節點下,遞歸調用時需要先將當前的特征去除掉myTree[bestFeatLabel][value] = subTreereturn myTree mytree=createTree(data,labels) print(mytree)
之后繪制可視化樹:
添加標簽重新繪制:
def makeTreeFull(myTree, labels_full, default):"""將樹中的不存在的特征標簽進行補全,補全為父節點中出現最多的類別:param myTree: 生成的樹:param labels_full: 特征的全部標簽:param parentClass: 父節點中所含最多的類別:param default: 如果缺失標簽中父節點無法判斷類別則使用該值:return:"""# 這里所說的父節點就是當前根節點,把當前根節點下不存在的特征標簽作為子節點# 拿到當前的根節點root_key = list(myTree.keys())[0]# 拿到根節點下的所有分類,可能是子節點(好瓜or壞瓜)也可能不是子節點(再次劃分的屬性值)sub_tree = myTree[root_key]# 如果是葉子節點就結束if isinstance(sub_tree, str):return# 找到使用當前節點分類下最多的種類,該分類結果作為新特征標簽的分類,如:色澤下面沒有淺白則用色澤中有的青綠分類作為淺白的分類root_class = []# 把已經分好類的結果記錄下來for sub_key in sub_tree.keys():if isinstance(sub_tree[sub_key], str):root_class.append(sub_tree[sub_key])# 找到本層出現最多的類別,可能會出現相同的情況取其一if len(root_class):most_class = collections.Counter(root_class).most_common(1)[0][0]else:most_class = None# 當前節點下沒有已經分類好的屬性# print(most_class)# 循環遍歷全部特征標簽,將不存在標簽添加進去for label in labels_full[root_key]:if label not in sub_tree.keys():if most_class is not None:sub_tree[label] = most_classelse:sub_tree[label] = default# 遞歸處理for sub_key in sub_tree.keys():if isinstance(sub_tree[sub_key], dict):makeTreeFull(myTree=sub_tree[sub_key], labels_full=labels_full, default=default) makeTreeFull(mytree,labels_full,default='未知') createPlot(mytree)得到:
使用sklearn庫實現
導入包并讀取:
# 導入包 import pandas as pd from sklearn import tree import graphviz df = pd.read_csv('..\\source\\watermalon.txt') df.head(10)
特征值轉為數字:
得到:
總結
本次實驗學習了解了決策樹,信息熵等的概念,對于ID3,C4.5和CART算法的各優缺點有更多的認知。
參考
決策樹3:基尼指數–Gini index(CART)
西瓜書中的決策樹算法實現(ID3)
總結
- 上一篇: 三星 android截屏快捷键,三星C5
- 下一篇: 如何关掉visual studio 20