生活随笔
收集整理的這篇文章主要介紹了
决策树Decision Tree 及实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文基于python逐步實現Decision Tree(決策樹),分為以下幾個步驟:
- 加載數據集
- 熵的計算
- 根據最佳分割feature進行數據分割
- 根據最大信息增益選擇最佳分割feature
- 遞歸構建決策樹
- 樣本分類
關于決策樹的理論方面本文幾乎不講,詳情請google keywords:“決策樹 信息增益 ?熵”
將分別體現于代碼。
本文只建一個.py文件,所有代碼都在這個py里
1.加載數據集
我們選用UCI經典Iris為例
Brief of IRIS:
| Data Set Characteristics:?? | Multivariate | Number of Instances: | 150 | Area: | Life |
| Attribute Characteristics: | Real | Number of Attributes: | 4 | Date Donated | 1988-07-01 |
| Associated Tasks: | Classification | Missing Values? | No | Number of Web Hits: | 533125 |
Code:
[python]?view plaincopy
from?numpy?import?*?? ?? traindata?=?loadtxt("D:\ZJU_Projects\machine?learning\ML_Action\Dataset\Iris.data",delimiter?=?',',usecols?=?(0,1,2,3),dtype?=?float)?? trainlabel?=?loadtxt("D:\ZJU_Projects\machine?learning\ML_Action\Dataset\Iris.data",delimiter?=?',',usecols?=?(range(4,5)),dtype?=?str)?? feaname?=?["#0","#1","#2","#3"]?#?feature?names?of?the?4?attributes?(features)??
Result
:
? ? ? ? ? ?
左圖為實際數據集,四個離散型feature,一個label表示類別(有Iris-setosa, Iris-versicolor,Iris-virginica?三個類)
2.?熵的計算
entropy是香農提出來的(信息論大牛),定義見wiki
注意這里的entropy是H(C|X=xi)而非H(C|X), H(C|X)的計算見第下一個點,還要乘以概率加和
Code:
[python]?view plaincopy
from?math?import?log?? def?calentropy(label):?? ????n?=?label.size??? ?????? ????count?=?{}??? ????for?curlabel?in?label:?? ????????if?curlabel?not?in?count.keys():?? ????????????count[curlabel]?=?0?? ????????count[curlabel]?+=?1?? ????entropy?=?0?? ?????? ????for?key?in?count:?? ????????pxi?=?float(count[key])/n??? ????????entropy?-=?pxi*log(pxi,2)?? ????return?entropy?? ?? ?? ??
Result:
3.?根據最佳分割feature進行數據分割
假定我們已經得到了最佳分割feature,在這里進行分割(最佳feature為splitfea_idx)
第二個函數idx2data是根據splitdata得到的分割數據的兩個index集合返回datal (samples less than pivot), datag(samples greater than pivot), labell, labelg。 這里我們根據所選特征的平均值作為pivot
[python]?view plaincopy
?? def?splitdata(oridata,splitfea_idx):?? ????arg?=?args[splitfea_idx]??? ????idx_less?=?[]??? ????idx_greater?=?[]??? ????n?=?len(oridata)?? ????for?idx?in?range(n):?? ????????d?=?oridata[idx]?? ????????if?d[splitfea_idx]?<?arg:?? ?????????????? ????????????idx_less.append(idx)?? ????????else:?? ????????????idx_greater.append(idx)?? ????return?idx_less,idx_greater?? ?? ?? ?? ?? ?? ?? def?idx2data(oridata,label,splitidx,fea_idx):?? ????idxl?=?splitidx[0]??? ????idxg?=?splitidx[1]??? ????datal?=?[]?? ????datag?=?[]?? ????labell?=?[]?? ????labelg?=?[]?? ????for?i?in?idxl:?? ????????datal.append(append(oridata[i][:fea_idx],oridata[i][fea_idx+1:]))?? ????for?i?in?idxg:?? ????????datag.append(append(oridata[i][:fea_idx],oridata[i][fea_idx+1:]))?? ????labell?=?label[idxl]?? ????labelg?=?label[idxg]?? ????return?datal,datag,labell,labelg??
這里args是參數,決定分裂節點的閾值(每個參數對應一個feature,大于該值分到>branch,小于該值分到<branch),我們可以定義如下:
[python]?view plaincopy
args?=?mean(traindata,axis?=?0)??
測試:按特征2進行分類,得到的less和greater set of indices分別為:
也就是按args[2]進行樣本集分割,<和>args[2]的branch分別有57和93個樣本。
4. 根據最大信息增益選擇最佳分割feature
信息增益為代碼中的info_gain, 注釋中是熵的計算
[python]?view plaincopy
?? def?choosebest_splitnode(oridata,label):?? ????n_fea?=?len(oridata[0])?? ????n?=?len(label)?? ????base_entropy?=?calentropy(label)?? ????best_gain?=?-1?? ????for?fea_i?in?range(n_fea):??? ????????cur_entropy?=?0?? ????????idxset_less,idxset_greater?=?splitdata(oridata,fea_i)?? ????????prob_less?=?float(len(idxset_less))/n?? ????????prob_greater?=?float(len(idxset_greater))/n?? ?????????? ?????????? ????????cur_entropy?+=?prob_less*calentropy(label[idxset_less])?? ????????cur_entropy?+=?prob_greater?*?calentropy(label[idxset_greater])?? ?????????? ????????info_gain?=?base_entropy?-?cur_entropy??? ????????if(info_gain>best_gain):?? ????????????best_gain?=?info_gain?? ????????????best_idx?=?fea_i?? ????return?best_idx???? ?? ?? ??
這里的測試針對所有數據,分裂一次選擇哪個特征呢?
5.?遞歸構建決策樹
詳見code注釋,buildtree遞歸地構建樹。
遞歸終止條件:
①該branch內沒有樣本(subset為空) or
②分割出的所有樣本屬于同一類?or?
③由于每次分割消耗一個feature,當沒有feature的時候停止遞歸,返回當前樣本集中大多數sample的label
[python]?view plaincopy
?? def?buildtree(oridata,?label):?? ????if?label.size==0:??? ????????return?"NULL"?? ????listlabel?=?label.tolist()?? ?????? ????if?listlabel.count(label[0])==label.size:?? ????????return?label[0]?? ?????????? ?????? ????if?len(feanamecopy)==0:?? ????????cnt?=?{}?? ????????for?cur_l?in?label:?? ????????????if?cur_l?not?in?cnt.keys():?? ????????????????cnt[cur_l]?=?0?? ????????????cnt[cur_l]?+=?1?? ????????maxx?=?-1??? ????????for?keys?in?cnt:?? ????????????if?maxx?<?cnt[keys]:?? ????????????????maxx?=?cnt[keys]?? ????????????????maxkey?=?keys?? ????????return?maxkey?? ?????? ????bestsplit_fea?=?choosebest_splitnode(oridata,label)??? ????print?bestsplit_fea,len(oridata[0])?? ????cur_feaname?=?feanamecopy[bestsplit_fea]??? ????print?cur_feaname?? ????nodedict?=?{cur_feaname:{}}??? ????del(feanamecopy[bestsplit_fea])??? ????split_idx?=?splitdata(oridata,bestsplit_fea)??? ????data_less,data_greater,label_less,label_greater?=?idx2data(oridata,label,split_idx,bestsplit_fea)?? ?????? ?????? ????nodedict[cur_feaname]["<"]?=?buildtree(data_less,label_less)?? ????nodedict[cur_feaname][">"]?=?buildtree(data_greater,label_greater)?? ????return?nodedict?? ?????? ?? ?? ??
Result:
mytree就是我們的結果,#1表示當前使用第一個feature做分割,'<'和'>'分別對應less 和 greater的數據。
6. 樣本分類
根據構建出的mytree進行分類,遞歸走分支
[python]?view plaincopy
?? def?classify(mytree,testdata):?? ????if?type(mytree).__name__?!=?'dict':?? ????????return?mytree?? ????fea_name?=?mytree.keys()[0]??? ????fea_idx?=?feaname.index(fea_name)??? ????val?=?testdata[fea_idx]?? ????nextbranch?=?mytree[fea_name]?? ?????? ?????? ????if?val>args[fea_idx]:?? ????????nextbranch?=?nextbranch[">"]?? ????else:?? ????????nextbranch?=?nextbranch["<"]?? ????return?classify(nextbranch,testdata)?? ?? ?? tt?=?traindata[0]?? x?=?classify(mytree,tt)?? print?x??
Result:
為了驗證代碼準確性,我們換一下args參數,把它們都設成0(很小)
args = [0,0,0,0]
建樹和分類的結果如下:
可見沒有小于pivot(0)的項,于是dict中每個<的key對應的value都為空。
本文中全部代碼下載:決策樹python實現
Reference:?Machine Learning in Action
from:?http://blog.csdn.net/abcjennifer/article/details/20905311
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的决策树Decision Tree 及实现的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。