1. 程式人生 > >決策樹之ID3演算法實現(python) [置頂] 怒寫一個digit classification(不斷更新中)

決策樹之ID3演算法實現(python) [置頂] 怒寫一個digit classification(不斷更新中)

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                 

決策樹之ID3演算法實現(python)

分類: python 演算法   107人閱讀
  評論(0)  收藏  舉報 演算法 decision tree machine learning

決策樹的概念其實不難理解,下面一張圖是某女生相親時用到的決策樹:

基本上可以理解為:一堆資料,附帶若干屬性,每一條記錄最後都有一個分類(見或者不見),然後根據每種屬性可以進行劃分(比如年齡是>30還是<=30),這樣構造出來的一棵樹就是我們所謂的決策樹了,決策的規則都在節點上,通俗易懂,分類效果好。

那為什麼跟節點要用年齡,而不是長相?這裡我們在實現決策樹的時候採用的是ID3演算法,在選擇哪個屬性作為節點的時候採用資訊理論原理,所謂的資訊增益。資訊增益指原有資料集的熵-按某個屬性分類後資料集的熵。資訊增益越大越好(說明按某個屬性分類後比較純),我們會選擇使得資訊增益最大的那個屬性作為當層節點的標記,再進行遞迴構造決策樹。

首先我們構造資料集:

[python]  view plain copy
  1. def createDataSet():  
  2.     dataSet = [[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]  
  3.     features = ['no surfacing'
    ,'flippers']  
  4.     return dataSet,features  


構造決策樹:(採用python字典來遞迴構造,一些程式碼看看就能看懂)

[python]  view plain copy
  1. def treeGrowth(dataSet,features):  
  2.     classList = [example[-1for example in dataSet]  
  3.     if classList.count(classList[0])==len(classList):  
  4.         return classList[0]  
  5.     if len(dataSet[0])==1:# no more features  
  6.         return classify(classList)  
  7.   
  8.     bestFeat = findBestSplit(dataSet)#bestFeat is the index of best feature  
  9.     bestFeatLabel = features[bestFeat]  
  10.     myTree = {bestFeatLabel:{}}  
  11.     featValues = [example[bestFeat] for example in dataSet]  
  12.     uniqueFeatValues = set(featValues)  
  13.     del (features[bestFeat])  
  14.     for values in uniqueFeatValues:  
  15.         subDataSet = splitDataSet(dataSet,bestFeat,values)  
  16.         myTree[bestFeatLabel][values] = treeGrowth(subDataSet,features)  
  17.     return myTree  

 

當沒有多餘的feature,但是剩下的樣本不完全是一樣的類別是,採用出現次數多的那個類別:

[python]  view plain copy
  1. def classify(classList):  
  2.     ''''' 
  3.     find the most in the set 
  4.     '''  
  5.     classCount = {}  
  6.     for vote in classList:  
  7.         if vote not in classCount.keys():  
  8.             classCount[vote] = 0  
  9.         classCount[vote] += 1  
  10.     sortedClassCount = sorted(classCount.iteritems(),key = operator.itemgetter(1),reverse = True)  
  11.     return sortedClassCount[0][0]  


 

尋找用於分裂的最佳屬性:(遍歷所有屬性,算資訊增益)

[python]  view plain copy
  1. def findBestSplit(dataset):  
  2.     numFeatures = len(dataset[0])-1  
  3.     baseEntropy = calcShannonEnt(dataset)  
  4.     bestInfoGain = 0.0  
  5.     bestFeat = -1  
  6.     for i in range(numFeatures):  
  7.         featValues = [example[i] for example in dataset]  
  8.         uniqueFeatValues = set(featValues)  
  9.         newEntropy = 0.0  
  10.         for val in uniqueFeatValues:  
  11.             subDataSet = splitDataSet(dataset,i,val)  
  12.             prob = len(subDataSet)/float(len(dataset))  
  13.             newEntropy += prob*calcShannonEnt(subDataSet)  
  14.         if(baseEntropy - newEntropy)>bestInfoGain:  
  15.             bestInfoGain = baseEntropy - newEntropy  
  16.             bestFeat = i  
  17.     return bestFeat  

 

選擇完分裂屬性後,就行資料集的分裂:

[python]  view plain copy
  1. def splitDataSet(dataset,feat,values):  
  2.     retDataSet = []  
  3.     for featVec in dataset:  
  4.         if featVec[feat] == values:  
  5.             reducedFeatVec = featVec[:feat]  
  6.             reducedFeatVec.extend(featVec[feat+1:])  
  7.             retDataSet.append(reducedFeatVec)  
  8.     return retDataSet  


計算資料集的熵:

[python]  view plain copy
  1. def calcShannonEnt(dataset):  
  2.     numEntries = len(dataset)  
  3.     labelCounts = {}  
  4.     for featVec in dataset:  
  5.         currentLabel = featVec[-1]  
  6.         if currentLabel not in labelCounts.keys():  
  7.             labelCounts[currentLabel] = 0  
  8.         labelCounts[currentLabel] += 1  
  9.     shannonEnt = 0.0  
  10.   
  11.     for key in labelCounts:  
  12.         prob = float(labelCounts[key])/numEntries  
  13.         if prob != 0:  
  14.             shannonEnt -= prob*log(prob,2)  
  15.     return shannonEnt  


下面根據上面構造的決策樹進行資料的分類:

[python]  view plain copy
  1. def predict(tree,newObject):  
  2.     while isinstance(tree,dict):  
  3.         key = tree.keys()[0]  
  4.         tree = tree[key][newObject[key]]  
  5.     return tree  
  6.   
  7. if __name__ == '__main__':  
  8.     dataset,features = createDataSet()  
  9.     tree = treeGrowth(dataset,features)  
  10.     print tree  
  11.     print predict(tree,{'no surfacing':1,'flippers':1})  
  12.     print predict(tree,{'no surfacing':1,'flippers':0})  
  13.     print predict(tree,{'no surfacing':0,'flippers':1})  
  14.     print predict(tree,{'no surfacing':0,'flippers':0})  


結果如下:

決策樹是這樣的:

{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

四個預測:

yes
no
no
no

和給定的資料集分類一樣(預測的資料是從給定資料集裡面抽取的,當然一般資料多的話,會拿一部分做訓練資料,剩餘的做測試資料)

 

歸納一下ID3的優缺點:

優點:實現比較簡單,產生的規則如果用圖表示出來的話,清晰易懂,分類效果好

缺點:只能處理屬性值離散的情況(連續的用C4.5),在選擇最佳分離屬性的時候容易選擇那些屬性值多的一些屬性。


[置頂] 怒寫一個digit classification(不斷更新中)

分類: python 演算法   151人閱讀  評論(0)  收藏  舉報 digit classification knn svm naive bayes decision tree


最近開始學習machine learning方面的內容,大致瀏覽了一遍《machine learning in action》一書,大概瞭解了一些常用的演算法如knn,svm等具體式幹啥的。

在kaggle上看到一個練手的專案:digit classification,又有良好的資料,於是打算用這個專案把各種演算法都跑一遍,加深自己對各演算法的研究,該文會不斷更新。。。。。。


我們的資料集是mnist,連結:http://yann.lecun.com/exdb/mnist/

mnist的結構如下,選取train-images

TRAINING SET IMAGE FILE (train-images-idx3-ubyte):

[offset] [type]          [value]          [description] 
0000     32 bit integer  0x00000803(2051) magic number 
0004     32 bit integer  60000            number of images 
0008     32 bit integer  28               number of rows 
0012     32 bit integer  28               number of columns 
0016     unsigned byte   ??               pixel 
0017     unsigned byte   ??               pixel 
........ 
xxxx     unsigned byte   ??               pixel

吶,由於智商比較拙急,看了這個形式居然沒看明白,所以特此寫出要注意的點,送給同樣沒看明白的童鞋。

首先該資料是以二進位制儲存的,我們讀取的時候要以'rb'方式讀取,其次,真正的資料只有[value]這一項,其他的[type]等只是來描述的,並不真正在資料檔案裡面。

由offset我們可以看出真正的pixel式從16開始的,一個int 32位元組,所以在讀取pixel之前我們要讀取4個 32 bit integer,也就是magic number,number of images,number of rows,number of columns,讀取二進位制檔案用struct比較方便,struct.unpack_from('>IIII',buf,index)表示按照大端方式讀取4個int.

雖然資料集網站寫著“Users of Intel processors and other low-endian machines must flip the bytes of the header.”,而我的電腦就是intel處理器,但是我嘗試了一把還是得用大端方式讀,讀出來才是“2051 60000 28 28”,用小端方式讀取就不正確了,這個小小實驗一把就行。


下面先把資料檔案直觀的表現出來,用matplotlib把二進位制檔案用影象表現出來。具體如下:


[python]  view plain copy
  1. # -*- coding:utf-8  
  2. import numpy as np   
  3. import struct  
  4. import matplotlib.pyplot as plt   
  5.   
  6. filename = 'train-images.idx3-ubyte'  
  7. binfile = open(filename,'rb')#以二進位制方式開啟  
  8. buf = binfile.read()  
  9.   
  10. index = 0  
  11. magic, numImages, numRows, numColums = struct.unpack_from('>IIII',buf,index)#讀取4個32 int  
  12. print magic,' ',numImages,' ',numRows,' ',numColums  
  13. index += struct.calcsize('>IIII')  
  14.   
  15.   
  16. im = struct.unpack_from('>784B',buf,index)#每張圖是28*28=784Byte,這裡只顯示第一張圖  
  17. index += struct.calcsize('>784B' )  
  18.   

決策樹的概念其實不難理解,下面一張圖是某女生相親時用到的決策樹:

基本上可以理解為:一堆資料,附帶若干屬性,每一條記錄最後都有一個分類(見或者不見),然後根據每種屬性可以進行劃分(比如年齡是>30還是<=30),這樣構造出來的一棵樹就是我們所謂的決策樹了,決策的規則都在節點上,通俗易懂,分類效果好。

那為什麼跟節點要用年齡,而不是長相?這裡我們在實現決策樹的時候採用的是ID3演算法,在選擇哪個屬性作為節點的時候採用資訊理論原理,所謂的資訊增益。資訊增益指原有資料集的熵-按某個屬性分類後資料集的熵。資訊增益越大越好(說明按某個屬性分類後比較純),我們會選擇使得資訊增益最大的那個屬性作為當層節點的標記,再進行遞迴構造決策樹。

首先我們構造資料集:

[python]  view plain copy
  1. def createDataSet():  
  2.     dataSet = [[1,1,'yes'],[1,1,'yes'],[1,0,'no'],[0,1,'no'],[0,1,'no']]  
  3.     features = ['no surfacing','flippers']  
  4.     return dataSet,features  


構造決策樹:(採用python字典來遞迴構造,一些程式碼看看就能看懂)

[python]  view plain copy
  1. def treeGrowth(dataSet,features):  
  2.     classList = [example[-1for example in dataSet]  
  3.     if classList.count(classList[0])==len(classList):  
  4.         return classList[0]  
  5.     if len(dataSet[0])==1:# no more features  
  6.         return classify(classList)  
  7.   
  8.     bestFeat = findBestSplit(dataSet)#bestFeat is the index of best feature  
  9.     bestFeatLabel = features[bestFeat]  
  10.     myTree = {bestFeatLabel:{}}  
  11.     featValues = [example[bestFeat] for example in dataSet]  
  12.     uniqueFeatValues = set(featValues)  
  13.     del (features[bestFeat])  
  14.     for values in uniqueFeatValues:  
  15.         subDataSet = splitDataSet(dataSet,bestFeat,values)  
  16.         myTree[bestFeatLabel][values] = treeGrowth(subDataSet,features)  
  17.     return myTree  

 

當沒有多餘的feature,但是剩下的樣本不完全是一樣的類別是,採用出現次數多的那個類別:

[python]  view plain copy
  1. def classify(classList):  
  2.     ''''' 
  3.     find the most in the set 
  4.     '''  
  5.     classCount = {}  
  6.     for vote in classList:  
  7.         if vote not in classCount.keys():  
  8.             classCount[vote] = 0  
  9.         classCount[vote] += 1  
  10.     sortedClassCount = sorted(classCount.iteritems(),key = operator.itemgetter(1),reverse = True)  
  11.     return sortedClassCount[0][0]  


 

尋找用於分裂的最佳屬性:(遍歷所有屬性,算資訊增益)

[python]  view plain copy
  1. def findBestSplit(dataset):  
  2.     numFeatures = len(dataset[0])-1  
  3.     baseEntropy = calcShannonEnt(dataset)  
  4.     bestInfoGain = 0.0  
  5.     bestFeat = -1  
  6.     for i in range(numFeatures):  
  7.         featValues = [example[i] for example in dataset]  
  8.         uniqueFeatValues = set(featValues)  
  9.         newEntropy = 0.0  
  10.         for val in uniqueFeatValues:  
  11.             subDataSet = splitDataSet(dataset,i,val)  
  12.             prob = len(subDataSet)/float(len(dataset))  
  13.             newEntropy += prob*calcShannonEnt(subDataSet)  
  14.         if(baseEntropy - newEntropy)>bestInfoGain:  
  15.             bestInfoGain = baseEntropy - newEntropy  
  16.             bestFeat = i  
  17.     return bestFeat  

 

選擇完分裂屬性後,就行資料集的分裂:

[python]  view plain copy
  1. def splitDataSet(dataset,feat,values):  
  2.     retDataSet = []  
  3.     for featVec in dataset:  
  4.         if featVec[feat] == values:  
  5.             reducedFeatVec = featVec[:feat]  
  6.             reducedFeatVec.extend(featVec[feat+1:])  
  7.             retDataSet.append(reducedFeatVec)  
  8.     return retDataSet  


計算資料集的熵:

[python]  view plain copy
  1. def calcShannonEnt(dataset):  
  2.     numEntries = len(dataset)  
  3.     labelCounts = {}  
  4.     for featVec in dataset:  
  5.         currentLabel = featVec[-1]  
  6.         if currentLabel not in labelCounts.keys():  
  7.             labelCounts[currentLabel] = 0  
  8.         labelCounts[currentLabel] += 1  
  9.     shannonEnt = 0.0  
  10.   
  11.     for key in labelCounts:  
  12.         prob = float(labelCounts[key])/numEntries  
  13.         if prob != 0:  
  14.             shannonEnt -= prob*log(prob,2)  
  15.     return shannonEnt  


下面根據上面構造的決策樹進行資料的分類:

[python]  view plain copy
  1. def predict(tree,newObject):  
  2.     while isinstance(tree,dict):  
  3.         key = tree.keys()[0]  
  4.         tree = tree[key][newObject[key]]  
  5.     return tree  
  6.   
  7. if __name__ == '__main__':  
  8.     dataset,features = createDataSet()  
  9.     tree = treeGrowth(dataset,features)  
  10.     print tree  
  11.     print predict(tree,{'no surfacing':1,'flippers':1})  
  12.     print predict(tree,{'no surfacing':1,'flippers':0})  
  13.     print predict(tree,{'no surfacing':0,'flippers':1})  
  14.     print predict(tree,{'no surfacing':0,'flippers':0})  


結果如下:

決策樹是這樣的:

{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

四個預測:

yes
no
no
no

和給定的資料集分類一樣(預測的資料是從給定資料集裡面抽取的,當然一般資料多的話,會拿一部分做訓練資料,剩餘的做測試資料)

 

歸納一下ID3的優缺點:

優點:實現比較簡單,產生的規則如果用圖表示出來的話,清晰易懂,分類效果好

缺點:只能處理屬性值離散的情況(連續的用C4.5),在選擇最佳分離屬性的時候容易選擇那些屬性值多的一些屬性。


[置頂] 怒寫一個digit classification(不斷更新中)

分類: python 演算法   151人閱讀  評論(0)  收藏  舉報 digit classification knn svm naive bayes decision tree


最近開始學習machine learning方面的內容,大致瀏覽了一遍《machine learning in action》一書,大概瞭解了一些常用的演算法如knn,svm等具體式幹啥的。

在kaggle上看到一個練手的專案:digit classification,又有良好的資料,於是打算用這個專案把各種演算法都跑一遍,加深自己對各演算法的研究,該文會不斷更新。。。。。。


我們的資料集是mnist,連結:http://yann.lecun.com/exdb/mnist/

mnist的結構如下,選取train-images

TRAINING SET IMAGE FILE (train-images-idx3-ubyte):

[offset] [type]          [value]          [description] 
0000     32 bit integer  0x00000803(2051) magic number 
0004     32 bit integer  60000            number of images 
0008     32 bit integer  28               number of rows 
0012     32 bit integer  28               number of columns 
0016     unsigned byte   ??               pixel 
0017     unsigned byte   ??               pixel 
........ 
xxxx     unsigned byte   ??               pixel

吶,由於智商比較拙急,看了這個形式居然沒看明白,所以特此寫出要注意的點,送給同樣沒看明白的童鞋。

首先該資料是以二進位制儲存的,我們讀取的時候要以'rb'方式讀取,其次,真正的資料只有[value]這一項,其他的[type]等只是來描述的,並不真正在資料檔案裡面。

由offset我們可以看出真正的pixel式從16開始的,一個int 32位元組,所以在讀取pixel之前我們要讀取4個 32 bit integer,也就是magic number,number of images,number of rows,number of columns,讀取二進位制檔案用struct比較方便,struct.unpack_from('>IIII',buf,index)表示按照大端方式讀取4個int.

雖然資料集網站寫著“Users of Intel processors and other low-endian machines must flip the bytes of the header.”,而我的電腦就是intel處理器,但是我嘗試了一把還是得用大端方式讀,讀出來才是“2051 60000 28 28”,用小端方式讀取就不正確了,這個小小實驗一把就行。


下面先把資料檔案直觀的表現出來,用matplotlib把二進位制檔案用影象表現出來。具體如下:


[python]  view plain copy
  1. # -*- coding:utf-8  
  2. import numpy as np   
  3. import struct  
  4. import matplotlib.pyplot as plt   
  5.   
  6. filename = 'train-images.idx3-ubyte'  
  7. binfile = open(filename,'rb')#以二進位制方式開啟  
  8. buf = binfile.read()  
  9.   
  10. index = 0  
  11. magic, numImages, numRows, numColums = struct.unpack_from('>IIII',buf,index)#讀取4個32 int  
  12. print magic,' ',numImages,' ',numRows,' ',numColums  
  13. index += struct.calcsize('>IIII')  
  14.   
  15.   
  16. im = struct.unpack_from('>784B',buf,index)#每張圖是28*28=784Byte,這裡只顯示第一張圖  
  17. index += struct.calcsize('>784B' )  
  18.   


最近開始學習machine learning方面的內容,大致瀏覽了一遍《machine learning in action》一書,大概瞭解了一些常用的演算法如knn,svm等具體式幹啥的。

在kaggle上看到一個練手的專案:digit classification,又有良好的資料,於是打算用這個專案把各種演算法都跑一遍,加深自己對各演算法的研究,該文會不斷更新。。。。。。


我們的資料集是mnist,連結:http://yann.lecun.com/exdb/mnist/

mnist的結構如下,選取train-images

TRAINING SET IMAGE FILE (train-images-idx3-ubyte):

[offset] [type]          [value]          [description] 
0000     32 bit integer  0x00000803(2051) magic number 
0004     32 bit integer  60000            number of images 
0008     32 bit integer  28               number of rows 
0012     32 bit integer  28               number of columns 
0016     unsigned byte   ??               pixel 
0017     unsigned byte   ??               pixel 
........ 
xxxx     unsigned byte   ??               pixel

吶,由於智商比較拙急,看了這個形式居然沒看明白,所以特此寫出要注意的點,送給同樣沒看明白的童鞋。

首先該資料是以二進位制儲存的,我們讀取的時候要以'rb'方式讀取,其次,真正的資料只有[value]這一項,其他的[type]等只是來描述的,並不真正在資料檔案裡面。

由offset我們可以看出真正的pixel式從16開始的,一個int 32位元組,所以在讀取pixel之前我們要讀取4個 32 bit integer,也就是magic number,number of images,number of rows,number of columns,讀取二進位制檔案用struct比較方便,struct.unpack_from('>IIII',buf,index)表示按照大端方式讀取4個int.

雖然資料集網站寫著“Users of Intel processors and other low-endian machines must flip the bytes of the header.”,而我的電腦就是intel處理器,但是我嘗試了一把還是得用大端方式讀,讀出來才是“2051 60000 28 28”,用小端方式讀取就不正確了,這個小小實驗一把就行。


下面先把資料檔案直觀的表現出來,用matplotlib把二進位制檔案用影象表現出來。具體如下:


[python]  view plain copy
  1. # -*- coding:utf-8  
  2. import numpy as np   
  3. import struct  
  4. import matplotlib.pyplot as plt   
  5.   
  6. filename = 'train-images.idx3-ubyte'  
  7. binfile = open(filename,'rb')#以二進位制方式開啟  
  8. buf = binfile.read()  
  9.  &n