决策树之ID3算法实现(python) [置顶] 怒写一个digit classification(不断更新中)

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

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               
 

决策树之ID3算法实现(python)

分类: python 算法   107人阅读  评论(0)  收藏  举报

决策树的概念其实不难理解,下面一张图是某女生相亲时用到的决策树:

基本上可以理解为:一堆数据,附带若干属性,每一条记录最后都有一个分类(见或者不见),然后根据每种属性可以进行划分(比如年龄是>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)  收藏  举报


最近开始学习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)  收藏  举报


最近开始学习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.   
  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.   

猜你喜欢

转载自blog.csdn.net/hggjgff/article/details/83858158