決策樹學習記錄
一。什麽是決策樹?讓我們從養寵物開始說起~
我們可以吧決策樹看作是一個if-then規則的集合。將決策樹轉換成if-then規則的過程是這樣的:
·有決策樹的根節點到葉節點的每一條路徑構建一條規則
·路徑上中間節點的特征對應著規則的條件,也就是葉節點的標簽對應著規則的結論
決策樹的路徑或者其對應的if-then規則集合有一個重要的性質:互斥並且完備。也就是說,每一個實例都被有且僅有一條路徑或者規則所覆蓋。這裏的覆蓋是指實例的特征與路徑上的特征一致
二。決策樹的構建準備工作
使用決策樹做分類的每一個步驟都很重要,首先我們要收集足夠多的數據,如果數據集收集不到位,將會倒數沒有足夠的特征去構建錯誤率低的決策樹。數據特征充足,但是不知道用哪些特征好,也會導致最終無法構建出分類效果好的決策樹。從算法方面看的話,決策樹的構建就是我們的核心內容。
決策樹如何構建呢?通常,這一過程可以概括為3個步驟:特征選擇,決策樹的生成和決策樹的剪枝。
1.特征選擇
特征選擇就是決定用哪個特征來劃分特征空間,其目的在於選取對訓練數據具有分類能力的特征。這樣可以提高決策樹學習的效率。如果利用一個特征進行分類的結果與隨機分類的結果沒有很大的差別,則稱這個特征是沒有分類能力的,經驗上扔掉這些特征對決策樹學習的精度影響不會很大。
那如何來選擇最優的特征來劃分呢?一般而言,隨著劃分過程不斷進行,我們希望決策樹的分支結點所包含的樣本盡可能屬於同一類別,也就是節點的純度越來越高。
下面三個圖表示的是純度越來越低的過程,最後一個表示的是純度最低的狀態。
在實際使用中,我們衡量的常常是不純度。度量不純度的指標有很多種,比如:熵,增益值,基尼指數
這裏使用的是熵,也叫做香濃熵,這個名字來源於信息論之父 克勞德·香濃
1.1香濃熵及計算函數
熵定義為信息的期望值。在信息論與概率論統計中,熵表示隨機變量不確定性的度量
假定當前樣本集合d中有n類樣本,第i類樣本為xi,那麽xi的信息定義為
通過上式,我們可以得到所有類別的信息。為了計算熵,我們需要計算所有類別所有可能值包含的信息期望值---數學期望,通過下面的公式得到
熵越大,d的不純度越低
def calEnt(dataSet): n= dataSet.Shape[0] #數據集總行數 iset = dataSet.iloc[:,-1].value_counts() #標簽的所有類別 p = iset/n #每一類標簽所占比 ent = (-p*np.log2(p)).sum() #計算信息熵 return ent
import numpy as np import pandas as pd def createDataSet(): row_data = {‘no surfacing‘:[1,1,1,0,0],‘flipper‘:[1,1,0,1,1],‘fish‘:[‘yes‘,‘yes‘,‘no‘,‘no‘,‘no‘]} dataSet = pd.DataFrame(row_data) return dataSet
1.2信息增益:
他的計算公式就是父節點的信息熵與旗下所有自己誒單總信息熵之差。需要註意的是,此時計算子節點的總信息熵不能簡單求和,而要求在求和匯總之前進行修正。
假設離散屬性a有V個可能的取值{a1,a2,.....,av},若使用a對樣本數據集D進行劃分,則會產生V個分支節點,其中第V個分支節點包含了D中所有在屬性a上取值為av的樣本,記為Dv,我們可根據信息熵的計算公式計算出Dv的信息熵,再考慮到不同的分支節點包含的樣本數不同,給分支節點賦予權重|Dv|/|D|,這就是所謂的修正。
所以信息增益的計算公式為:
結果:
同樣的方法,可以把第一列的信息增益也算出來,結果為0.17
2.數據集最佳切分函數
劃分數據集的最大準則是選擇最大信息增益,也就是信息下降最快的方向。(也就是最快到達葉節點,最快的生長速度)
""" 函數功能:根據信息增益選擇出最佳數據集切分的列 函數說明: dataSet:原始數據集 返回: axis:數據集最佳切分列的索引 """ def bestSplit(dataSet): baseEnt = calEnt(dataSet) #計算原始熵 bestGain = 0 #初始化信息增益 axis = -1 #初始化最佳切分列,標簽列 for i in range (dataSet.shape[1]-1): #對特征的每一列進行循環 levels = dataSet.iloc[:,i].value_counts().index #提取出當前的所有取值 ents = 0 #初始化子節點的信息熵 for j in levels: #對當前列的每一個取值進行循環 childSet = dataSet[dataSet.iloc[:i] == j] #某一個子節點的dataframe ent = calEnt(childSet) #計算某一個子節點的信息熵 ents += (childSet.shape[0]/dataSet.shape[0])*ent #計算當前列的信息熵 #print(f‘第{i}列的信息熵為{ents}‘) infoGain = baseEnt - ents #計算當前列的信息增益 #print(f‘第{i}列的信息增益為{infoGain}‘) if(infoGain > bestGain): bestGain = infoGain #選擇最大信息增益 axis = i #最大信息增益所在的列的索引 return axis
3.按照給定列來切分數據集
通過最佳切分函數返回最佳切分列的索引,我們就可以根據索引,構建一個按照給定列切分數據集
""" 函數功能:按照給定的列劃分數據集 函數說明: dataSet:原始數據集 value:指定的屬性值 返回: redataSet:按照指定列索引的屬性值切分後的數據集 """ def mysplit(dataSet,axis,value): col = dataSet.columns[axis] redataSet = dataSet.loc[dataSet[col]==value,:].drop(col,axis = 1) return redataSet axis = 0 value = 1 mysplit(dataSet,0,1)
三。遞歸構建決策樹
目前我們已經學習了從數據集構造決策樹算法所需要的子功能模塊,其工作原理如下:得到原始數據集,然後基於最好的屬性值劃分數據集,由於特征值可能多於兩個,因此可能存在大於兩個分支的數據集劃分。第一次劃分之後,數據集被向下傳遞到樹的分支的下一個節點。在這個節點上,我們可以再次劃分數據。因此我們可以采用遞歸的原則處理數據集
決策樹生成算法遞歸地產生決策樹,直到不能繼續下去為止。這樣產生的樹往往對訓練數據的分類很準確,但對未知的測試數據的分類卻沒有那麽準確,即出現過擬合現象。過擬合的原因在於學習時過多地考慮如何提高對訓練數據的正確分類,從而構建出過於復雜的決策樹。解決這個問題的辦法是考慮決策樹的復雜性,對已生成的決策樹進行簡化,也就是常說的剪枝處理。(剪枝又分為自下而上和自上而下兩種方法)
1.ID3算法
構建決策樹的方法有很多,比如ID3,c4.5和Cart,基於《機器學習實戰》這本書,我們選擇ID3算法。
ID3算法的核心是在決策樹各個節點上對應信息增益準則選擇特征,遞歸地構建決策樹。具體方法是:從根節點開始,對節點計算所有可能的特征的信息增益,選擇信息增益最大的特征作為節點的特征,由該特征的不同取值建立子節點;再對子節點遞歸地調用以上方法,構建決策樹;直到所有特征的信息增益均很小或沒有特征可以選擇為止。最後得到一個決策樹。
遞歸結束的條件是:程序遍歷完所有的特征列,或者每個分支下的所有實例都具有相同的分類。如果所有實例都具有相同的分類,則得到一個葉節點。任何到達葉節點的數據必然屬於葉節點的分類,即葉節點裏面必須是標簽。
""" 函數功能:基於最大信息增益切分數據集,遞歸構建決策樹 參數說明: dataSet:原始數據集(最後一列是標簽) 返回: myTree:字典形式的樹 """ def createTree(dataSet): featlist = list(dataSet.columns) #提取出數據集所有的列 classlist = dataSet.iloc[:,-1].value_counts() #獲取最後一列類標簽 #判斷最多標簽數目是否等於數據集行數,或者數據集是否只有一列 if classlist[0] == dataSet.shape[0] or dataSet.shape[1] == 1: return classlist.index[0] #如果是,返回類標簽 axis = bestSplit(dataSet) #確定出當前最佳且分列的索引 bestfeat = featlist[axis] #獲取該索引對應的特征 myTree = {bestfeat:{}} #采用字典嵌套的方式存儲樹信息 del featlist[axis] #刪除當前特征 valuelist = set(dataSet.iloc[:,axis]) #提取最佳切分列所有屬性值 for value in valuelist: #對每一個屬性值遞歸建樹 myTree[bestfeat][value] = createTree(mySplit(dataSet,axis,value)) return myTree
四。決策樹的存儲
構造決策樹是很耗時的任務,即使處理很小的數據集,也要花費幾秒的時間,如果數據集很大,將會耗費很多計算時間。因此為了節省時間,建好樹之後立馬將其保存,後續使用直接調用即可。
這裏使用的是numpy裏面的save()函數,它可以直接把字典形式的數據保存為.npy文件,調用的時候直接使用load()函數即可。
#樹的存儲 np.save(‘myTree.npy‘,myTree) #樹的讀取 read_myTree = np.load(‘myTree.npy‘).item() read_myTree
五。使用決策樹執行分類
def classify(inputTree,labels, testVec): firstStr = next(iter(inputTree)) #獲取決策樹第一個節點 secondDict = inputTree[firstStr] #下一個字典 featIndex = labels.index(firstStr) #第一個節點所在列的索引 for key in secondDict.keys(): if testVec[featIndex] == key: if type(secondDict[key]) == dict : classLabel = classify(secondDict[key], labels, testVec) else: classLabel = secondDict[key] return classLabel
未完待續。。。
總結!!!
決策樹的流程:
1.特征選擇
1.1香濃熵及計算函數
1.2信息增益
2.數據集最佳切分函數
3.遞歸構建決策樹(ID3算法(信息增益),c4.5算法,cart算法)
4.決策樹的存儲(numpy包)
5.使用決策樹執行分裂
6.決策樹可視化(葉子節點數據,樹的深度,繪制節點,標註有向邊屬性值)
決策樹學習記錄